/**
* Copyright 2013 Benjamin Lerer
*
* Licensed 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 io.horizondb.db;
import io.horizondb.db.metrics.Monitorable;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.codahale.metrics.MetricRegistry;
/**
* Base class for <code>Component</code> instance. This class manage the state of the component.
*
*/
public abstract class AbstractComponent implements Component {
/**
* The logger.
*/
protected final Logger logger = LoggerFactory.getLogger(getClass());
/**
* The status of this component.
*/
private final AtomicReference<Status> status = new AtomicReference<>(Status.NOT_STARTED);
/**
* {@inheritDoc}
*/
@Override
public final String getName() {
return MetricRegistry.name(this.getClass());
}
/**
* {@inheritDoc}
*/
@Override
public final void start() throws InterruptedException, IOException {
if (!this.status.compareAndSet(Status.NOT_STARTED, Status.STARTING)) {
throw new IllegalStateException("component " + getName()
+ " has already been started or is in the process of starting");
}
try {
this.logger.info("component {} is starting up", getName());
doStart();
this.logger.info("component {} has succesfully started", getName());
} catch (Exception e) {
this.status.set(Status.NOT_STARTED);
throw e;
}
this.status.set(Status.RUNNING);
}
/**
* {@inheritDoc}
*/
@Override
public final void shutdown() throws InterruptedException {
if (!this.status.compareAndSet(Status.RUNNING, Status.SHUTTING_DOWN)) {
throw new IllegalStateException("component " + getName()
+ " is not in a running state so it cannot be shutdown");
}
try {
this.logger.info("{} is shutting down", getName());
doShutdown();
} finally {
this.status.set(Status.SHUTDOWN);
}
this.logger.info("component {} has succesfully shutdown", getName());
}
/**
* Returns <code>true</code> if this component is running, <code>false</code> otherwise.
*
* @return <code>true</code> if this component is running, <code>false</code> otherwise.
*/
public final boolean isRunning() {
return this.status.get() == Status.RUNNING;
}
/**
* Starts the specified set of components in the specified order.
*
* @param components the components to start
* @throws IOException if one of the components throws an <code>IOException</code> while starting
* @throws InterruptedException if one of the components throws an <code>InterruptedException</code> while starting
*/
protected static final void start(Component... components) throws IOException, InterruptedException {
for (Component component : components) {
component.start();
}
}
/**
* Shutdown the specified set of components in the specified order.
*
* @param components the components to shutdown
* @throws InterruptedException if one of the components throws an <code>InterruptedException</code> while starting
*/
protected static final void shutdown(Component... components) throws InterruptedException {
for (Component component : components) {
component.shutdown();
}
}
/**
* Checks if the component is running and throw an <code>IllegalStateException</code> if it is not.
*
* @throws IllegalStateException if the component is not in a running state.
*/
protected final void checkRunning() {
if (!isRunning()) {
throw new IllegalStateException("The " + getName() + " component is not in a running state.");
}
}
/**
* Register the specified set of components in the specified order.
*
* @param monitorables the components to monitor
*/
protected static final void register(MetricRegistry registry, Monitorable... monitorables) {
for (Monitorable monitorable : monitorables) {
monitorable.register(registry);
}
}
/**
* Unregister the specified set of components in the specified order.
*
* @param monitorables the components to unregister
*/
protected static final void unregister(MetricRegistry registry, Monitorable... monitorables) {
for (Monitorable monitorable : monitorables) {
monitorable.unregister(registry);
}
}
/**
* Performs the actual component startup.
*
* @throws IOException if an IO problem occurs during startup.
* @throws InterruptedException if the thread has been interrupted.
*/
protected abstract void doStart() throws IOException, InterruptedException;
/**
* Performs the actual shutdown.
*
* @throws InterruptedException if the thread has been interrupted.
*/
protected abstract void doShutdown() throws InterruptedException;
/**
* The possible status for this component.
*/
protected static enum Status {
NOT_STARTED, STARTING, RUNNING, SHUTTING_DOWN, SHUTDOWN;
}
}