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 */ 017package org.apache.camel.support; 018 019import java.io.InputStream; 020import java.util.Properties; 021import java.util.concurrent.atomic.AtomicBoolean; 022 023import org.apache.camel.ServiceStatus; 024import org.apache.camel.StatefulService; 025import org.apache.camel.util.IOHelper; 026import org.slf4j.Logger; 027import 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 */ 038public 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 // if we have not yet initialized, then all options is false 238 boolean unused1 = !started.get() && !starting.get() && !stopping.get() && !stopped.get(); 239 boolean unused2 = !suspending.get() && !suspended.get() && !shutdown.get() && !shuttingdown.get(); 240 if (unused1 && unused2) { 241 return false; 242 } 243 return !isStoppingOrStopped(); 244 } 245 246 /** 247 * Is the service in progress of being stopped or already stopped 248 */ 249 public boolean isStoppingOrStopped() { 250 return stopping.get() || stopped.get(); 251 } 252 253 /** 254 * Is the service in progress of being suspended or already suspended 255 */ 256 public boolean isSuspendingOrSuspended() { 257 return suspending.get() || suspended.get(); 258 } 259 260 /** 261 * Implementations override this method to support customized start/stop. 262 * <p/> 263 * <b>Important: </b> See {@link #doStop()} for more details. 264 * 265 * @see #doStop() 266 */ 267 protected abstract void doStart() throws Exception; 268 269 /** 270 * Implementations override this method to support customized start/stop. 271 * <p/> 272 * <b>Important:</b> Camel will invoke this {@link #doStop()} method when 273 * the service is being stopped. This method will <b>also</b> be invoked 274 * if the service is still in <i>uninitialized</i> state (eg has not 275 * been started). The method is <b>always</b> called to allow the service 276 * to do custom logic when the service is being stopped, such as when 277 * {@link org.apache.camel.CamelContext} is shutting down. 278 * 279 * @see #doStart() 280 */ 281 protected abstract void doStop() throws Exception; 282 283 /** 284 * Implementations override this method to support customized suspend/resume. 285 */ 286 protected void doSuspend() throws Exception { 287 // noop 288 } 289 290 /** 291 * Implementations override this method to support customized suspend/resume. 292 */ 293 protected void doResume() throws Exception { 294 // noop 295 } 296 297 /** 298 * Implementations override this method to perform customized shutdown. 299 */ 300 protected void doShutdown() throws Exception { 301 // noop 302 } 303 304 @Override 305 public synchronized String getVersion() { 306 if (version != null) { 307 return version; 308 } 309 InputStream is = null; 310 // try to load from maven properties first 311 try { 312 Properties p = new Properties(); 313 is = getClass().getResourceAsStream("/META-INF/maven/org.apache.camel/camel-core/pom.properties"); 314 if (is != null) { 315 p.load(is); 316 version = p.getProperty("version", ""); 317 } 318 } catch (Exception e) { 319 // ignore 320 } finally { 321 if (is != null) { 322 IOHelper.close(is); 323 } 324 } 325 326 // fallback to using Java API 327 if (version == null) { 328 Package aPackage = getClass().getPackage(); 329 if (aPackage != null) { 330 version = aPackage.getImplementationVersion(); 331 if (version == null) { 332 version = aPackage.getSpecificationVersion(); 333 } 334 } 335 } 336 337 if (version == null) { 338 // we could not compute the version so use a blank 339 version = ""; 340 } 341 342 return version; 343 } 344}