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 */ 017package org.apache.camel.support; 018 019import java.io.InputStream; 020import java.util.Properties; 021import java.util.concurrent.atomic.AtomicBoolean; 022 023import org.apache.camel.ServiceStatus; 024import org.apache.camel.StatefulService; 025import org.apache.camel.util.IOHelper; 026import org.slf4j.Logger; 027import org.slf4j.LoggerFactory; 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 * <p/> 036 * <b>Important: </b> You should override the lifecycle methods that start with <tt>do</tt>, eg {@link #doStart()}}, 037 * {@link #doStop()}, etc. where you implement your logic. The methods {@link #start()}, {@link #stop()} should 038 * <b>NOT</b> be overriden as they are used internally to keep track of the state of this service and properly 039 * invoke the operation in a safe manner. 040 * 041 * @version 042 */ 043public abstract class ServiceSupport implements StatefulService { 044 private static final Logger LOG = LoggerFactory.getLogger(ServiceSupport.class); 045 046 protected final AtomicBoolean started = new AtomicBoolean(false); 047 protected final AtomicBoolean starting = new AtomicBoolean(false); 048 protected final AtomicBoolean stopping = new AtomicBoolean(false); 049 protected final AtomicBoolean stopped = new AtomicBoolean(false); 050 protected final AtomicBoolean suspending = new AtomicBoolean(false); 051 protected final AtomicBoolean suspended = new AtomicBoolean(false); 052 protected final AtomicBoolean shuttingdown = new AtomicBoolean(false); 053 protected final AtomicBoolean shutdown = new AtomicBoolean(false); 054 055 private String version; 056 057 /** 058 * <b>Important: </b> You should override the lifecycle methods that start with <tt>do</tt>, eg {@link #doStart()}, 059 * {@link #doStop()}, etc. where you implement your logic. The methods {@link #start()}, {@link #stop()} should 060 * <b>NOT</b> be overriden as they are used internally to keep track of the state of this service and properly 061 * invoke the operation in a safe manner. 062 */ 063 public void start() throws Exception { 064 if (isStarting() || isStarted()) { 065 // only start service if not already started 066 LOG.trace("Service already started"); 067 return; 068 } 069 if (starting.compareAndSet(false, true)) { 070 LOG.trace("Starting service"); 071 try { 072 doStart(); 073 started.set(true); 074 starting.set(false); 075 stopping.set(false); 076 stopped.set(false); 077 suspending.set(false); 078 suspended.set(false); 079 shutdown.set(false); 080 shuttingdown.set(false); 081 } catch (Exception e) { 082 try { 083 stop(); 084 } catch (Exception e2) { 085 // Ignore exceptions as we want to show the original exception 086 } finally { 087 // ensure flags get reset to stopped as we failed during starting 088 stopping.set(false); 089 stopped.set(true); 090 starting.set(false); 091 started.set(false); 092 suspending.set(false); 093 suspended.set(false); 094 shutdown.set(false); 095 shuttingdown.set(false); 096 } 097 throw e; 098 } 099 } 100 } 101 102 /** 103 * <b>Important: </b> You should override the lifecycle methods that start with <tt>do</tt>, eg {@link #doStart()}, 104 * {@link #doStop()}, etc. where you implement your logic. The methods {@link #start()}, {@link #stop()} should 105 * <b>NOT</b> be overriden as they are used internally to keep track of the state of this service and properly 106 * invoke the operation in a safe manner. 107 */ 108 public void stop() throws Exception { 109 if (isStopped()) { 110 LOG.trace("Service already stopped"); 111 return; 112 } 113 if (isStopping()) { 114 LOG.trace("Service already stopping"); 115 return; 116 } 117 stopping.set(true); 118 try { 119 doStop(); 120 } finally { 121 stopping.set(false); 122 stopped.set(true); 123 starting.set(false); 124 started.set(false); 125 suspending.set(false); 126 suspended.set(false); 127 shutdown.set(false); 128 shuttingdown.set(false); 129 } 130 } 131 132 /** 133 * <b>Important: </b> You should override the lifecycle methods that start with <tt>do</tt>, eg {@link #doStart()}, 134 * {@link #doStop()}, etc. where you implement your logic. The methods {@link #start()}, {@link #stop()} should 135 * <b>NOT</b> be overriden as they are used internally to keep track of the state of this service and properly 136 * invoke the operation in a safe manner. 137 */ 138 @Override 139 public void suspend() throws Exception { 140 if (!suspended.get()) { 141 if (suspending.compareAndSet(false, true)) { 142 try { 143 starting.set(false); 144 stopping.set(false); 145 doSuspend(); 146 } finally { 147 stopped.set(false); 148 stopping.set(false); 149 starting.set(false); 150 started.set(false); 151 suspending.set(false); 152 suspended.set(true); 153 shutdown.set(false); 154 shuttingdown.set(false); 155 } 156 } 157 } 158 } 159 160 /** 161 * <b>Important: </b> You should override the lifecycle methods that start with <tt>do</tt>, eg {@link #doStart()}, 162 * {@link #doStop()}, etc. where you implement your logic. The methods {@link #start()}, {@link #stop()} should 163 * <b>NOT</b> be overriden as they are used internally to keep track of the state of this service and properly 164 * invoke the operation in a safe manner. 165 */ 166 @Override 167 public void resume() throws Exception { 168 if (suspended.get()) { 169 if (starting.compareAndSet(false, true)) { 170 try { 171 doResume(); 172 } finally { 173 started.set(true); 174 starting.set(false); 175 stopping.set(false); 176 stopped.set(false); 177 suspending.set(false); 178 suspended.set(false); 179 shutdown.set(false); 180 shuttingdown.set(false); 181 } 182 } 183 } 184 } 185 186 /** 187 * <b>Important: </b> You should override the lifecycle methods that start with <tt>do</tt>, eg {@link #doStart()}, 188 * {@link #doStop()}, etc. where you implement your logic. The methods {@link #start()}, {@link #stop()} should 189 * <b>NOT</b> be overriden as they are used internally to keep track of the state of this service and properly 190 * invoke the operation in a safe manner. 191 */ 192 @Override 193 public void shutdown() throws Exception { 194 if (shutdown.get()) { 195 LOG.trace("Service already shut down"); 196 return; 197 } 198 // ensure we are stopped first 199 stop(); 200 201 if (shuttingdown.compareAndSet(false, true)) { 202 try { 203 doShutdown(); 204 } finally { 205 // shutdown is also stopped so only set shutdown flags 206 shutdown.set(true); 207 shuttingdown.set(false); 208 } 209 } 210 } 211 212 @Override 213 public ServiceStatus getStatus() { 214 // we should check the ---ing states first, as this indicate the state is in the middle of doing that 215 if (isStarting()) { 216 return ServiceStatus.Starting; 217 } 218 if (isStopping()) { 219 return ServiceStatus.Stopping; 220 } 221 if (isSuspending()) { 222 return ServiceStatus.Suspending; 223 } 224 225 // then check for the regular states 226 if (isStarted()) { 227 return ServiceStatus.Started; 228 } 229 if (isStopped()) { 230 return ServiceStatus.Stopped; 231 } 232 if (isSuspended()) { 233 return ServiceStatus.Suspended; 234 } 235 236 // use stopped as fallback 237 return ServiceStatus.Stopped; 238 } 239 240 @Override 241 public boolean isStarted() { 242 return started.get(); 243 } 244 245 @Override 246 public boolean isStarting() { 247 return starting.get(); 248 } 249 250 @Override 251 public boolean isStopping() { 252 return stopping.get(); 253 } 254 255 @Override 256 public boolean isStopped() { 257 return stopped.get(); 258 } 259 260 @Override 261 public boolean isSuspending() { 262 return suspending.get(); 263 } 264 265 @Override 266 public boolean isSuspended() { 267 return suspended.get(); 268 } 269 270 @Override 271 public boolean isRunAllowed() { 272 // if we have not yet initialized, then all options is false 273 boolean unused1 = !started.get() && !starting.get() && !stopping.get() && !stopped.get(); 274 boolean unused2 = !suspending.get() && !suspended.get() && !shutdown.get() && !shuttingdown.get(); 275 if (unused1 && unused2) { 276 return false; 277 } 278 return !isStoppingOrStopped(); 279 } 280 281 /** 282 * Is the service in progress of being stopped or already stopped 283 */ 284 public boolean isStoppingOrStopped() { 285 return stopping.get() || stopped.get(); 286 } 287 288 /** 289 * Is the service in progress of being suspended or already suspended 290 */ 291 public boolean isSuspendingOrSuspended() { 292 return suspending.get() || suspended.get(); 293 } 294 295 /** 296 * Implementations override this method to support customized start/stop. 297 * <p/> 298 * <b>Important: </b> See {@link #doStop()} for more details. 299 * 300 * @see #doStop() 301 */ 302 protected abstract void doStart() throws Exception; 303 304 /** 305 * Implementations override this method to support customized start/stop. 306 * <p/> 307 * <b>Important:</b> Camel will invoke this {@link #doStop()} method when 308 * the service is being stopped. This method will <b>also</b> be invoked 309 * if the service is still in <i>uninitialized</i> state (eg has not 310 * been started). The method is <b>always</b> called to allow the service 311 * to do custom logic when the service is being stopped, such as when 312 * {@link org.apache.camel.CamelContext} is shutting down. 313 * 314 * @see #doStart() 315 */ 316 protected abstract void doStop() throws Exception; 317 318 /** 319 * Implementations override this method to support customized suspend/resume. 320 */ 321 protected void doSuspend() throws Exception { 322 // noop 323 } 324 325 /** 326 * Implementations override this method to support customized suspend/resume. 327 */ 328 protected void doResume() throws Exception { 329 // noop 330 } 331 332 /** 333 * Implementations override this method to perform customized shutdown. 334 */ 335 protected void doShutdown() throws Exception { 336 // noop 337 } 338 339 @Override 340 public synchronized String getVersion() { 341 if (version != null) { 342 return version; 343 } 344 InputStream is = null; 345 // try to load from maven properties first 346 try { 347 Properties p = new Properties(); 348 is = getClass().getResourceAsStream("/META-INF/maven/org.apache.camel/camel-core/pom.properties"); 349 if (is != null) { 350 p.load(is); 351 version = p.getProperty("version", ""); 352 } 353 } catch (Exception e) { 354 // ignore 355 } finally { 356 if (is != null) { 357 IOHelper.close(is); 358 } 359 } 360 361 // fallback to using Java API 362 if (version == null) { 363 Package aPackage = getClass().getPackage(); 364 if (aPackage != null) { 365 version = aPackage.getImplementationVersion(); 366 if (version == null) { 367 version = aPackage.getSpecificationVersion(); 368 } 369 } 370 } 371 372 if (version == null) { 373 // we could not compute the version so use a blank 374 version = ""; 375 } 376 377 return version; 378 } 379}