| Manageable.java |
/*
* $Id: Manageable.java,v 1.37 2012/02/28 18:10:54 agoubard Exp $
*
* See the COPYRIGHT file for redistribution and use restrictions.
*/
package org.xins.common.manageable;
import java.util.Collections;
import java.util.Map;
import org.xins.common.MandatoryArgumentChecker;
import org.xins.common.collections.InvalidPropertyValueException;
import org.xins.common.collections.MissingRequiredPropertyException;
/**
* Abstraction of a manageable object. Abstract base class for classes that
* support bootstrap, initialization and deinitialization functions.
*
* <p>In environments where <code>Manageable</code> instances are constructed
* dynamically, they are typically expected to have a public no-argument
* constructor.
*
* <p>Initially the state of a manageable object is {@link #UNUSABLE}. In this
* state, the object should be considered unusable. To change to the
* {@link #USABLE} state, the {@link #bootstrap(Map)} and
* {@link #init(Map)} methods should be called first, as described
* below.
*
* <p>The {@link #bootstrap(Map)} method can only be called if the
* state of this object is {@link #UNUSABLE}. If it finishes successfully, the
* state then changes to {@link #BOOTSTRAPPED}.
*
* <p>After that the {@link #init(Map)} method should be called to
* initialize or re-initialize this object. This method can only be called
* successfully if the current state is either {@link #BOOTSTRAPPED} or even
* {@link #USABLE}.
*
* <p>The {@link #deinit()} method is called when this object is no
* longer needed. That changes the state back to {@link #UNUSABLE}. After
* that, {@link #bootstrap(Map)} could be called again, though.
*
* @version $Revision: 1.37 $ $Date: 2012/02/28 18:10:54 $
* @author <a href="mailto:ernst@ernstdehaan.com">Ernst de Haan</a>
*
* @since XINS 1.0.0
*/
public abstract class Manageable {
/**
* The <em>UNUSABLE</em> state.
*/
public static final State UNUSABLE = new State(0, "UNUSABLE");
/**
* The <em>BOOTSTRAPPING</em> state.
*/
public static final State BOOTSTRAPPING = new State(2, "BOOTSTRAPPING");
/**
* The <em>BOOTSTRAPPED</em> state.
*/
public static final State BOOTSTRAPPED = new State(3, "BOOTSTRAPPED");
/**
* The <em>INITIALIZING</em> state.
*/
public static final State INITIALIZING = new State(4, "INITIALIZING");
/**
* The <em>USABLE</em> state.
*/
public static final State USABLE = new State(5, "USABLE");
/**
* The <em>DEINITIALIZING</em> state.
*/
public static final State DEINITIALIZING = new State(1, "DEINITIALIZING");
/**
* The state of this manageable object.
*/
private State _state;
/**
* The lock for the state object.
*/
private Object _stateLock;
/**
* Constructs a new <code>Manageable</code>.
*/
protected Manageable() {
_state = UNUSABLE;
_stateLock = new Object();
}
/**
* Gets the current state of this object.
*
* @return
* the current state, never <code>null</code>.
*/
public final State getState() {
return _state;
}
/**
* Performs the bootstrap procedure (wrapper method).
*
* <p>If the state of this object is valid (it must be {@link #UNUSABLE})
* and the argument is not <code>null</code>, then
* {@link #bootstrapImpl(Map)} will be called. If that method
* succeeds, then this object will be left in the {@link #BOOTSTRAPPED}
* state.
*
* <p>If {@link #bootstrapImpl(Map)} throws any exception (even
* {@link Error}s), it is wrapped in an {@link BootstrapException} and then
* the latter is thrown instead.
*
* @param properties
* the bootstrap properties, can be <code>null</code>.
*
* @throws IllegalStateException
* if the current state is not {@link #UNUSABLE}.
*
* @throws MissingRequiredPropertyException
* if a required property is not given.
*
* @throws InvalidPropertyValueException
* if the value of a certain property is invalid.
*
* @throws BootstrapException
* if the bootstrapping failed for any other reason.
*/
public final void bootstrap(Map<String, String> properties)
throws IllegalStateException,
MissingRequiredPropertyException,
InvalidPropertyValueException,
BootstrapException {
State erroneousState = null;
// Get the current state and change to BOOTSTRAPPING if it is valid
synchronized (_stateLock) {
if (_state != UNUSABLE) {
erroneousState = _state;
} else {
_state = BOOTSTRAPPING;
}
}
// If the state was invalid, then fail
if (erroneousState != null) {
final String MESSAGE = "The current state is "
+ erroneousState
+ " instead of UNUSABLE.";
throw new IllegalStateException(MESSAGE);
}
// If no properties are passed, then use an empty set
if (properties == null) {
properties = Collections.EMPTY_MAP;
}
// Delegate to subclass
State newState = UNUSABLE;
try {
bootstrapImpl(properties);
newState = BOOTSTRAPPED;
// Catch expected exceptions
} catch (MissingRequiredPropertyException exception) {
throw exception;
} catch (InvalidPropertyValueException exception) {
throw exception;
} catch (BootstrapException exception) {
throw exception;
// Wrap other exceptions in an InitializationException
} catch (Throwable exception) {
throw new BootstrapException(exception);
// Always set the state before returning
} finally {
synchronized (_stateLock) {
_state = newState;
}
}
}
/**
* Performs the bootstrap procedure (actual implementation). When this
* method is called from {@link #bootstrap(Map)}, the state and
* the argument will have been checked and the state will have been set to
* {@link #BOOTSTRAPPING}.
*
* <p>The implementation of this method in class {@link Manageable} is
* empty.
*
* @param properties
* the bootstrap properties, not <code>null</code>.
*
* @throws MissingRequiredPropertyException
* if a required property is not given.
*
* @throws InvalidPropertyValueException
* if the value of a certain property is invalid.
*
* @throws BootstrapException
* if the bootstrapping failed for any other reason.
*/
protected void bootstrapImpl(Map<String, String> properties)
throws MissingRequiredPropertyException,
InvalidPropertyValueException,
BootstrapException {
// empty
}
/**
* Performs the initialization procedure (wrapper method).
*
* <p>If the state of this object is valid (it must be either
* {@link #BOOTSTRAPPED} or {@link #USABLE}) and the argument is not
* <code>null</code>, then {@link #initImpl(Map)} will be
* called. If that method succeeds, then this object will be left in the
* {@link #USABLE} state. If an exception is thrown, then this object will
* be left in the {@link #BOOTSTRAPPED} state instead.
*
* <p>If {@link #initImpl(Map)} throws any exception (even
* {@link Error}s), it is wrapped in an {@link InitializationException} and
* then the latter is thrown instead.
*
* @param properties
* the initialization properties, can be <code>null</code>.
*
* @throws IllegalStateException
* if the current state is not {@link #BOOTSTRAPPED} or {@link #USABLE}.
*
* @throws MissingRequiredPropertyException
* if a required property is not given.
*
* @throws InvalidPropertyValueException
* if the value of a certain property is invalid.
*
* @throws InitializationException
* if the initialization failed for any other reason.
*/
public final void init(Map<String, String> properties)
throws IllegalStateException,
MissingRequiredPropertyException,
InvalidPropertyValueException,
InitializationException {
State erroneousState = null;
// Get the current state and change to INITIALIZING if it is valid
synchronized (_stateLock) {
if (_state != BOOTSTRAPPED && _state != USABLE) {
erroneousState = _state;
} else {
_state = INITIALIZING;
}
}
// If the state was invalid, then fail
if (erroneousState != null) {
String message = "The current state is "
+ erroneousState
+ " instead of either "
+ BOOTSTRAPPED
+ " or "
+ USABLE
+ '.';
throw new IllegalStateException(message);
}
// If no properties are passed, then use an empty set
if (properties == null) {
properties = Collections.EMPTY_MAP;
}
// Delegate to subclass
State newState = BOOTSTRAPPED;
try {
initImpl(properties);
newState = USABLE;
// Catch expected exceptions
} catch (MissingRequiredPropertyException exception) {
throw exception;
} catch (InvalidPropertyValueException exception) {
throw exception;
} catch (InitializationException exception) {
throw exception;
// Wrap other exceptions in an InitializationException
} catch (Throwable exception) {
throw new InitializationException(exception);
// Always set the state before returning
} finally {
synchronized (_stateLock) {
_state = newState;
}
}
}
/**
* Performs the initialization procedure (actual implementation). When this
* method is called from {@link #init(Map)}, the state and the
* argument will have been checked and the state will have been set to
* {@link #INITIALIZING}.
*
* <p>The implementation of this method in class {@link Manageable} is
* empty.
*
* @param properties
* the initialization properties, not <code>null</code>.
*
* @throws MissingRequiredPropertyException
* if a required property is not given.
*
* @throws InvalidPropertyValueException
* if the value of a certain property is invalid.
*
* @throws InitializationException
* if the initialization failed, for any other reason.
*/
protected void initImpl(Map<String, String> properties)
throws MissingRequiredPropertyException,
InvalidPropertyValueException,
InitializationException {
// empty
}
/**
* Deinitializes this instance (wrapper method). This method relies on
* {@link #deinitImpl()} to actually perform the deinitialization.
*
* <p>The current state of this object must be either {@link #BOOTSTRAPPED}
* or {@link #USABLE}.
*
* <p>When this method returns, the state has been set to
* {@link #UNUSABLE}, even if {@link #deinitImpl()} threw an exception.
*
* <p>If {@link #deinitImpl()} throws any exception, it is wrapped in a
* {@link DeinitializationException} and
* then the latter is thrown instead.
*
* @throws IllegalStateException
* if the state is not {@link #BOOTSTRAPPED} nor {@link #USABLE}.
*
* @throws DeinitializationException
* if the deinitialization caused an exception in
* {@link #deinitImpl()}.
*/
public final void deinit()
throws IllegalStateException, DeinitializationException {
State erroneousState = null;
// Get the current state and change to DEINITIALIZING if it is valid
synchronized (_stateLock) {
if (_state != BOOTSTRAPPED && _state != USABLE) {
erroneousState = _state;
} else {
_state = DEINITIALIZING;
}
}
// If the state was invalid, then fail
if (erroneousState != null) {
String message = "The current state is "
+ erroneousState
+ " instead of either "
+ BOOTSTRAPPED
+ " or "
+ USABLE
+ '.';
throw new IllegalStateException(message);
}
// Delegate to subclass
State newState = BOOTSTRAPPED;
try {
deinitImpl();
newState = UNUSABLE;
// Catch and wrap all caught exceptions
} catch (Throwable exception) {
throw new DeinitializationException(exception);
// Always set the state before returning
} finally {
synchronized (_stateLock) {
_state = newState;
}
}
}
/**
* Deinitializes this instance (actual implementation). This method will be
* called from {@link #deinit()} each time the latter is called and it
* finds that the state is correct. The state will have been set to
* {@link #DEINITIALIZING}.
*
* @throws Throwable
* if the deinitialization caused an exception.
*/
protected void deinitImpl()
throws Throwable {
// empty
}
/**
* Determines if this object is currently bootstrapped. Even if this object
* is already initialized, then it is still considered bootstrapped.
*
* @return
* <code>true</code> if this object is bootstrapped,
* <code>false</code> if it is not.
*
* @since XINS 1.5.0
*/
public final boolean isBootstrapped() {
State state;
synchronized (_stateLock) {
state = _state;
}
return state.getLevel() >= BOOTSTRAPPED.getLevel();
}
/**
* Determines if this object is currently usable.
*
* @return
* <code>true</code> if this object is usable,
* <code>false</code> if it is not.
*/
public final boolean isUsable() {
State state;
synchronized (_stateLock) {
state = _state;
}
return state == USABLE;
}
/**
* Asserts that this object is currently usable. If it is not, then an
* {@link IllegalStateException} is thrown.
*
* @throws IllegalStateException
* if this object is not in the {@link #USABLE} state.
*/
protected final void assertUsable()
throws IllegalStateException {
// Minimize the time the lock is held
State state;
synchronized (_stateLock) {
state = _state;
}
// Construct and throw an exception, if appropriate
if (state != USABLE) {
String message = "The current state is "
+ state
+ " instead of "
+ USABLE
+ '.';
throw new IllegalStateException(message);
}
}
/**
* State of a <code>Manageable</code> object.
*
* @version $Revision: 1.37 $ $Date: 2012/02/28 18:10:54 $
* @author <a href="mailto:ernst@ernstdehaan.com">Ernst de Haan</a>
*
* @since XINS 1.0.0
*/
public static final class State {
/**
* Constructs a new <code>State</code> object.
*
* @param level
* the level of this state.
*
* @param name
* the name of this state, cannot be <code>null</code>.
*
* @throws IllegalArgumentException
* if <code>name == null</code>.
*/
private State(int level, String name) throws IllegalArgumentException {
// Check preconditions
MandatoryArgumentChecker.check("name", name);
_level = level;
_name = name;
}
/**
* The level of this state.
*/
private final int _level;
/**
* The name of this state. Cannot be <code>null</code>.
*/
private final String _name;
/**
* Returns the level of this state.
*
* @return
* the level of this state, cannot be <code>null</code>.
*/
int getLevel() {
return _level;
}
/**
* Returns the name of this state.
*
* @return
* the name of this state, cannot be <code>null</code>.
*/
public String getName() {
return _name;
}
/**
* Returns a textual representation of this object.
*
* @return
* the name of this state, never <code>null</code>.
*/
public String toString() {
return _name;
}
}
}