Importing some code
* Various annotations * New component information class
This commit is contained in:
parent
48466ed511
commit
e2de94eb7b
14 changed files with 826 additions and 0 deletions
22
src/main/java/info/ebenoit/ebul/cmp/Autostart.java
Normal file
22
src/main/java/info/ebenoit/ebul/cmp/Autostart.java
Normal file
|
@ -0,0 +1,22 @@
|
|||
package info.ebenoit.ebul.cmp;
|
||||
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This annotation indicates that a component will be started automatically, even if it is not needed by other
|
||||
* components.
|
||||
*
|
||||
* @author <a href="mailto:ebenoit@ebenoit.info">E. Benoît</a>
|
||||
*/
|
||||
@Retention( RetentionPolicy.RUNTIME )
|
||||
@Target( ElementType.TYPE )
|
||||
public @interface Autostart
|
||||
{
|
||||
// EMPTY
|
||||
}
|
25
src/main/java/info/ebenoit/ebul/cmp/Component.java
Normal file
25
src/main/java/info/ebenoit/ebul/cmp/Component.java
Normal file
|
@ -0,0 +1,25 @@
|
|||
package info.ebenoit.ebul.cmp;
|
||||
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This annotation indicates that a class will be considered as a component. The optional value specifies the
|
||||
* component's name if it differs from the class' name.
|
||||
*
|
||||
* @author <a href="mailto:ebenoit@ebenoit.info">E. Benoît</a>
|
||||
*/
|
||||
@Retention( RetentionPolicy.RUNTIME )
|
||||
@Target( ElementType.TYPE )
|
||||
public @interface Component
|
||||
{
|
||||
|
||||
/** Name of the component (blank to use the class' name) */
|
||||
public String value( ) default "";
|
||||
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package info.ebenoit.ebul.cmp;
|
||||
|
||||
|
||||
@SuppressWarnings( "serial" )
|
||||
public class ComponentCreationException
|
||||
extends ComponentException
|
||||
{
|
||||
|
||||
public ComponentCreationException( final Throwable cause )
|
||||
{
|
||||
super( cause );
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package info.ebenoit.ebul.cmp;
|
||||
|
||||
|
||||
@SuppressWarnings( "serial" )
|
||||
public class ComponentDefinitionException
|
||||
extends ComponentException
|
||||
{
|
||||
|
||||
public ComponentDefinitionException( )
|
||||
{
|
||||
super( );
|
||||
}
|
||||
|
||||
|
||||
public ComponentDefinitionException( String message )
|
||||
{
|
||||
super( message );
|
||||
}
|
||||
|
||||
|
||||
public ComponentDefinitionException( Throwable cause )
|
||||
{
|
||||
super( cause );
|
||||
}
|
||||
|
||||
|
||||
public ComponentDefinitionException( String message , Throwable cause )
|
||||
{
|
||||
super( message , cause );
|
||||
}
|
||||
|
||||
}
|
32
src/main/java/info/ebenoit/ebul/cmp/ComponentException.java
Normal file
32
src/main/java/info/ebenoit/ebul/cmp/ComponentException.java
Normal file
|
@ -0,0 +1,32 @@
|
|||
package info.ebenoit.ebul.cmp;
|
||||
|
||||
|
||||
@SuppressWarnings( "serial" )
|
||||
public class ComponentException
|
||||
extends RuntimeException
|
||||
{
|
||||
|
||||
public ComponentException( )
|
||||
{
|
||||
super( );
|
||||
}
|
||||
|
||||
|
||||
public ComponentException( String message )
|
||||
{
|
||||
super( message );
|
||||
}
|
||||
|
||||
|
||||
public ComponentException( Throwable cause )
|
||||
{
|
||||
super( cause );
|
||||
}
|
||||
|
||||
|
||||
public ComponentException( String message , Throwable cause )
|
||||
{
|
||||
super( message , cause );
|
||||
}
|
||||
|
||||
}
|
26
src/main/java/info/ebenoit/ebul/cmp/Dependencies.java
Normal file
26
src/main/java/info/ebenoit/ebul/cmp/Dependencies.java
Normal file
|
@ -0,0 +1,26 @@
|
|||
package info.ebenoit.ebul.cmp;
|
||||
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This annotation lists dependencies for a component. If some components are injected as dependencies through the
|
||||
* UseComponent annotation, they don't need to be listed here (same goes for the component indicated by
|
||||
* {@link IsDriverFor}).
|
||||
*
|
||||
* @author <a href="mailto:ebenoit@ebenoit.info">E. Benoît</a>
|
||||
*/
|
||||
@Retention( RetentionPolicy.RUNTIME )
|
||||
@Target( ElementType.TYPE )
|
||||
public @interface Dependencies
|
||||
{
|
||||
|
||||
/** List the component's dependencies */
|
||||
public String[]value( );
|
||||
|
||||
}
|
113
src/main/java/info/ebenoit/ebul/cmp/DependencyInfo.java
Normal file
113
src/main/java/info/ebenoit/ebul/cmp/DependencyInfo.java
Normal file
|
@ -0,0 +1,113 @@
|
|||
package info.ebenoit.ebul.cmp;
|
||||
|
||||
|
||||
/**
|
||||
* This class carries information about a dependency's target. Dependencies may be specified by name or by type.
|
||||
*
|
||||
* @author <a href="mailto:ebenoit@ebenoit.info">E. Benoît</a>
|
||||
*/
|
||||
public class DependencyInfo
|
||||
{
|
||||
|
||||
/**
|
||||
* The name of the component being depended upon, or <code>null</code> if the dependency information is based on
|
||||
* type.
|
||||
*/
|
||||
private final String name;
|
||||
/**
|
||||
* The type of the component being depended upon, or <code>null</code> if the dependency information is based on the
|
||||
* name.
|
||||
*/
|
||||
private final Class< ? > klass;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a name-based dependency information record
|
||||
*
|
||||
* @param name
|
||||
* the name of the component being depended upon
|
||||
*/
|
||||
public DependencyInfo( final String name )
|
||||
{
|
||||
this.name = name;
|
||||
this.klass = null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a type-based dependency information record
|
||||
*
|
||||
* @param klass
|
||||
* the type of the component being depended upon
|
||||
* @throws ComponentDefinitionException
|
||||
* if the specified type is not supported (e.g. primitives or arrays)
|
||||
*/
|
||||
public DependencyInfo( final Class< ? > klass )
|
||||
throws ComponentDefinitionException
|
||||
{
|
||||
this.name = null;
|
||||
this.klass = klass;
|
||||
if ( klass.isPrimitive( ) ) {
|
||||
throw new ComponentDefinitionException( "specified dependency is a primitive type" );
|
||||
}
|
||||
if ( klass.isArray( ) ) {
|
||||
throw new ComponentDefinitionException( "specified dependency is an array type" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode( )
|
||||
{
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ( this.klass == null ? 0 : this.klass.hashCode( ) );
|
||||
result = prime * result + ( this.name == null ? 0 : this.name.hashCode( ) );
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals( final Object obj )
|
||||
{
|
||||
if ( this == obj ) {
|
||||
return true;
|
||||
}
|
||||
if ( obj == null ) {
|
||||
return false;
|
||||
}
|
||||
if ( this.getClass( ) != obj.getClass( ) ) {
|
||||
return false;
|
||||
}
|
||||
final DependencyInfo other = (DependencyInfo) obj;
|
||||
if ( this.klass == null ) {
|
||||
if ( other.klass != null ) {
|
||||
return false;
|
||||
}
|
||||
return this.name.equals( other.name );
|
||||
} else if ( this.klass != other.klass ) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the name of the component being depended upon, or <code>null</code> if the dependency information is
|
||||
* based on type.
|
||||
*/
|
||||
public String getTargetName( )
|
||||
{
|
||||
return this.name;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the type of the component being depended upon, or <code>null</code> if the dependency information is
|
||||
* based on the name.
|
||||
*/
|
||||
public Class< ? > getTargetClass( )
|
||||
{
|
||||
return this.klass;
|
||||
}
|
||||
}
|
26
src/main/java/info/ebenoit/ebul/cmp/DriverFor.java
Normal file
26
src/main/java/info/ebenoit/ebul/cmp/DriverFor.java
Normal file
|
@ -0,0 +1,26 @@
|
|||
package info.ebenoit.ebul.cmp;
|
||||
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This annotation is meant to indicate that a component acts as a driver for another component.
|
||||
*
|
||||
* @author <a href="mailto:ebenoit@ebenoit.info">E. Benoît</a>
|
||||
*/
|
||||
@Inherited
|
||||
@Retention( RetentionPolicy.RUNTIME )
|
||||
@Target( ElementType.TYPE )
|
||||
public @interface DriverFor
|
||||
{
|
||||
|
||||
/** Name of the component to act as a driver for. Implies a dependency. */
|
||||
public String value( );
|
||||
|
||||
}
|
24
src/main/java/info/ebenoit/ebul/cmp/LifecycleMethod.java
Normal file
24
src/main/java/info/ebenoit/ebul/cmp/LifecycleMethod.java
Normal file
|
@ -0,0 +1,24 @@
|
|||
package info.ebenoit.ebul.cmp;
|
||||
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This annotation can be used to indicate that a method should be called as part of the component's lifecycle.
|
||||
*
|
||||
* @author <a href="mailto:ebenoit@ebenoit.info">E. Benoît</a>
|
||||
*/
|
||||
@Retention( RetentionPolicy.RUNTIME )
|
||||
@Target( ElementType.METHOD )
|
||||
public @interface LifecycleMethod
|
||||
{
|
||||
|
||||
/** The lifecycle stage to which the method corresponds. */
|
||||
public LifecycleStage value( );
|
||||
|
||||
}
|
20
src/main/java/info/ebenoit/ebul/cmp/LifecycleStage.java
Normal file
20
src/main/java/info/ebenoit/ebul/cmp/LifecycleStage.java
Normal file
|
@ -0,0 +1,20 @@
|
|||
package info.ebenoit.ebul.cmp;
|
||||
|
||||
|
||||
/**
|
||||
* This enumaration represents the 4 stages of a component's lifecycle.
|
||||
*
|
||||
* @author <a href="mailto:ebenoit@ebenoit.info">E. Benoît</a>
|
||||
*/
|
||||
public enum LifecycleStage {
|
||||
|
||||
/** Initialisation stage */
|
||||
INITIALISE ,
|
||||
/** Startup stage */
|
||||
START ,
|
||||
/** Shutdown stage */
|
||||
STOP ,
|
||||
/** Destruction stage */
|
||||
DESTROY
|
||||
|
||||
}
|
440
src/main/java/info/ebenoit/ebul/cmp/NewComponentInfo.java
Normal file
440
src/main/java/info/ebenoit/ebul/cmp/NewComponentInfo.java
Normal file
|
@ -0,0 +1,440 @@
|
|||
package info.ebenoit.ebul.cmp;
|
||||
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
||||
import info.ebenoit.ebul.func.ThrowingBiConsumer;
|
||||
import info.ebenoit.ebul.func.ThrowingConsumer;
|
||||
import info.ebenoit.ebul.func.ThrowingSupplier;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This class carries information about a component to be added to the registry. Instances can be fed directly to the
|
||||
* registry, or generated from a class' annotations.
|
||||
*
|
||||
* @author <a href="mailto:ebenoit@ebenoit.info">E. Benoît</a>
|
||||
*/
|
||||
public final class NewComponentInfo< T >
|
||||
{
|
||||
/**
|
||||
* Creates a new component information record based on a component's annotated class.
|
||||
*
|
||||
* @param klass
|
||||
* the class to extract the information from
|
||||
* @return the generated information record
|
||||
*/
|
||||
public static < T > NewComponentInfo< T > fromClass( final Class< T > klass )
|
||||
{
|
||||
final NewComponentInfo< T > info = new NewComponentInfo< >( ( ) -> klass.newInstance( ) );
|
||||
|
||||
// Sets the new component's name
|
||||
final Component aComp = klass.getAnnotation( Component.class );
|
||||
if ( aComp != null && !"".equals( aComp.value( ) ) ) {
|
||||
info.setName( aComp.value( ) );
|
||||
} else if ( !ParametricComponent.class.isAssignableFrom( klass ) ) {
|
||||
info.setName( klass.getSimpleName( ) );
|
||||
} else {
|
||||
info.setName( null );
|
||||
}
|
||||
|
||||
// Autostart flag
|
||||
info.setAutostart( klass.isAnnotationPresent( Autostart.class ) );
|
||||
|
||||
// Driver for other component?
|
||||
final DriverFor driverFor = klass.getAnnotation( DriverFor.class );
|
||||
if ( driverFor != null ) {
|
||||
info.setDriverFor( driverFor.value( ) );
|
||||
}
|
||||
|
||||
// Explicit dependencies
|
||||
final Dependencies dependencies = klass.getAnnotation( Dependencies.class );
|
||||
if ( dependencies != null ) {
|
||||
for ( final String dependency : dependencies.value( ) ) {
|
||||
info.addDependency( dependency );
|
||||
}
|
||||
}
|
||||
|
||||
// Find lifecycle methods and dependency injections
|
||||
NewComponentInfo.findMemberAnnotations( info , klass , klass );
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finds annotations from the members of a component's class and of its ancestors
|
||||
*
|
||||
* @param info
|
||||
* the information record being built
|
||||
* @param klass
|
||||
* the class of the component
|
||||
* @param current
|
||||
* the class being examined
|
||||
*
|
||||
* @throws ComponentDefinitionException
|
||||
* if errors are found while reading the annotations
|
||||
*/
|
||||
private static < T > void findMemberAnnotations( final NewComponentInfo< T > info , final Class< T > klass ,
|
||||
final Class< ? super T > current )
|
||||
throws ComponentDefinitionException
|
||||
{
|
||||
if ( current == Object.class ) {
|
||||
return;
|
||||
}
|
||||
NewComponentInfo.findMemberAnnotations( info , klass , current.getSuperclass( ) );
|
||||
NewComponentInfo.addLifecycleActions( info , current );
|
||||
NewComponentInfo.addDependencyInjectors( info , current );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds component lifecycle actions to an information record by looking for annotated methods
|
||||
*
|
||||
* @param info
|
||||
* the information record to be updated
|
||||
* @param current
|
||||
* the class being examined
|
||||
*/
|
||||
private static < T > void addLifecycleActions( final NewComponentInfo< T > info , final Class< ? super T > current )
|
||||
{
|
||||
final boolean found[] = {
|
||||
false , false , false , false
|
||||
};
|
||||
for ( final Method method : current.getDeclaredMethods( ) ) {
|
||||
final LifecycleMethod lcm = method.getAnnotation( LifecycleMethod.class );
|
||||
if ( lcm == null ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check for duplicates
|
||||
final int order = lcm.value( ).ordinal( );
|
||||
if ( found[ order ] ) {
|
||||
throw new ComponentDefinitionException(
|
||||
"in class " + current + ": duplicate lifecycle action for stage " + lcm.value( ) );
|
||||
}
|
||||
|
||||
// Check method
|
||||
if ( method.getParameterCount( ) != 0 ) {
|
||||
throw new ComponentDefinitionException( "in class " + current + ": lifecycle action method for stage "
|
||||
+ lcm.value( ) + " has arguments" );
|
||||
}
|
||||
if ( ( method.getModifiers( ) & Modifier.STATIC ) != 0 ) {
|
||||
throw new ComponentDefinitionException(
|
||||
"in class " + current + ": lifecycle action method for stage " + lcm.value( ) + " is static" );
|
||||
}
|
||||
|
||||
// Add it
|
||||
found[ order ] = true;
|
||||
info.setLifecycleAction( lcm.value( ) , o -> {
|
||||
method.setAccessible( true );
|
||||
method.invoke( o );
|
||||
method.setAccessible( false );
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finds fields that require dependency injection in a component's class and adds them to the information record
|
||||
*
|
||||
* @param info
|
||||
* the information record being updated
|
||||
* @param current
|
||||
* the class being examined
|
||||
*/
|
||||
private static < T > void addDependencyInjectors( final NewComponentInfo< T > info ,
|
||||
final Class< ? super T > current )
|
||||
{
|
||||
for ( final Field field : current.getDeclaredFields( ) ) {
|
||||
final UseComponent uc = field.getAnnotation( UseComponent.class );
|
||||
if ( uc == null ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check the field
|
||||
final int mod = field.getModifiers( );
|
||||
if ( ( mod & Modifier.STATIC ) != 0 ) {
|
||||
throw new ComponentDefinitionException(
|
||||
"in class " + current + ": field " + field.getName( ) + " is static" );
|
||||
}
|
||||
if ( ( mod & Modifier.FINAL ) != 0 ) {
|
||||
throw new ComponentDefinitionException(
|
||||
"in class " + current + ": field " + field.getName( ) + " is final" );
|
||||
}
|
||||
|
||||
// Add the dependency
|
||||
final String ucn = uc.value( );
|
||||
final DependencyInfo di;
|
||||
if ( "".equals( ucn ) ) {
|
||||
try {
|
||||
di = new DependencyInfo( field.getType( ) );
|
||||
} catch ( final ComponentDefinitionException e ) {
|
||||
throw new ComponentDefinitionException(
|
||||
"in class " + current + ", field " + field.getName( ) + ": " + e.getMessage( ) );
|
||||
}
|
||||
} else {
|
||||
di = new DependencyInfo( ucn );
|
||||
}
|
||||
|
||||
// Add the injector
|
||||
info.addDependencyInjector( di , ( c , d ) -> {
|
||||
field.setAccessible( true );
|
||||
field.set( c , d );
|
||||
field.setAccessible( false );
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
/** The component's instance, or <code>null</code> if a supplier is being used */
|
||||
private final T component;
|
||||
/** The component's supplier, or <code>null</code> if an instance has been supplied */
|
||||
private final ThrowingSupplier< T > supplier;
|
||||
|
||||
/** The component's name, or <code>null</code> if it must be guessed */
|
||||
private String name;
|
||||
/**
|
||||
* The name of a component this new component acts as a driver for, or <code>null</code> if this component is not a
|
||||
* driver.
|
||||
*/
|
||||
private String driverFor;
|
||||
/** The set of dependency names for the new component */
|
||||
private final HashSet< DependencyInfo > dependencies;
|
||||
|
||||
/** Lifecylce actions for the new component */
|
||||
private final EnumMap< LifecycleStage , ThrowingConsumer< ? super T > > lcActions;
|
||||
/** Dependency injectors */
|
||||
private final HashMap< DependencyInfo , ArrayList< ThrowingBiConsumer< ? super T , ? > > > injectors;
|
||||
|
||||
/** Whether the component should be activated automatically */
|
||||
private boolean autostart;
|
||||
|
||||
|
||||
/**
|
||||
* Initialises a component based on a existing instance
|
||||
*
|
||||
* @param component
|
||||
* the component's instance
|
||||
*/
|
||||
public NewComponentInfo( final T component )
|
||||
{
|
||||
this.component = component;
|
||||
this.supplier = null;
|
||||
this.dependencies = new HashSet< >( );
|
||||
this.lcActions = new EnumMap< >( LifecycleStage.class );
|
||||
this.injectors = new HashMap< >( );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialises a component based on a component supplier
|
||||
*
|
||||
* @param supplier
|
||||
* the supplier to obtain the component from
|
||||
*/
|
||||
public NewComponentInfo( final ThrowingSupplier< T > supplier )
|
||||
{
|
||||
this.component = null;
|
||||
this.supplier = supplier;
|
||||
this.dependencies = new HashSet< >( );
|
||||
this.lcActions = new EnumMap< >( LifecycleStage.class );
|
||||
this.injectors = new HashMap< >( );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the component, or <code>null</code> if a supplier is in use
|
||||
*/
|
||||
public T getComponent( )
|
||||
{
|
||||
return this.component;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the component supplier, or <code>null</code> if an instance was provided
|
||||
*/
|
||||
public ThrowingSupplier< T > getSupplier( )
|
||||
{
|
||||
return this.supplier;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the new component's name
|
||||
*
|
||||
* @param name
|
||||
* the new component's name
|
||||
* @return the current object
|
||||
*/
|
||||
public NewComponentInfo< T > setName( final String name )
|
||||
{
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the name of the new component
|
||||
*/
|
||||
public String getName( )
|
||||
{
|
||||
return this.name;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the name of the component for which the current component acts as a driver for
|
||||
*/
|
||||
public String getDriverFor( )
|
||||
{
|
||||
return this.driverFor;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the name of the component for which the current component acts as a driver for
|
||||
*
|
||||
* @param driverFor
|
||||
* the name of the main component
|
||||
* @return the current object
|
||||
*/
|
||||
public NewComponentInfo< T > setDriverFor( final String driverFor )
|
||||
{
|
||||
this.driverFor = driverFor;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the action for one of the component's lifecycle stages
|
||||
*
|
||||
* @param stage
|
||||
* the lifecyle stage for which the action is being set
|
||||
* @param action
|
||||
* the action to perform
|
||||
* @return the current object
|
||||
*/
|
||||
public NewComponentInfo< T > setLifecycleAction( final LifecycleStage stage ,
|
||||
final ThrowingConsumer< ? super T > action )
|
||||
{
|
||||
this.lcActions.put( stage , action );
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reads the action for one of the component's lifecycle stages
|
||||
*
|
||||
* @param stage
|
||||
* the lifecycle stage for which the action must be returned
|
||||
* @return the action corresponding to the specified lifecycle stage
|
||||
*/
|
||||
public ThrowingConsumer< ? > getLifecycleAction( final LifecycleStage stage )
|
||||
{
|
||||
return this.lcActions.get( stage );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the modifiable set of dependencies
|
||||
*/
|
||||
public HashSet< DependencyInfo > getDependencies( )
|
||||
{
|
||||
return this.dependencies;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a dependency to the component
|
||||
*
|
||||
* @param name
|
||||
* the dependency's name
|
||||
* @return the current object
|
||||
*/
|
||||
public NewComponentInfo< T > addDependency( final String name )
|
||||
{
|
||||
this.dependencies.add( new DependencyInfo( name ) );
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a dependency to the component
|
||||
*
|
||||
* @param klass
|
||||
* the dependency's type
|
||||
* @return the current object
|
||||
*/
|
||||
public NewComponentInfo< T > addDependency( final Class< ? > klass )
|
||||
{
|
||||
this.dependencies.add( new DependencyInfo( klass ) );
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return <code>true</code> if the component is supposed to start automatically
|
||||
*/
|
||||
public boolean getAutostart( )
|
||||
{
|
||||
return this.autostart;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the autostart flag
|
||||
*
|
||||
* @param autostart
|
||||
* a flag indicating whether the component should be started automatically
|
||||
* @return the current object
|
||||
*/
|
||||
public NewComponentInfo< T > setAutostart( final boolean autostart )
|
||||
{
|
||||
this.autostart = autostart;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Accesses the map of dependency injectors. Note that any direct change should be reflected on the dependencies
|
||||
* manually.
|
||||
*
|
||||
* @return the modifiable map of dependency injectors, with the dependency information as the key and a list of
|
||||
* functions that inject the dependency into the target component as the value.
|
||||
*/
|
||||
public HashMap< DependencyInfo , ArrayList< ThrowingBiConsumer< ? super T , ? > > > getInjectors( )
|
||||
{
|
||||
return this.injectors;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a dependency injector and registers the new dependency.
|
||||
*
|
||||
* @param dependency
|
||||
* the name of the component being depended upon
|
||||
* @param injector
|
||||
* a function that accepts two parameters: the instance into which the dependency is being injected, and
|
||||
* the instance of the component being injected
|
||||
* @return the current object
|
||||
*/
|
||||
public NewComponentInfo< T > addDependencyInjector( final DependencyInfo dependency ,
|
||||
final ThrowingBiConsumer< ? super T , ? > injector )
|
||||
{
|
||||
ArrayList< ThrowingBiConsumer< ? super T , ? > > injectors = this.injectors.get( dependency );
|
||||
if ( injectors == null ) {
|
||||
injectors = new ArrayList< >( );
|
||||
this.injectors.put( dependency , injectors );
|
||||
}
|
||||
injectors.add( injector );
|
||||
this.dependencies.add( dependency );
|
||||
return this;
|
||||
}
|
||||
}
|
20
src/main/java/info/ebenoit/ebul/cmp/ParametricComponent.java
Normal file
20
src/main/java/info/ebenoit/ebul/cmp/ParametricComponent.java
Normal file
|
@ -0,0 +1,20 @@
|
|||
package info.ebenoit.ebul.cmp;
|
||||
|
||||
|
||||
/**
|
||||
* This interface must be implemented by components which are loaded dynamically depending on configuration (for example
|
||||
* VFS drivers). Note that such components need not be annotated with {@link Component}, and that they never start
|
||||
* automatically.
|
||||
*
|
||||
* @author <a href="mailto:ebenoit@ebenoit.info">E. Benoît</a>
|
||||
*/
|
||||
public interface ParametricComponent
|
||||
{
|
||||
/**
|
||||
* Determine the name of the current instance of this component.
|
||||
*
|
||||
* @return the name of the component
|
||||
*/
|
||||
public String getComponentName( );
|
||||
|
||||
}
|
26
src/main/java/info/ebenoit/ebul/cmp/UseComponent.java
Normal file
26
src/main/java/info/ebenoit/ebul/cmp/UseComponent.java
Normal file
|
@ -0,0 +1,26 @@
|
|||
package info.ebenoit.ebul.cmp;
|
||||
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This annotation indicates that a component's field is meant to receive the instance of another component (creating a
|
||||
* dependency implicitly). If no name is specified, the component to inject will be determined based on the type of the
|
||||
* field.
|
||||
*
|
||||
* @author <a href="mailto:ebenoit@ebenoit.info">E. Benoît</a>
|
||||
*/
|
||||
@Retention( RetentionPolicy.RUNTIME )
|
||||
@Target( ElementType.FIELD )
|
||||
public @interface UseComponent
|
||||
{
|
||||
|
||||
/** The injected component's name, or blank if it is to be guessed from the field's type */
|
||||
public String value( ) default "";
|
||||
|
||||
}
|
6
src/main/java/info/ebenoit/ebul/cmp/package-info.java
Normal file
6
src/main/java/info/ebenoit/ebul/cmp/package-info.java
Normal file
|
@ -0,0 +1,6 @@
|
|||
/**
|
||||
* Component management library and related annotations.
|
||||
*
|
||||
* @author <a href="mailto:ebenoit@ebenoit.info">E. Benoît</a>
|
||||
*/
|
||||
package info.ebenoit.ebul.cmp;
|
Loading…
Reference in a new issue