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     */
017    package org.apache.camel.support;
018    
019    import java.io.InputStream;
020    import java.util.Properties;
021    import java.util.concurrent.atomic.AtomicBoolean;
022    
023    import org.apache.camel.ServiceStatus;
024    import org.apache.camel.StatefulService;
025    import org.apache.camel.util.IOHelper;
026    import org.slf4j.Logger;
027    import 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     */
038    public 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    }