All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.camel.support.service.BaseService Maven / Gradle / Ivy

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.camel.support.service;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.camel.RuntimeCamelException;
import org.apache.camel.ServiceStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A useful base class which ensures that a service is only initialized once and provides some helper methods for
 * enquiring of its status.
 * 

* Implementations can extend this base class and implement {@link org.apache.camel.SuspendableService} in case they * support suspend/resume. *

* Important: You should override the lifecycle methods that start with do, eg {@link #doStart()}}, * {@link #doStop()}, etc. where you implement your logic. The methods {@link #start()}, {@link #stop()} should * NOT be overridden as they are used internally to keep track of the state of this service and properly invoke * the operation in a safe manner. */ public abstract class BaseService { protected static final byte NEW = 0; protected static final byte BUILT = 1; protected static final byte INITIALIZING = 2; protected static final byte INITIALIZED = 3; protected static final byte STARTING = 4; protected static final byte STARTED = 5; protected static final byte SUSPENDING = 6; protected static final byte SUSPENDED = 7; protected static final byte STOPPING = 8; protected static final byte STOPPED = 9; protected static final byte SHUTTING_DOWN = 10; protected static final byte SHUTDOWN = 11; protected static final byte FAILED = 12; protected final Lock lock = new ReentrantLock(); protected volatile byte status = NEW; public void build() { if (status == NEW) { lock.lock(); try { if (status == NEW) { try (AutoCloseable ignored = doLifecycleChange()) { doBuild(); } catch (Exception e) { doFail(e); } status = BUILT; } } finally { lock.unlock(); } } } public void init() { // allow to initialize again if stopped or failed if (status <= BUILT || status >= STOPPED) { lock.lock(); try { if (status <= BUILT || status >= STOPPED) { build(); try (AutoCloseable ignored = doLifecycleChange()) { status = INITIALIZING; doInit(); status = INITIALIZED; } catch (Exception e) { logger().trace("Error while initializing service: {}", this, e); fail(e); } } } finally { lock.unlock(); } } } /** * Important: You should override the lifecycle methods that start with do, eg {@link #doStart()}, * {@link #doStop()}, etc. where you implement your logic. The methods {@link #start()}, {@link #stop()} should * NOT be overridden as they are used internally to keep track of the state of this service and properly * invoke the operation in a safe manner. */ public void start() { lock.lock(); try { if (status == STARTED) { logger().trace("Service: {} already started", this); return; } if (status == STARTING) { logger().trace("Service: {} already starting", this); return; } init(); if (status == FAILED) { logger().trace("Init failed"); return; } try (AutoCloseable ignored = doLifecycleChange()) { status = STARTING; logger().trace("Starting service: {}", this); doStart(); status = STARTED; logger().trace("Started service: {}", this); } catch (Exception e) { // need to stop as some resources may have been started during startup try { stop(); } catch (Exception e2) { // ignore logger().trace("Error while stopping service after it failed to start: {}. This exception is ignored", this, e); } logger().trace("Error while starting service: {}", this, e); fail(e); } } finally { lock.unlock(); } } /** * Important: You should override the lifecycle methods that start with do, eg {@link #doStart()}, * {@link #doStop()}, etc. where you implement your logic. The methods {@link #start()}, {@link #stop()} should * NOT be overridden as they are used internally to keep track of the state of this service and properly * invoke the operation in a safe manner. */ public void stop() { lock.lock(); try { if (status == FAILED) { logger().trace("Service: {} failed and regarded as already stopped", this); return; } if (status == STOPPED || status == SHUTTING_DOWN || status == SHUTDOWN) { logger().trace("Service: {} already stopped", this); return; } if (status == STOPPING) { logger().trace("Service: {} already stopping", this); return; } status = STOPPING; logger().trace("Stopping service: {}", this); try (AutoCloseable ignored = doLifecycleChange()) { doStop(); status = STOPPED; logger().trace("Stopped: {} service", this); } catch (Exception e) { logger().trace("Error while stopping service: {}", this, e); fail(e); } } finally { lock.unlock(); } } /** * Important: You should override the lifecycle methods that start with do, eg {@link #doStart()}, * {@link #doStop()}, etc. where you implement your logic. The methods {@link #start()}, {@link #stop()} should * NOT be overridden as they are used internally to keep track of the state of this service and properly * invoke the operation in a safe manner. */ public void suspend() { lock.lock(); try { if (status == SUSPENDED) { logger().trace("Service: {} already suspended", this); return; } if (status == SUSPENDING) { logger().trace("Service: {} already suspending", this); return; } status = SUSPENDING; logger().trace("Suspending service: {}", this); try (AutoCloseable ignored = doLifecycleChange()) { doSuspend(); status = SUSPENDED; logger().trace("Suspended service: {}", this); } catch (Exception e) { logger().trace("Error while suspending service: {}", this, e); fail(e); } } finally { lock.unlock(); } } /** * Important: You should override the lifecycle methods that start with do, eg {@link #doStart()}, * {@link #doStop()}, etc. where you implement your logic. The methods {@link #start()}, {@link #stop()} should * NOT be overridden as they are used internally to keep track of the state of this service and properly * invoke the operation in a safe manner. */ public void resume() { lock.lock(); try { if (status != SUSPENDED) { logger().trace("Service is not suspended: {}", this); return; } status = STARTING; logger().trace("Resuming service: {}", this); try (AutoCloseable ignored = doLifecycleChange()) { doResume(); status = STARTED; logger().trace("Resumed service: {}", this); } catch (Exception e) { logger().trace("Error while resuming service: {}", this, e); fail(e); } } finally { lock.unlock(); } } /** * Important: You should override the lifecycle methods that start with do, eg {@link #doStart()}, * {@link #doStop()}, etc. where you implement your logic. The methods {@link #start()}, {@link #stop()} should * NOT be overridden as they are used internally to keep track of the state of this service and properly * invoke the operation in a safe manner. */ public void shutdown() { lock.lock(); try { if (status == SHUTDOWN) { logger().trace("Service: {} already shutdown", this); return; } if (status == SHUTTING_DOWN) { logger().trace("Service: {} already shutting down", this); return; } stop(); status = SHUTDOWN; logger().trace("Shutting down service: {}", this); try (AutoCloseable ignored = doLifecycleChange()) { doShutdown(); logger().trace("Shutdown service: {}", this); status = SHUTDOWN; } catch (Exception e) { logger().trace("Error shutting down service: {}", this, e); fail(e); } } finally { lock.unlock(); } } public ServiceStatus getStatus() { switch (status) { case INITIALIZING: return ServiceStatus.Initializing; case INITIALIZED: return ServiceStatus.Initialized; case STARTING: return ServiceStatus.Starting; case STARTED: return ServiceStatus.Started; case SUSPENDING: return ServiceStatus.Suspending; case SUSPENDED: return ServiceStatus.Suspended; case STOPPING: return ServiceStatus.Stopping; default: return ServiceStatus.Stopped; } } public boolean isNew() { return status == NEW; } public boolean isBuild() { return status == BUILT; } public boolean isInit() { return status == INITIALIZED; } public boolean isStarted() { return status == STARTED; } public boolean isStarting() { return status == STARTING; } public boolean isStopping() { return status == STOPPING; } public boolean isStopped() { return status < STARTING || status >= STOPPED; } public boolean isSuspending() { return status == SUSPENDING; } public boolean isSuspended() { return status == SUSPENDED; } public boolean isRunAllowed() { return status >= STARTING && status <= SUSPENDED; } public boolean isShutdown() { return status == SHUTDOWN; } /** * Is the service in progress of being stopped or already stopped */ public boolean isStoppingOrStopped() { return status < STARTING || status > SUSPENDED; } /** * Is the service in progress of being suspended or already suspended */ public boolean isSuspendingOrSuspended() { return status == SUSPENDING || status == SUSPENDED; } /** * Is the service in progress of being suspended or already suspended */ public boolean isStartingOrStarted() { return status == STARTING || status == STARTED; } protected void fail(Exception e) { try { doFail(e); } finally { status = FAILED; } } /** * Optional build phase of the service. This method will only be called by frameworks which supports pre-building * projects such as camel-quarkus. */ protected void doBuild() throws Exception { // noop } /** * Initialize the service. This method will only be called once before starting. */ protected void doInit() throws Exception { // noop } /** * Implementations override this method to support customized start/stop. *

* Important: See {@link #doStop()} for more details. * * @see #doStop() */ protected void doStart() throws Exception { // noop } /** * Implementations override this method to support customized start/stop. *

* Important: Camel will invoke this {@link #doStop()} method when the service is being stopped. This method * will also be invoked if the service is still in uninitialized state (eg has not been started). The * method is always called to allow the service to do custom logic when the service is being stopped, such as * when {@link org.apache.camel.CamelContext} is shutting down. * * @see #doStart() */ protected void doStop() throws Exception { // noop } /** * Implementations override this method to support customized suspend/resume. */ protected void doSuspend() throws Exception { // noop } /** * Implementations override this method to support customized suspend/resume. */ protected void doResume() throws Exception { // noop } /** * Implementations override this method to perform customized shutdown. */ protected void doShutdown() throws Exception { // noop } /** * Implementations override this method to perform any action upon failure. */ protected void doFail(Exception e) { throw RuntimeCamelException.wrapRuntimeException(e); } /** * Implementations may return an object that will be closed when the lifecycle action is completed. */ protected AutoCloseable doLifecycleChange() { return null; } /* * NOTE: see CAMEL-19724. We log like this instead of using a statically declared logger in order to * reduce the risk of dropping log messages due to slf4j log substitution behavior during its own * initialization. */ private static final class Holder { static final Logger LOG = LoggerFactory.getLogger(Holder.class); } private static Logger logger() { return Holder.LOG; } protected Lock getInternalLock() { return lock; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy