Component state - documentation

Also some minor refactoring.
This commit is contained in:
Emmanuel BENOîT 2015-09-16 10:11:34 +02:00
parent e688bb366c
commit 1969094daa
2 changed files with 192 additions and 15 deletions

View file

@ -490,13 +490,7 @@ public class ComponentRegistry
private void startComponent( ComponentState cmp )
throws ComponentStartupException
{
try {
cmp.runLifecycleAction( LifecycleStage.START );
} catch ( ComponentStartupException e ) {
throw e;
} catch ( Throwable t ) {
throw new ComponentStartupException( t );
}
cmp.runStartupAction( );
cmp.setActive( true );
}
}

View file

@ -11,24 +11,56 @@ import info.ebenoit.ebul.func.ThrowingConsumer;
/**
* Component state information and control class
* <p>
* This class tracks a component's state once it has been bound to a {@link ComponentRegistry}. In addition, it also
* allows control over the component by providing methods to start, stop and restart a component (these methods may only
* be used if the registry is active).
*
* @author <a href="mailto:ebenoit@ebenoit.info">E. Benoît</a>
*/
public final class ComponentState
{
/** The registry this state belongs to */
private final ComponentRegistry registry;
/** The component's object */
private final Object component;
/** The component's name */
private final String name;
/** Whether the component is supposed to start automatically */
private final boolean autostart;
/** Whether the component has been initialised */
private boolean initialised;
/** Whether the component has been started */
private boolean active;
/** Whether the component was active when {@link #stop()} was called */
private boolean wasActive;
/** State records for the component's dependencies */
private final HashSet< ComponentState > dependencies = new HashSet< >( );
/** State records for the component's reverse dependencies */
private final HashSet< ComponentState > reverseDependencies = new HashSet< >( );
/** Lifecycle actions for the current component */
@SuppressWarnings( "rawtypes" )
private final ThrowingConsumer[] lcActions;
/**
* Initialise the component state record based on a {@link NewComponentInfo} instance
* <p>
* This constructor obtains the component itself (either directly or by calling the supplier from the registration
* information), determines the component's name, and fetches autostart and lifecycle actions information.
*
* @param registry
* the registry generating this state record
* @param ci
* the component registration information record
* @throws ComponentCreationException
* if the registration information uses a supplier and that supplier somehow fails.
*/
ComponentState( final ComponentRegistry registry , final NewComponentInfo< ? > ci )
throws ComponentCreationException
{
@ -64,6 +96,14 @@ public final class ComponentState
}
/**
* Returns a description of the component.
* <p>
* The description generated by this method includes the name of the component, if there is one, as well as its
* type.
*
* @return the component's description
*/
@Override
public String toString( )
{
@ -78,60 +118,109 @@ public final class ComponentState
}
/**
* @return the component's instance
*/
public Object getComponent( )
{
return this.component;
}
/**
* @return the component's name
*/
public String getName( )
{
return this.name;
}
/**
* @return <code>true</code> if the component is set up to start automatically, <code>false</code> otherwise
*/
public boolean hasAutostart( )
{
return this.autostart;
}
/**
* @return an unmodifiable set describing the component's dependencies
*/
public Set< ComponentState > getDependencies( )
{
return Collections.unmodifiableSet( this.dependencies );
}
/**
* Adds the state records for the component's dependencies to a collection
*
* @param output
* the collection to add the records to
*/
public void getDependencies( final Collection< ComponentState > output )
{
output.addAll( this.dependencies );
}
/**
* @return an unmodifiable set describing the component's reverse dependencies
*/
public Set< ComponentState > getReverseDependencies( )
{
return Collections.unmodifiableSet( this.reverseDependencies );
}
/**
* Adds the state records for the component's reverse dependencies to a collection
*
* @param output
* the collection to add the records to
*/
public void getReverseDependencies( final Collection< ComponentState > output )
{
output.addAll( this.reverseDependencies );
}
/**
* @return <code>true</code> if the component was initialised, <code>false</code> if it wasn't (or if it was
* destroyed)
*/
public boolean isInitialised( )
{
return this.initialised;
}
/**
* @return <code>true</code> if the component is currently active, <code>false</code> otherwise
*/
public boolean isActive( )
{
return this.active;
}
/**
* Attempts to start the component
* <p>
* This method attempts to start the component. The component must be initialised and the registry must be active.
* If the component is already active, nothing is done. If it is inactive, the method will try starting the
* component's dependencies, then call the startup action if one is configured.
* <p>
* If the method succeeds, the component will be marked as active.
*
* @throws ComponentStartupException
* if an exception occurs while running the startup action for this component or for one of its
* dependencies
* @throws IllegalStateException
* if the component is not initialised, if the registry has failed or if it is inactive
*/
public void start( )
throws ComponentStartupException , IllegalStateException
{
@ -149,6 +238,19 @@ public final class ComponentState
}
/**
* Attempts to stop the component
* <p>
* This method attempts to shut down the component. If the component has reverse dependencies, it will start by
* shutting them down. Then it will call the configured shutdown action, if there is one. If both steps succeed, it
* will mark the component as inactive.
* <p>
* If the method is called on an inactive component, nothing will happen.
*
* @return <code>null</code> if the method succeeds, or an exception thrown by a shutdown action.
* @throws IllegalStateException
* if the registry has failed.
*/
public Throwable stop( )
throws IllegalStateException
{
@ -179,6 +281,17 @@ public final class ComponentState
}
/**
* Attempts to restart the component
* <p>
* This method attempts to restart a component. It will stop the component and its reverse dependencies, then start
* all components that were previously active.
*
* @throws IllegalStateException
* if the registry has failed
* @throws ComponentRestartException
* if an error occurred either during shutdown or during startup
*/
public void restart( )
throws IllegalStateException , ComponentRestartException
{
@ -191,25 +304,60 @@ public final class ComponentState
}
/**
* Sets the component's initialisation flag. Internal method used by {@link ComponentRegistry}.
*
* @param initialised
* <code>true</code> if the component is to be marked as initialised, <code>false</code> if it is to be
* marked as uninitialised
*/
void setInitialised( final boolean initialised )
{
this.initialised = initialised;
}
/**
* Sets the component's activity flag. Internal method used by {@link ComponentRegistry}.
*
* @param active
* <code>true</code> if the component is to be marked as active, <code>false</code> if it is to be marked
* as inactive
*/
void setActive( final boolean active )
{
this.active = active;
}
/**
* Adds another state record to the set of dependencies.
* <p>
* This internal method (called by {@link ComponentRegistry}) adds a new dependency to the current record. It also
* updates the dependency's record, adding the current record as a reverse dependency.
*
* @param dep
* the state record of the dependency to add
*/
void addDependency( final ComponentState dep )
{
assert dep.registry == this.registry;
this.dependencies.add( dep );
dep.reverseDependencies.add( this );
}
/**
* Executes a lifecycle action.
* <p>
* This method attempts to execute the lifecycle action for a specified lifecycle stage. If no action is configured,
* it does nothing.
*
* @param stage
* the lifecycle stage whose action must be executed
* @throws Throwable
* any exception thrown by the lifecycle action
*/
void runLifecycleAction( final LifecycleStage stage )
throws Throwable
{
@ -226,6 +374,38 @@ public final class ComponentState
}
/**
* Internal method to run the component's startup action
* <p>
* Runs the action for {@link LifecycleStage#START}, wrapping exceptions that are not
* {@link ComponentStartupException} into a {@link ComponentStartupException} instance.
*
* @throws ComponentStartupException
* if the start action throws an exception
*/
void runStartupAction( )
throws ComponentStartupException
{
try {
this.runLifecycleAction( LifecycleStage.START );
} catch ( final ComponentStartupException e ) {
throw e;
} catch ( final Throwable t ) {
throw new ComponentStartupException( t );
}
}
/**
* Internal component startup method.
* <p>
* This method implements the actions behind {@link #start()}, but it doesn't check if the record and registry are
* in legal states.
*
* @throws ComponentStartupException
* if an exception occurs while running the startup action for this component or for one of its
* dependencies
*/
private void startNoChecks( )
throws ComponentStartupException
{
@ -235,23 +415,26 @@ public final class ComponentState
for ( final ComponentState depState : this.dependencies ) {
depState.startNoChecks( );
}
try {
this.runLifecycleAction( LifecycleStage.START );
} catch ( final ComponentStartupException e ) {
throw e;
} catch ( final Throwable t ) {
throw new ComponentStartupException( t );
}
runStartupAction( );
this.active = true;
}
/**
* Internal component restart method
* <p>
* Attempts to restart a component if it was active before {@link #restart()} was called.
*
* @throws ComponentRestartException
* if an exception occurs while running the startup action for this component or for one of its reverse
* dependencies
*/
private void startIfPreviouslyActive( )
throws ComponentRestartException
{
if ( this.wasActive ) {
try {
this.startNoChecks( );
runStartupAction( );
} catch ( final ComponentStartupException e ) {
throw new ComponentRestartException( "failed to restart " + this , e );
}