Component state - documentation
Also some minor refactoring.
This commit is contained in:
parent
e688bb366c
commit
1969094daa
2 changed files with 192 additions and 15 deletions
|
@ -490,13 +490,7 @@ public class ComponentRegistry
|
||||||
private void startComponent( ComponentState cmp )
|
private void startComponent( ComponentState cmp )
|
||||||
throws ComponentStartupException
|
throws ComponentStartupException
|
||||||
{
|
{
|
||||||
try {
|
cmp.runStartupAction( );
|
||||||
cmp.runLifecycleAction( LifecycleStage.START );
|
|
||||||
} catch ( ComponentStartupException e ) {
|
|
||||||
throw e;
|
|
||||||
} catch ( Throwable t ) {
|
|
||||||
throw new ComponentStartupException( t );
|
|
||||||
}
|
|
||||||
cmp.setActive( true );
|
cmp.setActive( true );
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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
|
public final class ComponentState
|
||||||
{
|
{
|
||||||
|
/** The registry this state belongs to */
|
||||||
private final ComponentRegistry registry;
|
private final ComponentRegistry registry;
|
||||||
|
/** The component's object */
|
||||||
private final Object component;
|
private final Object component;
|
||||||
|
/** The component's name */
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
||||||
|
/** Whether the component is supposed to start automatically */
|
||||||
private final boolean autostart;
|
private final boolean autostart;
|
||||||
|
/** Whether the component has been initialised */
|
||||||
private boolean initialised;
|
private boolean initialised;
|
||||||
|
/** Whether the component has been started */
|
||||||
private boolean active;
|
private boolean active;
|
||||||
|
/** Whether the component was active when {@link #stop()} was called */
|
||||||
private boolean wasActive;
|
private boolean wasActive;
|
||||||
|
|
||||||
|
/** State records for the component's dependencies */
|
||||||
private final HashSet< ComponentState > dependencies = new HashSet< >( );
|
private final HashSet< ComponentState > dependencies = new HashSet< >( );
|
||||||
|
/** State records for the component's reverse dependencies */
|
||||||
private final HashSet< ComponentState > reverseDependencies = new HashSet< >( );
|
private final HashSet< ComponentState > reverseDependencies = new HashSet< >( );
|
||||||
|
|
||||||
|
/** Lifecycle actions for the current component */
|
||||||
@SuppressWarnings( "rawtypes" )
|
@SuppressWarnings( "rawtypes" )
|
||||||
private final ThrowingConsumer[] lcActions;
|
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 )
|
ComponentState( final ComponentRegistry registry , final NewComponentInfo< ? > ci )
|
||||||
throws ComponentCreationException
|
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
|
@Override
|
||||||
public String toString( )
|
public String toString( )
|
||||||
{
|
{
|
||||||
|
@ -78,60 +118,109 @@ public final class ComponentState
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the component's instance
|
||||||
|
*/
|
||||||
public Object getComponent( )
|
public Object getComponent( )
|
||||||
{
|
{
|
||||||
return this.component;
|
return this.component;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the component's name
|
||||||
|
*/
|
||||||
public String getName( )
|
public String getName( )
|
||||||
{
|
{
|
||||||
return this.name;
|
return this.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return <code>true</code> if the component is set up to start automatically, <code>false</code> otherwise
|
||||||
|
*/
|
||||||
public boolean hasAutostart( )
|
public boolean hasAutostart( )
|
||||||
{
|
{
|
||||||
return this.autostart;
|
return this.autostart;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return an unmodifiable set describing the component's dependencies
|
||||||
|
*/
|
||||||
public Set< ComponentState > getDependencies( )
|
public Set< ComponentState > getDependencies( )
|
||||||
{
|
{
|
||||||
return Collections.unmodifiableSet( this.dependencies );
|
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 )
|
public void getDependencies( final Collection< ComponentState > output )
|
||||||
{
|
{
|
||||||
output.addAll( this.dependencies );
|
output.addAll( this.dependencies );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return an unmodifiable set describing the component's reverse dependencies
|
||||||
|
*/
|
||||||
public Set< ComponentState > getReverseDependencies( )
|
public Set< ComponentState > getReverseDependencies( )
|
||||||
{
|
{
|
||||||
return Collections.unmodifiableSet( this.reverseDependencies );
|
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 )
|
public void getReverseDependencies( final Collection< ComponentState > output )
|
||||||
{
|
{
|
||||||
output.addAll( this.reverseDependencies );
|
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( )
|
public boolean isInitialised( )
|
||||||
{
|
{
|
||||||
return this.initialised;
|
return this.initialised;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return <code>true</code> if the component is currently active, <code>false</code> otherwise
|
||||||
|
*/
|
||||||
public boolean isActive( )
|
public boolean isActive( )
|
||||||
{
|
{
|
||||||
return this.active;
|
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( )
|
public void start( )
|
||||||
throws ComponentStartupException , IllegalStateException
|
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( )
|
public Throwable stop( )
|
||||||
throws IllegalStateException
|
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( )
|
public void restart( )
|
||||||
throws IllegalStateException , ComponentRestartException
|
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 )
|
void setInitialised( final boolean initialised )
|
||||||
{
|
{
|
||||||
this.initialised = 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 )
|
void setActive( final boolean active )
|
||||||
{
|
{
|
||||||
this.active = 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 )
|
void addDependency( final ComponentState dep )
|
||||||
{
|
{
|
||||||
|
assert dep.registry == this.registry;
|
||||||
this.dependencies.add( dep );
|
this.dependencies.add( dep );
|
||||||
dep.reverseDependencies.add( this );
|
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 )
|
void runLifecycleAction( final LifecycleStage stage )
|
||||||
throws Throwable
|
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( )
|
private void startNoChecks( )
|
||||||
throws ComponentStartupException
|
throws ComponentStartupException
|
||||||
{
|
{
|
||||||
|
@ -235,23 +415,26 @@ public final class ComponentState
|
||||||
for ( final ComponentState depState : this.dependencies ) {
|
for ( final ComponentState depState : this.dependencies ) {
|
||||||
depState.startNoChecks( );
|
depState.startNoChecks( );
|
||||||
}
|
}
|
||||||
try {
|
runStartupAction( );
|
||||||
this.runLifecycleAction( LifecycleStage.START );
|
|
||||||
} catch ( final ComponentStartupException e ) {
|
|
||||||
throw e;
|
|
||||||
} catch ( final Throwable t ) {
|
|
||||||
throw new ComponentStartupException( t );
|
|
||||||
}
|
|
||||||
this.active = true;
|
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( )
|
private void startIfPreviouslyActive( )
|
||||||
throws ComponentRestartException
|
throws ComponentRestartException
|
||||||
{
|
{
|
||||||
if ( this.wasActive ) {
|
if ( this.wasActive ) {
|
||||||
try {
|
try {
|
||||||
this.startNoChecks( );
|
runStartupAction( );
|
||||||
} catch ( final ComponentStartupException e ) {
|
} catch ( final ComponentStartupException e ) {
|
||||||
throw new ComponentRestartException( "failed to restart " + this , e );
|
throw new ComponentRestartException( "failed to restart " + this , e );
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue