All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.refcodes.decoupling.ext.application.ApplicationReactor Maven / Gradle / Ivy

// /////////////////////////////////////////////////////////////////////////////
// REFCODES.ORG
// /////////////////////////////////////////////////////////////////////////////
// This code is copyright (c) by Siegfried Steiner, Munich, Germany and licensed
// under the following (see "http://en.wikipedia.org/wiki/Multi-licensing")
// licenses:
// -----------------------------------------------------------------------------
// GNU General Public License, v3.0 ("http://www.gnu.org/licenses/gpl-3.0.html")
// -----------------------------------------------------------------------------
// Apache License, v2.0 ("http://www.apache.org/licenses/LICENSE-2.0")
// -----------------------------------------------------------------------------
// Please contact the copyright holding author(s) of the software artifacts in
// question for licensing issues not being covered by the above listed licenses,
// also regarding commercial licensing models or regarding the compatibility
// with other open source licenses.
// /////////////////////////////////////////////////////////////////////////////

package org.refcodes.decoupling.ext.application;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.text.ParseException;
import java.util.Collection;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.refcodes.cli.ArgsSyntaxException;
import org.refcodes.component.Initializable;
import org.refcodes.component.InitializeException;
import org.refcodes.component.StartException;
import org.refcodes.component.Startable;
import org.refcodes.component.StopException;
import org.refcodes.decoupling.AmbigousClaimException;
import org.refcodes.decoupling.AmbigousDependencyException;
import org.refcodes.decoupling.AmbigousFactoryException;
import org.refcodes.decoupling.AmbigousInitializerException;
import org.refcodes.decoupling.CircularDependencyException;
import org.refcodes.decoupling.Claim;
import org.refcodes.decoupling.Context;
import org.refcodes.decoupling.Dependency;
import org.refcodes.decoupling.DependencyBuilder;
import org.refcodes.decoupling.DuplicateClaimException;
import org.refcodes.decoupling.DuplicateDependencyException;
import org.refcodes.decoupling.FactoryClaim;
import org.refcodes.decoupling.InitializerClaim;
import org.refcodes.decoupling.InstallDependencyException;
import org.refcodes.decoupling.InstanceMode;
import org.refcodes.decoupling.Reactor;
import org.refcodes.decoupling.UnsatisfiedClaimException;
import org.refcodes.decoupling.UnsatisfiedDependencyException;
import org.refcodes.decoupling.UnsatisfiedFactoryException;
import org.refcodes.decoupling.UnsatisfiedInitializerException;
import org.refcodes.eventbus.ext.application.ApplicationBus;
import org.refcodes.exception.Trap;
import org.refcodes.properties.ProfileProperties;
import org.refcodes.properties.Properties;
import org.refcodes.properties.ext.application.ApplicationProperties;
import org.refcodes.properties.ext.application.ApplicationPropertiesAccessor;
import org.refcodes.runtime.ConfigLocator;
import org.refcodes.runtime.Execution;

/**
 * The {@link ApplicationReactor} enables harnessing the
 * {@link ApplicationProperties} alongside the {@link ApplicationProperties}
 * profile support (as of {@link ProfileProperties#getRuntimeProfiles()}) by
 * automatically placing an accordingly configured {@link ApplicationProperties}
 * instance into the container and enabling it for injection. The
 * {@link ApplicationProperties} {@link Dependency}'s alias is set to
 * {@value #APPLICATION_PROPERTIES_ALIAS} (as of this class's constant
 * {@link #APPLICATION_PROPERTIES_ALIAS}). Also if declared by a
 * {@link Dependency}, an {@link ApplicationBus} singleton is provided for
 * asynchronous communication between the components.
 */
public class ApplicationReactor extends Reactor implements ApplicationPropertiesAccessor {

	// /////////////////////////////////////////////////////////////////////////
	// STATICS:
	// /////////////////////////////////////////////////////////////////////////

	private static final Logger LOGGER = Logger.getLogger( ApplicationReactor.class.getName() );

	// /////////////////////////////////////////////////////////////////////////
	// CONSTANTS:
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * The {@link ApplicationContext} {@link Dependency}'s alias to be used.
	 */
	public static final String APPLICATION_CONTEXT_ALIAS = "applicationContext";

	/**
	 * The {@link ApplicationProperties} {@link Dependency}'s alias to be used.
	 */
	public static final String APPLICATION_PROPERTIES_ALIAS = "applicationProperties";

	/**
	 * The {@link ApplicationBus} {@link Dependency}'s alias to be used.
	 */
	public static final String APPLICATION_BUS_ALIAS = "applicationBus";

	/**
	 * The {@link ProfilesMode} determines how to handle implicit profiles
	 * information as provided by the {@link ApplicationProperties} instance
	 * (implicitly added as {@link Dependency} by the
	 * {@link ApplicationReactor}).
	 */
	public enum ProfilesMode {

		/**
		 * None profile is being applied at all. Any profiles as being provided
		 * upon initialization to the according constructors by the
		 * {@link ApplicationProperties} (or any sub-type of the
		 * {@link ProfileProperties}) are ignored.
		 */
		NONE,

		/**
		 * The profiles as being provided upon initialization to the according
		 * constructors by the {@link ApplicationProperties} are determined and
		 * used for the overall configuration. If {@link Properties} other(!)
		 * than a sub-type of the {@link ProfileProperties} (such as the
		 * {@link ApplicationProperties}) were provided manually, then
		 * determination of the profiles will result in none profiles (as of
		 * {@link #NONE}) being set!
		 */
		DETERMINED
	}

	// /////////////////////////////////////////////////////////////////////////
	// VARIABLES:
	// /////////////////////////////////////////////////////////////////////////

	protected ApplicationProperties _properties;

	// /////////////////////////////////////////////////////////////////////////
	// CONSTRUCTORS:
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * Constructs a {@link ApplicationReactor} instance.
	 */
	public ApplicationReactor() {
		_properties = new ApplicationProperties();
	}

	/**
	 * Constructs a {@link ApplicationReactor} instance.
	 * 
	 * @param aProperties The {@link ApplicationProperties} to use as initial
	 *        configuration.
	 */
	public ApplicationReactor( ApplicationProperties aProperties ) {
		_properties = aProperties;
	}

	/**
	 * Create a {@link ApplicationReactor} using the provided
	 * {@link ApplicationProperties} instance.
	 * 
	 * @param aInputStream The {@link InputStream} pointing to the properties to
	 *        be loaded.
	 * 
	 * @throws IOException thrown in case accessing or processing the properties
	 *         file failed.
	 * 
	 * @throws ParseException Signals that an error has been reached
	 *         unexpectedly while parsing the data to be loaded.
	 */
	public ApplicationReactor( InputStream aInputStream ) throws IOException, ParseException {
		final ApplicationProperties theProperties = new ApplicationProperties();
		theProperties.withInputStream( aInputStream );
		_properties = theProperties;
	}

	/**
	 * Create a {@link ApplicationReactor} using the provided
	 * {@link ApplicationProperties} instance.
	 * 
	 * @param aInputStream The {@link InputStream} pointing to the properties to
	 *        be loaded.
	 * 
	 * @param aArgs The command line arguments to be evaluated.
	 * 
	 * @throws IOException thrown in case accessing or processing the properties
	 *         file failed.
	 * 
	 * @throws ParseException Signals that an error has been reached
	 *         unexpectedly while parsing the data to be loaded.
	 * @throws ArgsSyntaxException thrown in case of a command line arguments
	 *         mismatch regarding provided and expected args.
	 */
	public ApplicationReactor( InputStream aInputStream, String[] aArgs ) throws IOException, ParseException, ArgsSyntaxException {
		final ApplicationProperties theProperties = new ApplicationProperties();
		theProperties.withInputStream( aInputStream );
		theProperties.evalArgs( aArgs );
		_properties = theProperties;
	}

	/**
	 * Create a {@link ApplicationReactor} using the provided
	 * {@link ApplicationProperties} instance.
	 * 
	 * @param aUrl The {@link URL} pointing to the properties to be loaded.
	 * 
	 * @throws IOException thrown in case accessing or processing the properties
	 *         file failed.
	 * 
	 * @throws ParseException Signals that an error has been reached
	 *         unexpectedly while parsing the data to be loaded.
	 */
	public ApplicationReactor( URL aUrl ) throws IOException, ParseException {
		final ApplicationProperties theProperties = new ApplicationProperties();
		theProperties.withUrl( aUrl );
		_properties = theProperties;
	}

	/**
	 * Create a {@link ApplicationReactor} using the provided
	 * {@link ApplicationProperties} instance.
	 * 
	 * @param aUrl The {@link URL} pointing to the properties to be loaded.
	 * 
	 * @param aArgs The command line arguments to be evaluated.
	 * 
	 * @throws IOException thrown in case accessing or processing the properties
	 *         file failed.
	 * 
	 * @throws ParseException Signals that an error has been reached
	 *         unexpectedly while parsing the data to be loaded.
	 * @throws ArgsSyntaxException thrown in case of a command line arguments
	 *         mismatch regarding provided and expected args.
	 */
	public ApplicationReactor( URL aUrl, String[] aArgs ) throws IOException, ParseException, ArgsSyntaxException {
		final ApplicationProperties theProperties = new ApplicationProperties();
		theProperties.withUrl( aUrl );
		theProperties.evalArgs( aArgs );
		_properties = theProperties;
	}

	/**
	 * Create a {@link ApplicationReactor} using the provided
	 * {@link ApplicationProperties} instance.
	 * 
	 * @param aFile The {@link File} pointing to the properties to be loaded.
	 * 
	 * @throws IOException thrown in case accessing or processing the properties
	 *         file failed.
	 * 
	 * @throws ParseException Signals that an error has been reached
	 *         unexpectedly while parsing the data to be loaded.
	 */
	public ApplicationReactor( File aFile ) throws IOException, ParseException {
		final ApplicationProperties theProperties = new ApplicationProperties();
		theProperties.withFile( aFile, ConfigLocator.APPLICATION_DIR );
		_properties = theProperties;
	}

	/**
	 * Create a {@link ApplicationReactor} using the provided
	 * {@link ApplicationProperties} instance.
	 * 
	 * @param aFile The {@link File} pointing to the properties to be loaded.
	 * 
	 * @param aArgs The command line arguments to be evaluated.
	 * 
	 * @throws IOException thrown in case accessing or processing the properties
	 *         file failed.
	 * 
	 * @throws ParseException Signals that an error has been reached
	 *         unexpectedly while parsing the data to be loaded.
	 * @throws ArgsSyntaxException thrown in case of a command line arguments
	 *         mismatch regarding provided and expected args.
	 */
	public ApplicationReactor( File aFile, String[] aArgs ) throws IOException, ParseException, ArgsSyntaxException {
		final ApplicationProperties theProperties = new ApplicationProperties();
		theProperties.withFile( aFile, ConfigLocator.APPLICATION_DIR );
		theProperties.evalArgs( aArgs );
		_properties = theProperties;
	}

	/**
	 * Create a {@link ApplicationReactor} using the provided
	 * {@link ApplicationProperties} instance.
	 * 
	 * @param aFile The {@link File} pointing to the properties to be loaded.
	 * 
	 * @param aConfigLocator The {@link ConfigLocator} to be used when seeking
	 *        for the given {@link File}.
	 * 
	 * @throws IOException thrown in case accessing or processing the properties
	 *         file failed.
	 * 
	 * @throws ParseException Signals that an error has been reached
	 *         unexpectedly while parsing the data to be loaded.
	 */
	public ApplicationReactor( File aFile, ConfigLocator aConfigLocator ) throws IOException, ParseException {
		final ApplicationProperties theProperties = new ApplicationProperties();
		theProperties.withFile( aFile, aConfigLocator );
		_properties = theProperties;
	}

	/**
	 * Create a {@link ApplicationReactor} using the provided
	 * {@link ApplicationProperties} instance.
	 *
	 * @param aFile The {@link File} pointing to the properties to be loaded.
	 * @param aConfigLocator The {@link ConfigLocator} to be used when seeking
	 *        for the given {@link File}.
	 * @param aArgs the args
	 * 
	 * @throws IOException thrown in case accessing or processing the properties
	 *         file failed.
	 * @throws ParseException Signals that an error has been reached
	 *         unexpectedly while parsing the data to be loaded.
	 * @throws ArgsSyntaxException thrown in case of a command line arguments
	 *         mismatch regarding provided and expected args.
	 */
	public ApplicationReactor( File aFile, ConfigLocator aConfigLocator, String[] aArgs ) throws IOException, ParseException, ArgsSyntaxException {
		final ApplicationProperties theProperties = new ApplicationProperties();
		theProperties.withFile( aFile, aConfigLocator );
		theProperties.evalArgs( aArgs );
		_properties = theProperties;
	}

	/**
	 * Create a {@link ApplicationReactor} using the provided
	 * {@link ApplicationProperties} instance.
	 * 
	 * @param aFilePath The properties' file path pointing to the properties to
	 *        be loaded.
	 * 
	 * @throws IOException thrown in case accessing or processing the properties
	 *         file failed.
	 * 
	 * @throws ParseException Signals that an error has been reached
	 *         unexpectedly while parsing the data to be loaded.
	 */
	public ApplicationReactor( String aFilePath ) throws IOException, ParseException {
		final ApplicationProperties theProperties = new ApplicationProperties();
		theProperties.withResourceClass( aFilePath, ConfigLocator.APPLICATION_DIR );
		_properties = theProperties;
	}

	/**
	 * Create a {@link ApplicationReactor} using the provided
	 * {@link ApplicationProperties} instance.
	 * 
	 * @param aFilePath The properties' file path pointing to the properties to
	 *        be loaded.
	 * 
	 * @param aArgs The command line arguments to be evaluated.
	 * 
	 * @throws IOException thrown in case accessing or processing the properties
	 *         file failed.
	 * 
	 * @throws ParseException Signals that an error has been reached
	 *         unexpectedly while parsing the data to be loaded.
	 * @throws ArgsSyntaxException thrown in case of a command line arguments
	 *         mismatch regarding provided and expected args.
	 */
	public ApplicationReactor( String aFilePath, String[] aArgs ) throws IOException, ParseException, ArgsSyntaxException {
		final ApplicationProperties theProperties = new ApplicationProperties();
		theProperties.withResourceClass( aFilePath, ConfigLocator.APPLICATION_DIR );
		theProperties.evalArgs( aArgs );
		_properties = theProperties;
	}

	/**
	 * Create a {@link ApplicationReactor} using the provided
	 * {@link ApplicationProperties} instance.
	 * 
	 * @param aFilePath The properties' file path pointing to the properties to
	 *        be loaded.
	 * 
	 * @param aConfigLocator The {@link ConfigLocator} to be used when seeking
	 *        for the given {@link String}.
	 * 
	 * @throws IOException thrown in case accessing or processing the properties
	 *         file failed.
	 * 
	 * @throws ParseException Signals that an error has been reached
	 *         unexpectedly while parsing the data to be loaded.
	 */
	public ApplicationReactor( String aFilePath, ConfigLocator aConfigLocator ) throws IOException, ParseException {
		final ApplicationProperties theProperties = new ApplicationProperties();
		theProperties.withResourceClass( aFilePath, aConfigLocator );
		_properties = theProperties;
	}

	/**
	 * Create a {@link ApplicationReactor} using the provided
	 * {@link ApplicationProperties} instance.
	 *
	 * @param aFilePath The properties' file path pointing to the properties to
	 *        be loaded.
	 * @param aConfigLocator The {@link ConfigLocator} to be used when seeking
	 *        for the given {@link String}.
	 * @param aArgs the args
	 * 
	 * @throws IOException thrown in case accessing or processing the properties
	 *         file failed.
	 * @throws ParseException Signals that an error has been reached
	 *         unexpectedly while parsing the data to be loaded.
	 * @throws ArgsSyntaxException thrown in case of a command line arguments
	 *         mismatch regarding provided and expected args.
	 */
	public ApplicationReactor( String aFilePath, ConfigLocator aConfigLocator, String[] aArgs ) throws IOException, ParseException, ArgsSyntaxException {
		final ApplicationProperties theProperties = new ApplicationProperties();
		theProperties.withResourceClass( aFilePath, aConfigLocator );
		theProperties.evalArgs( aArgs );
		_properties = theProperties;
	}

	// /////////////////////////////////////////////////////////////////////////
	// METHODS:
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * Adds a specially fabricated {@link Dependency} to the {@link Reactor}
	 * which's {@link DependencyBuilder} is returned for fluently configuring
	 * your {@link Dependency}: The {@link Dependency} will be initialized by
	 * default by the {@link ApplicationReactor} instance's underlying
	 * {@link Properties} instance (being instantiated by the
	 * {@link ApplicationReactor}).
	 * 
	 * This is a shortcut for configuring and applying a {@link FactoryClaim}
	 * with the according {@link Properties} to your {@link Dependency}:
	 * 
	 * 
	 * addDependency( aType ).withFactory( Properties.class, ( p ) -> p.toType( aType ), "applicationProperties" );
	 * 
	 * 
	 * @param  The type of the {@link Dependency}.
	 * 
	 * @param aType The type of the {@link Dependency}.
	 * 
	 * @return An instance of the {@link DependencyBuilder} class which is used
	 *         to fluently configure your {@link Dependency}.
	 */
	public  DependencyBuilder addConfiguration( Class aType ) {
		final DependencyBuilder theBuilder = new DependencyBuilder<>( aType ).withFactory( Properties.class, ( p ) -> p.toType( aType ), APPLICATION_PROPERTIES_ALIAS );
		_dependencies.add( theBuilder );
		return theBuilder;
	}

	/**
	 * Adds a specially fabricated {@link Dependency} to the {@link Reactor}
	 * which's {@link DependencyBuilder} is returned for fluently configuring
	 * your {@link Dependency}: The {@link Dependency} will be initialized by
	 * default by the {@link ApplicationReactor} instance's underlying
	 * {@link Properties} instance (being instantiated by the
	 * {@link ApplicationReactor}).
	 * 
	 * This is a shortcut for configuring and applying a {@link FactoryClaim}
	 * with the according {@link Properties} to your {@link Dependency}:
	 * 
	 * 
	 * addDependency( aType ).withFactory( Properties.class, ( p ) -> p.toType( "service.datasource", aType ), "applicationProperties" );
	 * 
	 * 
	 * @param  The type of the {@link Dependency}.
	 * 
	 * @param aType The type of the {@link Dependency}.
	 * 
	 * @param aNamespace The namespace within the {@link Properties} managed by
	 *        the {@link ApplicationReactor}. The namespace can be regarded a
	 *        property key's prefix.
	 * 
	 * @return An instance of the {@link DependencyBuilder} class which is used
	 *         to fluently configure your {@link Dependency}.
	 */
	public  DependencyBuilder addConfiguration( Class aType, String aNamespace ) {
		final DependencyBuilder theBuilder = new DependencyBuilder<>( aType ).withFactory( Properties.class, ( p ) -> p.toType( aNamespace, aType ), APPLICATION_PROPERTIES_ALIAS );
		_dependencies.add( theBuilder );
		return theBuilder;
	}

	/**
	 * Adds a specially fabricated {@link Dependency} to the {@link Reactor}
	 * which's {@link DependencyBuilder} is returned for fluently configuring
	 * your {@link Dependency}: The {@link Dependency} will be initialized by
	 * default by the {@link ApplicationReactor} instance's underlying
	 * {@link Properties} instance (being instantiated by the
	 * {@link ApplicationReactor}).
	 * 
	 * This is a shortcut for configuring and applying a {@link FactoryClaim}
	 * with the according {@link Properties} to your {@link Dependency}:
	 * 
	 * 
	 * addDependency( aType ).withFactory( Properties.class, ( p ) -> p.toType( "service.datasource", aType ), "applicationProperties" );
	 * 
	 * 
	 * @param  The type of the {@link Dependency}.
	 * 
	 * @param aType The type of the {@link Dependency}.
	 * 
	 * @param aNamespace The namespace within the {@link Properties} managed by
	 *        the {@link ApplicationReactor}. The namespace can be regarded a
	 *        property key's prefix.
	 * 
	 * @return An instance of the {@link DependencyBuilder} class which is used
	 *         to fluently configure your {@link Dependency}.
	 */
	public  DependencyBuilder addConfiguration( Class aType, Object aNamespace ) {
		final DependencyBuilder theBuilder = new DependencyBuilder<>( aType ).withFactory( Properties.class, ( p ) -> p.toType( aNamespace, aType ), APPLICATION_PROPERTIES_ALIAS );
		_dependencies.add( theBuilder );
		return theBuilder;
	}

	/**
	 * Adds a specially fabricated {@link Dependency} to the {@link Reactor}
	 * which's {@link DependencyBuilder} is returned for fluently configuring
	 * your {@link Dependency}: The {@link Dependency} will be initialized by
	 * default by the {@link ApplicationReactor} instance's underlying
	 * {@link Properties} instance (being instantiated by the
	 * {@link ApplicationReactor}).
	 * 
	 * This is a shortcut for configuring and applying a {@link FactoryClaim}
	 * with the according {@link Properties} to your {@link Dependency}:
	 * 
	 * 
	 * addDependency( aType ).withFactory( Properties.class, ( p ) -> p.toType( aType. "service", "datasource" ), "applicationProperties" );
	 * 
	 * 
	 * @param  The type of the {@link Dependency}.
	 * 
	 * @param aType The type of the {@link Dependency}.
	 * 
	 * @param aNamespaceElements The namespace elements denoting the namespace
	 *        (not requiring denoting any delimiters separating the namespace
	 *        elements from each other) within the {@link Properties} managed by
	 *        the {@link ApplicationReactor}. The namespace can be regarded a
	 *        property key's prefix.
	 * 
	 * @return An instance of the {@link DependencyBuilder} class which is used
	 *         to fluently configure your {@link Dependency}.
	 */
	public  DependencyBuilder addConfiguration( Class aType, String... aNamespaceElements ) {
		final DependencyBuilder theBuilder = new DependencyBuilder<>( aType ).withFactory( Properties.class, ( p ) -> p.toType( aType, aNamespaceElements ), APPLICATION_PROPERTIES_ALIAS );
		_dependencies.add( theBuilder );
		return theBuilder;
	}

	/**
	 * Adds a specially fabricated {@link Dependency} to the {@link Reactor}
	 * which's {@link DependencyBuilder} is returned for fluently configuring
	 * your {@link Dependency}: The {@link Dependency} will be initialized by
	 * default by the {@link ApplicationReactor} instance's underlying
	 * {@link Properties} instance (being instantiated by the
	 * {@link ApplicationReactor}).
	 * 
	 * This is a shortcut for configuring and applying a {@link FactoryClaim}
	 * with the according {@link Properties} to your {@link Dependency}:
	 * 
	 * 
	 * addDependency( aType ).withFactory( Properties.class, ( p ) -> p.toType( aType. "service", "datasource" ), "applicationProperties" );
	 * 
	 * 
	 * @param  The type of the {@link Dependency}.
	 * 
	 * @param aType The type of the {@link Dependency}.
	 * 
	 * @param aNamespaceElements The namespace elements denoting the namespace
	 *        (not requiring denoting any delimiters separating the namespace
	 *        elements from each other) within the {@link Properties} managed by
	 *        the {@link ApplicationReactor}. The namespace can be regarded a
	 *        property key's prefix.
	 * 
	 * @return An instance of the {@link DependencyBuilder} class which is used
	 *         to fluently configure your {@link Dependency}.
	 */
	public  DependencyBuilder addConfiguration( Class aType, Object... aNamespaceElements ) {
		final DependencyBuilder theBuilder = new DependencyBuilder<>( aType ).withFactory( Properties.class, ( p ) -> p.toType( aType, aNamespaceElements ), APPLICATION_PROPERTIES_ALIAS );
		_dependencies.add( theBuilder );
		return theBuilder;
	}

	/**
	 * Adds a specially fabricated {@link Dependency} to the {@link Reactor}
	 * which's {@link DependencyBuilder} is returned for fluently configuring
	 * your {@link Dependency}: The {@link Dependency} will be initialized by
	 * default by the {@link ApplicationReactor} instance's underlying
	 * {@link Properties} instance (being instantiated by the
	 * {@link ApplicationReactor}).
	 * 
	 * This is a shortcut for configuring and applying a {@link FactoryClaim}
	 * with the according {@link Properties} to your {@link Dependency}:
	 * 
	 * 
	 * addDependency( aType ).withFactory( Properties.class, ( p ) -> p.toType( aType. someNameSpaceList ), "applicationProperties" );
	 * 
	 * 
	 * @param  The type of the {@link Dependency}.
	 * 
	 * @param aType The type of the {@link Dependency}.
	 * 
	 * @param aNamespaceElements The namespace elements denoting the namespace
	 *        (not requiring denoting any delimiters separating the namespace
	 *        elements from each other) within the {@link Properties} managed by
	 *        the {@link ApplicationReactor}. The namespace can be regarded a
	 *        property key's prefix.
	 * 
	 * @return An instance of the {@link DependencyBuilder} class which is used
	 *         to fluently configure your {@link Dependency}.
	 */
	public  DependencyBuilder addConfiguration( Class aType, Collection aNamespaceElements ) {
		final DependencyBuilder theBuilder = new DependencyBuilder<>( aType ).withFactory( Properties.class, ( p ) -> p.toType( aNamespaceElements, aType ), APPLICATION_PROPERTIES_ALIAS );
		_dependencies.add( theBuilder );
		return theBuilder;
	}

	/**
	 * {@inheritDoc}
	 * 
	 * Uses the profiles as provided by the {@link Properties} initialized with
	 * as of the according constructors.
	 */
	@Override
	public ApplicationContext createContext() throws AmbigousDependencyException, UnsatisfiedDependencyException, CircularDependencyException, DuplicateDependencyException, DuplicateClaimException, UnsatisfiedClaimException, AmbigousClaimException, AmbigousInitializerException, UnsatisfiedInitializerException, AmbigousFactoryException, UnsatisfiedFactoryException, InstallDependencyException {
		return createContext( toProfiles() );
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ApplicationContext createContext( String... aProfiles ) throws AmbigousDependencyException, UnsatisfiedDependencyException, CircularDependencyException, DuplicateDependencyException, DuplicateClaimException, UnsatisfiedClaimException, AmbigousClaimException, AmbigousInitializerException, UnsatisfiedInitializerException, AmbigousFactoryException, UnsatisfiedFactoryException, InstallDependencyException {
		return createContext( (Object[]) aProfiles );
	}

	/**
	 * Creates the {@link ApplicationContext} and invokes the
	 * {@link Initializable#initialize()} methods before invoking the
	 * {@link Startable#start()} methods on all dependencies' instances. The
	 * {@link ApplicationContext} returned then is in lifecycle status
	 * {@link ApplicationContext#isRunning()}.
	 *
	 * @param aProfilesMode the {@link ProfilesMode} determining on how to
	 *        handle implicit profiles information as provided by the
	 *        {@link ApplicationProperties} instance (implicitly added as
	 *        {@link Dependency} by the {@link ApplicationReactor}).
	 * 
	 * @return The accordingly created {@link ApplicationContext} instance.
	 * 
	 * @throws AmbigousDependencyException is thrown in case for a required
	 *         {@link Dependency} there are more than one matching
	 *         {@link Dependency} candidates in the {@link Reactor}.
	 * @throws UnsatisfiedDependencyException is thrown in case for a required
	 *         {@link Dependency} none matching {@link Dependency} candidates in
	 *         the {@link Reactor} have been found (taking the profiles applied
	 *         into account) for the constructors of the given
	 *         {@link Dependency}.
	 * @throws CircularDependencyException is thrown in case there is some
	 *         (transitive) circular dependency between two {@link Dependency}
	 *         instances.
	 * @throws DuplicateDependencyException is thrown in case there are multiple
	 *         identical dependencies applicable by the {@link Reactor}.
	 * @throws DuplicateClaimException is thrown in case there are multiple
	 *         identical claims declared by a {@link Dependency}.
	 * @throws UnsatisfiedClaimException is thrown in case a {@link Claim}
	 *         instance cannot be satisfied by any known {@link Dependency}
	 *         instance.
	 * @throws AmbigousClaimException is thrown in case one {@link Claim} can be
	 *         matched by multiple dependencies.
	 * @throws AmbigousInitializerException is thrown in case one
	 *         {@link InitializerClaim} instance can be matched by multiple
	 *         dependencies.
	 * @throws UnsatisfiedInitializerException is thrown in case a
	 *         {@link InitializerClaim} instance cannot be satisfied by any
	 *         known {@link Dependency} instance.
	 * @throws AmbigousFactoryException is thrown in case one
	 *         {@link FactoryClaim} instance can be matched by multiple
	 *         dependencies.
	 * @throws UnsatisfiedFactoryException is thrown in case a
	 *         {@link FactoryClaim} instance cannot be satisfied by any known
	 *         {@link Dependency} instance.
	 * @throws InstallDependencyException is thrown when trying to install a
	 *         {@link Dependency} into the {@link Context}, though installing
	 *         failed die to the dependency throwing an exception during the
	 *         process of installing (e.g. instantiation).
	 */
	public ApplicationContext createContext( ProfilesMode aProfilesMode ) throws AmbigousDependencyException, UnsatisfiedDependencyException, CircularDependencyException, DuplicateDependencyException, DuplicateClaimException, UnsatisfiedClaimException, AmbigousClaimException, AmbigousInitializerException, UnsatisfiedInitializerException, AmbigousFactoryException, UnsatisfiedFactoryException, InstallDependencyException {
		Object[] theProfiles = {};
		if ( aProfilesMode == ProfilesMode.DETERMINED ) {
			theProfiles = toProfiles();
		}
		return createContext( theProfiles );
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ApplicationContext createContext( Object... aProfiles ) throws AmbigousDependencyException, UnsatisfiedDependencyException, CircularDependencyException, DuplicateDependencyException, DuplicateClaimException, UnsatisfiedClaimException, AmbigousClaimException, AmbigousInitializerException, UnsatisfiedInitializerException, AmbigousFactoryException, UnsatisfiedFactoryException, InstallDependencyException {
		String[] theProfiles = {};
		if ( aProfiles != null && aProfiles.length != 0 ) {
			theProfiles = new String[aProfiles.length];
			for ( int i = 0; i < theProfiles.length; i++ ) {
				theProfiles[i] = aProfiles[i] != null ? aProfiles[i].toString() : null;
			}
		}
		addDependency( _properties.toRuntimeProfile( theProfiles ) ).withAlias( APPLICATION_PROPERTIES_ALIAS ).withInstanceMetrics( InstanceMode.SINGLETON_BY_DEFAULT ).withProfiles( aProfiles );
		try {
			addDependency( ApplicationBus.class ).withAlias( APPLICATION_BUS_ALIAS ).withInstanceMetrics( InstanceMode.SINGLETON_ON_DEMAND ).withProfiles( aProfiles );
		}
		catch ( Exception | Error ignore ) {
			// The "refcodes-eventbus-ext-application" dependency is optional!	
		}
		final ApplicationContext theCtx = (ApplicationContext) super.createContext( aProfiles );
		addDependency( theCtx ).withAlias( APPLICATION_CONTEXT_ALIAS ).withInstanceMetrics( InstanceMode.SINGLETON_BY_DEFAULT ).withProfiles( aProfiles );
		initialize( theCtx );
		start( theCtx );

		final Thread theHook = new Thread( () -> {
			try {
				theCtx.stop();
			}
			catch ( StopException e ) {
				LOGGER.log( Level.WARNING, "Cannot (completely) stop the context as of: " + e.toMessage(), e );
			}
			try {
				theCtx.destroy();
			}
			catch ( RuntimeException e ) {
				LOGGER.log( Level.WARNING, "Cannot (completely) destroy the context as of: " + Trap.asMessage( e ), e );
			}
		} );
		theHook.setDaemon( true );
		Execution.addShutdownHook( theHook );
		return theCtx;
	}

	/**
	 * Retrieves the initial properties (configuration) represented by this
	 * {@link ApplicationProperties} instance.
	 * 
	 * @return The according initial configuration.
	 */
	@Override
	public ApplicationProperties getApplicationProperties() {
		return _properties;
	}

	// /////////////////////////////////////////////////////////////////////////
	// HOOKS:
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * {@inheritDoc}
	 */
	@Override
	protected ApplicationContext toContext( Object[] aProfiles ) {
		final ApplicationContext theCtx = new ApplicationContext( aProfiles );
		addDependency( theCtx ).withAlias( APPLICATION_CONTEXT_ALIAS ).withInstanceMetrics( InstanceMode.SINGLETON_BY_DEFAULT ).withProfiles( aProfiles );
		return theCtx;
	}

	// /////////////////////////////////////////////////////////////////////////
	// HELPER:
	// /////////////////////////////////////////////////////////////////////////

	private void initialize( ApplicationContext aCtx ) throws InstallDependencyException {
		try {
			aCtx.doInitialize();
		}
		catch ( InitializeException e ) {
			throw new IllegalStateException( "Cannot initialize the application context!", e );
		}
		for ( Object eObj : aCtx.getInstances() ) {
			if ( eObj instanceof Initializable eComponent && eComponent != aCtx ) {
				try {
					eComponent.initialize();
				}
				catch ( InitializeException e ) {
					final Dependency theDependency = aCtx.getDependencyByInstance( eComponent );
					throw new InstallDependencyException( "Cannot initialize the dependency <{0}>!", theDependency, e );
				}
			}
		}
	}

	private void start( ApplicationContext aCtx ) throws InstallDependencyException {
		try {
			aCtx.doStart();
		}
		catch ( StartException e ) {
			throw new IllegalStateException( "Cannot start the application context!", e );
		}
		for ( Object eObj : aCtx.getInstances() ) {
			if ( eObj instanceof Startable eComponent && eComponent != aCtx ) {
				try {
					eComponent.start();
				}
				catch ( StartException e ) {
					final Dependency theDependency = aCtx.getDependencyByInstance( eComponent );
					throw new InstallDependencyException( "Cannot start the dependency <{0}>!", theDependency, e );
				}
			}
		}
	}

	private Object[] toProfiles() {
		return _properties instanceof ProfileProperties ? ( (ProfileProperties) _properties ).getRuntimeProfiles() : new Object[] {};
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy