ServiceContainerImpl.java

package org.apache.fulcrum.yaafi.framework.container;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Properties;

import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.configuration.DefaultConfiguration;
import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder;
import org.apache.avalon.framework.container.ContainerUtil;
import org.apache.avalon.framework.context.Context;
import org.apache.avalon.framework.context.ContextException;
import org.apache.avalon.framework.context.DefaultContext;
import org.apache.avalon.framework.logger.Logger;
import org.apache.avalon.framework.parameters.ParameterException;
import org.apache.avalon.framework.parameters.Parameters;
import org.apache.avalon.framework.service.ServiceException;
import org.apache.avalon.framework.service.ServiceManager;
import org.apache.fulcrum.yaafi.framework.component.AvalonServiceComponentImpl;
import org.apache.fulcrum.yaafi.framework.component.ServiceComponent;
import org.apache.fulcrum.yaafi.framework.configuration.ComponentConfigurationPropertiesResolver;
import org.apache.fulcrum.yaafi.framework.configuration.ComponentConfigurationPropertiesResolverImpl;
import org.apache.fulcrum.yaafi.framework.constant.AvalonYaafiConstants;
import org.apache.fulcrum.yaafi.framework.context.AvalonToYaafiContextMapper;
import org.apache.fulcrum.yaafi.framework.context.YaafiToAvalonContextMapper;
import org.apache.fulcrum.yaafi.framework.crypto.CryptoStreamFactory;
import org.apache.fulcrum.yaafi.framework.role.RoleConfigurationParser;
import org.apache.fulcrum.yaafi.framework.role.RoleConfigurationParserImpl;
import org.apache.fulcrum.yaafi.framework.role.RoleEntry;
import org.apache.fulcrum.yaafi.framework.util.ConfigurationUtil;
import org.apache.fulcrum.yaafi.framework.util.InputStreamLocator;
import org.apache.fulcrum.yaafi.framework.util.ToStringBuilder;
import org.apache.fulcrum.yaafi.framework.util.Validate;

import org.apache.commons.lang3.StringUtils;

/**
 * Yet another avalon framework implementation (YAAFI).
 *
 * @author <a href="mailto:siegfried.goeschl@it20one.at">Siegfried Goeschl</a>
 */

public class ServiceContainerImpl implements ServiceContainer, ServiceConstants {
	/** Default timeout before disposing the container */
	private static final int DISPOSAL_DELAY_DEFAULT = 0;

	/** Default timeout before reconfiguring the container or services */
	private static final int RECONFIGURATION_DELAY_DEFAULT = 2000;

	/** The role configuration file to be used */
	private String componentRolesLocation;

	/** is the component role file encrypted? */
	private String isComponentRolesEncrypted;

	/** which flavour of component role file we have to parse? */
	private String componentRolesFlavour;

	/** The service configuration file to be used */
	private String componentConfigurationLocation;

	/** is the component configuration file encrypted? */
	private String isComponentConfigurationEncrypted;

	/** The parameters file to be used */
	private String parametersLocation;

	/** is the parameters file encrypted? */
	private String isParametersEncrypted;

	/** The application directory aka the current working directory */
	private File applicationRootDir;

	/** The directory for storing temporary files */
	private File tempRootDir;

	/** The logger to be used passed by the caller */
	private Logger logger;

	/** The service manager passed to the container */
	private ServiceManager parentServiceManager;

	/** The list of services instantiated */
	private List<ServiceComponent> serviceList;

	/** The map of services used for the lookup */
	private HashMap<String, ServiceComponent> serviceMap;

	/** The Avalon role configuration loaded by this class */
	private Configuration roleConfiguration;

	/** The Avalon service configuration loaded by this class */
	private Configuration serviceConfiguration;

	/** The temporary Avalon context passed to the implementation */
	private Context callerContext;

	/** The default Avalon context passed to the services */
	private Context context;

	/** The default Avalon parameters */
	private Parameters parameters;

	/** Is this instance already disposed? */
	private volatile boolean isAlreadyDisposed;

	/** Is this instance currently disposed? */
	private volatile boolean isCurrentlyDisposing;

	/** The type of container where YAAFI is embedded */
	private String containerFlavour;

	/**
	 * The ms to wait before triggering a reconfiguration of the container or
	 * service
	 */
	private int reconfigurationDelay;

	/** The ms to wait before triggering a disposal of the container */
	private int disposalDelay;

	/** global flag for enabling/disabling dynamic proxies */
	private boolean hasDynamicProxies;

	/** The list of interceptor services applied to all services */
	private ArrayList<String> defaultInterceptorServiceList;

	/** The list of ServiceManagers as fallback service lookup */
	private ArrayList<String> fallbackServiceManagerList;

	/**
	 * the configuration for running the ComponentConfigurationPropertiesResolver
	 */
	private Configuration componentConfigurationPropertiesResolverConfig;

	/////////////////////////////////////////////////////////////////////////
	// Avalon Service Lifecycle
	/////////////////////////////////////////////////////////////////////////

	/**
	 * Constructor using sensible defaults.
	 */
	public ServiceContainerImpl() {
		super();

		this.containerFlavour = COMPONENT_CONTAINERFLAVOUR_VALUE;
		this.componentRolesFlavour = COMPONENT_ROLECONFIGFLAVOUR_VALUE;

		this.componentRolesLocation = COMPONENT_ROLE_VALUE;
		this.componentConfigurationLocation = COMPONENT_CONFIG_VALUE;
		this.parametersLocation = COMPONENT_PARAMETERS_VALUE;

		this.isComponentConfigurationEncrypted = "false";
		this.isComponentRolesEncrypted = "false";
		this.isParametersEncrypted = "false";

		this.isAlreadyDisposed = false;
		this.isCurrentlyDisposing = false;

		this.serviceList = new ArrayList<ServiceComponent>();
		this.serviceMap = new HashMap<String, ServiceComponent>();

		this.applicationRootDir = new File(new File("").getAbsolutePath());
		this.tempRootDir = new File(System.getProperty("java.io.tmpdir", "."));

		this.fallbackServiceManagerList = new ArrayList<String>();
		this.defaultInterceptorServiceList = new ArrayList<String>();

		this.disposalDelay = DISPOSAL_DELAY_DEFAULT;
		this.reconfigurationDelay = RECONFIGURATION_DELAY_DEFAULT;
	}

	/**
	 * @see org.apache.avalon.framework.logger.LogEnabled#enableLogging(org.apache.avalon.framework.logger.Logger)
	 */
	public void enableLogging(Logger logger) {
		Validate.notNull(logger, "logger");
		this.logger = logger;
	}

	/**
	 * @see org.apache.avalon.framework.context.Contextualizable#contextualize(org.apache.avalon.framework.context.Context)
	 */
	public void contextualize(Context context) throws ContextException {
		Validate.notNull(context, "context");
		// Argghhh - I need to to parse the Configuration before I can map the Context
		this.callerContext = context;
	}

	/**
	 * @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager)
	 */
	public void service(ServiceManager serviceManager) throws ServiceException {
		this.parentServiceManager = serviceManager;
	}

	/**
	 * @see org.apache.avalon.framework.configuration.Configurable#configure(org.apache.avalon.framework.configuration.Configuration)
	 */
	public void configure(Configuration configuration) throws ConfigurationException {
		Validate.notNull(configuration, "configuration");

		// retrieve the reconfigurationDelay

		this.reconfigurationDelay = configuration.getChild(RECONFIGURATION_DELAY_KEY)
				.getValueAsInteger(RECONFIGURATION_DELAY_DEFAULT);

		// retrieve the disposal delay

		this.disposalDelay = configuration.getChild(DISPOSAL_DELAY_KEY).getValueAsInteger(DISPOSAL_DELAY_DEFAULT);

		// evaluate if we are using dynamic proxies

		this.hasDynamicProxies = configuration.getChild(DYNAMICPROXY_ENABLED_KEY).getValueAsBoolean(false);

		// retrieve the container flavour

		this.setContainerFlavour(
				configuration.getChild(CONTAINERFLAVOUR_CONFIG_KEY).getValue(COMPONENT_CONTAINERFLAVOUR_VALUE));

		this.getLogger().debug("Using the following container type : " + this.getContainerFlavour());

		// process the caller-supplied context here

		try {
			// instantiate a mapper using the existing context - it might
			// contain application specific entries we are not aware of

			AvalonToYaafiContextMapper mapper = new AvalonToYaafiContextMapper(this.getTempRootDir(),
					this.callerContext, this.getClassLoader());

			// do the magic mapping

			this.context = mapper.mapFrom(this.callerContext, this.getContainerFlavour());

			// don't keep a reference of the caller-supplied context

			this.callerContext = null;
		} catch (ContextException e) {
			String msg = "Failed to parse the caller-supplied context";
			this.getLogger().error(msg, e);
			throw new ConfigurationException(msg);
		}

		// evaluate componentRoles

		Configuration currComponentRoles = configuration.getChild(COMPONENT_ROLE_KEYS);

		this.setComponentRolesLocation(
				currComponentRoles.getChild(COMPONENT_LOCATION_KEY).getValue(COMPONENT_ROLE_VALUE));

		this.setComponentRolesFlavour(
				currComponentRoles.getChild(CONTAINERFLAVOUR_CONFIG_KEY).getValue(COMPONENT_ROLECONFIGFLAVOUR_VALUE));

		this.setComponentRolesEncrypted(currComponentRoles.getChild(COMPONENT_ISENCRYPTED_KEY).getValue("false"));

		// evaluate componentConfiguraion

		Configuration currComponentConfiguration = configuration.getChild(COMPONENT_CONFIG_KEY);

		this.setComponentConfigurationLocation(
				currComponentConfiguration.getChild(COMPONENT_LOCATION_KEY).getValue(COMPONENT_CONFIG_VALUE));

		this.setComponentConfigurationEncrypted(
				currComponentConfiguration.getChild(COMPONENT_ISENCRYPTED_KEY).getValue("false"));

		// get the configuration for componentConfigurationPropertiesResolver

		this.componentConfigurationPropertiesResolverConfig = configuration.getChild(COMPONENT_CONFIG_PROPERTIES_KEY);

		// evaluate parameters

		Configuration currParameters = configuration.getChild(COMPONENT_PARAMETERS_KEY);

		this.setParametersLocation(
				currParameters.getChild(COMPONENT_LOCATION_KEY).getValue(COMPONENT_PARAMETERS_VALUE));

		this.setParametersEncrypted(currParameters.getChild(COMPONENT_ISENCRYPTED_KEY).getValue("false"));

		// evaluate the default interceptors

		Configuration currInterceptorList = configuration.getChild(INTERCEPTOR_LIST_KEY);

		Configuration[] interceptorConfigList = currInterceptorList.getChildren(INTERCEPTOR_KEY);

		for (int i = 0; i < interceptorConfigList.length; i++) {
			String interceptorServiceName = interceptorConfigList[i].getValue(null);

			if (!StringUtils.isEmpty(interceptorServiceName) && this.hasDynamicProxies()) {
				this.defaultInterceptorServiceList.add(interceptorServiceName);

				this.getLogger().debug("Using the following default interceptor service : " + interceptorServiceName);
			}
		}

		// evaluate a list of service managers managing their own set of services
		// independent from the Avalon container. This service managers are used
		// to find services implemented as Spring bean or remote web services.

		Configuration currServiceManagerList = configuration.getChild(SERVICEMANAGER_LIST_KEY);

		Configuration[] serviceManagerConfigList = currServiceManagerList.getChildren(SERVICEMANAGER_KEY);

		for (int i = 0; i < serviceManagerConfigList.length; i++) {
			String serviceManagerName = serviceManagerConfigList[i].getValue(null);

			if (!StringUtils.isEmpty(serviceManagerName)) {
				this.fallbackServiceManagerList.add(serviceManagerName);

				this.getLogger().debug("Using the following fallback service manager : " + serviceManagerName);
			}
		}
	}

	/**
	 * @see org.apache.avalon.framework.parameters.Parameterizable#parameterize(org.apache.avalon.framework.parameters.Parameters)
	 */
	public void parameterize(Parameters parameters) throws ParameterException {
		this.parameters = parameters;
	}

	/**
	 * @see org.apache.avalon.framework.activity.Initializable#initialize()
	 */
	public void initialize() throws Exception {
		this.getLogger().debug("YAAFI Service Framework is starting up");

		// set the directories being used

		this.setApplicationRootDir((File) this.getContext().get(AvalonYaafiConstants.URN_AVALON_HOME));

		this.setTempRootDir((File) this.getContext().get(AvalonYaafiConstants.URN_AVALON_TEMP));

		// get the configuration files

		this.roleConfiguration = loadConfiguration(this.componentRolesLocation, this.isComponentRolesEncrypted());

		if (this.roleConfiguration == null) {
			String msg = "Unable to locate the role configuration : " + this.componentRolesLocation;
			this.getLogger().error(msg);
			throw new ConfigurationException(msg);
		}

		this.serviceConfiguration = loadConfiguration(this.componentConfigurationLocation,
				this.isComponentConfigurationEncrypted());

		// create the configuration properties

		Properties componentConfigurationProperties = this.loadComponentConfigurationProperties();

		// expand the componentConfiguration using the componentConfigurationProperties

		ConfigurationUtil.expand(this.getLogger(), (DefaultConfiguration) this.serviceConfiguration,
				componentConfigurationProperties);

		// create the default parameters

		if (this.getParameters() == null) {
			this.parameters = this.loadParameters(this.parametersLocation, this.isParametersEncrypted());
		}

		// create the service implementation instances

		List<ServiceComponent> currServiceList = this.createServiceComponents(this.roleConfiguration, this.getLogger());

		this.setServiceList(currServiceList);

		// fill the service map mapping from a service name to an instance

		for (int i = 0; i < this.getServiceList().size(); i++) {
			ServiceComponent serviceComponent = (ServiceComponent) this.getServiceList().get(i);
			this.getServiceMap().put(serviceComponent.getName(), serviceComponent);
		}

		// ensure that fallback service managers are available

		for (int i = 0; i < this.fallbackServiceManagerList.size(); i++) {
			String currServiceManagerName = (String) this.fallbackServiceManagerList.get(i);
			if (this.getServiceMap().get(currServiceManagerName) == null) {
				String msg = "The following fallback service manager was not found : " + currServiceManagerName;
				throw new IllegalArgumentException(msg);
			}
		}

		// run the various lifecycle stages

		this.incarnateAll(this.getServiceList());

		// we are up and running

		this.isCurrentlyDisposing = false;
		this.isAlreadyDisposed = false;
		this.getLogger().debug("YAAFI Avalon Service Container is up and running");
	}

	/**
	 * Disposes the service container implementation.
	 *
	 * @see org.apache.avalon.framework.activity.Disposable#dispose()
	 */
	public void dispose() {
		if (this.isCurrentlyDisposing() || this.isAlreadyDisposed()) {
			return;
		}

		this.isCurrentlyDisposing = true;

		if (this.getLogger() != null) {
			this.getLogger().debug("Disposing all services");
		}

		// wait some time before disposing all services

		waitForDisposal();

		synchronized (this) {
			// de-commission all services

			this.decommissionAll(this.getServiceList());

			// dispose all services

			this.disposeAll(this.getServiceList());

			// clean up

			this.getServiceList().clear();
			this.getServiceMap().clear();

			this.componentRolesLocation = null;
			this.componentConfigurationLocation = null;
			this.context = null;
			this.parametersLocation = null;
			this.roleConfiguration = null;
			this.serviceConfiguration = null;
			this.parameters = null;
			this.fallbackServiceManagerList = null;
			this.defaultInterceptorServiceList = null;
			this.isCurrentlyDisposing = false;
			this.isAlreadyDisposed = true;

			if (this.getLogger() != null) {
				this.getLogger().debug("All services are disposed");
			}
		}
	}

	/**
	 * Reconfiguring the services. I'm not sure how to implement this properly since
	 * the Avalon docs is vague on this subject. For now we suspend, reconfigure and
	 * resume the services in the correct order.
	 *
	 * @see org.apache.avalon.framework.configuration.Reconfigurable#reconfigure(org.apache.avalon.framework.configuration.Configuration)
	 */
	public synchronized void reconfigure(Configuration configuration) throws ConfigurationException {
		Validate.notNull(configuration, "configuration");

		int exceptionCounter = 0;
		ServiceComponent serviceComponent;

		this.getLogger().warn("Reconfiguring all services ...");

		// 1) wait for some time

		this.waitForReconfiguration();

		// 2) store the new configuration

		this.serviceConfiguration = configuration;

		Properties componentConfigurationProperties = this.loadComponentConfigurationProperties();

		ConfigurationUtil.expand(this.getLogger(), (DefaultConfiguration) this.serviceConfiguration,
				componentConfigurationProperties);

		// 3) reconfigure the services

		for (int i = 0; i < this.getServiceList().size(); i++) {
			serviceComponent = (ServiceComponent) this.getServiceList().get(i);

			Configuration serviceComponentConfiguration = this.serviceConfiguration
					.getChild(serviceComponent.getShorthand());

			try {
				serviceComponent.setConfiguration(serviceComponentConfiguration);
				serviceComponent.reconfigure();
			} catch (Throwable t) {
				String msg = "Reconfiguring of " + serviceComponent.getShorthand() + " failed";
				this.getLogger().error(msg);
				exceptionCounter++;
			}
		}

		// 4) check the result

		if (exceptionCounter > 0) {
			String msg = "The reconfiguration failed with " + exceptionCounter + " exception(s)";
			this.getLogger().error(msg);
			throw new ConfigurationException(msg);
		}
	}

	/////////////////////////////////////////////////////////////////////////
	// Server Interface Implementation
	/////////////////////////////////////////////////////////////////////////

	/**
	 * @see org.apache.fulcrum.yaafi.framework.container.ServiceLifecycleManager#getRoleEntry(java.lang.String)
	 */
	public synchronized RoleEntry getRoleEntry(String name) throws ServiceException {
		return this.getServiceComponentEx(name).getRoleEntry();
	}

	/**
	 * @see org.apache.fulcrum.yaafi.framework.container.ServiceLifecycleManager#getRoleEntries()
	 */
	public synchronized RoleEntry[] getRoleEntries() {
		ServiceComponent serviceComponent;
		List<ServiceComponent> serviceList = this.getServiceList();
		RoleEntry[] result = new RoleEntry[serviceList.size()];

		for (int i = 0; i < result.length; i++) {
			serviceComponent = (ServiceComponent) serviceList.get(i);
			result[i] = serviceComponent.getRoleEntry();
		}

		return result;
	}

	/**
	 * @see org.apache.fulcrum.yaafi.framework.container.ServiceLifecycleManager#reconfigure(java.lang.String[])
	 */
	public synchronized void reconfigure(String[] names) throws ServiceException, ConfigurationException {
		Validate.notNull(names, "names");
		Validate.noNullElements(names, "names");

		this.waitForReconfiguration();

		for (int i = 0; i < names.length; i++) {
			// ensure that the service exists since during our reconfiguration
			// we might use a stale reconfiguration entry

			if (this.getServiceMap().get(names[i]) != null) {
				this.reconfigure(names[i]);
			}
		}
	}

	/**
	 * @see org.apache.avalon.framework.service.ServiceManager#hasService(java.lang.String)
	 */
	public boolean hasService(String name) {
		Validate.notEmpty(name, "name");

		boolean result;

		if (this.isCurrentlyDisposing() || this.isAlreadyDisposed()) {
			return false;
		}

		synchronized (this) {
			// look at our available service
			result = this.getLocalServiceComponent(name) != null;

			// look at fallback service managers
			if (!result)
				result = this.hasFallbackService(name);
		}

		// if we haven't found anything ask the parent ServiceManager
		if (!result && this.hasParentServiceManager())
			result = this.getParentServiceManager().hasService(name);

		return result;
	}

	/**
	 * Lookup a service instance. The implementation uses the following mechanism
	 * <ul>
	 * <li>look for a matching local service
	 * <li>use the fallback service manager as they might know the service
	 * <li>ask the parent service manager
	 * </ul>
	 *
	 * @see org.apache.avalon.framework.service.ServiceManager#lookup(java.lang.String)
	 */
	public Object lookup(String name) throws ServiceException {
		Validate.notEmpty(name, "name");

		Object result = null;
		ServiceComponent serviceManagerComponent;

		if (this.isAlreadyDisposed()) {
			String msg = "The container is disposed an no services are available";
			this.getLogger().error(msg);
			throw new ServiceException(name, msg);
		}

		try {
			synchronized (this) {
				// 1) check our local services
				serviceManagerComponent = this.getLocalServiceComponent(name);

				if (serviceManagerComponent != null) {
					result = serviceManagerComponent.getInstance();

					if (result != null && this.getLogger().isDebugEnabled()) {
						String msg = "Located the service '" + name + "' in the local container";
						this.getLogger().debug(msg);
					}
				}

				// 2) look at fallback service managers
				if (result == null) {
					result = this.getFallbackService(name);
				}
			}
		} catch (ServiceException e) {
			String msg = "Failed to lookup a service " + name;
			this.getLogger().error(msg, e);
			throw e;
		} catch (Throwable t) {
			String msg = "Failed to lookup a service " + name;
			this.getLogger().error(msg, t);
			throw new ServiceException(name, msg, t);
		}

		// 3) if we haven't found anything ask the parent ServiceManager
		if (result == null && this.hasParentServiceManager()) {
			result = this.getParentServiceManager().lookup(name);

			if (result != null && this.getLogger().isDebugEnabled()) {
				String msg = "Located the service '" + name + "' using the parent service manager";
				this.getLogger().debug(msg);
			}
		}

		// if we still haven't found anything then complain

		if (result == null) {
			String msg = "The following component does not exist : " + name;
			this.getLogger().error(msg);
			throw new ServiceException(AvalonYaafiConstants.AVALON_CONTAINER_YAAFI, name);
		}

		return result;
	}

	/* (non-Javadoc)
	 * @see org.apache.avalon.framework.service.ServiceManager#release(java.lang.Object)
	 */
	public void release(Object object) {
		// AFAIK this is only useful for lifecycle management regarding
		// lifestyle other than singleton.
	}

	/**
	 * @see org.apache.fulcrum.yaafi.framework.container.ServiceContainer#decommission(java.lang.String)
	 */
	public synchronized void decommission(String name) throws ServiceException {
		this.waitForReconfiguration();
		ServiceComponent serviceComponent = this.getServiceComponentEx(name);
		this.decommission(serviceComponent);
	}

	/**
	 * @see org.apache.fulcrum.yaafi.framework.container.ServiceContainer#getParameters()
	 */
	public Parameters getParameters() {
		return this.parameters;
	}

	/////////////////////////////////////////////////////////////////////////
	// Service Implementation
	/////////////////////////////////////////////////////////////////////////

	/**
	 * @see java.lang.Object#toString()
	 */
	public String toString() {
		ToStringBuilder toStringBuilder = new ToStringBuilder(this);
		toStringBuilder.append("applicationRootDir", this.getApplicationRootDir());
		toStringBuilder.append("tempRootDir", this.getTempRootDir());
		toStringBuilder.append("componentRolesLocation", this.componentRolesLocation);
		toStringBuilder.append("componentConfigurationLocation", this.componentConfigurationLocation);
		toStringBuilder.append("parametersLocation", parametersLocation);
		toStringBuilder.append("logger", this.getLogger().getClass().getName());
		toStringBuilder.append("hasDynamicProxies", this.hasDynamicProxies);
		toStringBuilder.append("containerFlavour", this.containerFlavour);
		toStringBuilder.append("componentRolesFlavour", this.componentRolesFlavour);
		toStringBuilder.append("isComponentRolesEncrypted", this.isComponentRolesEncrypted);
		toStringBuilder.append("isComponentConfigurationEncrypted", this.isComponentConfigurationEncrypted);
		toStringBuilder.append("isParametersEncrypted", this.isParametersEncrypted);

		return toStringBuilder.toString();
	}

	/**
	 * Create a role configuration parser based on the container flavour.
	 * 
	 * @return the role configuration parser
	 */
	private RoleConfigurationParser createRoleConfigurationParser() {
		return new RoleConfigurationParserImpl(this.getComponentRolesFlavour());
	}

	/**
	 * Reconfigure a single service
	 *
	 * @param name the name of the service to be reconfigured
	 * @throws ServiceException       the service was not found
	 * @throws ConfigurationException the reconfiguration failed
	 */
	private void reconfigure(String name) throws ServiceException, ConfigurationException {
		Validate.notEmpty(name, "name");
		ServiceComponent serviceComponent = this.getServiceComponentEx(name);

		// reconfigure the component

		try {
			serviceComponent.reconfigure();
		} catch (ConfigurationException e) {
			String msg = "Reconfiguring failed : " + serviceComponent.getShorthand();
			this.getLogger().error(msg, e);
			throw new ConfigurationException(msg, e);
		} catch (Throwable t) {
			String msg = "Reconfiguring failed : " + serviceComponent.getShorthand();
			this.getLogger().error(msg, t);
			throw new ConfigurationException(msg, t);
		}
	}

	/**
	 * Enforce that a service is known to simplify error handling.
	 *
	 * @param name the name of the service component
	 * @return the service component
	 * @throws ServiceException the service was not found
	 */
	private ServiceComponent getServiceComponentEx(String name) throws ServiceException {
		Validate.notEmpty(name, "name");
		ServiceComponent result = (ServiceComponent) this.getServiceMap().get(name);

		if (result == null) {
			String msg = "The following component does not exist : " + name;
			this.getLogger().error(msg);
			throw new ServiceException(AvalonYaafiConstants.AVALON_CONTAINER_YAAFI, name);
		}

		return result;
	}

	/**
	 * Try to get a local service component.
	 *
	 * @param name the name of the service component
	 * @return the service component if any
	 */
	private ServiceComponent getLocalServiceComponent(String name) {
		Validate.notEmpty(name, "name");
		ServiceComponent result = (ServiceComponent) this.getServiceMap().get(name);
		return result;
	}

	/**
	 * Try to get a service component provided by a fallback service manager.
	 *
	 * @param name the name of the service component
	 * @return the service component if any
	 * @throws Exception getting the service failed
	 */
	private Object getFallbackService(String name) throws Exception {

		Validate.notEmpty(name, "name");

		Object result = null;
		ServiceComponent serviceManagerComponent;

		for (int i = 0; i < this.fallbackServiceManagerList.size(); i++) {
			String serviceManagerComponentName = (String) fallbackServiceManagerList.get(i);
			serviceManagerComponent = this.getLocalServiceComponent(serviceManagerComponentName);

			if (serviceManagerComponent != null) {
				ServiceManager currServiceManager = (ServiceManager) serviceManagerComponent.getInstance();

				if (currServiceManager.hasService(name)) {
					result = currServiceManager.lookup(name);

					if (result != null && this.getLogger().isDebugEnabled()) {
						String msg = "Located the service '" + name + "' using the fallback service manager '"
								+ serviceManagerComponentName + "'";
						this.getLogger().debug(msg);
					}
				}
			}
		}

		return result;
	}

	/**
	 * Try to get a service provided by a fallback service manager.
	 *
	 * @param name the name of the service component
	 * @return the service component if any
	 */
	private boolean hasFallbackService(String name) {
		Validate.notEmpty(name, "name");

		ServiceComponent serviceManagerComponent;

		for (int i = 0; i < this.fallbackServiceManagerList.size(); i++) {
			String serviceManagerComponentName = (String) fallbackServiceManagerList.get(i);
			serviceManagerComponent = this.getLocalServiceComponent(serviceManagerComponentName);

			if (serviceManagerComponent != null) {
				ServiceManager currServiceManager;

				try {
					currServiceManager = (ServiceManager) serviceManagerComponent.getInstance();
					if (currServiceManager.hasService(name)) {
						return true;
					}
				} catch (Exception e) {
					String msg = "Unable to invoke fallback service manager '" + serviceManagerComponentName + "'";
					this.getLogger().error(msg, e);
					throw new RuntimeException(msg);
				}
			}
		}

		return false;
	}

	/**
	 * @param string The location of the component configuration file
	 */
	private void setComponentConfigurationLocation(String string) {
		this.componentConfigurationLocation = string;
	}

	/**
	 * @param string The location of the component role file
	 */
	private void setComponentRolesLocation(String string) {
		this.componentRolesLocation = string;
	}

	/**
	 * @param string The location of the parameters file
	 */
	private void setParametersLocation(String string) {
		this.parametersLocation = string;
	}

	/**
	 * @return The logger of the service container
	 */
	private Logger getLogger() {
		return this.logger;
	}

	/**
	 * @return Returns the serviceMap.
	 */
	private HashMap<String, ServiceComponent> getServiceMap() {
		return this.serviceMap;
	}

	/**
	 * Incarnation of a list of services.
	 *
	 * @param serviceList the list of available services
	 * @throws Exception the incarnation of a service failed
	 */
	private void incarnateAll(List<ServiceComponent> serviceList) throws Exception {
		ServiceComponent serviceComponent;

		// configure all services

		for (int i = 0; i < serviceList.size(); i++) {
			serviceComponent = (ServiceComponent) this.getServiceList().get(i);
			this.configure(serviceComponent);
		}

		// incarnate all services

		for (int i = 0; i < serviceList.size(); i++) {
			serviceComponent = (ServiceComponent) this.getServiceList().get(i);
			this.incarnate(serviceComponent);
		}

	}

	/**
	 * Configure a single service component. After the invocation the service
	 * component is ready to be incarnated.
	 *
	 * @param serviceComponent The service component to be configured
	 * @throws Exception the configuration failed
	 */
	private void configure(ServiceComponent serviceComponent) throws Exception {
		this.getLogger().debug("Configuring the service component " + serviceComponent.getShorthand());

		// map the context according to the Avalon component type

		YaafiToAvalonContextMapper mapper = new YaafiToAvalonContextMapper(serviceComponent.getName(),
				this.getClassLoader());

		RoleEntry roleEntry = serviceComponent.getRoleEntry();
		String componentFlavour = roleEntry.getComponentFlavour();

		DefaultContext serviceComponentContext = mapper.mapTo(this.getContext(), componentFlavour);

		// create the remaining Avalon artifacts for the service component

		Logger serviceComponentLogger = this.getLogger().getChildLogger(roleEntry.getLogCategory());

		Configuration serviceComponentConfiguration = this.serviceConfiguration.getChild(roleEntry.getShorthand());

		Parameters serviceComponentParameters = this.getParameters();

		// configure the service component with all the artifacts

		serviceComponent.setLogger(serviceComponentLogger);
		serviceComponent.setServiceManager(this);
		serviceComponent.setContext(serviceComponentContext);
		serviceComponent.setConfiguration(serviceComponentConfiguration);
		serviceComponent.setParameters(serviceComponentParameters);

		// load the implementation class of the service

		serviceComponent.loadImplemtationClass(this.getClassLoader());
	}

	/**
	 * Incarnation of a configured service component. After the incarnation the
	 * service component is operational.
	 *
	 * @param serviceComponent The service component to incarnate
	 * @exception Exception incarnating the service component failed
	 */
	private void incarnate(ServiceComponent serviceComponent) throws Exception {
		this.getLogger().debug("Incarnating the service " + serviceComponent.getShorthand());

		serviceComponent.incarnate();
	}

	/**
	 * De-commission a ist of services.
	 *
	 * @param serviceList the list of services to decommission
	 */
	private void decommissionAll(List<ServiceComponent> serviceList) {
		for (int i = serviceList.size() - 1; i >= 0; i--) {
			ServiceComponent serviceComponent = (ServiceComponent) serviceList.get(i);
			this.decommission(serviceComponent);
		}
	}

	/**
	 * Decommission of a single service component. Decommission consists of running
	 * the whole Avalon decommission lifecycle process for a service component.
	 * After decommission the service is not operational any longer. During
	 * decommissioning we ignore any exceptions since it is quite common that
	 * something goes wrong.
	 *
	 * @param serviceComponent The service component to decommission
	 */
	private void decommission(ServiceComponent serviceComponent) {
		this.getLogger().debug("Decommission the service " + serviceComponent.getShorthand());

		try {
			serviceComponent.decommision();
		} catch (Throwable e) {
			String msg = "Decommissioning the following service failed : " + serviceComponent.getName();
			this.getLogger().error(msg, e);
		}
	}

	/**
	 * Disposing a ist of services
	 *
	 * @param serviceList the list of services to dispose
	 */
	private void disposeAll(List<ServiceComponent> serviceList) {
		for (int i = serviceList.size() - 1; i >= 0; i--) {
			ServiceComponent serviceComponent = (ServiceComponent) serviceList.get(i);
			this.dispose(serviceComponent);
		}
	}

	/**
	 * Disposing of a single service component.
	 * 
	 * @param serviceComponent The service component to decommission
	 */
	private void dispose(ServiceComponent serviceComponent) {
		this.getLogger().debug("Disposing the service " + serviceComponent.getShorthand());

		try {
			serviceComponent.dispose();
		} catch (Throwable e) {
			String msg = "Disposing the following service failed : " + serviceComponent.getName();
			this.getLogger().error(msg, e);
		}
	}

	private boolean isCurrentlyDisposing() {
		return isCurrentlyDisposing;
	}

	private boolean isAlreadyDisposed() {
		return isAlreadyDisposed;
	}

	/**
	 * @return The list of currently know services
	 */
	private List<ServiceComponent> getServiceList() {
		return this.serviceList;
	}

	/**
	 * @param list The list of known services
	 */
	private void setServiceList(List<ServiceComponent> list) {
		this.serviceList = list;
	}

	/**
	 * Factory method for creating services. The service instances are not
	 * initialized at this point.
	 *
	 * @param roleConfiguration the role configuration file
	 * @param logger            the logger
	 * @return the list of service components
	 * @throws ConfigurationException creating the service instance failed
	 */
	private List<ServiceComponent> createServiceComponents(Configuration roleConfiguration, Logger logger)
			throws ConfigurationException {
		Validate.notNull(roleConfiguration, "roleConfiguration");
		Validate.notNull(logger, "logger");

		ArrayList<ServiceComponent> result = new ArrayList<ServiceComponent>();
		ServiceComponent serviceComponent = null;

		// create an appropriate instance of role configuration parser

		RoleConfigurationParser roleConfigurationParser = this.createRoleConfigurationParser();

		// extract the role entries

		RoleEntry[] roleEntryList = roleConfigurationParser.parse(roleConfiguration);

		// get the default interceptors defined for the container

		ArrayList<String> defaultInterceptorList = this.getDefaultInterceptorServiceList();

		// create the service components based on the role entries

		for (int i = 0; i < roleEntryList.length; i++) {
			try {
				// add the default interceptors to all role entries

				RoleEntry roleEntry = roleEntryList[i];

				if (this.hasDynamicProxies()) {
					roleEntry.addInterceptors(defaultInterceptorList);
				} else {
					roleEntry.setHasDynamicProxy(false);
				}

				serviceComponent = new AvalonServiceComponentImpl(roleEntry, this.getLogger(), logger);

				result.add(serviceComponent);
			} catch (Throwable t) {
				String serviceComponentName = (serviceComponent != null ? serviceComponent.getName() : "unknown");
				String msg = "Failed to load the service " + serviceComponentName;
				this.getLogger().error(msg, t);
				throw new ConfigurationException(msg, t);
			}
		}

		return result;
	}

	/**
	 * Load a configuration file either from a file or using the class loader.
	 * 
	 * @param location    the location of the file
	 * @param isEncrypted is the configuration encrypted
	 * @return The loaded configuration
	 * @throws Exception Something went wrong
	 */
	private Configuration loadConfiguration(String location, String isEncrypted) throws Exception {
		Configuration result = null;
		InputStreamLocator locator = this.createInputStreamLocator();
		InputStream is = locator.locate(location);
		DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder();

		if (is != null) {
			try {
				is = this.getDecryptingInputStream(is, isEncrypted);
				result = builder.build(is);
			} catch (Exception e) {
				String msg = "Unable to parse the following file : " + location;
				this.getLogger().error(msg, e);
				throw e;
			} finally {
				safeClose(is);
			}
		}

		return result;
	}

	/**
	 * Load a configuration property file either from a file or using the class
	 * loader.
	 * 
	 * @return The loaded property file
	 * @throws ConfigurationException Something went wrong
	 */
	private Properties loadComponentConfigurationProperties() throws ConfigurationException {
		Properties result;
		ComponentConfigurationPropertiesResolver resolver;

		String className = this.componentConfigurationPropertiesResolverConfig.getChild("resolver")
				.getValue(ComponentConfigurationPropertiesResolverImpl.class.getName());

		try {
			Class<?> resolverClass = this.getClassLoader().loadClass(className);
			resolver = (ComponentConfigurationPropertiesResolver) resolverClass.newInstance();
			ContainerUtil.enableLogging(resolver, this.getLogger());
			ContainerUtil.contextualize(resolver, this.getContext());
			ContainerUtil.configure(resolver, this.componentConfigurationPropertiesResolverConfig);

			result = resolver.resolve(null);

			this.getLogger().debug("Using the following componentConfigurationProperties: " + result);
		} catch (Exception e) {
			String msg = "Resolving componentConfigurationProperties failed using the following class : " + className;
			this.getLogger().error(msg, e);
			throw new ConfigurationException(msg, e);
		}

		return result;
	}

	/**
	 * Load the parameters
	 * 
	 * @param location    The location as a file
	 * @param isEncrypted is the file encrypted
	 * @return The loaded configuration
	 * @throws Exception Something went wrong
	 */
	private Parameters loadParameters(String location, String isEncrypted) throws Exception {
		InputStreamLocator locator = this.createInputStreamLocator();
		InputStream is = locator.locate(location);
		Parameters result = new Parameters();

		if (is != null) {
			try {
				is = this.getDecryptingInputStream(is, isEncrypted);
				Properties props = new Properties();
				props.load(is);
				result = Parameters.fromProperties(props);
			} finally {
				safeClose(is);
			}
		}

		return result;
	}

	/**
	 * Creates a locator to find a resource either in the file system or in the
	 * classpath.
	 *
	 * @return the locator
	 */
	private InputStreamLocator createInputStreamLocator() {
		return new InputStreamLocator(this.getApplicationRootDir(), this.getLogger());
	}

	/**
	 * Set the application directory of the container.
	 *
	 * @param dir The applicationRootDir to set.
	 */
	private void setApplicationRootDir(File dir) {
		this.getLogger().debug("Setting applicationRootDir to " + dir.getAbsolutePath());

		Validate.notNull(dir, "applicationRootDir is <null>");
		Validate.isTrue(dir.exists(), "applicationRootDir does not exist");

		this.applicationRootDir = dir;
	}

	/**
	 * @return Returns the applicationRootDir.
	 */
	private File getApplicationRootDir() {
		return this.applicationRootDir;
	}

	/**
	 * @return Returns the serviceManager of the parent container
	 */
	private ServiceManager getParentServiceManager() {
		return this.parentServiceManager;
	}

	/**
	 * @return is a parent ServiceManager available
	 */
	private boolean hasParentServiceManager() {
		return this.getParentServiceManager() != null;
	}

	/**
	 * Set the temporary directory of the container.
	 *
	 * @param dir The tempRootDir to set.
	 */
	private void setTempRootDir(File dir) {
		this.getLogger().debug("Setting tempRootDir to " + dir.getAbsolutePath());

		Validate.notNull(dir, "tempRootDir is <null>");
		Validate.isTrue(dir.exists(), "tempRootDir does not exist");
		Validate.isTrue(dir.canWrite(), "tempRootDir is not writeable");

		this.tempRootDir = dir;
	}

	/**
	 * @return Returns the tempRootDir.
	 */
	private File getTempRootDir() {
		return tempRootDir;
	}

	/**
	 * @return Returns the isComponentConfigurationEncrypted.
	 */
	private String isComponentConfigurationEncrypted() {
		return isComponentConfigurationEncrypted;
	}

	/**
	 * @param isComponentConfigurationEncrypted The
	 *                                          isComponentConfigurationEncrypted to
	 *                                          set.
	 */
	private void setComponentConfigurationEncrypted(String isComponentConfigurationEncrypted) {
		this.isComponentConfigurationEncrypted = isComponentConfigurationEncrypted;
	}

	/**
	 * @return Returns the isComponentRolesEncrypted.
	 */
	private String isComponentRolesEncrypted() {
		return isComponentRolesEncrypted;
	}

	/**
	 * @param isComponentRolesEncrypted The isComponentRolesEncrypted to set.
	 */
	private void setComponentRolesEncrypted(String isComponentRolesEncrypted) {
		this.isComponentRolesEncrypted = isComponentRolesEncrypted;
	}

	/**
	 * @return Returns the isParametersEncrypted.
	 */
	private String isParametersEncrypted() {
		return isParametersEncrypted;
	}

	/**
	 * @param isParametersEncrypted The isParametersEncrypted to set.
	 */
	private void setParametersEncrypted(String isParametersEncrypted) {
		this.isParametersEncrypted = isParametersEncrypted;
	}

	/**
	 * Create a decrypting input stream using the default password.
	 *
	 * @param is          the input stream to be decrypted
	 * @param isEncrypted the encryption mode (true|false|auto)
	 * @return an decrypting input stream
	 * @throws Exception reading the input stream failed
	 */
	private InputStream getDecryptingInputStream(InputStream is, String isEncrypted) throws Exception {
		return CryptoStreamFactory.getDecryptingInputStream(is, isEncrypted);
	}

	/**
	 * @return Returns the containerFlavour.
	 */
	private String getContainerFlavour() {
		return containerFlavour;
	}

	/**
	 * @param containerFlavour The containerFlavour to set.
	 */
	private void setContainerFlavour(String containerFlavour) {
		this.containerFlavour = containerFlavour;
	}

	/**
	 * @return Returns the componentRolesFlavour.
	 */
	private String getComponentRolesFlavour() {
		return componentRolesFlavour;
	}

	/**
	 * @param componentRolesFlavour The componentRolesFlavour to set.
	 */
	private void setComponentRolesFlavour(String componentRolesFlavour) {
		this.componentRolesFlavour = componentRolesFlavour;
	}

	/**
	 * @return Returns the context.
	 */
	private Context getContext() {
		return context;
	}

	/**
	 * @return Returns the hasDynamicProxies.
	 */
	private boolean hasDynamicProxies() {
		return this.hasDynamicProxies;
	}

	/**
	 * @return Returns the defaultInterceptorServiceList.
	 */
	private ArrayList<String> getDefaultInterceptorServiceList() {
		return defaultInterceptorServiceList;
	}

	/**
	 * @return the containers class loader
	 */
	private ClassLoader getClassLoader() {
		return this.getClass().getClassLoader();
	}

	/**
	 * Wait for the time configured as 'reconfigurationDelay' before reconfiguring
	 * the container or services.
	 */
	private void waitForReconfiguration() {
		try {
			Thread.sleep(this.reconfigurationDelay);
		} catch (InterruptedException e) {
			// nothing to do
		}
	}

	/**
	 * Wait for the time configured as 'disposalDelay' before disposing the
	 * container.
	 */
	private void waitForDisposal() {
		try {
			Thread.sleep(this.disposalDelay);
		} catch (InterruptedException e) {
			// nothing to do
		}
	}

	private void safeClose(InputStream is) {
		if (is != null) {
			try {
				is.close();
			} catch (Exception e) {
				getLogger().error("Failed to close an input stream", e);
			}
		}
	}
}