001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.camel.support;
018
019 import java.io.InputStream;
020 import java.util.Properties;
021 import java.util.concurrent.atomic.AtomicBoolean;
022
023 import org.apache.camel.ServiceStatus;
024 import org.apache.camel.StatefulService;
025 import org.slf4j.Logger;
026 import org.slf4j.LoggerFactory;
027
028 /**
029 * A useful base class which ensures that a service is only initialized once and
030 * provides some helper methods for enquiring of its status.
031 * <p/>
032 * Implementations can extend this base class and implement {@link org.apache.camel.SuspendableService}
033 * in case they support suspend/resume.
034 *
035 * @version
036 */
037 public abstract class ServiceSupport implements StatefulService {
038 private static final transient Logger LOG = LoggerFactory.getLogger(ServiceSupport.class);
039
040 protected final AtomicBoolean started = new AtomicBoolean(false);
041 protected final AtomicBoolean starting = new AtomicBoolean(false);
042 protected final AtomicBoolean stopping = new AtomicBoolean(false);
043 protected final AtomicBoolean stopped = new AtomicBoolean(false);
044 protected final AtomicBoolean suspending = new AtomicBoolean(false);
045 protected final AtomicBoolean suspended = new AtomicBoolean(false);
046 protected final AtomicBoolean shuttingdown = new AtomicBoolean(false);
047 protected final AtomicBoolean shutdown = new AtomicBoolean(false);
048
049 private String version;
050
051 public void start() throws Exception {
052 if (isStarting() || isStarted()) {
053 // only start service if not already started
054 LOG.trace("Service already started");
055 return;
056 }
057 if (starting.compareAndSet(false, true)) {
058 LOG.trace("Starting service");
059 try {
060 doStart();
061 started.set(true);
062 starting.set(false);
063 stopping.set(false);
064 stopped.set(false);
065 suspending.set(false);
066 suspended.set(false);
067 shutdown.set(false);
068 shuttingdown.set(false);
069 } catch (Exception e) {
070 try {
071 stop();
072 } catch (Exception e2) {
073 // Ignore exceptions as we want to show the original exception
074 }
075 throw e;
076 }
077 }
078 }
079
080 public void stop() throws Exception {
081 if (isStopped()) {
082 LOG.trace("Service already stopped");
083 return;
084 }
085 if (isStopping()) {
086 LOG.trace("Service already stopping");
087 return;
088 }
089 stopping.set(true);
090 try {
091 doStop();
092 } finally {
093 stopping.set(false);
094 stopped.set(true);
095 starting.set(false);
096 started.set(false);
097 suspending.set(false);
098 suspended.set(false);
099 shutdown.set(false);
100 shuttingdown.set(false);
101 }
102 }
103
104 @Override
105 public void suspend() throws Exception {
106 if (!suspended.get()) {
107 if (suspending.compareAndSet(false, true)) {
108 try {
109 starting.set(false);
110 stopping.set(false);
111 doSuspend();
112 } finally {
113 stopped.set(false);
114 stopping.set(false);
115 starting.set(false);
116 started.set(false);
117 suspending.set(false);
118 suspended.set(true);
119 shutdown.set(false);
120 shuttingdown.set(false);
121 }
122 }
123 }
124 }
125
126 @Override
127 public void resume() throws Exception {
128 if (suspended.get()) {
129 if (starting.compareAndSet(false, true)) {
130 try {
131 doResume();
132 } finally {
133 started.set(true);
134 starting.set(false);
135 stopping.set(false);
136 stopped.set(false);
137 suspending.set(false);
138 suspended.set(false);
139 shutdown.set(false);
140 shuttingdown.set(false);
141 }
142 }
143 }
144 }
145
146 @Override
147 public void shutdown() throws Exception {
148 // ensure we are stopped first
149 stop();
150
151 if (shuttingdown.compareAndSet(false, true)) {
152 try {
153 doShutdown();
154 } finally {
155 // shutdown is also stopped so only set shutdown flags
156 shutdown.set(true);
157 shuttingdown.set(false);
158 }
159 }
160 }
161
162 @Override
163 public ServiceStatus getStatus() {
164 // we should check the ---ing states first, as this indicate the state is in the middle of doing that
165 if (isStarting()) {
166 return ServiceStatus.Starting;
167 }
168 if (isStopping()) {
169 return ServiceStatus.Stopping;
170 }
171 if (isSuspending()) {
172 return ServiceStatus.Suspending;
173 }
174
175 // then check for the regular states
176 if (isStarted()) {
177 return ServiceStatus.Started;
178 }
179 if (isStopped()) {
180 return ServiceStatus.Stopped;
181 }
182 if (isSuspended()) {
183 return ServiceStatus.Suspended;
184 }
185
186 // use stopped as fallback
187 return ServiceStatus.Stopped;
188 }
189
190 @Override
191 public boolean isStarted() {
192 return started.get();
193 }
194
195 @Override
196 public boolean isStarting() {
197 return starting.get();
198 }
199
200 @Override
201 public boolean isStopping() {
202 return stopping.get();
203 }
204
205 @Override
206 public boolean isStopped() {
207 return stopped.get();
208 }
209
210 @Override
211 public boolean isSuspending() {
212 return suspending.get();
213 }
214
215 @Override
216 public boolean isSuspended() {
217 return suspended.get();
218 }
219
220 @Override
221 public boolean isRunAllowed() {
222 return !(stopping.get() || stopped.get());
223 }
224
225 /**
226 * Implementations override this method to support customized start/stop.
227 * <p/>
228 * <b>Important: </b> See {@link #doStop()} for more details.
229 *
230 * @see #doStop()
231 */
232 protected abstract void doStart() throws Exception;
233
234 /**
235 * Implementations override this method to support customized start/stop.
236 * <p/>
237 * <b>Important:</b> Camel will invoke this {@link #doStop()} method when
238 * the service is being stopped. This method will <b>also</b> be invoked
239 * if the service is still in <i>uninitialized</i> state (eg has not
240 * been started). The method is <b>always</b> called to allow the service
241 * to do custom logic when the service is being stopped, such as when
242 * {@link org.apache.camel.CamelContext} is shutting down.
243 *
244 * @see #doStart()
245 */
246 protected abstract void doStop() throws Exception;
247
248 /**
249 * Implementations override this method to support customized suspend/resume.
250 */
251 protected void doSuspend() throws Exception {
252 }
253
254 /**
255 * Implementations override this method to support customized suspend/resume.
256 */
257 protected void doResume() throws Exception {
258 }
259
260 /**
261 * Implementations override this method to perform customized shutdown.
262 */
263 protected void doShutdown() throws Exception {
264 // noop
265 }
266
267 @Override
268 public synchronized String getVersion() {
269 if (version != null) {
270 return version;
271 }
272
273 // try to load from maven properties first
274 try {
275 Properties p = new Properties();
276 InputStream is = getClass().getResourceAsStream("/META-INF/maven/org.apache.camel/camel-core/pom.properties");
277 if (is != null) {
278 p.load(is);
279 version = p.getProperty("version", "");
280 }
281 } catch (Exception e) {
282 // ignore
283 }
284
285 // fallback to using Java API
286 if (version == null) {
287 Package aPackage = getClass().getPackage();
288 if (aPackage != null) {
289 version = aPackage.getImplementationVersion();
290 if (version == null) {
291 version = aPackage.getSpecificationVersion();
292 }
293 }
294 }
295
296 if (version == null) {
297 // we could not compute the version so use a blank
298 version = "";
299 }
300
301 return version;
302 }
303 }