Modified: turbine/fulcrum/trunk/yaafi/src/java/org/apache/fulcrum/yaafi/framework/container/ServiceContainerImpl.java URL: http://svn.apache.org/viewvc/turbine/fulcrum/trunk/yaafi/src/java/org/apache/fulcrum/yaafi/framework/container/ServiceContainerImpl.java?rev=1848876&r1=1848875&r2=1848876&view=diff ============================================================================== --- turbine/fulcrum/trunk/yaafi/src/java/org/apache/fulcrum/yaafi/framework/container/ServiceContainerImpl.java (original) +++ turbine/fulcrum/trunk/yaafi/src/java/org/apache/fulcrum/yaafi/framework/container/ServiceContainerImpl.java Thu Dec 13 18:17:39 2018 @@ -57,1632 +57,1357 @@ import org.apache.fulcrum.yaafi.framewor import org.apache.commons.lang3.StringUtils; - /** * Yet another avalon framework implementation (YAAFI). * * @author <a href="mailto:siegfried.goes...@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.getServiceConfiguration().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; - } - - /** - * @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#decommision(java.lang.String) - */ - public synchronized void decommision(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.getServiceConfiguration().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; - } - - /** - * @return The service configuration - */ - private Configuration getServiceConfiguration() - { - return this.serviceConfiguration; - } - - /** - * 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); - } - } - } +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; + } + + /**
[... 273 lines stripped ...]