package org.oddjob.framework;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.log4j.Logger;
import org.oddjob.FailedToStopException;
import org.oddjob.Resetable;
import org.oddjob.Stateful;
import org.oddjob.Stoppable;
import org.oddjob.arooa.life.ComponentPersistException;
import org.oddjob.images.IconHelper;
import org.oddjob.images.StateIcons;
import org.oddjob.logging.LogEnabled;
import org.oddjob.persist.Persistable;
import org.oddjob.state.IsAnyState;
import org.oddjob.state.IsExecutable;
import org.oddjob.state.IsHardResetable;
import org.oddjob.state.IsSoftResetable;
import org.oddjob.state.IsStoppable;
import org.oddjob.state.ServiceState;
import org.oddjob.state.ServiceStateChanger;
import org.oddjob.state.ServiceStateHandler;
/**
* Base class for providing a common Service implementation.
* <p>
* Note that this class is only required when special interaction with
* Oddjob is required such as different state handling. Most simple
* service can be provided a classes to be proxied.
*
* @author rob
*
*/
abstract public class SimpleService extends BaseComponent
implements Runnable, Stateful, Resetable,
Stoppable, LogEnabled {
private static final AtomicInteger instanceCount = new AtomicInteger();
/** Instance of the logger. */
private final Logger logger = Logger.getLogger(getClass().getName() +
"." + instanceCount.incrementAndGet());
/** Handle state. */
private final ServiceStateHandler stateHandler;
/** Used to notify clients of an icon change. */
private final IconHelper iconHelper;
/** Used to change state. */
private final ServiceStateChanger stateChanger;
/**
* @oddjob.property
* @oddjob.description A name, can be any text.
* @oddjob.required No.
*/
private volatile String name;
/**
* Constructor.
*
*/
public SimpleService() {
stateHandler = new ServiceStateHandler(this);
iconHelper = new IconHelper(this,
StateIcons.iconFor(stateHandler.getState()));
stateChanger = new ServiceStateChanger(stateHandler, iconHelper,
new Persistable() {
@Override
public void persist() throws ComponentPersistException {
save();
}
});
}
@Override
protected Logger logger() {
return logger;
}
@Override
public String loggerName() {
return logger.getName();
}
@Override
protected ServiceStateHandler stateHandler() {
return stateHandler;
}
@Override
protected IconHelper iconHelper() {
return iconHelper;
}
protected ServiceStateChanger getStateChanger() {
return stateChanger;
}
public void run() {
ComponentBoundry.push(logger().getName(), this);
try {
if (!stateHandler.waitToWhen(new IsExecutable(), new Runnable() {
public void run() {
getStateChanger().setState(ServiceState.STARTING);
}
})) {
return;
}
logger().info("Starting.");
try {
configure(SimpleService.this);
onStart();
stateHandler.waitToWhen(new IsAnyState(), new Runnable() {
public void run() {
getStateChanger().setState(ServiceState.STARTED);
}
});
}
catch (final Throwable e) {
logger().warn("Exception starting:", e);
stateHandler.waitToWhen(new IsAnyState(), new Runnable() {
public void run() {
getStateChanger().setStateException(e);
}
});
}
}
finally {
ComponentBoundry.pop();
}
}
/**
*
* @throws Exception
*/
abstract protected void onStart() throws Throwable;
@Override
public void stop() throws FailedToStopException {
ComponentBoundry.push(logger().getName(), this);
try {
logger().debug("Stop requested.");
if (!stateHandler.waitToWhen(new IsStoppable(),
new Runnable() {
public void run() {
iconHelper.changeIcon(IconHelper.STOPPING);
}
})) {
logger().debug("Stop ignored - not running.");
return;
}
logger().info("Stopping.");
try {
onStop();
logger().info("Stopped.");
stateHandler.waitToWhen(new IsStoppable(), new Runnable() {
public void run() {
getStateChanger().setState(ServiceState.STOPPED);
}
});
} catch (final Exception e) {
logger().warn("Exception starting:", e);
stateHandler.waitToWhen(new IsAnyState(), new Runnable() {
public void run() {
getStateChanger().setStateException(e);
}
});
}
}
finally {
ComponentBoundry.pop();
}
}
/**
* Allow sub classes to do something on stop.
*/
protected void onStop() throws FailedToStopException { }
/**
* Perform a soft reset on the job.
*/
@Override
public boolean softReset() {
ComponentBoundry.push(loggerName(), this);
try {
return stateHandler.waitToWhen(new IsSoftResetable(), new Runnable() {
public void run() {
getStateChanger().setState(ServiceState.STARTABLE);
logger().info("Soft Reset complete." );
}
});
}
finally {
ComponentBoundry.pop();
}
}
/**
* Perform a hard reset on the job.
*/
@Override
public boolean hardReset() {
ComponentBoundry.push(loggerName(), this);
try {
return stateHandler.waitToWhen(new IsHardResetable(), new Runnable() {
public void run() {
getStateChanger().setState(ServiceState.STARTABLE);
logger().info("Hard Reset complete." );
}
});
}
finally {
ComponentBoundry.pop();
}
}
/**
* Get the name.
*
* @return The name.
*/
public String getName() {
return name;
}
/**
* Set the name
*
* @param name The name.
*/
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
if (name == null) {
return getClass().getSimpleName();
}
else {
return name;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
try {
stop();
} catch (FailedToStopException e) {
logger().warn(e);
}
}
/**
* Internal method to fire state.
*/
@Override
protected void fireDestroyedState() {
if (!stateHandler().waitToWhen(new IsAnyState(), new Runnable() {
public void run() {
stateHandler().setState(ServiceState.DESTROYED);
stateHandler().fireEvent();
}
})) {
throw new IllegalStateException("[" + SimpleService.this +
"[ Failed set state DESTROYED");
}
logger().debug("[" + this + "] Destroyed.");
}
}