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;
018
019import java.io.InputStream;
020import java.util.Properties;
021import java.util.concurrent.atomic.AtomicBoolean;
022
023import org.apache.camel.ServiceStatus;
024import org.apache.camel.StatefulService;
025import org.apache.camel.util.IOHelper;
026import org.slf4j.Logger;
027import org.slf4j.LoggerFactory;
028
029/**
030 * A useful base class which ensures that a service is only initialized once and
031 * provides some helper methods for enquiring of its status.
032 * <p/>
033 * Implementations can extend this base class and implement {@link org.apache.camel.SuspendableService}
034 * in case they support suspend/resume.
035 * <p/>
036 * <b>Important: </b> You should override the lifecycle methods that start with <tt>do</tt>, eg {@link #doStart()}},
037 * {@link #doStop()}, etc. where you implement your logic. The methods {@link #start()}, {@link #stop()} should
038 * <b>NOT</b> be overriden as they are used internally to keep track of the state of this service and properly
039 * invoke the operation in a safe manner.
040 *
041 * @version 
042 */
043public abstract class ServiceSupport implements StatefulService {
044    private static final Logger LOG = LoggerFactory.getLogger(ServiceSupport.class);
045
046    protected final AtomicBoolean started = new AtomicBoolean(false);
047    protected final AtomicBoolean starting = new AtomicBoolean(false);
048    protected final AtomicBoolean stopping = new AtomicBoolean(false);
049    protected final AtomicBoolean stopped = new AtomicBoolean(false);
050    protected final AtomicBoolean suspending = new AtomicBoolean(false);
051    protected final AtomicBoolean suspended = new AtomicBoolean(false);
052    protected final AtomicBoolean shuttingdown = new AtomicBoolean(false);
053    protected final AtomicBoolean shutdown = new AtomicBoolean(false);
054
055    private String version;
056
057    /**
058     * <b>Important: </b> You should override the lifecycle methods that start with <tt>do</tt>, eg {@link #doStart()},
059     * {@link #doStop()}, etc. where you implement your logic. The methods {@link #start()}, {@link #stop()} should
060     * <b>NOT</b> be overriden as they are used internally to keep track of the state of this service and properly
061     * invoke the operation in a safe manner.
062     */
063    public void start() throws Exception {
064        if (isStarting() || isStarted()) {
065            // only start service if not already started
066            LOG.trace("Service already started");
067            return;
068        }
069        if (starting.compareAndSet(false, true)) {
070            LOG.trace("Starting service");
071            try {
072                doStart();
073                started.set(true);
074                starting.set(false);
075                stopping.set(false);
076                stopped.set(false);
077                suspending.set(false);
078                suspended.set(false);
079                shutdown.set(false);
080                shuttingdown.set(false);
081            } catch (Exception e) {
082                try {
083                    stop();
084                } catch (Exception e2) {
085                    // Ignore exceptions as we want to show the original exception
086                } finally {
087                    // ensure flags get reset to stopped as we failed during starting
088                    stopping.set(false);
089                    stopped.set(true);
090                    starting.set(false);
091                    started.set(false);
092                    suspending.set(false);
093                    suspended.set(false);
094                    shutdown.set(false);
095                    shuttingdown.set(false);
096                }
097                throw e;
098            } 
099        }
100    }
101
102    /**
103     * <b>Important: </b> You should override the lifecycle methods that start with <tt>do</tt>, eg {@link #doStart()},
104     * {@link #doStop()}, etc. where you implement your logic. The methods {@link #start()}, {@link #stop()} should
105     * <b>NOT</b> be overriden as they are used internally to keep track of the state of this service and properly
106     * invoke the operation in a safe manner.
107     */
108    public void stop() throws Exception {
109        if (isStopped()) {
110            LOG.trace("Service already stopped");
111            return;
112        }
113        if (isStopping()) {
114            LOG.trace("Service already stopping");
115            return;
116        }
117        stopping.set(true);
118        try {
119            doStop();
120        } finally {
121            stopping.set(false);
122            stopped.set(true);
123            starting.set(false);
124            started.set(false);
125            suspending.set(false);
126            suspended.set(false);
127            shutdown.set(false);
128            shuttingdown.set(false);            
129        }
130    }
131
132    /**
133     * <b>Important: </b> You should override the lifecycle methods that start with <tt>do</tt>, eg {@link #doStart()},
134     * {@link #doStop()}, etc. where you implement your logic. The methods {@link #start()}, {@link #stop()} should
135     * <b>NOT</b> be overriden as they are used internally to keep track of the state of this service and properly
136     * invoke the operation in a safe manner.
137     */
138    @Override
139    public void suspend() throws Exception {
140        if (!suspended.get()) {
141            if (suspending.compareAndSet(false, true)) {
142                try {
143                    starting.set(false);
144                    stopping.set(false);
145                    doSuspend();
146                } finally {
147                    stopped.set(false);
148                    stopping.set(false);
149                    starting.set(false);
150                    started.set(false);
151                    suspending.set(false);
152                    suspended.set(true);
153                    shutdown.set(false);
154                    shuttingdown.set(false);
155                }
156            }
157        }
158    }
159
160    /**
161     * <b>Important: </b> You should override the lifecycle methods that start with <tt>do</tt>, eg {@link #doStart()},
162     * {@link #doStop()}, etc. where you implement your logic. The methods {@link #start()}, {@link #stop()} should
163     * <b>NOT</b> be overriden as they are used internally to keep track of the state of this service and properly
164     * invoke the operation in a safe manner.
165     */
166    @Override
167    public void resume() throws Exception {
168        if (suspended.get()) {
169            if (starting.compareAndSet(false, true)) {
170                try {
171                    doResume();
172                } finally {
173                    started.set(true);
174                    starting.set(false);
175                    stopping.set(false);
176                    stopped.set(false);
177                    suspending.set(false);
178                    suspended.set(false);
179                    shutdown.set(false);
180                    shuttingdown.set(false);
181                }
182            }
183        }
184    }
185
186    /**
187     * <b>Important: </b> You should override the lifecycle methods that start with <tt>do</tt>, eg {@link #doStart()},
188     * {@link #doStop()}, etc. where you implement your logic. The methods {@link #start()}, {@link #stop()} should
189     * <b>NOT</b> be overriden as they are used internally to keep track of the state of this service and properly
190     * invoke the operation in a safe manner.
191     */
192    @Override
193    public void shutdown() throws Exception {
194        if (shutdown.get()) {
195            LOG.trace("Service already shut down");
196            return;
197        }
198        // ensure we are stopped first
199        stop();
200
201        if (shuttingdown.compareAndSet(false, true)) {
202            try {
203                doShutdown();
204            } finally {
205                // shutdown is also stopped so only set shutdown flags
206                shutdown.set(true);
207                shuttingdown.set(false);
208            }
209        }
210    }
211
212    @Override
213    public ServiceStatus getStatus() {
214        // we should check the ---ing states first, as this indicate the state is in the middle of doing that
215        if (isStarting()) {
216            return ServiceStatus.Starting;
217        }
218        if (isStopping()) {
219            return ServiceStatus.Stopping;
220        }
221        if (isSuspending()) {
222            return ServiceStatus.Suspending;
223        }
224
225        // then check for the regular states
226        if (isStarted()) {
227            return ServiceStatus.Started;
228        }
229        if (isStopped()) {
230            return ServiceStatus.Stopped;
231        }
232        if (isSuspended()) {
233            return ServiceStatus.Suspended;
234        }
235
236        // use stopped as fallback
237        return ServiceStatus.Stopped;
238    }
239    
240    @Override
241    public boolean isStarted() {
242        return started.get();
243    }
244
245    @Override
246    public boolean isStarting() {
247        return starting.get();
248    }
249
250    @Override
251    public boolean isStopping() {
252        return stopping.get();
253    }
254
255    @Override
256    public boolean isStopped() {
257        return stopped.get();
258    }
259
260    @Override
261    public boolean isSuspending() {
262        return suspending.get();
263    }
264
265    @Override
266    public boolean isSuspended() {
267        return suspended.get();
268    }
269
270    @Override
271    public boolean isRunAllowed() {
272        // if we have not yet initialized, then all options is false
273        boolean unused1 = !started.get() && !starting.get() && !stopping.get() && !stopped.get();
274        boolean unused2 = !suspending.get() && !suspended.get() && !shutdown.get() && !shuttingdown.get();
275        if (unused1 && unused2) {
276            return false;
277        }
278        return !isStoppingOrStopped();
279    }
280
281    /**
282     * Is the service in progress of being stopped or already stopped
283     */
284    public boolean isStoppingOrStopped() {
285        return stopping.get() || stopped.get();
286    }
287
288    /**
289     * Is the service in progress of being suspended or already suspended
290     */
291    public boolean isSuspendingOrSuspended() {
292        return suspending.get() || suspended.get();
293    }
294
295    /**
296     * Implementations override this method to support customized start/stop.
297     * <p/>
298     * <b>Important: </b> See {@link #doStop()} for more details.
299     * 
300     * @see #doStop()
301     */
302    protected abstract void doStart() throws Exception;
303
304    /**
305     * Implementations override this method to support customized start/stop.
306     * <p/>
307     * <b>Important:</b> Camel will invoke this {@link #doStop()} method when
308     * the service is being stopped. This method will <b>also</b> be invoked
309     * if the service is still in <i>uninitialized</i> state (eg has not
310     * been started). The method is <b>always</b> called to allow the service
311     * to do custom logic when the service is being stopped, such as when
312     * {@link org.apache.camel.CamelContext} is shutting down.
313     * 
314     * @see #doStart() 
315     */
316    protected abstract void doStop() throws Exception;
317
318    /**
319     * Implementations override this method to support customized suspend/resume.
320     */
321    protected void doSuspend() throws Exception {
322        // noop
323    }
324
325    /**
326     * Implementations override this method to support customized suspend/resume.
327     */
328    protected void doResume() throws Exception {
329        // noop
330    }
331
332    /**
333     * Implementations override this method to perform customized shutdown.
334     */
335    protected void doShutdown() throws Exception {
336        // noop
337    }
338
339    @Override
340    public synchronized String getVersion() {
341        if (version != null) {
342            return version;
343        }
344        InputStream is = null;
345        // try to load from maven properties first
346        try {
347            Properties p = new Properties();
348            is = getClass().getResourceAsStream("/META-INF/maven/org.apache.camel/camel-core/pom.properties");
349            if (is != null) {
350                p.load(is);
351                version = p.getProperty("version", "");
352            }
353        } catch (Exception e) {
354            // ignore
355        } finally {
356            if (is != null) {
357                IOHelper.close(is);
358            }
359        }
360
361        // fallback to using Java API
362        if (version == null) {
363            Package aPackage = getClass().getPackage();
364            if (aPackage != null) {
365                version = aPackage.getImplementationVersion();
366                if (version == null) {
367                    version = aPackage.getSpecificationVersion();
368                }
369            }
370        }
371
372        if (version == null) {
373            // we could not compute the version so use a blank
374            version = "";
375        }
376
377        return version;
378    }
379}