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}