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.Collection;
020    import java.util.concurrent.CopyOnWriteArrayList;
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     *
033     * @version $Revision: 18474 $
034     */
035    public abstract class ServiceSupport implements Service, ShutdownableService {
036    
037        private final AtomicBoolean started = new AtomicBoolean(false);
038        private final AtomicBoolean starting = new AtomicBoolean(false);
039        private final AtomicBoolean stopping = new AtomicBoolean(false);
040        private final AtomicBoolean stopped = new AtomicBoolean(false);
041        private final AtomicBoolean shuttingdown = new AtomicBoolean(false);
042        private final AtomicBoolean shutdown = new AtomicBoolean(false);
043        private Collection<Object> childServices;
044        private String version;
045    
046        public void start() throws Exception {
047            if (!started.get()) {
048                if (starting.compareAndSet(false, true)) {
049                    boolean childrenStarted = false;
050                    Exception ex = null;
051                    try {
052                        if (childServices != null) {
053                            ServiceHelper.startServices(childServices);
054                        }
055                        childrenStarted = true;
056                        doStart();
057                    } catch (Exception e) {
058                        ex = e;
059                    } finally {
060                        if (ex != null) {
061                            try {
062                                stop(childrenStarted);
063                            } catch (Exception e) {
064                                // Ignore exceptions as we want to show the original exception
065                            }
066                            throw ex;
067                        } else {
068                            started.set(true);
069                            starting.set(false);
070                            stopping.set(false);
071                            stopped.set(false);
072                            shutdown.set(false);
073                            shuttingdown.set(false);
074                        }
075                    }
076                }
077            }
078        }
079        
080        private void stop(boolean childrenStarted) throws Exception {
081            if (stopping.compareAndSet(false, true)) {
082                try {
083                    try {
084                        starting.set(false);
085                        if (childrenStarted) {
086                            doStop();
087                        }
088                    } finally {
089                        started.set(false);
090                        if (childServices != null) {
091                            ServiceHelper.stopServices(childServices);
092                        }
093                    }
094                } finally {
095                    stopped.set(true);
096                    stopping.set(false);
097                    starting.set(false);
098                    started.set(false);
099                    shutdown.set(false);
100                    shuttingdown.set(false);
101                }
102            }
103        }
104    
105        public void stop() throws Exception {
106            if (started.get()) {
107                stop(true);
108            }
109        }
110    
111        public void shutdown() throws Exception {
112            // ensure we are stopped first
113            stop();
114    
115            if (shuttingdown.compareAndSet(false, true)) {
116                try {
117                    try {
118                        doShutdown();
119                    } finally {
120                        if (childServices != null) {
121                            ServiceHelper.stopAndShutdownService(childServices);
122                        }
123                    }
124                } finally {
125                    // shutdown is also stopped so only set shutdown flags
126                    shutdown.set(true);
127                    shuttingdown.set(false);
128                }
129            }
130        }
131    
132        /**
133         * Returns the current status
134         */
135        public ServiceStatus getStatus() {
136            // lets check these in oldest first as these flags can be changing in a concurrent world
137            if (isStarting()) {
138                return ServiceStatus.Starting;
139            }
140            if (isStarted()) {
141                return ServiceStatus.Started;
142            }
143            if (isStopping()) {
144                return ServiceStatus.Stopping;
145            }
146            if (isStopped()) {
147                return ServiceStatus.Stopped;
148            }
149    
150            // use stopped as fallback
151            return ServiceStatus.Stopped;
152        }
153        
154        /**
155         * @return true if this service has been started
156         */
157        public boolean isStarted() {
158            return started.get();
159        }
160    
161        /**
162         * @return true if this service is
163         */
164        public boolean isStarting() {
165            return starting.get();
166        }
167    
168        /**
169         * @return true if this service is in the process of closing
170         */
171        public boolean isStopping() {
172            return stopping.get();
173        }
174    
175        /**
176         * @return true if this service is closed
177         */
178        public boolean isStopped() {
179            return stopped.get();
180        }
181    
182        /**
183         * Helper methods so the service knows if it should keep running.
184         * Returns false if the service is being stopped or is stopped.
185         *
186         * @return true if the service should continue to run.
187         */
188        public boolean isRunAllowed() {
189            return !(stopping.get() || stopped.get());
190        }
191    
192        protected abstract void doStart() throws Exception;
193    
194        protected abstract void doStop() throws Exception;
195    
196        protected void doShutdown() throws Exception {
197            // noop
198        }
199    
200        @SuppressWarnings("unchecked")
201        protected void addChildService(Object childService) {
202            synchronized (this) {
203                if (childServices == null) {
204                    childServices = new CopyOnWriteArrayList();
205                }
206            }
207            childServices.add(childService);
208        }
209    
210        protected boolean removeChildService(Object childService) {
211            return childServices != null && childServices.remove(childService);
212        }
213    
214        /**
215         * Returns the version of this service
216         */
217        public synchronized String getVersion() {
218            if (ObjectHelper.isNotEmpty(version)) {
219                return version;
220            }
221            
222            Package aPackage = getClass().getPackage();
223            if (aPackage != null) {
224                version = aPackage.getImplementationVersion();
225                if (version == null) {
226                    version = aPackage.getSpecificationVersion();
227                }
228            }
229            return version != null ? version : "";
230        }
231    }