diff --git a/src/main/java/info/ebenoit/ebul/cmp/ComponentInitialisationException.java b/src/main/java/info/ebenoit/ebul/cmp/ComponentInitialisationException.java index 1e405fa..14aaee9 100644 --- a/src/main/java/info/ebenoit/ebul/cmp/ComponentInitialisationException.java +++ b/src/main/java/info/ebenoit/ebul/cmp/ComponentInitialisationException.java @@ -6,7 +6,6 @@ public class ComponentInitialisationException { private static final long serialVersionUID = 6150762006182320443L; - private ComponentInitialisationException chain; public ComponentInitialisationException( ) @@ -15,34 +14,21 @@ public class ComponentInitialisationException } - public ComponentInitialisationException( String message , Throwable cause ) + public ComponentInitialisationException( final String message , final Throwable cause ) { super( message , cause ); } - public ComponentInitialisationException( String message ) + public ComponentInitialisationException( final String message ) { super( message ); } - public ComponentInitialisationException( Throwable cause ) + public ComponentInitialisationException( final 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 11c2226..be97855 100644 --- a/src/main/java/info/ebenoit/ebul/cmp/ComponentRegistry.java +++ b/src/main/java/info/ebenoit/ebul/cmp/ComponentRegistry.java @@ -40,7 +40,7 @@ public class ComponentRegistry private boolean failed; private boolean initialised; - // private boolean active; + private boolean active; public ComponentRegistry( ) @@ -63,6 +63,12 @@ public class ComponentRegistry } + public boolean isActive( ) + { + return active; + } + + public ComponentState getState( String name ) { return this.byName.get( name ); @@ -157,7 +163,8 @@ public class ComponentRegistry public ComponentRegistry register( Collection< NewComponentInfo< ? > > components ) throws ComponentCreationException , DuplicateComponentException , RecursiveDependenciesException , - DependencyInjectionException , AmbiguousComponentException , ComponentInitialisationException + DependencyInjectionException , AmbiguousComponentException , ComponentInitialisationException , + ComponentStartupException { if ( this.failed ) { throw new IllegalStateException( "register() called on failed registry" ); @@ -206,7 +213,15 @@ public class ComponentRegistry } } - // TODO: activate autostart components + // Activate autostart components + if ( this.active ) { + for ( int i = 0 ; i < nOld + nAdd ; i++ ) { + ComponentState cmp = this.components.get( i ); + if ( cmp.hasAutostart( ) ) { + startComponentWithFailure( cmp ); + } + } + } return this; } @@ -232,12 +247,14 @@ public class ComponentRegistry public boolean destroy( ) + throws IllegalStateException { return this.destroy( null ); } public boolean destroy( Collection< Throwable > errors ) + throws IllegalStateException { if ( this.failed ) { throw new IllegalStateException( "destroy() called on failed registry" ); @@ -245,6 +262,9 @@ public class ComponentRegistry if ( !this.initialised ) { return true; } + if ( this.active && !this.stop( errors ) ) { + return false; + } for ( int i = this.components.size( ) - 1 ; i >= 0 ; i-- ) { try { @@ -264,6 +284,68 @@ public class ComponentRegistry } + public void start( ) + throws IllegalStateException , ComponentStartupException + { + if ( this.failed ) { + throw new IllegalStateException( "start() called on failed registry" ); + } + if ( !this.initialised ) { + throw new IllegalStateException( "start() called on uninitialised registry" ); + } + if ( this.active ) { + return; + } + + int nComponents = this.components.size( ); + for ( int i = 0 ; i < nComponents ; i++ ) { + ComponentState cmp = this.components.get( i ); + if ( cmp.hasAutostart( ) && !cmp.isActive( ) ) { + startComponentWithFailure( cmp ); + } + } + + this.initialised = true; + } + + + public boolean stop( ) + throws IllegalStateException + { + return this.stop( null ); + } + + + public boolean stop( Collection< Throwable > errors ) + throws IllegalStateException + { + if ( this.failed ) { + throw new IllegalStateException( "destroy() called on failed registry" ); + } + if ( !this.active ) { + return true; + } + + for ( int i = this.components.size( ) - 1 ; i >= 0 ; i-- ) { + try { + ComponentState cmp = this.components.get( i ); + if ( cmp.isActive( ) ) { + cmp.runLifecycleAction( LifecycleStage.STOP ); + cmp.setActive( false ); + } + } catch ( Throwable t ) { + if ( errors != null ) { + errors.add( t ); + } + this.failed = true; + } + } + + this.active = false; + return !this.failed; + } + + private static int resolveNewDependencies( ArrayList< TempBuildRec > adding , ArrayList< ComponentState > nComponents , HashMap< String , ComponentState > byName , HashMap< Class< ? > , ArrayList< ComponentState > > byType ) @@ -391,4 +473,30 @@ public class ComponentRegistry throw e; } } + + + private void startComponentWithFailure( ComponentState cmp ) + throws ComponentStartupException + { + try { + this.startComponent( cmp ); + } catch ( ComponentStartupException e ) { + this.failed = true; + throw e; + } + } + + + 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.setActive( true ); + } } \ No newline at end of file diff --git a/src/main/java/info/ebenoit/ebul/cmp/ComponentRestartException.java b/src/main/java/info/ebenoit/ebul/cmp/ComponentRestartException.java new file mode 100644 index 0000000..5d95599 --- /dev/null +++ b/src/main/java/info/ebenoit/ebul/cmp/ComponentRestartException.java @@ -0,0 +1,34 @@ +package info.ebenoit.ebul.cmp; + + +public class ComponentRestartException + extends ComponentException +{ + + private static final long serialVersionUID = -6812486665718289373L; + + + public ComponentRestartException( ) + { + super( ); + } + + + public ComponentRestartException( final String message , final Throwable cause ) + { + super( message , cause ); + } + + + public ComponentRestartException( final String message ) + { + super( message ); + } + + + public ComponentRestartException( final Throwable cause ) + { + super( cause ); + } + +} diff --git a/src/main/java/info/ebenoit/ebul/cmp/ComponentStartupException.java b/src/main/java/info/ebenoit/ebul/cmp/ComponentStartupException.java new file mode 100644 index 0000000..263522b --- /dev/null +++ b/src/main/java/info/ebenoit/ebul/cmp/ComponentStartupException.java @@ -0,0 +1,34 @@ +package info.ebenoit.ebul.cmp; + + +public class ComponentStartupException + extends ComponentException +{ + + private static final long serialVersionUID = -8010582342593242803L; + + + public ComponentStartupException( ) + { + super( ); + } + + + public ComponentStartupException( final String message , final Throwable cause ) + { + super( message , cause ); + } + + + public ComponentStartupException( final String message ) + { + super( message ); + } + + + public ComponentStartupException( final Throwable cause ) + { + super( cause ); + } + +} diff --git a/src/main/java/info/ebenoit/ebul/cmp/ComponentState.java b/src/main/java/info/ebenoit/ebul/cmp/ComponentState.java index 657b381..74ed424 100644 --- a/src/main/java/info/ebenoit/ebul/cmp/ComponentState.java +++ b/src/main/java/info/ebenoit/ebul/cmp/ComponentState.java @@ -17,6 +17,7 @@ public final class ComponentState private final boolean autostart; private boolean initialised; private boolean active; + private boolean wasActive; private final HashSet< ComponentState > dependencies = new HashSet< >( ); private final HashSet< ComponentState > reverseDependencies = new HashSet< >( ); @@ -104,6 +105,106 @@ public final class ComponentState } + public void start( ) + throws ComponentStartupException , IllegalStateException + { + if ( !this.initialised ) { + throw new IllegalStateException( "attempting to start uninitialised component" ); + } + if ( this.registry.hasFailed( ) ) { + throw new IllegalStateException( "registry has failed" ); + } + if ( !this.registry.isActive( ) ) { + throw new IllegalStateException( "registry is inactive" ); + } + + startNoChecks( ); + } + + + public Throwable stop( ) + throws IllegalStateException + { + if ( this.registry.hasFailed( ) ) { + throw new IllegalStateException( "registry has failed" ); + } + this.wasActive = this.active; + if ( this.active ) { + return null; + } + + for ( ComponentState rdepState : this.reverseDependencies ) { + Throwable t = rdepState.stop( ); + if ( t != null ) { + return t; + } + } + + try { + this.runLifecycleAction( LifecycleStage.START ); + } catch ( Throwable t ) { + return t; + } + + this.active = false; + return null; + } + + + public void restart( ) + throws IllegalStateException , ComponentRestartException + { + if ( this.registry.hasFailed( ) ) { + throw new IllegalStateException( "registry has failed" ); + } + + Throwable t = this.stop( ); + if ( t != null ) { + throw new ComponentRestartException( "failed to stop" , t ); + } + + this.startIfPreviouslyActive( ); + } + + + private void startNoChecks( ) + throws ComponentStartupException + { + if ( this.active ) { + return; + } + for ( ComponentState depState : this.dependencies ) { + depState.startNoChecks( ); + } + try { + this.runLifecycleAction( LifecycleStage.START ); + } catch ( ComponentStartupException e ) { + throw e; + } catch ( Throwable t ) { + throw new ComponentStartupException( t ); + } + this.active = true; + } + + + private void startIfPreviouslyActive( ) + throws ComponentRestartException + { + if ( this.wasActive ) { + try { + this.startNoChecks( ); + } catch ( ComponentStartupException e ) { + throw new ComponentRestartException( "failed to restart " + this , e ); + } + + for ( ComponentState rDepState : this.reverseDependencies ) { + rDepState.startIfPreviouslyActive( ); + } + } + + } + + void setInitialised( boolean initialised ) { this.initialised = initialised;