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}