From d083be2676f0c561ad79af1c4181eb57080738eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emmanuel=20Beno=C3=AEt?= Date: Tue, 15 Sep 2015 15:12:44 +0200 Subject: [PATCH] Components registry - Initialisation and destruction --- .../cmp/ComponentInitialisationException.java | 48 ++++++++ .../ebenoit/ebul/cmp/ComponentRegistry.java | 109 +++++++++++++++++- .../info/ebenoit/ebul/cmp/ComponentState.java | 42 ++++++- 3 files changed, 192 insertions(+), 7 deletions(-) create mode 100644 src/main/java/info/ebenoit/ebul/cmp/ComponentInitialisationException.java diff --git a/src/main/java/info/ebenoit/ebul/cmp/ComponentInitialisationException.java b/src/main/java/info/ebenoit/ebul/cmp/ComponentInitialisationException.java new file mode 100644 index 0000000..1e405fa --- /dev/null +++ b/src/main/java/info/ebenoit/ebul/cmp/ComponentInitialisationException.java @@ -0,0 +1,48 @@ +package info.ebenoit.ebul.cmp; + + +public class ComponentInitialisationException + extends ComponentException +{ + + private static final long serialVersionUID = 6150762006182320443L; + private ComponentInitialisationException chain; + + + public ComponentInitialisationException( ) + { + super( ); + } + + + public ComponentInitialisationException( String message , Throwable cause ) + { + super( message , cause ); + } + + + public ComponentInitialisationException( String message ) + { + super( message ); + } + + + public ComponentInitialisationException( Throwable cause ) + { + super( cause ); + } + + + ComponentInitialisationException( Throwable cause , ComponentInitialisationException initial ) + { + super( "failed to revert incomplete initialisation" , cause ); + this.chain = initial; + } + + + public ComponentInitialisationException getChain( ) + { + return chain; + } + +} diff --git a/src/main/java/info/ebenoit/ebul/cmp/ComponentRegistry.java b/src/main/java/info/ebenoit/ebul/cmp/ComponentRegistry.java index b93d32b..11c2226 100644 --- a/src/main/java/info/ebenoit/ebul/cmp/ComponentRegistry.java +++ b/src/main/java/info/ebenoit/ebul/cmp/ComponentRegistry.java @@ -17,7 +17,7 @@ import info.ebenoit.ebul.reflection.Classes; public class ComponentRegistry { - private static final class TempBuildRec + private final class TempBuildRec { private final NewComponentInfo< ? > info; private final ComponentState state; @@ -28,7 +28,7 @@ public class ComponentRegistry private TempBuildRec( NewComponentInfo< ? > info ) { this.info = info; - this.state = new ComponentState( info ); + this.state = new ComponentState( ComponentRegistry.this , info ); this.deps = new HashMap< >( ); } @@ -38,10 +38,11 @@ public class ComponentRegistry private HashMap< String , ComponentState > byName; private HashMap< Class< ? > , ArrayList< ComponentState > > byType; - - // private boolean initialised; + private boolean failed; + private boolean initialised; // private boolean active; + public ComponentRegistry( ) { this.components = new ArrayList< >( ); @@ -50,6 +51,18 @@ public class ComponentRegistry } + public boolean hasFailed( ) + { + return failed; + } + + + public boolean isInitialised( ) + { + return initialised; + } + + public ComponentState getState( String name ) { return this.byName.get( name ); @@ -144,8 +157,12 @@ public class ComponentRegistry public ComponentRegistry register( Collection< NewComponentInfo< ? > > components ) throws ComponentCreationException , DuplicateComponentException , RecursiveDependenciesException , - DependencyInjectionException , AmbiguousComponentException + DependencyInjectionException , AmbiguousComponentException , ComponentInitialisationException { + if ( this.failed ) { + throw new IllegalStateException( "register() called on failed registry" ); + } + // Create "raw" state records for the new components int nAdd = components.size( ); int nLeft = nAdd; @@ -177,16 +194,76 @@ public class ComponentRegistry injectDependencies( adding ); // Replace registry contents + int nOld = this.components.size( ); this.components = nComponents; this.byName = byName; this.byType = byType; - // TODO: handle initialisation and activation + // Initialise all new components if the registry has been initialised + if ( this.initialised ) { + for ( int i = nOld ; i < nOld + nAdd ; i++ ) { + initComponent( i ); + } + } + + // TODO: activate autostart components return this; } + public void initialise( ) + throws ComponentInitialisationException + { + if ( this.failed ) { + throw new IllegalStateException( "initialise() called on failed registry" ); + } + if ( this.initialised ) { + return; + } + + int nComponents = this.components.size( ); + for ( int i = 0 ; i < nComponents ; i++ ) { + initComponent( i ); + } + + this.initialised = true; + } + + + public boolean destroy( ) + { + return this.destroy( null ); + } + + + public boolean destroy( Collection< Throwable > errors ) + { + if ( this.failed ) { + throw new IllegalStateException( "destroy() called on failed registry" ); + } + if ( !this.initialised ) { + return true; + } + + for ( int i = this.components.size( ) - 1 ; i >= 0 ; i-- ) { + try { + ComponentState cmp = this.components.get( i ); + cmp.runLifecycleAction( LifecycleStage.DESTROY ); + cmp.setInitialised( false ); + } catch ( Throwable t ) { + if ( errors != null ) { + errors.add( t ); + } + this.failed = true; + } + } + + this.initialised = false; + return !this.failed; + } + + private static int resolveNewDependencies( ArrayList< TempBuildRec > adding , ArrayList< ComponentState > nComponents , HashMap< String , ComponentState > byName , HashMap< Class< ? > , ArrayList< ComponentState > > byType ) @@ -294,4 +371,24 @@ public class ComponentRegistry } } } + + + private void initComponent( int i ) + throws ComponentInitialisationException + { + try { + ComponentState cmp = this.components.get( i ); + try { + cmp.runLifecycleAction( LifecycleStage.INITIALISE ); + } catch ( ComponentInitialisationException e ) { + throw e; + } catch ( Throwable t ) { + throw new ComponentInitialisationException( t ); + } + cmp.setInitialised( true ); + } catch ( ComponentInitialisationException e ) { + this.failed = true; + throw e; + } + } } \ No newline at end of file diff --git a/src/main/java/info/ebenoit/ebul/cmp/ComponentState.java b/src/main/java/info/ebenoit/ebul/cmp/ComponentState.java index d72cd63..657b381 100644 --- a/src/main/java/info/ebenoit/ebul/cmp/ComponentState.java +++ b/src/main/java/info/ebenoit/ebul/cmp/ComponentState.java @@ -4,11 +4,13 @@ package info.ebenoit.ebul.cmp; import java.util.HashSet; import info.ebenoit.ebul.func.FunctionException; +import info.ebenoit.ebul.func.ThrowingConsumer; public final class ComponentState { + private final ComponentRegistry registry; private final Object component; private final String name; @@ -19,10 +21,15 @@ public final class ComponentState private final HashSet< ComponentState > dependencies = new HashSet< >( ); private final HashSet< ComponentState > reverseDependencies = new HashSet< >( ); + @SuppressWarnings( "rawtypes" ) + private final ThrowingConsumer[] lcActions; - ComponentState( final NewComponentInfo< ? > ci ) + + ComponentState( ComponentRegistry registry , final NewComponentInfo< ? > ci ) throws ComponentCreationException { + this.registry = registry; + Object component = ci.getComponent( ); if ( component == null ) { try { @@ -45,6 +52,11 @@ public final class ComponentState this.name = name; this.autostart = ci.getAutostart( ); + + this.lcActions = new ThrowingConsumer[ 4 ]; + for ( LifecycleStage stage : LifecycleStage.values( ) ) { + this.lcActions[ stage.ordinal( ) ] = ci.getLifecycleAction( stage ); + } } @@ -92,10 +104,38 @@ public final class ComponentState } + void setInitialised( boolean initialised ) + { + this.initialised = initialised; + } + + + void setActive( boolean active ) + { + this.active = active; + } + + void addDependency( ComponentState dep ) { this.dependencies.add( dep ); dep.reverseDependencies.add( this ); } + + void runLifecycleAction( LifecycleStage stage ) + throws Throwable + { + @SuppressWarnings( "unchecked" ) + ThrowingConsumer< Object > tc = this.lcActions[ stage.ordinal( ) ]; + + if ( tc != null ) { + try { + tc.accept( this.component ); + } catch ( FunctionException e ) { + throw e.getCause( ); + } + } + } + }