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.impl;
018    
019    import java.util.LinkedHashSet;
020    import java.util.Set;
021    import java.util.concurrent.atomic.AtomicBoolean;
022    
023    import org.apache.camel.Service;
024    import org.apache.camel.ServiceStatus;
025    import org.apache.camel.ShutdownableService;
026    import org.apache.camel.util.ObjectHelper;
027    import org.apache.camel.util.ServiceHelper;
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 $Revision: 20684 $
037     */
038    public abstract class ServiceSupport implements Service, ShutdownableService {
039    
040        private final AtomicBoolean started = new AtomicBoolean(false);
041        private final AtomicBoolean starting = new AtomicBoolean(false);
042        private final AtomicBoolean stopping = new AtomicBoolean(false);
043        private final AtomicBoolean stopped = new AtomicBoolean(false);
044        private final AtomicBoolean suspending = new AtomicBoolean(false);
045        private final AtomicBoolean suspended = new AtomicBoolean(false);
046        private final AtomicBoolean shuttingdown = new AtomicBoolean(false);
047        private final AtomicBoolean shutdown = new AtomicBoolean(false);
048        private Set<Object> childServices;
049        private String version;
050    
051        public void start() throws Exception {
052            start(true);
053        }
054    
055        public void start(boolean startChildren) throws Exception {
056            if (!started.get()) {
057                if (starting.compareAndSet(false, true)) {
058                    boolean childrenStarted = false;
059                    Exception ex = null;
060                    try {
061                        if (childServices != null && startChildren) {
062                            ServiceHelper.startServices(childServices);
063                        }
064                        childrenStarted = true;
065                        doStart();
066                    } catch (Exception e) {
067                        ex = e;
068                    } finally {
069                        if (ex != null) {
070                            try {
071                                stop(childrenStarted);
072                            } catch (Exception e) {
073                                // Ignore exceptions as we want to show the original exception
074                            }
075                            throw ex;
076                        } else {
077                            started.set(true);
078                            starting.set(false);
079                            stopping.set(false);
080                            stopped.set(false);
081                            suspending.set(false);
082                            suspended.set(false);
083                            shutdown.set(false);
084                            shuttingdown.set(false);
085                        }
086                    }
087                }
088            }
089        }
090        
091        private void stop(boolean childrenStarted) throws Exception {
092            if (stopping.compareAndSet(false, true)) {
093                try {
094                    try {
095                        starting.set(false);
096                        suspending.set(false);
097                        if (childrenStarted) {
098                            doStop();
099                        }
100                    } finally {
101                        started.set(false);
102                        suspended.set(false);
103                        if (childServices != null) {
104                            ServiceHelper.stopServices(childServices);
105                        }
106                    }
107                } finally {
108                    stopped.set(true);
109                    stopping.set(false);
110                    starting.set(false);
111                    started.set(false);
112                    suspending.set(false);
113                    suspended.set(false);
114                    shutdown.set(false);
115                    shuttingdown.set(false);
116                }
117            }
118        }
119    
120        public void stop() throws Exception {
121            if (!stopped.get()) {
122                stop(true);
123            }
124        }
125    
126        public void suspend() throws Exception {
127            if (!suspended.get()) {
128                if (suspending.compareAndSet(false, true)) {
129                    try {
130                        starting.set(false);
131                        stopping.set(false);
132                        doSuspend();
133                    } finally {
134                        stopped.set(false);
135                        stopping.set(false);
136                        starting.set(false);
137                        started.set(false);
138                        suspending.set(false);
139                        suspended.set(true);
140                        shutdown.set(false);
141                        shuttingdown.set(false);
142                    }
143                }
144            }
145        }
146    
147        public void resume() throws Exception {
148            if (suspended.get()) {
149                if (starting.compareAndSet(false, true)) {
150                    try {
151                        doResume();
152                    } finally {
153                        started.set(true);
154                        starting.set(false);
155                        stopping.set(false);
156                        stopped.set(false);
157                        suspending.set(false);
158                        suspended.set(false);
159                        shutdown.set(false);
160                        shuttingdown.set(false);
161                    }
162                }
163            }
164        }
165    
166        public void shutdown() throws Exception {
167            // ensure we are stopped first
168            stop();
169    
170            if (shuttingdown.compareAndSet(false, true)) {
171                try {
172                    try {
173                        doShutdown();
174                    } finally {
175                        if (childServices != null) {
176                            ServiceHelper.stopAndShutdownService(childServices);
177                        }
178                    }
179                } finally {
180                    // shutdown is also stopped so only set shutdown flags
181                    shutdown.set(true);
182                    shuttingdown.set(false);
183                }
184            }
185        }
186    
187        /**
188         * Returns the current status
189         */
190        public ServiceStatus getStatus() {
191            // lets check these in oldest first as these flags can be changing in a concurrent world
192            if (isStarting()) {
193                return ServiceStatus.Starting;
194            }
195            if (isStarted()) {
196                return ServiceStatus.Started;
197            }
198            if (isStopping()) {
199                return ServiceStatus.Stopping;
200            }
201            if (isStopped()) {
202                return ServiceStatus.Stopped;
203            }
204            if (isSuspending()) {
205                return ServiceStatus.Suspending;
206            }
207            if (isSuspended()) {
208                return ServiceStatus.Suspended;
209            }
210    
211            // use stopped as fallback
212            return ServiceStatus.Stopped;
213        }
214        
215        /**
216         * @return true if this service has been started
217         */
218        public boolean isStarted() {
219            return started.get();
220        }
221    
222        /**
223         * @return true if this service is being started
224         */
225        public boolean isStarting() {
226            return starting.get();
227        }
228    
229        /**
230         * @return true if this service is in the process of stopping
231         */
232        public boolean isStopping() {
233            return stopping.get();
234        }
235    
236        /**
237         * @return true if this service is stopped
238         */
239        public boolean isStopped() {
240            return stopped.get();
241        }
242    
243        /**
244         * @return true if this service is in the process of suspending
245         */
246        public boolean isSuspending() {
247            return suspending.get();
248        }
249    
250        /**
251         * @return true if this service is suspended
252         */
253        public boolean isSuspended() {
254            return suspended.get();
255        }
256    
257        /**
258         * Helper methods so the service knows if it should keep running.
259         * Returns <tt>false</tt> if the service is being stopped or is stopped.
260         *
261         * @return <tt>true</tt> if the service should continue to run.
262         */
263        public boolean isRunAllowed() {
264            return !(stopping.get() || stopped.get());
265        }
266    
267        protected abstract void doStart() throws Exception;
268    
269        protected abstract void doStop() throws Exception;
270    
271        /**
272         * Implementations override this method to support customized suspend/resume.
273         */
274        protected void doSuspend() throws Exception {
275        }
276    
277        /**
278         * Implementations override this method to support customized suspend/resume.
279         */
280        protected void doResume() throws Exception {
281        }
282    
283        /**
284         * Implementations override this method to perform customized shutdown
285         */
286        protected void doShutdown() throws Exception {
287            // noop
288        }
289    
290        @SuppressWarnings("unchecked")
291        protected void addChildService(Object childService) {
292            synchronized (this) {
293                if (childServices == null) {
294                    childServices = new LinkedHashSet();
295                }
296            }
297            childServices.add(childService);
298        }
299    
300        protected boolean removeChildService(Object childService) {
301            return childServices != null && childServices.remove(childService);
302        }
303    
304        /**
305         * Returns the version of this service
306         */
307        public synchronized String getVersion() {
308            if (ObjectHelper.isNotEmpty(version)) {
309                return version;
310            }
311            
312            Package aPackage = getClass().getPackage();
313            if (aPackage != null) {
314                version = aPackage.getImplementationVersion();
315                if (version == null) {
316                    version = aPackage.getSpecificationVersion();
317                }
318            }
319            return version != null ? version : "";
320        }
321    }