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.impl;
018
019 import java.util.LinkedHashSet;
020 import java.util.Set;
021 import java.util.concurrent.atomic.AtomicBoolean;
022
023 import org.apache.camel.Service;
024 import org.apache.camel.ServiceStatus;
025 import org.apache.camel.ShutdownableService;
026 import org.apache.camel.util.ObjectHelper;
027 import org.apache.camel.util.ServiceHelper;
028
029 /**
030 * A useful base class which ensures that a service is only initialized once and
031 * provides some helper methods for enquiring of its status.
032 * <p/>
033 * Implementations can extend this base class and implement {@link org.apache.camel.SuspendableService}
034 * in case they support suspend/resume.
035 *
036 * @version $Revision: 20684 $
037 */
038 public abstract class ServiceSupport implements Service, ShutdownableService {
039
040 private final AtomicBoolean started = new AtomicBoolean(false);
041 private final AtomicBoolean starting = new AtomicBoolean(false);
042 private final AtomicBoolean stopping = new AtomicBoolean(false);
043 private final AtomicBoolean stopped = new AtomicBoolean(false);
044 private final AtomicBoolean suspending = new AtomicBoolean(false);
045 private final AtomicBoolean suspended = new AtomicBoolean(false);
046 private final AtomicBoolean shuttingdown = new AtomicBoolean(false);
047 private final AtomicBoolean shutdown = new AtomicBoolean(false);
048 private Set<Object> childServices;
049 private String version;
050
051 public void start() throws Exception {
052 start(true);
053 }
054
055 public void start(boolean startChildren) throws Exception {
056 if (!started.get()) {
057 if (starting.compareAndSet(false, true)) {
058 boolean childrenStarted = false;
059 Exception ex = null;
060 try {
061 if (childServices != null && startChildren) {
062 ServiceHelper.startServices(childServices);
063 }
064 childrenStarted = true;
065 doStart();
066 } catch (Exception e) {
067 ex = e;
068 } finally {
069 if (ex != null) {
070 try {
071 stop(childrenStarted);
072 } catch (Exception e) {
073 // Ignore exceptions as we want to show the original exception
074 }
075 throw ex;
076 } else {
077 started.set(true);
078 starting.set(false);
079 stopping.set(false);
080 stopped.set(false);
081 suspending.set(false);
082 suspended.set(false);
083 shutdown.set(false);
084 shuttingdown.set(false);
085 }
086 }
087 }
088 }
089 }
090
091 private void stop(boolean childrenStarted) throws Exception {
092 if (stopping.compareAndSet(false, true)) {
093 try {
094 try {
095 starting.set(false);
096 suspending.set(false);
097 if (childrenStarted) {
098 doStop();
099 }
100 } finally {
101 started.set(false);
102 suspended.set(false);
103 if (childServices != null) {
104 ServiceHelper.stopServices(childServices);
105 }
106 }
107 } finally {
108 stopped.set(true);
109 stopping.set(false);
110 starting.set(false);
111 started.set(false);
112 suspending.set(false);
113 suspended.set(false);
114 shutdown.set(false);
115 shuttingdown.set(false);
116 }
117 }
118 }
119
120 public void stop() throws Exception {
121 if (!stopped.get()) {
122 stop(true);
123 }
124 }
125
126 public void suspend() throws Exception {
127 if (!suspended.get()) {
128 if (suspending.compareAndSet(false, true)) {
129 try {
130 starting.set(false);
131 stopping.set(false);
132 doSuspend();
133 } finally {
134 stopped.set(false);
135 stopping.set(false);
136 starting.set(false);
137 started.set(false);
138 suspending.set(false);
139 suspended.set(true);
140 shutdown.set(false);
141 shuttingdown.set(false);
142 }
143 }
144 }
145 }
146
147 public void resume() throws Exception {
148 if (suspended.get()) {
149 if (starting.compareAndSet(false, true)) {
150 try {
151 doResume();
152 } finally {
153 started.set(true);
154 starting.set(false);
155 stopping.set(false);
156 stopped.set(false);
157 suspending.set(false);
158 suspended.set(false);
159 shutdown.set(false);
160 shuttingdown.set(false);
161 }
162 }
163 }
164 }
165
166 public void shutdown() throws Exception {
167 // ensure we are stopped first
168 stop();
169
170 if (shuttingdown.compareAndSet(false, true)) {
171 try {
172 try {
173 doShutdown();
174 } finally {
175 if (childServices != null) {
176 ServiceHelper.stopAndShutdownService(childServices);
177 }
178 }
179 } finally {
180 // shutdown is also stopped so only set shutdown flags
181 shutdown.set(true);
182 shuttingdown.set(false);
183 }
184 }
185 }
186
187 /**
188 * Returns the current status
189 */
190 public ServiceStatus getStatus() {
191 // lets check these in oldest first as these flags can be changing in a concurrent world
192 if (isStarting()) {
193 return ServiceStatus.Starting;
194 }
195 if (isStarted()) {
196 return ServiceStatus.Started;
197 }
198 if (isStopping()) {
199 return ServiceStatus.Stopping;
200 }
201 if (isStopped()) {
202 return ServiceStatus.Stopped;
203 }
204 if (isSuspending()) {
205 return ServiceStatus.Suspending;
206 }
207 if (isSuspended()) {
208 return ServiceStatus.Suspended;
209 }
210
211 // use stopped as fallback
212 return ServiceStatus.Stopped;
213 }
214
215 /**
216 * @return true if this service has been started
217 */
218 public boolean isStarted() {
219 return started.get();
220 }
221
222 /**
223 * @return true if this service is being started
224 */
225 public boolean isStarting() {
226 return starting.get();
227 }
228
229 /**
230 * @return true if this service is in the process of stopping
231 */
232 public boolean isStopping() {
233 return stopping.get();
234 }
235
236 /**
237 * @return true if this service is stopped
238 */
239 public boolean isStopped() {
240 return stopped.get();
241 }
242
243 /**
244 * @return true if this service is in the process of suspending
245 */
246 public boolean isSuspending() {
247 return suspending.get();
248 }
249
250 /**
251 * @return true if this service is suspended
252 */
253 public boolean isSuspended() {
254 return suspended.get();
255 }
256
257 /**
258 * Helper methods so the service knows if it should keep running.
259 * Returns <tt>false</tt> if the service is being stopped or is stopped.
260 *
261 * @return <tt>true</tt> if the service should continue to run.
262 */
263 public boolean isRunAllowed() {
264 return !(stopping.get() || stopped.get());
265 }
266
267 protected abstract void doStart() throws Exception;
268
269 protected abstract void doStop() throws Exception;
270
271 /**
272 * Implementations override this method to support customized suspend/resume.
273 */
274 protected void doSuspend() throws Exception {
275 }
276
277 /**
278 * Implementations override this method to support customized suspend/resume.
279 */
280 protected void doResume() throws Exception {
281 }
282
283 /**
284 * Implementations override this method to perform customized shutdown
285 */
286 protected void doShutdown() throws Exception {
287 // noop
288 }
289
290 @SuppressWarnings("unchecked")
291 protected void addChildService(Object childService) {
292 synchronized (this) {
293 if (childServices == null) {
294 childServices = new LinkedHashSet();
295 }
296 }
297 childServices.add(childService);
298 }
299
300 protected boolean removeChildService(Object childService) {
301 return childServices != null && childServices.remove(childService);
302 }
303
304 /**
305 * Returns the version of this service
306 */
307 public synchronized String getVersion() {
308 if (ObjectHelper.isNotEmpty(version)) {
309 return version;
310 }
311
312 Package aPackage = getClass().getPackage();
313 if (aPackage != null) {
314 version = aPackage.getImplementationVersion();
315 if (version == null) {
316 version = aPackage.getSpecificationVersion();
317 }
318 }
319 return version != null ? version : "";
320 }
321 }