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