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