Component state and registry - Drivers support

A component with "drivers" will initialise/start its drivers after it is
initialised/started itself. It will also stop/destroy non-driver reverse
dependencies before stopping/destroying its drivers.
This commit is contained in:
Emmanuel BENOîT 2015-09-17 10:57:46 +02:00
parent 81ea62047e
commit 68a337ec6a
3 changed files with 71 additions and 11 deletions

6
TODO
View file

@ -1,8 +1,7 @@
To Do: To Do:
* Test for ComponentState.{init,destroy} * Test for ComponentState.{init,destroy} and driver-related stuff
* Registry tests * Registry tests
* Registry doc * Registry doc
* Implement "DriverFor" support
* General usage documentation * General usage documentation
* Uncouple component-provided names from the library * Uncouple component-provided names from the library
* Automatically-updated singletons * Automatically-updated singletons
@ -11,4 +10,5 @@ To Do:
Other ideas (maybe later if needed): Other ideas (maybe later if needed):
* Unregistering components * Unregistering components
* Hierarchy of registries * Hierarchy of registries
* Driver-related helpers (e.g. determine type of driver components, register/unregister drivers with main)

View file

@ -197,6 +197,15 @@ public class ComponentRegistry
nLeft -= found; nLeft -= found;
} }
// Set up driver components
for ( int i = 0 ; i < nAdd ; i++ ) {
TempBuildRec c = adding.get( i );
String df = c.info.getDriverFor( );
if ( df != null ) {
c.state.setDriverFor( byName.get( df ) );
}
}
// Inject and track dependencies // Inject and track dependencies
injectDependencies( adding ); injectDependencies( adding );
@ -446,6 +455,7 @@ public class ComponentRegistry
} }
} }
private void destroyComponent( int index ) private void destroyComponent( int index )
throws ComponentDestructionException throws ComponentDestructionException
{ {

View file

@ -44,6 +44,11 @@ public final class ComponentState
/** State records for the component's reverse dependencies */ /** State records for the component's reverse dependencies */
private final HashSet< ComponentState > reverseDependencies = new HashSet< >( ); private final HashSet< ComponentState > reverseDependencies = new HashSet< >( );
/** State records for the component's drivers */
private final HashSet< ComponentState > drivers = new HashSet< >( );
/** Main component (i.e. component this component is a driver for) */
private ComponentState mainComponent;
/** Lifecycle actions for the current component */ /** Lifecycle actions for the current component */
@SuppressWarnings( "rawtypes" ) @SuppressWarnings( "rawtypes" )
private final ThrowingConsumer[] lcActions; private final ThrowingConsumer[] lcActions;
@ -217,7 +222,8 @@ public final class ComponentState
* If the component is already active, nothing is done. If it is inactive, the method will try starting the * 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. * component's dependencies, then call the startup action if one is configured.
* <p> * <p>
* If the method succeeds, the component will be marked as active. * If these actions succeed, the component will be marked as active. Then, if the component has drivers, they will
* be started as well.
* *
* @throws ComponentStartupException * @throws ComponentStartupException
* if an exception occurs while running the startup action for this component or for one of its * if an exception occurs while running the startup action for this component or for one of its
@ -246,8 +252,8 @@ public final class ComponentState
* Stops the component * Stops the component
* <p> * <p>
* This method attempts to shut down the component. If the component has reverse dependencies, it will start by * 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 * shutting them down (stopping "normal" reverse dependencies first, then shutting down drivers). Then it will call
* will mark the component as inactive. * the configured shutdown action, if there is one. If both steps succeed, it will mark the component as inactive.
* <p> * <p>
* If the method is called on an inactive component, nothing will happen. * If the method is called on an inactive component, nothing will happen.
* *
@ -269,7 +275,12 @@ public final class ComponentState
assert this.initialised; assert this.initialised;
for ( final ComponentState rdepState : this.reverseDependencies ) { for ( final ComponentState rdepState : this.reverseDependencies ) {
rdepState.stop( ); if ( !this.drivers.contains( rdepState ) ) {
rdepState.stop( );
}
}
for ( final ComponentState drvState : this.drivers ) {
drvState.stop( );
} }
this.runLifecycleAction( LifecycleStage.STOP ); this.runLifecycleAction( LifecycleStage.STOP );
this.active = false; this.active = false;
@ -301,7 +312,8 @@ public final class ComponentState
* Initialises the component * Initialises the component
* <p> * <p>
* This method attempts to initialise a component. It makes sure that all of the component's dependencies have been * This method attempts to initialise a component. It makes sure that all of the component's dependencies have been
* initialised, then executes the lifecycle action for the {@link LifecycleStage#INITIALISE INITIALISE} stage. * initialised, then executes the lifecycle action for the {@link LifecycleStage#INITIALISE INITIALISE} stage. Then,
* if the component has drivers, it initialises them.
* *
* @throws ComponentInitialisationException * @throws ComponentInitialisationException
* if an error occurs while executing an initialisation action * if an error occurs while executing an initialisation action
@ -323,6 +335,9 @@ public final class ComponentState
} }
this.runLifecycleAction( LifecycleStage.INITIALISE ); this.runLifecycleAction( LifecycleStage.INITIALISE );
this.initialised = true; this.initialised = true;
for ( final ComponentState drvState : this.drivers ) {
drvState.init( );
}
} }
@ -330,7 +345,8 @@ public final class ComponentState
* Destroys the component * Destroys the component
* <p> * <p>
* This method attempts to destroy a component. It makes sure that all of the component's reverse dependencies have * This method attempts to destroy a component. It makes sure that all of the component's reverse dependencies have
* been destroyed, then executes the lifecycle action for the {@link LifecycleStage#DESTROY DESTROY} stage. * been destroyed (starting with "normal" reverse dependencies, then processing the drivers), then executes the
* lifecycle action for the {@link LifecycleStage#DESTROY DESTROY} stage.
* *
* @throws ComponentDestructionException * @throws ComponentDestructionException
* if an error occurs while executing a destruction action * if an error occurs while executing a destruction action
@ -351,7 +367,12 @@ public final class ComponentState
} }
for ( final ComponentState rdepState : this.reverseDependencies ) { for ( final ComponentState rdepState : this.reverseDependencies ) {
rdepState.destroy( ); if ( !this.drivers.contains( rdepState ) ) {
rdepState.destroy( );
}
}
for ( final ComponentState drvState : this.drivers ) {
drvState.destroy( );
} }
this.runLifecycleAction( LifecycleStage.DESTROY ); this.runLifecycleAction( LifecycleStage.DESTROY );
this.initialised = false; this.initialised = false;
@ -375,6 +396,23 @@ public final class ComponentState
} }
/**
* Sets a component as the one this component is a driver for.
* <p>
* This will also update the main component to add this component as a driver.
*
* @param main
* the main component
*/
void setDriverFor( final ComponentState main )
{
assert main.registry == this.registry;
assert this.mainComponent == null;
this.mainComponent = main;
main.drivers.add( this );
}
/** /**
* Executes a lifecycle action. * Executes a lifecycle action.
* <p> * <p>
@ -438,15 +476,24 @@ public final class ComponentState
for ( final ComponentState depState : this.dependencies ) { for ( final ComponentState depState : this.dependencies ) {
depState.startNoChecks( ); depState.startNoChecks( );
} }
if ( this.active ) {
// If the current component is a driver, it will have been started by its main component, so we need to
// avoid re-executing the start action.
return;
}
this.runLifecycleAction( LifecycleStage.START ); this.runLifecycleAction( LifecycleStage.START );
this.active = true; this.active = true;
for ( final ComponentState drvState : this.drivers ) {
drvState.startNoChecks( );
}
} }
/** /**
* Internal component restart method * Internal component restart method
* <p> * <p>
* Attempts to restart a component if it was active before {@link #restart()} was called. * Attempts to restart a component if it was active before {@link #restart()} was called. Calls the start action
* first, then restart the drivers, then other reverse dependencies.
* *
* @throws ComponentStartupException * @throws ComponentStartupException
* if an exception occurs while running the startup action for this component or for one of its reverse * if an exception occurs while running the startup action for this component or for one of its reverse
@ -457,6 +504,9 @@ public final class ComponentState
{ {
if ( this.wasActive ) { if ( this.wasActive ) {
this.runLifecycleAction( LifecycleStage.START ); this.runLifecycleAction( LifecycleStage.START );
for ( final ComponentState drvState : this.drivers ) {
drvState.startIfPreviouslyActive( );
}
for ( final ComponentState rDepState : this.reverseDependencies ) { for ( final ComponentState rDepState : this.reverseDependencies ) {
rDepState.startIfPreviouslyActive( ); rDepState.startIfPreviouslyActive( );
} }