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