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

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

Go to download

Artifact extending the refcodes-decoupling artifact with out of the box application functionality such as properties management (alongside profiles support) and CLI support, an event bus (application bus) as well as lifecycle management.

There is a newer version: 3.3.8
Show newest version
// /////////////////////////////////////////////////////////////////////////////
// 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;
	}

	// /////////////////////////////////////////////////////////////////////////
	// INJECTION:
	// /////////////////////////////////////////////////////////////////////////

	// /////////////////////////////////////////////////////////////////////////
	// 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[] {};
	}

	// /////////////////////////////////////////////////////////////////////////
	// INNER CLASSES:
	// /////////////////////////////////////////////////////////////////////////
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy