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            if (shutdown.get()) {
149                LOG.trace("Service already shut down");
150                return;
151            }
152            // ensure we are stopped first
153            stop();
154    
155            if (shuttingdown.compareAndSet(false, true)) {
156                try {
157                    doShutdown();
158                } finally {
159                    // shutdown is also stopped so only set shutdown flags
160                    shutdown.set(true);
161                    shuttingdown.set(false);
162                }
163            }
164        }
165    
166        @Override
167        public ServiceStatus getStatus() {
168            // we should check the ---ing states first, as this indicate the state is in the middle of doing that
169            if (isStarting()) {
170                return ServiceStatus.Starting;
171            }
172            if (isStopping()) {
173                return ServiceStatus.Stopping;
174            }
175            if (isSuspending()) {
176                return ServiceStatus.Suspending;
177            }
178    
179            // then check for the regular states
180            if (isStarted()) {
181                return ServiceStatus.Started;
182            }
183            if (isStopped()) {
184                return ServiceStatus.Stopped;
185            }
186            if (isSuspended()) {
187                return ServiceStatus.Suspended;
188            }
189    
190            // use stopped as fallback
191            return ServiceStatus.Stopped;
192        }
193        
194        @Override
195        public boolean isStarted() {
196            return started.get();
197        }
198    
199        @Override
200        public boolean isStarting() {
201            return starting.get();
202        }
203    
204        @Override
205        public boolean isStopping() {
206            return stopping.get();
207        }
208    
209        @Override
210        public boolean isStopped() {
211            return stopped.get();
212        }
213    
214        @Override
215        public boolean isSuspending() {
216            return suspending.get();
217        }
218    
219        @Override
220        public boolean isSuspended() {
221            return suspended.get();
222        }
223    
224        @Override
225        public boolean isRunAllowed() {
226            return !(stopping.get() || stopped.get());
227        }
228    
229        /**
230         * Implementations override this method to support customized start/stop.
231         * <p/>
232         * <b>Important: </b> See {@link #doStop()} for more details.
233         * 
234         * @see #doStop()
235         */
236        protected abstract void doStart() throws Exception;
237    
238        /**
239         * Implementations override this method to support customized start/stop.
240         * <p/>
241         * <b>Important:</b> Camel will invoke this {@link #doStop()} method when
242         * the service is being stopped. This method will <b>also</b> be invoked
243         * if the service is still in <i>uninitialized</i> state (eg has not
244         * been started). The method is <b>always</b> called to allow the service
245         * to do custom logic when the service is being stopped, such as when
246         * {@link org.apache.camel.CamelContext} is shutting down.
247         * 
248         * @see #doStart() 
249         */
250        protected abstract void doStop() throws Exception;
251    
252        /**
253         * Implementations override this method to support customized suspend/resume.
254         */
255        protected void doSuspend() throws Exception {
256        }
257    
258        /**
259         * Implementations override this method to support customized suspend/resume.
260         */
261        protected void doResume() throws Exception {
262        }
263    
264        /**
265         * Implementations override this method to perform customized shutdown.
266         */
267        protected void doShutdown() throws Exception {
268            // noop
269        }
270    
271        @Override
272        public synchronized String getVersion() {
273            if (version != null) {
274                return version;
275            }
276    
277            // try to load from maven properties first
278            try {
279                Properties p = new Properties();
280                InputStream is = getClass().getResourceAsStream("/META-INF/maven/org.apache.camel/camel-core/pom.properties");
281                if (is != null) {
282                    p.load(is);
283                    version = p.getProperty("version", "");
284                }
285            } catch (Exception e) {
286                // ignore
287            }
288    
289            // fallback to using Java API
290            if (version == null) {
291                Package aPackage = getClass().getPackage();
292                if (aPackage != null) {
293                    version = aPackage.getImplementationVersion();
294                    if (version == null) {
295                        version = aPackage.getSpecificationVersion();
296                    }
297                }
298            }
299    
300            if (version == null) {
301                // we could not compute the version so use a blank
302                version = "";
303            }
304    
305            return version;
306        }
307    }