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 overriden 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");
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");
076                return;
077            }
078            if (status == STARTING) {
079                log.trace("Service already starting");
080                return;
081            }
082            init();
083            try {
084                status = STARTING;
085                log.trace("Starting service");
086                doStart();
087                status = STARTED;
088                log.trace("Service started");
089            } catch (Exception e) {
090                status = FAILED;
091                log.trace("Error while starting service", 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 overriden 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");
107                return;
108            }
109            if (status == STOPPING) {
110                log.trace("Service already stopping");
111                return;
112            }
113            status = STOPPING;
114            log.trace("Stopping service");
115            try {
116                doStop();
117                status = STOPPED;
118                log.trace("Service stopped service");
119            } catch (Exception e) {
120                status = FAILED;
121                log.trace("Error while stopping service", 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 overriden 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");
138                return;
139            }
140            if (status == SUSPENDING) {
141                log.trace("Service already suspending");
142                return;
143            }
144            status = SUSPENDING;
145            log.trace("Suspending service");
146            try {
147                doSuspend();
148                status = SUSPENDED;
149                log.trace("Service suspended");
150            } catch (Exception e) {
151                status = FAILED;
152                log.trace("Error while suspending service", 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");
169                return;
170            }
171            status = STARTING;
172            log.trace("Resuming service");
173            try {
174                doResume();
175                status = STARTED;
176                log.trace("Service resumed");
177            } catch (Exception e) {
178                status = FAILED;
179                log.trace("Error while resuming service", 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");
196                return;
197            }
198            if (status == SHUTTINGDOWN) {
199                log.trace("Service already shutting down");
200                return;
201            }
202            stop();
203            status = SHUTDOWN;
204            log.trace("Shutting down service");
205            try {
206                doShutdown();
207                log.trace("Service shut down");
208                status = SHUTDOWN;
209            } catch (Exception e) {
210                status = FAILED;
211                log.trace("Error shutting down service", 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}