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.service; 018 019import org.apache.camel.RuntimeCamelException; 020import org.apache.camel.ServiceStatus; 021import org.apache.camel.StatefulService; 022import org.slf4j.Logger; 023import org.slf4j.LoggerFactory; 024 025/** 026 * A useful base class which ensures that a service is only initialized once and 027 * provides some helper methods for enquiring of its status. 028 * <p/> 029 * Implementations can extend this base class and implement {@link org.apache.camel.SuspendableService} 030 * in case they support suspend/resume. 031 * <p/> 032 * <b>Important: </b> You should override the lifecycle methods that start with <tt>do</tt>, eg {@link #doStart()}}, 033 * {@link #doStop()}, etc. where you implement your logic. The methods {@link #start()}, {@link #stop()} should 034 * <b>NOT</b> be overridden as they are used internally to keep track of the state of this service and properly 035 * invoke the operation in a safe manner. 036 */ 037public abstract class ServiceSupport implements StatefulService { 038 039 protected static final int NEW = 0; 040 protected static final int INITIALIZED = 1; 041 protected static final int STARTING = 2; 042 protected static final int STARTED = 3; 043 protected static final int SUSPENDING = 4; 044 protected static final int SUSPENDED = 5; 045 protected static final int STOPPING = 6; 046 protected static final int STOPPED = 7; 047 protected static final int SHUTTINGDOWN = 8; 048 protected static final int SHUTDOWN = 9; 049 protected static final int FAILED = 10; 050 051 protected final Logger log = LoggerFactory.getLogger(getClass()); 052 protected final Object lock = new Object(); 053 protected volatile int status = NEW; 054 055 @Override 056 public void init() { 057 if (status == NEW) { 058 synchronized (lock) { 059 if (status == NEW) { 060 log.trace("Initializing service: {}", this); 061 try { 062 doInit(); 063 } catch (Exception e) { 064 throw RuntimeCamelException.wrapRuntimeException(e); 065 } 066 status = INITIALIZED; 067 } 068 } 069 } 070 } 071 072 /** 073 * <b>Important: </b> You should override the lifecycle methods that start with <tt>do</tt>, eg {@link #doStart()}, 074 * {@link #doStop()}, etc. where you implement your logic. The methods {@link #start()}, {@link #stop()} should 075 * <b>NOT</b> be overridden as they are used internally to keep track of the state of this service and properly 076 * invoke the operation in a safe manner. 077 */ 078 @Override 079 public void start() { 080 synchronized (lock) { 081 if (status == STARTED) { 082 log.trace("Service: {} already started", this); 083 return; 084 } 085 if (status == STARTING) { 086 log.trace("Service: {} already starting", this); 087 return; 088 } 089 try { 090 init(); 091 } catch (Exception e) { 092 status = FAILED; 093 log.trace("Error while initializing service: " + this, e); 094 throw e; 095 } 096 try { 097 status = STARTING; 098 log.trace("Starting service: {}", this); 099 doStart(); 100 status = STARTED; 101 log.trace("Service started: {}", this); 102 } catch (Exception e) { 103 // need to stop as some resources may have been started during startup 104 try { 105 stop(); 106 } catch (Exception e2) { 107 // ignore 108 log.trace("Error while stopping service after it failed to start: " + this + ". This exception is ignored", e); 109 } 110 status = FAILED; 111 log.trace("Error while starting service: " + this, e); 112 throw RuntimeCamelException.wrapRuntimeException(e); 113 } 114 } 115 } 116 117 /** 118 * <b>Important: </b> You should override the lifecycle methods that start with <tt>do</tt>, eg {@link #doStart()}, 119 * {@link #doStop()}, etc. where you implement your logic. The methods {@link #start()}, {@link #stop()} should 120 * <b>NOT</b> be overridden as they are used internally to keep track of the state of this service and properly 121 * invoke the operation in a safe manner. 122 */ 123 @Override 124 public void stop() { 125 synchronized (lock) { 126 if (status == FAILED) { 127 log.trace("Service: {} failed and regarded as already stopped", this); 128 return; 129 } 130 if (status == STOPPED || status == SHUTTINGDOWN || status == SHUTDOWN) { 131 log.trace("Service: {} already stopped", this); 132 return; 133 } 134 if (status == STOPPING) { 135 log.trace("Service: {} already stopping", this); 136 return; 137 } 138 status = STOPPING; 139 log.trace("Stopping service: {}", this); 140 try { 141 doStop(); 142 status = STOPPED; 143 log.trace("Service: {} stopped service", this); 144 } catch (Exception e) { 145 status = FAILED; 146 log.trace("Error while stopping service: " + this, e); 147 throw RuntimeCamelException.wrapRuntimeException(e); 148 } 149 } 150 } 151 152 /** 153 * <b>Important: </b> You should override the lifecycle methods that start with <tt>do</tt>, eg {@link #doStart()}, 154 * {@link #doStop()}, etc. where you implement your logic. The methods {@link #start()}, {@link #stop()} should 155 * <b>NOT</b> be overridden as they are used internally to keep track of the state of this service and properly 156 * invoke the operation in a safe manner. 157 */ 158 @Override 159 public void suspend() { 160 synchronized (lock) { 161 if (status == SUSPENDED) { 162 log.trace("Service: {} already suspended", this); 163 return; 164 } 165 if (status == SUSPENDING) { 166 log.trace("Service: {} already suspending", this); 167 return; 168 } 169 status = SUSPENDING; 170 log.trace("Suspending service: {}", this); 171 try { 172 doSuspend(); 173 status = SUSPENDED; 174 log.trace("Service suspended: {}", this); 175 } catch (Exception e) { 176 status = FAILED; 177 log.trace("Error while suspending service: " + this, e); 178 throw RuntimeCamelException.wrapRuntimeException(e); 179 } 180 } 181 } 182 183 /** 184 * <b>Important: </b> You should override the lifecycle methods that start with <tt>do</tt>, eg {@link #doStart()}, 185 * {@link #doStop()}, etc. where you implement your logic. The methods {@link #start()}, {@link #stop()} should 186 * <b>NOT</b> be overridden as they are used internally to keep track of the state of this service and properly 187 * invoke the operation in a safe manner. 188 */ 189 @Override 190 public void resume() { 191 synchronized (lock) { 192 if (status != SUSPENDED) { 193 log.trace("Service is not suspended: {}", this); 194 return; 195 } 196 status = STARTING; 197 log.trace("Resuming service: {}", this); 198 try { 199 doResume(); 200 status = STARTED; 201 log.trace("Service resumed: {}", this); 202 } catch (Exception e) { 203 status = FAILED; 204 log.trace("Error while resuming service: " + this, e); 205 throw RuntimeCamelException.wrapRuntimeException(e); 206 } 207 } 208 } 209 210 /** 211 * <b>Important: </b> You should override the lifecycle methods that start with <tt>do</tt>, eg {@link #doStart()}, 212 * {@link #doStop()}, etc. where you implement your logic. The methods {@link #start()}, {@link #stop()} should 213 * <b>NOT</b> be overridden as they are used internally to keep track of the state of this service and properly 214 * invoke the operation in a safe manner. 215 */ 216 @Override 217 public void shutdown() { 218 synchronized (lock) { 219 if (status == SHUTDOWN) { 220 log.trace("Service: {} already shut down", this); 221 return; 222 } 223 if (status == SHUTTINGDOWN) { 224 log.trace("Service: {} already shutting down", this); 225 return; 226 } 227 stop(); 228 status = SHUTDOWN; 229 log.trace("Shutting down service: {}", this); 230 try { 231 doShutdown(); 232 log.trace("Service: {} shut down", this); 233 status = SHUTDOWN; 234 } catch (Exception e) { 235 status = FAILED; 236 log.trace("Error shutting down service: " + this, e); 237 throw RuntimeCamelException.wrapRuntimeException(e); 238 } 239 } 240 } 241 242 @Override 243 public ServiceStatus getStatus() { 244 switch (status) { 245 case STARTING: 246 return ServiceStatus.Starting; 247 case STARTED: 248 return ServiceStatus.Started; 249 case SUSPENDING: 250 return ServiceStatus.Suspending; 251 case SUSPENDED: 252 return ServiceStatus.Suspended; 253 case STOPPING: 254 return ServiceStatus.Stopping; 255 default: 256 return ServiceStatus.Stopped; 257 } 258 } 259 260 public boolean isNew() { 261 return status == NEW; 262 } 263 264 public boolean isInit() { 265 return status == INITIALIZED; 266 } 267 268 @Override 269 public boolean isStarted() { 270 return status == STARTED; 271 } 272 273 @Override 274 public boolean isStarting() { 275 return status == STARTING; 276 } 277 278 @Override 279 public boolean isStopping() { 280 return status == STOPPING; 281 } 282 283 @Override 284 public boolean isStopped() { 285 return status == NEW || status == INITIALIZED || status == STOPPED || status == SHUTTINGDOWN || status == SHUTDOWN || status == FAILED; 286 } 287 288 @Override 289 public boolean isSuspending() { 290 return status == SUSPENDING; 291 } 292 293 @Override 294 public boolean isSuspended() { 295 return status == SUSPENDED; 296 } 297 298 @Override 299 public boolean isRunAllowed() { 300 return isStartingOrStarted() || isSuspendingOrSuspended(); 301 } 302 303 public boolean isShutdown() { 304 return status == SHUTDOWN; 305 } 306 307 /** 308 * Is the service in progress of being stopped or already stopped 309 */ 310 public boolean isStoppingOrStopped() { 311 return isStopping() || isStopped(); 312 } 313 314 /** 315 * Is the service in progress of being suspended or already suspended 316 */ 317 public boolean isSuspendingOrSuspended() { 318 return isSuspending() || isSuspended(); 319 } 320 321 /** 322 * Is the service in progress of being suspended or already suspended 323 */ 324 public boolean isStartingOrStarted() { 325 return isStarting() || isStarted(); 326 } 327 328 /** 329 * Initialize the service. 330 * This method will only be called once before starting. 331 */ 332 protected void doInit() throws Exception { 333 } 334 335 /** 336 * Implementations override this method to support customized start/stop. 337 * <p/> 338 * <b>Important: </b> See {@link #doStop()} for more details. 339 * 340 * @see #doStop() 341 */ 342 protected abstract void doStart() throws Exception; 343 344 /** 345 * Implementations override this method to support customized start/stop. 346 * <p/> 347 * <b>Important:</b> Camel will invoke this {@link #doStop()} method when 348 * the service is being stopped. This method will <b>also</b> be invoked 349 * if the service is still in <i>uninitialized</i> state (eg has not 350 * been started). The method is <b>always</b> called to allow the service 351 * to do custom logic when the service is being stopped, such as when 352 * {@link org.apache.camel.CamelContext} is shutting down. 353 * 354 * @see #doStart() 355 */ 356 protected abstract void doStop() throws Exception; 357 358 /** 359 * Implementations override this method to support customized suspend/resume. 360 */ 361 protected void doSuspend() throws Exception { 362 // noop 363 } 364 365 /** 366 * Implementations override this method to support customized suspend/resume. 367 */ 368 protected void doResume() throws Exception { 369 // noop 370 } 371 372 /** 373 * Implementations override this method to perform customized shutdown. 374 */ 375 protected void doShutdown() throws Exception { 376 // noop 377 } 378 379}