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 *
036 * @version 
037 */
038public abstract class ServiceSupport implements StatefulService {
039    private static final Logger LOG = LoggerFactory.getLogger(ServiceSupport.class);
040
041    protected final AtomicBoolean started = new AtomicBoolean(false);
042    protected final AtomicBoolean starting = new AtomicBoolean(false);
043    protected final AtomicBoolean stopping = new AtomicBoolean(false);
044    protected final AtomicBoolean stopped = new AtomicBoolean(false);
045    protected final AtomicBoolean suspending = new AtomicBoolean(false);
046    protected final AtomicBoolean suspended = new AtomicBoolean(false);
047    protected final AtomicBoolean shuttingdown = new AtomicBoolean(false);
048    protected final AtomicBoolean shutdown = new AtomicBoolean(false);
049
050    private String version;
051
052    public void start() throws Exception {
053        if (isStarting() || isStarted()) {
054            // only start service if not already started
055            LOG.trace("Service already started");
056            return;
057        }
058        if (starting.compareAndSet(false, true)) {
059            LOG.trace("Starting service");
060            try {
061                doStart();
062                started.set(true);
063                starting.set(false);
064                stopping.set(false);
065                stopped.set(false);
066                suspending.set(false);
067                suspended.set(false);
068                shutdown.set(false);
069                shuttingdown.set(false);
070            } catch (Exception e) {
071                try {
072                    stop();
073                } catch (Exception e2) {
074                    // Ignore exceptions as we want to show the original exception
075                } finally {
076                    // ensure flags get reset to stopped as we failed during starting
077                    stopping.set(false);
078                    stopped.set(true);
079                    starting.set(false);
080                    started.set(false);
081                    suspending.set(false);
082                    suspended.set(false);
083                    shutdown.set(false);
084                    shuttingdown.set(false);
085                }
086                throw e;
087            } 
088        }
089    }
090    
091    public void stop() throws Exception {
092        if (isStopped()) {
093            LOG.trace("Service already stopped");
094            return;
095        }
096        if (isStopping()) {
097            LOG.trace("Service already stopping");
098            return;
099        }
100        stopping.set(true);
101        try {
102            doStop();
103        } finally {
104            stopping.set(false);
105            stopped.set(true);
106            starting.set(false);
107            started.set(false);
108            suspending.set(false);
109            suspended.set(false);
110            shutdown.set(false);
111            shuttingdown.set(false);            
112        }
113    }
114
115    @Override
116    public void suspend() throws Exception {
117        if (!suspended.get()) {
118            if (suspending.compareAndSet(false, true)) {
119                try {
120                    starting.set(false);
121                    stopping.set(false);
122                    doSuspend();
123                } finally {
124                    stopped.set(false);
125                    stopping.set(false);
126                    starting.set(false);
127                    started.set(false);
128                    suspending.set(false);
129                    suspended.set(true);
130                    shutdown.set(false);
131                    shuttingdown.set(false);
132                }
133            }
134        }
135    }
136
137    @Override
138    public void resume() throws Exception {
139        if (suspended.get()) {
140            if (starting.compareAndSet(false, true)) {
141                try {
142                    doResume();
143                } finally {
144                    started.set(true);
145                    starting.set(false);
146                    stopping.set(false);
147                    stopped.set(false);
148                    suspending.set(false);
149                    suspended.set(false);
150                    shutdown.set(false);
151                    shuttingdown.set(false);
152                }
153            }
154        }
155    }
156
157    @Override
158    public void shutdown() throws Exception {
159        if (shutdown.get()) {
160            LOG.trace("Service already shut down");
161            return;
162        }
163        // ensure we are stopped first
164        stop();
165
166        if (shuttingdown.compareAndSet(false, true)) {
167            try {
168                doShutdown();
169            } finally {
170                // shutdown is also stopped so only set shutdown flags
171                shutdown.set(true);
172                shuttingdown.set(false);
173            }
174        }
175    }
176
177    @Override
178    public ServiceStatus getStatus() {
179        // we should check the ---ing states first, as this indicate the state is in the middle of doing that
180        if (isStarting()) {
181            return ServiceStatus.Starting;
182        }
183        if (isStopping()) {
184            return ServiceStatus.Stopping;
185        }
186        if (isSuspending()) {
187            return ServiceStatus.Suspending;
188        }
189
190        // then check for the regular states
191        if (isStarted()) {
192            return ServiceStatus.Started;
193        }
194        if (isStopped()) {
195            return ServiceStatus.Stopped;
196        }
197        if (isSuspended()) {
198            return ServiceStatus.Suspended;
199        }
200
201        // use stopped as fallback
202        return ServiceStatus.Stopped;
203    }
204    
205    @Override
206    public boolean isStarted() {
207        return started.get();
208    }
209
210    @Override
211    public boolean isStarting() {
212        return starting.get();
213    }
214
215    @Override
216    public boolean isStopping() {
217        return stopping.get();
218    }
219
220    @Override
221    public boolean isStopped() {
222        return stopped.get();
223    }
224
225    @Override
226    public boolean isSuspending() {
227        return suspending.get();
228    }
229
230    @Override
231    public boolean isSuspended() {
232        return suspended.get();
233    }
234
235    @Override
236    public boolean isRunAllowed() {
237        return !isStoppingOrStopped();
238    }
239
240    public boolean isStoppingOrStopped() {
241        return stopping.get() || stopped.get();
242    }
243
244    /**
245     * Implementations override this method to support customized start/stop.
246     * <p/>
247     * <b>Important: </b> See {@link #doStop()} for more details.
248     * 
249     * @see #doStop()
250     */
251    protected abstract void doStart() throws Exception;
252
253    /**
254     * Implementations override this method to support customized start/stop.
255     * <p/>
256     * <b>Important:</b> Camel will invoke this {@link #doStop()} method when
257     * the service is being stopped. This method will <b>also</b> be invoked
258     * if the service is still in <i>uninitialized</i> state (eg has not
259     * been started). The method is <b>always</b> called to allow the service
260     * to do custom logic when the service is being stopped, such as when
261     * {@link org.apache.camel.CamelContext} is shutting down.
262     * 
263     * @see #doStart() 
264     */
265    protected abstract void doStop() throws Exception;
266
267    /**
268     * Implementations override this method to support customized suspend/resume.
269     */
270    protected void doSuspend() throws Exception {
271        // noop
272    }
273
274    /**
275     * Implementations override this method to support customized suspend/resume.
276     */
277    protected void doResume() throws Exception {
278        // noop
279    }
280
281    /**
282     * Implementations override this method to perform customized shutdown.
283     */
284    protected void doShutdown() throws Exception {
285        // noop
286    }
287
288    @Override
289    public synchronized String getVersion() {
290        if (version != null) {
291            return version;
292        }
293        InputStream is = null;
294        // try to load from maven properties first
295        try {
296            Properties p = new Properties();
297            is = getClass().getResourceAsStream("/META-INF/maven/org.apache.camel/camel-core/pom.properties");
298            if (is != null) {
299                p.load(is);
300                version = p.getProperty("version", "");
301            }
302        } catch (Exception e) {
303            // ignore
304        } finally {
305            if (is != null) {
306                IOHelper.close(is);
307            }
308        }
309
310        // fallback to using Java API
311        if (version == null) {
312            Package aPackage = getClass().getPackage();
313            if (aPackage != null) {
314                version = aPackage.getImplementationVersion();
315                if (version == null) {
316                    version = aPackage.getSpecificationVersion();
317                }
318            }
319        }
320
321        if (version == null) {
322            // we could not compute the version so use a blank
323            version = "";
324        }
325
326        return version;
327    }
328}