mcconnell 02/03/03 07:51:13
Added: src/scratchpad/org/apache/avalon/excalibur/service
DefaultServiceManager.java DependencyInfo.java
PipelineException.java
PipelineRuntimeException.java ServiceContext.java
ServiceFactory.java ServiceInfo.java
ServiceLoader.java ServiceLoaderContext.java
ServiceProvider.java ServiceRegistry.java
SingletonProvider.java TransientProvider.java
UnitInfo.java package.html
Log:
initial implementation of a service management framework
Revision Changes Path
1.1
jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/service/DefaultServiceManager.java
Index: DefaultServiceManager.java
===================================================================
/*
* File: DefaultServiceManager.java
* License: etc/LICENSE.TXT
* Copyright: Copyright (C) The Apache Software Foundation. All rights
reserved.
* Copyright: OSM SARL 2002, All Rights Reserved.
*/
package org.apache.avalon.excalibur.service;
import java.util.Enumeration;
import java.util.Hashtable;
import org.apache.avalon.framework.service.ServiceManager;
import org.apache.avalon.framework.service.ServiceException;
/**
* Internal helper class the implements the service manager interface and
* is supplied to dynamically crerated componets during service lifecyle
* pipeline processing.
*/
class DefaultServiceManager implements ServiceManager
{
/**
* Hashtable containing service providers keyed by role name.
* The manager use the providers in this table to aquire services
* in response to <code>lookup</code> invocations. Provider
* types fall into one of the following three catagories:
*
* <table>
* <tr><td><b>Policy</b></td><td><b>Description</b></td><tr>
* <tr><td>SINGLETON_LIFETIME_POLICY</td><td>
* Service of the type singleton are distinguished by the fact
* that they do not inherit from Pool or Transient. The singleton
* provider object is a reference to the singleton service and is
* return directly by the implemetation on invocation of lookup.
* </td>
* <tr><td>POOLED_LIFETIME_POLICY</td><td>
* Pooled services implement the Pool interface. The service
* resolves lookup aquires the pooled service by invoking
* <code>checkout</code> on the pool implementation. Clients
* using pooled services are required to release services using
* the manager <code>release</code> method. The implemetation will
* attempt to locate the issuing pool and release the object on
* behalf of the client.
* </td>
* <tr><td>TRANSIENT_LIFETIME_POLICY</td><td>
* A transient provider is factory from which new instances are
* created and pipelined following a invocation of <code>lookup</code>.
* The invocing client is totally responsible for service disposal.
* </td>
*/
private Hashtable m_providers = new Hashtable();
public DefaultServiceManager( Hashtable providers ) throws Exception
{
m_providers = providers;
}
public boolean hasService( String role )
{
return (m_providers.get( role ) != null );
}
public Object lookup( String role ) throws ServiceException
{
Object provider = m_providers.get( role );
if( provider == null ) throw new ServiceException(
"Could not locate a provider for the role: " + role );
if( provider instanceof TransientProvider )
{
//
// return a transient instance
//
return ((TransientProvider)provider).create( );
}
/*
else if( provider instanceof PooledProvider )
{
//
// return a pooled service
//
return ((PooledProvider)provider).checkout( );
}
*/
else
{
//
// return a singleton service
//
return ((SingletonProvider)provider).provide( );
}
}
public void release( Object object )
{
//
// release a pooled service
//
/*
Class c = object.getClass();
Enumeration providers = m_providers.elements();
while( providers.hasMoreElements())
{
Object provider = providers.nextElement();
if( provider instanceof PooledProvider )
{
PooledProvider pool = (PooledProvider) provider;
if( pool.getBaseClass().isAssignableFrom( c ) )
{
pool.release( object );
break;
}
}
}
*/
}
}
1.1
jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/service/DependencyInfo.java
Index: DependencyInfo.java
===================================================================
/*
* File: DependencyInfo.java
* License: etc/LICENSE.TXT
* Copyright: Copyright (C) The Apache Software Foundation. All rights
reserved.
* Copyright: OSM SARL 2002, All Rights Reserved.
*/
package org.apache.avalon.excalibur.service;
import org.apache.avalon.framework.configuration.Configuration;
/**
* Abstract meta-information that describes a single dependancy that a
* <code>Serviceable</code> object may have towards other
* components.
*/
class DependencyInfo extends ServiceInfo
{
private String m_role;
/**
* Creation of a new <code<DependencyInfo</code> instance.
* @param config a configuration corresponding to a <depends>
statement
*/
public DependencyInfo( final Configuration config ) throws Exception
{
super( config.getChild("service") );
m_role = config.getChild("role").getValue();
}
/**
* Returns the role name that the component uses to lookup a
* the dependency.
* @return the dependecy role name
* @see org.apache.avalon.framework.service.ServiceManager
* @see org.apache.avalon.framework.service.Serviceable
*/
public String getRole()
{
return m_role;
}
}
1.1
jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/service/PipelineException.java
Index: PipelineException.java
===================================================================
/*
* File: PipelineException.java
* License: etc/LICENSE.TXT
* Copyright: Copyright (C) The Apache Software Foundation. All rights
reserved.
* Copyright: OSM SARL 2002, All Rights Reserved.
*/
package org.apache.avalon.excalibur.service;
import java.io.StringWriter;
import java.util.StringTokenizer;
import java.io.PrintWriter;
import org.apache.avalon.framework.CascadingException;
/**
* Thrown by an Pipeline as a result of an unexpected error
* during execution.
*
* @author mcconnell
* @version 1.0
*/
public class PipelineException extends CascadingException {
/**
* Construct a new <code>PipelineRuntimeException</code> instance with
the
* supplied message parameter and a null value for the cause exception.
*
* @param message Message summarising the exception.
*/
public PipelineException( final String message )
{
this( message, null );
}
/**
* Construct a new <code>PipelineRuntimeException</code> instance with
the
* supplied message parameter and a supplied cause exception.
*
* @param message The detail message for this exception.
* @param cause the root cause of the exception
*/
public PipelineException( final String message, final Throwable cause )
{
super( message, cause );
}
public String toString()
{
final StringBuffer sb = new StringBuffer();
sb.append( "\n=================================================" );
sb.append( "\nException: " + this.getClass().getName() + ", " +
getMessage());
if( this.getCause() != null ) appendCause( sb, this.getCause() );
sb.append( "\n=================================================" );
return sb.toString();
}
private void appendCause( StringBuffer buffer, Throwable cause )
{
if( cause == null ) return;
buffer.append( "\nCause: " + cause.getClass().getName()
+ ", " + cause.getMessage() );
if( cause.getCause() != null )
{
appendCause( buffer, cause.getCause() );
}
else
{
buffer.append(
"\n-------------------------------------------------" );
String[] stack = captureStackTrace( cause );
for( int i=0; i<stack.length; i++ )
{
buffer.append( "\n" + stack[i] );
}
}
}
private static String[] captureStackTrace( final Throwable throwable )
{
final StringWriter sw = new StringWriter();
throwable.printStackTrace( new PrintWriter( sw, true ) );
return splitString( sw.toString(), "\n" );
}
/**
* Splits the string on every token into an array of stack frames.
*
* @param string the string
* @param onToken the token
* @return the resultant array
* @deprecated This is an internal utility method that should not be used
*/
private static String[] splitString( final String string, final String
onToken )
{
final StringTokenizer tokenizer = new StringTokenizer( string,
onToken );
final String[] result = new String[ tokenizer.countTokens() ];
for( int i = 0; i < result.length; i++ )
{
result[ i ] = tokenizer.nextToken();
}
return result;
}
}
1.1
jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/service/PipelineRuntimeException.java
Index: PipelineRuntimeException.java
===================================================================
/*
* File: PipelineRuntimeException.java
* License: etc/LICENSE.TXT
* Copyright: Copyright (C) The Apache Software Foundation. All rights
reserved.
* Copyright: OSM SARL 2002, All Rights Reserved.
*/
package org.apache.avalon.excalibur.service;
import java.io.StringWriter;
import java.util.StringTokenizer;
import java.io.PrintWriter;
import org.apache.avalon.framework.CascadingRuntimeException;
/**
* Thrown by an Pipeline as a result of an unexpected runtime error
* during execution.
*
* @author mcconnell
* @version 1.0
*/
public class PipelineRuntimeException extends CascadingRuntimeException {
/**
* Construct a new <code>PipelineRuntimeException</code> instance with
the
* supplied message parameter and a null value for the cause exception.
*
* @param message Message summarising the exception.
*/
public PipelineRuntimeException( final String message )
{
this( message, null );
}
/**
* Construct a new <code>PipelineRuntimeException</code> instance with
the
* supplied message parameter and a supplied cause exception.
*
* @param message The detail message for this exception.
* @param cause the root cause of the exception
*/
public PipelineRuntimeException( final String message, final Throwable
cause )
{
super( message, cause );
}
public String toString()
{
final StringBuffer sb = new StringBuffer();
sb.append( "\n=================================================" );
sb.append( "\nException: " + this.getClass().getName() + ", " +
getMessage());
if( this.getCause() != null ) appendCause( sb, this.getCause() );
sb.append( "\n=================================================" );
return sb.toString();
}
private void appendCause( StringBuffer buffer, Throwable cause )
{
if( cause == null ) return;
buffer.append( "\nCause: " + cause.getClass().getName()
+ ", " + cause.getMessage() );
if( cause.getCause() != null )
{
appendCause( buffer, cause.getCause() );
}
else
{
buffer.append(
"\n-------------------------------------------------" );
String[] stack = captureStackTrace( cause );
for( int i=0; i<stack.length; i++ )
{
buffer.append( "\n" + stack[i] );
}
}
}
private static String[] captureStackTrace( final Throwable throwable )
{
final StringWriter sw = new StringWriter();
throwable.printStackTrace( new PrintWriter( sw, true ) );
return splitString( sw.toString(), "\n" );
}
/**
* Splits the string on every token into an array of stack frames.
*
* @param string the string
* @param onToken the token
* @return the resultant array
* @deprecated This is an internal utility method that should not be used
*/
private static String[] splitString( final String string, final String
onToken )
{
final StringTokenizer tokenizer = new StringTokenizer( string,
onToken );
final String[] result = new String[ tokenizer.countTokens() ];
for( int i = 0; i < result.length; i++ )
{
result[ i ] = tokenizer.nextToken();
}
return result;
}
}
1.1
jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/service/ServiceContext.java
Index: ServiceContext.java
===================================================================
/*
* File: ServiceContext.java
* License: etc/LICENSE.TXT
* Copyright: Copyright (C) The Apache Software Foundation. All rights
reserved.
* Copyright: OSM SARL 2002, All Rights Reserved.
*/
package org.apache.avalon.excalibur.service;
import java.io.File;
import org.apache.avalon.phoenix.BlockContext;
import org.apache.avalon.framework.logger.Logger;
import org.apache.avalon.framework.context.DefaultContext;
/**
* ServiceContext context object to hold command line arguments and
* base directory that is supplied to a <code>Contextualizable</code>
* component.
* @author <a href="mailto:[EMAIL PROTECTED]">Stephen McConnell</a>
*/
public class ServiceContext extends DefaultContext implements BlockContext
{
public static final String ARGS_KEY = "ARGS";
public static final String BASE_DIRECTORY_KEY = "APP_DIR";
private String[] m_args;
private File m_root;
private String m_name;
private Logger m_logger;
public ServiceContext( String[] args, File base, String name )
{
m_args = args;
m_root = base;
m_name = name;
super.put( ARGS_KEY, m_args );
super.put( BASE_DIRECTORY_KEY, m_root );
}
public String[] getArgs()
{
return m_args;
}
public File getBaseDirectory()
{
return m_root;
}
public Logger getLogger( String name )
{
throw new RuntimeException("Not implemented.");
}
public String getName()
{
return m_name;
}
}
1.1
jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/service/ServiceFactory.java
Index: ServiceFactory.java
===================================================================
/*
* File: ServiceFactory.java
* License: etc/LICENSE.TXT
* Copyright: Copyright (C) The Apache Software Foundation. All rights
reserved.
* Copyright: OSM SARL 2002, All Rights Reserved.
*/
package org.apache.avalon.excalibur.service;
import java.io.File;
import java.util.Hashtable;
import java.util.Enumeration;
import java.util.Vector;
import org.apache.log.Hierarchy;
import org.apache.log.Priority;
import org.apache.log.output.io.StreamTarget;
import org.apache.avalon.framework.logger.Logger;
import org.apache.avalon.framework.CascadingRuntimeException;
import org.apache.avalon.framework.CascadingException;
import org.apache.avalon.framework.logger.LogKitLogger;
import org.apache.avalon.framework.logger.AvalonFormatter;
import org.apache.avalon.framework.logger.AbstractLogEnabled;
import org.apache.avalon.framework.logger.LogEnabled;
import org.apache.avalon.framework.parameters.Parameterizable;
import org.apache.avalon.framework.parameters.Parameters;
import org.apache.avalon.framework.configuration.Configurable;
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.context.Context;
import org.apache.avalon.framework.context.DefaultContext;
import org.apache.avalon.framework.context.Contextualizable;
import org.apache.avalon.framework.context.ContextException;
import org.apache.avalon.framework.component.Component;
import org.apache.avalon.framework.component.Composable;
import org.apache.avalon.framework.component.ComponentManager;
import org.apache.avalon.framework.component.DefaultComponentManager;
import org.apache.avalon.framework.component.ComponentException;
import org.apache.avalon.framework.service.Serviceable;
import org.apache.avalon.framework.service.ServiceManager;
import org.apache.avalon.framework.service.ServiceException;
import org.apache.avalon.framework.activity.Initializable;
import org.apache.avalon.framework.activity.Startable;
import org.apache.avalon.framework.activity.Disposable;
import org.apache.avalon.excalibur.configuration.CascadingConfiguration;
/**
*
*/
class ServiceFactory extends AbstractLogEnabled implements Disposable
{
private Hierarchy m_hierarchy;
private ServiceRegistry m_registry;
private Hashtable m_table = new Hashtable();
private Hashtable m_pools = new Hashtable();
private Hashtable m_singletons = new Hashtable();
private Hashtable m_transients = new Hashtable();
private File m_root;
private boolean m_verbose;
private Hashtable m_services = new Hashtable();
private Hashtable m_lookup = new Hashtable();
public ServiceFactory( Hierarchy hierarchy, Configuration config, File
base, boolean verbose ) throws Exception
{
m_hierarchy = hierarchy;
m_registry = new ServiceRegistry( verbose );
m_table = initalizeBlockConfigurations( config );
m_root = base;
m_verbose = verbose;
}
public void enableLogging( Logger logger )
{
super.enableLogging( logger );
m_registry.enableLogging( logger.getChildLogger("registry") );
}
/**
* Populates the set of available services based on a supplied
* vector of jar files.
* @param list a list of jar files
*/
public void register( Vector list ) throws PipelineException
{
m_registry.register( list );
}
public void validate( UnitInfo info ) throws Exception
{
DependencyInfo[] dependencies = info.getDependencies();
for( int i=0; i<dependencies.length; i++ )
{
ServiceInfo d = dependencies[i];
if( m_registry.lookup( d ) == null ) throw new Exception(
"Could not resolve dependent service " +
d.getInterface().getName()
+ " for block " + info.getClassName() );
}
}
private ServiceManager createServiceManager( DependencyInfo[]
dependencies ) throws ServiceException
{
Hashtable providers = new Hashtable();
try
{
for( int i=0; i<dependencies.length; i++ )
{
DependencyInfo info = dependencies[i];
providers.put( info.getRole(), getProvider( info ));
}
return new DefaultServiceManager( providers );
}
catch( Throwable e )
{
final String error = "Unexpected exception while attempting to
create a ServiceManager.";
throw new ServiceException( error, e );
}
}
private Object getProvider( DependencyInfo info ) throws Exception
{
UnitInfo block_info = m_registry.lookup( info );
//
// Try to establish the type of object by the interface it implements.
//
Object provider = null;
Class provider_class = block_info.getBaseClass();
//
// Resolve the provider based on meta-infomation in the .xinfo file
// which will result in a transient service if no other info
available.
//
int policy = block_info.getPolicy();
switch( policy )
{
case UnitInfo.SINGLETON_LIFETIME_POLICY :
//
// return a singleton instance
//
provider = m_singletons.get( provider_class );
if( provider == null )
{
if( m_verbose ) if( getLogger().isDebugEnabled() )
getLogger().debug(
"Creating singleton provider for :" +
provider_class.getName());
// create and pipeline the singleton instance and
// add it to the list of singletons
Object object = pipeline( block_info, info.getRole() );
provider = new SingletonProvider( object, info.getRole() );
m_singletons.put( provider_class, provider );
}
return provider;
case UnitInfo.POOLED_LIFETIME_POLICY :
final String error = "Cannot provide a default provider for a
pool at this time.";
throw new RuntimeException( error );
default :
//
// UnitInfo.TRANSIENT_LIFETIME_POLICY :
// new service instances are always created on invocation of
// a service lookup
//
provider = m_transients.get( provider_class );
if( provider == null )
{
if( m_verbose ) if( getLogger().isDebugEnabled() )
getLogger().debug(
"Creating transient provider for :" +
provider_class.getName());
// create and pipeline the singleton instance and
// add it to the list of singletons
provider = new TransientProvider( this, block_info,
info.getRole() );
m_transients.put( provider_class, provider );
}
return provider;
}
}
public Object pipeline( UnitInfo info, String role ) throws Exception
{
//
// create and pipeline the new instance
//
Object m_object;
try
{
m_object = info.getBaseClass().newInstance();
}
catch( Throwable e )
{
final String error = "Failed to instantiate an object: ";
throw new PipelineException( error +
info.getBaseClass().getName(), e );
}
if( m_verbose ) if( getLogger().isDebugEnabled() ) getLogger().debug(
"pipeline for role: " + role + ", implementation: " +
m_object.getClass().getName() );
//
// assign a logging channel
//
if( m_object instanceof LogEnabled ) try
{
Logger logger = new LogKitLogger( m_hierarchy.getLoggerFor( role
) );
if( m_verbose ) getLogger().debug( "applying logger to " + role );
((LogEnabled)m_object).enableLogging( logger );
}
catch( Throwable e )
{
final String error = "Failed to assign a logger channel.";
throw new PipelineException( error, e );
}
//
// configure the object
//
if( m_object instanceof Configurable ) try
{
if( m_verbose ) getLogger().debug( role + " configuration" );
Configuration defaults = info.getDefaultConfiguration();
Configuration primary = getConfigurationForClass(
info.getClassName() );
((Configurable)m_object).configure( new CascadingConfiguration(
primary, defaults ));
}
catch( Throwable e )
{
final String error = "Failed to configure an object.";
throw new PipelineException( error, e );
}
else if( m_object instanceof Parameterizable ) try
{
if( m_verbose ) getLogger().debug( role + " parameterization" );
((Parameterizable)m_object).parameterize(
Parameters.fromConfiguration( getConfigurationForClass(
info.getClassName() ) ) );
}
catch( Throwable e )
{
final String error = "Failed to parameterize an object.";
throw new PipelineException( error, e );
}
//
// contextualize the server
// [need to update this to handle an args sequence]
//
if( m_object instanceof Contextualizable ) try
{
if( m_verbose ) getLogger().debug( role + " contextualization" );
Context context = new ServiceContext(
new String[0], m_root, info.getClassName() );
((Contextualizable)m_object).contextualize( context );
}
catch( Throwable e )
{
final String error = "Failed to contextualize the object.";
throw new PipelineException( error, e );
}
//
// get the blocks required to support the dependencies
//
if( m_object instanceof Serviceable ) try
{
if( m_verbose ) getLogger().debug( role + " service composition"
);
DependencyInfo[] dependencies = info.getDependencies();
ServiceManager manager = createServiceManager( dependencies );
((Serviceable)m_object).service( manager );
}
catch( Throwable e )
{
final String error = "Failed to service the object.";
throw new PipelineException( error, e );
}
else if( m_object instanceof Composable ) try
{
if( m_verbose ) getLogger().debug( role + " composition" );
DefaultComponentManager manager = new DefaultComponentManager();
DependencyInfo[] dependencies = info.getDependencies();
for( int i=0; i<dependencies.length; i++ )
{
DependencyInfo dependency = dependencies[i];
String dependency_role = dependency.getRole();
UnitInfo block_info = m_registry.lookup( dependency );
Object object = pipeline( block_info, dependency_role );
manager.put( dependency_role, (Component) object );
}
((Composable)m_object).compose( manager );
}
catch( Throwable e )
{
final String error = "Failed to compose the object.";
throw new PipelineException( error, e );
}
//
// initialize the object
//
if( m_object instanceof Initializable ) try
{
if( m_verbose ) getLogger().debug( role + " initilization" );
((Initializable)m_object).initialize();
}
catch( Throwable e )
{
final String error = "Failed to initilize the object.";
throw new PipelineException( error, e );
}
//
// start the object
//
if( m_object instanceof Startable ) try
{
if( m_verbose ) getLogger().debug( "starting " + role );
((Startable)m_object).start();
}
catch( Throwable e )
{
final String error = "Failed to start the service.";
throw new PipelineException( error, e );
}
return m_object;
}
/**
* Notification byb the controlling application to dispose of the
* service factory.
*/
public void dispose()
{
//
// dispose of all of the service pools
//
if( m_verbose ) if( getLogger().isDebugEnabled() ) getLogger().debug(
"pool disposal (" + m_pools.size() + ")");
Enumeration pools = m_pools.elements();
while( pools.hasMoreElements() )
{
Object pool = pools.nextElement();
if( pool instanceof Disposable )
{
try
{
((Disposable)pool).dispose();
}
catch( Throwable e )
{
final String warning = "Ignoring exception while invoking
pool disposal.";
if( getLogger().isWarnEnabled() ) getLogger().warn(
warning, e );
}
}
}
//
// dispose of all of the service singletons
//
if( m_verbose ) if( getLogger().isDebugEnabled() ) getLogger().debug(
"singleton disposal (" + m_singletons.size() + ")");
Enumeration singletons = m_singletons.elements();
while( singletons.hasMoreElements() )
{
Object singleton = singletons.nextElement();
if( singleton instanceof Disposable )
{
try
{
((Disposable)singleton).dispose();
}
catch( Throwable e )
{
final String warning = "Ignoring exception while invoking
singleton provider disposal.";
if( getLogger().isWarnEnabled() ) getLogger().warn(
warning, e );
}
}
}
//
// dispose of all of the transient service providers
//
if( m_verbose ) if( getLogger().isDebugEnabled() ) getLogger().debug(
"transient disposal (" + m_transients.size() + ")");
Enumeration transients = m_transients.elements();
while( transients.hasMoreElements() )
{
Object object = transients.nextElement();
if( object instanceof Disposable )
{
try
{
((Disposable)object).dispose();
}
catch( Throwable e )
{
final String warning = "Ignoring exception while invoking
transient provider disposal.";
if( getLogger().isWarnEnabled() ) getLogger().warn(
warning, e );
}
}
}
}
//==========================================================
// configuration
//==========================================================
/**
* Creates a hashtable of configurations keyed by the block implmentation
* class name. The configuration file is ssumed to in the following form.
* <pre>
* <block class="org.apache.demo.MyFirstBlock">
* <any-child value="red"/>
* </block>
*
* <k class="org.apache.demo.MySecondBlock"/>
* </pre>
*/
private Hashtable initalizeBlockConfigurations( Configuration config )
throws ConfigurationException
{
Hashtable configs = new Hashtable();
Configuration[] blocks = config.getChildren("block");
for( int i=0; i<blocks.length; i++ )
{
final String key = blocks[i].getAttribute("class");
configs.put( key, blocks[i] );
}
return configs;
}
private Configuration getConfigurationForClass( String name )
{
Configuration config = (Configuration) m_table.get( name );
if( config != null ) return config;
return new DefaultConfiguration( name, null );
}
}
1.1
jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/service/ServiceInfo.java
Index: ServiceInfo.java
===================================================================
/*
* File: ServiceInfo.java
* License: etc/LICENSE.TXT
* Copyright: Copyright (C) The Apache Software Foundation. All rights
reserved.
* Copyright: OSM SARL 2002, All Rights Reserved.
*/
package org.apache.avalon.excalibur.service;
import org.apache.avalon.framework.Version;
import org.apache.avalon.framework.configuration.Configuration;
/**
* Meta-information about a particular <code>service</code> exposed by a
component.
*/
class ServiceInfo
{
private Class m_interface;
private Version m_version;
/**
* Creation of a new <code>ServiceInfo</code> instance.
*/
public ServiceInfo( Configuration config ) throws Exception
{
ClassLoader loader = Thread.currentThread().getContextClassLoader( );
m_interface = loader.loadClass( config.getAttribute("name") );
m_version = Version.getVersion( config.getAttribute("version") );
}
/**
* Equality test.
* @return TRUE if this instance is equal to the supplied instance, where
* equality is derived from equivalent interface values and versions.
*/
public boolean equals( Object object )
{
if( object instanceof ServiceInfo ) return equals( (ServiceInfo)
object );
return false;
}
/**
* Equality test.
* @return TRUE if this instance is equal to the supplied instance, where
* equality is derived from equivalent interface values and versions.
*/
public boolean equals( ServiceInfo other )
{
if( !other.getInterface().isAssignableFrom( m_interface ) ) return
false;
return m_version.equals( other.getVersion() );
}
/**
* Returns the class representing the service interface.
* @return the service interface
*/
public Class getInterface()
{
return m_interface;
}
/**
* Returns the service version.
* @return the service version
*/
public Version getVersion()
{
return m_version;
}
}
1.1
jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/service/ServiceLoader.java
Index: ServiceLoader.java
===================================================================
/*
* File: ServiceLoader.java
* License: etc/LICENSE.TXT
* Copyright: Copyright (C) The Apache Software Foundation. All rights
reserved.
* Copyright: OSM SARL 2002, All Rights Reserved.
*/
package org.apache.avalon.excalibur.service;
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.MalformedURLException;
import java.util.jar.JarFile;
import java.util.jar.Attributes;
import java.util.jar.Attributes.Name;
import java.util.Vector;
import java.util.Hashtable;
import java.util.Enumeration;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.FileInputStream;
import org.apache.log.Hierarchy;
import org.apache.log.Priority;
import org.apache.log.output.io.StreamTarget;
import org.apache.avalon.framework.logger.Logger;
import org.apache.avalon.framework.CascadingRuntimeException;
import org.apache.avalon.framework.CascadingException;
import org.apache.avalon.framework.logger.LogKitLogger;
import org.apache.avalon.framework.logger.AvalonFormatter;
import org.apache.avalon.framework.logger.AbstractLogEnabled;
import org.apache.avalon.framework.logger.LogEnabled;
import org.apache.avalon.framework.configuration.Configurable;
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.context.Context;
import org.apache.avalon.framework.context.DefaultContext;
import org.apache.avalon.framework.context.Contextualizable;
import org.apache.avalon.framework.context.ContextException;
import org.apache.avalon.framework.component.Component;
import org.apache.avalon.framework.component.Composable;
import org.apache.avalon.framework.component.ComponentManager;
import org.apache.avalon.framework.component.DefaultComponentManager;
import org.apache.avalon.framework.component.ComponentException;
import org.apache.avalon.framework.service.Serviceable;
import org.apache.avalon.framework.service.ServiceManager;
import org.apache.avalon.framework.service.ServiceException;
import org.apache.avalon.framework.activity.Initializable;
import org.apache.avalon.framework.activity.Startable;
import org.apache.avalon.framework.activity.Disposable;
/**
* A service loader that loads a target class, manages full component
lifecycle
* processing for the target and dependent services, and service
decommissioning.
*
* <p><table border="1" cellpadding="3" cellspacing="0" width="100%">
* <tr bgcolor="#ccccff">
* <td colspan="2"><b>Pipeline Lifecycle Phases</b></td>
* <tr><td width="20%"><b>Phase</b></td><td><b>Description</b></td></tr>
* <tr>
* <td width="20%" valign="top">Configurable</td>
* <td>
* The configuration phase supplies static information to the pipeline
processor
* that may be used to configure target and supporting services. During the
loading
* of a component, the pipline processor will attempt to locate a block
configuration
* based on the class name of the block. If there is no configuration
matching the
* class name an empty configuration will be supplied to the component or a
component
* the the target component is depedant on (assuming the component in
question
* implements the Configurable interface).
*
* <pre>
* <config>
*
* <block class="com.magic.DirectoryBlock">
* <-- block specific content -->
* </block>
*
* <block class="com.magic.ActivatorBlock">
* <-- block specific content -->
* </block>
*
* </config>
* </pre>
* </td></tr>
* <tr>
* <td width="20%" valign="top">Contextualizable</td>
* <td>
* The contextualization phase hadles the capture of runtime context
information
* needed by the pipeline processor. This information must be provided in
the
* for of a <code>ServiceLoaderContext</code> otherwise the contextualize
method will
* throw an exception. The following information is provided by the
* <code>ServiceLoaderContext</code>:
* <p><table border="0" cellpadding="3" cellspacing="0" width="100%">
* <tr>
* <td width="20%" valign="top"><code>ARGS_KEY</code></td>
* <td>
* Contains the command line arguments as a <code>String[]</code>.
* </td></tr>
* <tr>
* <td width="20%" valign="top"><code>BASE_DIRECTORY_KEY</code></td>
* <td>
* Contains the application base directory <code>File</code>.
* </td></tr>
* <tr>
* <td width="20%" valign="top"><code>TARGET_KEY</code></td>
* <td>
* Contains the name of the target class to be instantiated.
* </td></tr>
* <tr>
* <td width="20%" valign="top"><code>INCLUDES_KEY</code></td>
* <td>
* Contains an array of jar files that will be added to the pipeline
classloader.
* Jar files included in the array will be checked for block manifest
entries. If
* block entries exist that will be registered as potetentially available
services
* that may be instantiated during the recursive dependecy resolution
process.
* </td></tr>
* <tr>
* <td width="20%" valign="top"><code>CONFIGURATION_FILE_KEY</code></td>
* <td>
* A <code>File</code> instance that may or may not exist. If the file exists
and is
* confiugration, it will be used as the base configuration reference when
looking up
* service configuration details. The default behaviour is to look for a
file named
* "config.xml" in the user's working directory.
* </td></tr>
* <tr>
* <td width="20%" valign="top"><code>DISPOSAL_POLICY_KEY</code></td>
* <td>
* A <code>Boolean</code> value that is applied on completionof the startup
of a
* <code>Startable</code> component. If the value TRUE, the component is
stopped
* and disposed of (including termination of all dependencies) immediately
following
* startup. This policy only applies to <code>Startable</code> components.
Setting
* policy value to FALSE ensures that the component can respond as a server.
Non
* <code>Startable</code> target component are immediately terminated
(providing a
* useful for component testing during development cycles).
* </td></tr>
* <td width="20%" valign="top"><code>LOGGING_PRIORITY_KEY</code></td>
* <td>
* Contains the logging priority to be applied during the pipeline session.
* </td></tr>
* <td width="20%" valign="top"><code>VERBOSE_POLICY_KEY</code></td>
* <td>
* If this value is TRUE, debug logging entries from the pipeline processor
will be
* if the overall logging priority permits this. If FALSE (the default), the
logging
* entires generated by the pipeline processor will be limited to WARN,
ERROR,
* and FATAL_ERROR.
* </td></tr>
* </table>
*
* </td></tr>
* <tr><td width="20%">Initalizable</td>
* <td>
* The initilization phase handles the creation of a new instance of the
supplied
* target class, resolves any service dependencies, and runs the instance
through
* the lifecycle pipeline.
* </td></tr>
* <tr><td width="20%" valign="top">Disposable</td>
* <td>
* Cleanup and disposal of state members following termination and diposal of
* all current components.
* </td></tr>
* </table>
*/
public class ServiceLoader extends AbstractLogEnabled
implements Configurable, Contextualizable, Initializable, Disposable
{
private static final String DEFAULT_FORMAT = "[%7.7{priority}]
(%{category}): %{message}\\n%{throwable}";
private static OutputStream m_out = System.out;
private String[] m_args;
private PipelineClassLoader m_classloader;
private Priority m_priority = Priority.INFO;
private String m_target;
private Object m_object;
private File[] m_includes;
private boolean m_policy = true;
private boolean m_verbose = false;
private boolean m_terminated = false;
private boolean m_disposed = false;
private ServiceFactory m_factory;
private Configuration m_config;
/**
* Command line entry point supporting establishment and initalization of
a service
* loader instance with a supplied target component and supporting
services.
*
* <p><strong>java -jar form</strong></p>
* <p>Execution using java -jar pattern requires the presence
* of the following files under the same directory:</p>
* <ul>
* <li><code>merlin.jar</code>
* <li><code>avalon-framework.jar</code>
* <li><code>logkit.jar</code>
* </ul>
* <p>An example command line is shown below:</p>
* <pre>
* $ java -jar <strong>merlin.jar</strong> <supporting-jar-files>
* -target <class-name>
* </pre>
* <p><strong>java -classpath form</strong></p>
* <p>Execution using java -classpath pattern requires the inclusions
* of the pipeline, framework and logkit jar files in the classpath
* statement.</p>
* <p>An example command line is shown below:</p>
* <pre>
* $ java -classpath
<strong>merlin.jar;avalon-framework.jar;logkit.jar</strong>
* org.apache.avalon.excalibur.service.ServiceLoader
* <supporting-jar-files> -target <class-name>
* </pre>
* </ul>
*
* <p><table border="1" cellpadding="3" cellspacing="0" width="100%">
* <tr bgcolor="#ccccff">
* <td colspan="2"><b>Command Line Parameters and Arguments</b></td>
* <tr><td
width="20%"><b>Parameter</b></td><td><b>Description</b></td></tr>
* <tr>
* <td width="20%" valign="top"><code>-target
<class-name></code></td>
* <td>
* <p>The class to instantiate. If the class exposes any Avalon lifecycle
interface
* (such as <code>Configurable</code>, <code>Contextualizable</code>,
<code>Serviceable</code>,
* <code>Initializable</code>, <code>Startable</code>, or
<code>Disposable</code>, the
* pipeline will automate lifecycle processing and termination.
* </p>
* </td></tr>
* <tr>
* <td width="20%"
valign="top"><code><supporting-jar-files></code></td>
* <td>
* <p>A list of space seperated jar files that will be added to the
pipeline
* as supporting classes and components. Any jar file included in the list
* that contains an Avalon <code>Block</code> manifest will be registered
as
* an available service when resolving component dependecies.</p>
* </td></tr>
* <tr><td width="20%"><code>-verbose <boolean></code></td>
* <td>
* <p>A value of <code>true</code> will force debug level logging of the
actual pipeline
* processor. A value of <code>false</code> will disable pipeline debug
priority logging.
* Visibility of logging inffomration is dependent on the level supplied
under the
* <code>priority</code parameter.</p>
* </td></tr>
* <tr><td width="20%" valign="top"><code>-priority
<priority></code></td>
* <td>
* <p>Declaration of the logging priority to use during pipeline
execution. Valid values
* include FATAL_ERROR, ERROR, WARN, INFO, and DEBUG. </p>
* </td></tr>
* <tr><td width="20%" valign="top"><code>-dispose
<boolean></code></td>
* <td>
* If the target component is <code>Startable</code>, and the dispose
argument is <code>FALSE</code> the
* component will be treated as a server and will continue to run
following initialization.
* Otherwise, the component will be disposed of.
* </td></tr>
* <tr><td width="20%" valign="top"><code>-configuration
<file-path></code></td>
* <td>
* Optional parameter naming a file to be used as the configuration source.
* </td></tr>
* </table>
*
* @param args a array of <code>String</code> argument values passed under
the command line.
*/
public static void main( String[] args )
{
ServiceLoader pipeline = null;
try
{
CLI cli = new CLI( args );
pipeline = new ServiceLoader();
ServiceLoaderContext context = cli.getContext();
File path = cli.getConfigurationPath();
Configuration config = new DefaultConfiguration("-", null );
if( path != null ) config = getRuntimeConfiguration( path );
pipeline.configure( config );
pipeline.contextualize( context );
pipeline.initialize();
}
catch( IllegalParameterException ipe )
{
System.err.println( ipe.getMessage() );
}
catch( PipelineException e )
{
final String error = "Service loader processing exception.";
System.err.println( error );
System.err.println( e );
}
catch( PipelineRuntimeException e )
{
final String error = "Service loader runtime exception.";
System.err.println( error );
System.err.println( e );
}
catch( Throwable e )
{
final String error = "Unexpected service loader exception.";
System.err.println( error );
e.printStackTrace();
}
finally
{
if( pipeline != null )
{
pipeline.dispose();
}
}
}
private boolean getVerbose()
{
return m_verbose;
}
/**
* Configuration of the pipeline.
*/
public void configure( Configuration config ) throws
ConfigurationException
{
m_config = config;
}
/**
* Contextualization of the pipeline including the supply of command-line
* arguments, include files, target class, and related execution policies.
* @see ServiceLoaderContext
* @param context the pipeline context
* @exception ContextException if the supplied context is not an instance
* of ServiceLoaderContext, or if an internal error occurs while
resolving
* context information.
*/
public void contextualize( Context context ) throws ContextException
{
try
{
ServiceLoaderContext c = (ServiceLoaderContext) context;
m_includes = c.getIncludes();
m_verbose = c.getVerbose();
m_priority = c.getLoggingPriority();
m_policy = c.getDisposalPolicy();
m_target = c.getTarget();
}
catch( Throwable e )
{
final String error = "Unexpected error while reslving pipeline
context.";
throw new ContextException( error, e );
}
}
/**
* Start the pipeline and handle any errors arrising from loader execution.
*/
public void initialize() throws PipelineException
{
if( m_target == null ) throw new PipelineException(
"The pipeline task required attribute 'target' has not been not
supplied.");
try
{
Hierarchy hierarchy = createBootstrapLogger();
Logger logger = new LogKitLogger( hierarchy.getLoggerFor(
"loader" ) );
if( getLogger() == null ) super.enableLogging( logger );
if( m_classloader == null ) m_classloader = createClassloader();
m_factory = new ServiceFactory( hierarchy, m_config,
new File( System.getProperty("user.dir") ), getVerbose() );
m_factory.enableLogging( getLogger().getChildLogger("factory") );
}
catch( Throwable e )
{
final String error = "Service loader validation error.";
getLogger().error( error, e );
return;
}
try
{
process();
}
catch( PipelineException e )
{
final String error = "Service loader processing exception.";
getLogger().error( error );
}
catch( PipelineRuntimeException e )
{
final String error = "Service loader runtime exception.";
getLogger().error( error );
}
catch( Throwable e )
{
final String error = "Unexpected pipeline exception.";
getLogger().error( error, e );
}
finally
{
if( m_policy )
{
if( m_object != null ) terminate();
dispose();
return;
}
}
}
/**
* Initates execution of the loading of supporting services derived from
a
* target.
*/
private void process( ) throws PipelineException
{
//
// add the included jar files to the classloader
//
Vector list = new Vector();
Vector stack = new Vector();
for (int i=0; i<m_includes.length; i++)
{
File target = m_includes[i];
stack.add( target );
list.add( target );
}
load( stack );
// build the registry of available services
try
{
m_factory.register( list );
}
catch( Throwable e )
{
final String error = "Service loader exception encounter while
preparing services.";
throw new PipelineException( error, e );
}
//
// create an instance of the target and if verify that we can in fact
build a
// service manager if needed
//
Class target;
UnitInfo info;
try
{
target = m_classloader.loadClass( m_target );
}
catch( Throwable e )
{
final String error = "Could not load target class: " + m_target;
throw new PipelineException( error , e );
}
try
{
info = ServiceRegistry.createUnitInfo( target );
if( getVerbose() ) if( getLogger().isDebugEnabled() )
getLogger().debug( "validating target\n" + info );
try
{
m_factory.validate( info );
}
catch( Throwable e )
{
final String error = "Could not resolve a valid dependency
solution for ";
throw new CascadingException( error + info.getClassName(), e
);
}
}
catch( Throwable e )
{
final String error = "Service loader exception encounter while
preparing services.";
throw new PipelineException( error, e );
}
//
// process the target
//
try
{
m_object = m_factory.pipeline( info, "target" );
}
catch( Throwable e )
{
final String error = "Service loader exception encounter during
target execution.";
throw new PipelineException( error, e );
}
//
// add a shutdown hook so we can stop services and target and invoke
disposal
//
Runtime.getRuntime().addShutdownHook(
new Thread()
{
public void run()
{
terminate();
dispose();
}
}
);
}
/**
* Run the termination lifecycle actions on the primary object.
*/
private void terminate( )
{
if( m_terminated ) return;
m_terminated = true;
if( m_object == null ) return;
if( getVerbose() ) if( getLogger().isDebugEnabled() )
getLogger().debug(
"terminating " + m_object.getClass().getName() );
if( m_object instanceof Startable )
{
try
{
((Startable)m_object).stop();
}
catch( Throwable e )
{
final String warning = "Ignoring error while stopping
target.";
if( getLogger().isWarnEnabled() ) getLogger().warn( warning,
e );
}
}
if( m_object instanceof Disposable )
{
try
{
((Disposable)m_object).dispose();
}
catch( Throwable e )
{
final String warning = "Ignoring error while disposing of
target.";
if( getLogger().isWarnEnabled() ) getLogger().warn( warning,
e );
}
}
}
/**
* Disposal of the pipeline and release of all resources.
*/
public void dispose()
{
if( m_disposed ) return;
if( !m_terminated ) try
{
terminate();
}
catch( Throwable e )
{
// ignore and continue
}
m_disposed = true;
if( getVerbose() ) if( getLogger().isDebugEnabled() )
getLogger().debug( "loader disposal" );
if( m_factory instanceof Disposable )
{
try
{
((Disposable)m_factory).dispose();
}
catch( Throwable e )
{
final String warning = "Ignoring error while disposing of
service factory.";
getLogger().warn( warning, e );
}
}
}
//==========================================================
// classloader
//==========================================================
private PipelineClassLoader createClassloader()
{
try
{
PipelineClassLoader loader = new PipelineClassLoader();
Thread.currentThread().setContextClassLoader( loader );
return loader;
}
catch( Throwable e )
{
final String error = "Failed to instantial a classloader.";
throw new CascadingRuntimeException( error, e );
}
}
class PipelineClassLoader extends URLClassLoader
{
PipelineClassLoader( )
{
super( new URL[0], Thread.currentThread().getContextClassLoader()
);
}
protected void addURL( URL url )
{
try
{
URL jarURL = new URL("jar:" + url.toString() + "!/");
super.addURL( jarURL );
}
catch( Throwable e )
{
throw new CascadingRuntimeException(
"Unexpected error while attempting to add classloader URL:
" + url, e );
}
}
protected Class loadClass( String name, boolean resolve ) throws
ClassNotFoundException
{
return super.loadClass( name, resolve );
}
}
/**
* Load the supplied jar files under the pipeline classloader.
* For each entry in the stack, try to load it and
* if sucessfull, remove it from the stack - on completion
* the stack should be less than its original size - recursivly
* invoke load until the stack is empty.
* @param stack a <code>Vecor</code> containing a sequence of jar files
* to be added to the classloader.
*/
private void load( Vector stack )
{
int size = stack.size();
Hashtable errors = new Hashtable();
Enumeration enum = stack.elements();
while( enum.hasMoreElements() )
{
File target = (File) enum.nextElement();
try
{
m_classloader.addURL( target.toURL() );
if( getVerbose() ) getLogger().debug( "Loaded file: " +
target );
stack.remove( target );
}
catch( Throwable error )
{
errors.put( target, error );
}
}
if( stack.size() == 0 ) return;
if( stack.size() < size )
{
load( stack );
}
else
{
Enumeration keys = errors.keys();
getLogger().error("Load error count = " + errors.size() );
while( keys.hasMoreElements() )
{
File key = (File) keys.nextElement();
getLogger().error(
"Error while loading file."
+ "\n\tfile: " + key.toString(), (Throwable) errors.get(
key ) );
}
throw new RuntimeException("Unable to load file stack - see trace
for details.");
}
}
//==========================================================
// configuration
//==========================================================
/**
* Get client configuration from a file.
*/
private static Configuration getRuntimeConfiguration( final File file )
{
try
{
DefaultConfigurationBuilder builder = new
DefaultConfigurationBuilder( );
InputStream is = new FileInputStream( file );
if( is == null ) throw new Exception(
"Could not load the configuration resource \"" + file + "\"" );
return builder.build( is );
}
catch( Throwable e )
{
final String error = "Unable to create configuration.";
throw new CascadingRuntimeException( error, e );
}
}
//==========================================================
// logging
//==========================================================
private Hierarchy createBootstrapLogger()
{
try
{
Hierarchy hierarchy = Hierarchy.getDefaultHierarchy();
hierarchy.setDefaultLogTarget(
new StreamTarget( m_out, new AvalonFormatter( DEFAULT_FORMAT )
) );
hierarchy.setDefaultPriority( m_priority );
return hierarchy;
}
catch( Throwable e )
{
final String error = "Unexpected exception while creating
bootstrap logger.";
throw new CascadingRuntimeException( error, e );
}
}
//==========================================================
// command-line
//==========================================================
private static class IllegalParameterException extends Exception
{
/**
* Construct a new <code>IllegalParameterException</code> instance
with the
* supplied message parameter.
* @param message Message summarising the exception.
*/
public IllegalParameterException( final String message )
{
super( message );
}
}
private static class CLI
{
private String[] m_args = new String[0];
private Priority m_priority = Priority.INFO;
private String m_target = "";
private boolean m_policy = true;
private boolean m_verbose = false;
private File[] m_files = new File[0];
private File m_path = null;
public CLI( String[] args ) throws IllegalParameterException
{
m_args = args;
Vector vector = new Vector();
for( int i=0; i < m_args.length; i++ )
{
if( m_args[i].toLowerCase().startsWith("-tar") )
{
if( i+1 < m_args.length )
{
m_target = m_args[i+1];
i = i+1;
}
else
{
final String error = "Missing -target parameter.";
throw new RuntimeException( error );
}
}
else if( m_args[i].toLowerCase().startsWith("-conf") )
{
if( i+1 < m_args.length )
{
m_path = new File( m_args[i+1] );
i = i+1;
}
else
{
final String error = "Missing -configuration
parameter.";
throw new RuntimeException( error );
}
}
else if( m_args[i].toLowerCase().startsWith("-pri") )
{
if( i+1 < m_args.length )
{
m_priority = Priority.getPriorityForName(
m_args[i+1].toUpperCase() );
i = i+1;
}
else
{
final String error = "Missing -priority argument.";
throw new RuntimeException( error );
}
}
else if( m_args[i].toLowerCase().startsWith("-dis") )
{
if( i+1 < m_args.length )
{
m_policy = (m_args[i+1].toUpperCase().equals("TRUE"));
i = i+1;
}
else
{
final String error = "Missing -disposal argument.";
throw new RuntimeException( error );
}
}
else if( m_args[i].toLowerCase().startsWith("-ver") )
{
if( i+1 < m_args.length )
{
m_verbose =
(m_args[i+1].toUpperCase().equals("TRUE"));
i = i+1;
}
else
{
final String error = "Missing -verbose argument.";
throw new RuntimeException( error );
}
}
else if( m_args[i].startsWith("-") )
{
final String error = "Unrecognized parameter: " +
m_args[i];
throw new IllegalParameterException( error );
}
else
{
final File file = new File( m_args[i] );
if( file.exists() ) vector.add( file );
}
}
m_files = (File[]) vector.toArray( new File[0] );
}
public ServiceLoaderContext getContext()
{
return new ServiceLoaderContext( m_args, m_target, m_files,
m_policy, m_priority, m_verbose );
}
public File getConfigurationPath()
{
return m_path;
}
}
}
1.1
jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/service/ServiceLoaderContext.java
Index: ServiceLoaderContext.java
===================================================================
/*
* File: ServiceLoaderContext.java
* License: etc/LICENSE.TXT
* Copyright: Copyright (C) The Apache Software Foundation. All rights
reserved.
* Copyright: OSM SARL 2002, All Rights Reserved.
*/
package org.apache.avalon.excalibur.service;
import java.io.File;
import org.apache.avalon.framework.context.DefaultContext;
import org.apache.avalon.framework.context.ContextException;
import org.apache.log.Priority;
/**
* <code>ServiceLoaderContext</code> context to hold command line arguments,
* base directory, target class name, and include files for a
* <code>ServiceLoader</code> component.
*
* @author <a href="mailto:[EMAIL PROTECTED]">Stephen McConnell</a>
*/
public class ServiceLoaderContext extends DefaultContext
{
/**
* Context key for command line arguments.
* @see #getArgs()
*/
public static final String ARGS_KEY = "ARGS";
/**
* Context key for the base directory.
* @see #getBaseDirectory()
*/
public static final String BASE_DIRECTORY_KEY = "APP_DIR";
/**
* Context key for the target class name.
* @see #getTarget()
*/
public static final String TARGET_KEY = "TARGET";
/**
* Context key for the set of supporting include jar files.
* @see #getIncludes()
*/
public static final String INCLUDES_KEY = "INCLUDES";
/**
* Context key for the disposal policy.
* @see #getDisposalPolicy()
*/
public static final String DISPOSAL_POLICY_KEY = "DISPOSAL.POLICY";
/**
* Context key for the logging priority.
* @see #getLoggingPriority()
*/
public static final String LOGGING_PRIORITY_KEY = "LOGGING.PRIORITY";
/**
* Context key for the verbose policy.
* @see #getVerbose()
*/
public static final String VERBOSE_POLICY_KEY = "VERBOSE.POLICY";
/**
* Creation of a new <oce>ServiceLoaderContext</code> with the supplied
* arguments.
* @param args command line arguments
* @param target class name of the target object to be loaded
* @param includes set of jar files to be added to the loader classloader
* @param disposal boolean disposal policy
* @param priority logging priority
* @param verbose policy concerning display of loader debug messages
*/
public ServiceLoaderContext( final String[] args, final String target,
final File[] includes, final boolean disposal, final Priority priority, final
boolean verbose )
{
super.put( ARGS_KEY, args );
super.put( TARGET_KEY, target );
super.put( BASE_DIRECTORY_KEY, new File(
System.getProperty("user.dir")) );
super.put( INCLUDES_KEY, includes );
super.put( DISPOSAL_POLICY_KEY, new Boolean( disposal ));
super.put( VERBOSE_POLICY_KEY, new Boolean( verbose ));
super.put( LOGGING_PRIORITY_KEY, priority );
}
/**
* Returns the command-line argument <code>String[]</code>.
* @return String[] command line arguments
*/
public String[] getArgs()
{
try
{
return (String[]) super.get( ARGS_KEY );
}
catch( ContextException e )
{
final String error = "Unexpected exception while retrieving
command line arguments.";
throw new PipelineRuntimeException( error, e );
}
}
/**
* Returns the base directory as a <code>File</code>.
* @return File the base working directory
*/
public File getBaseDirectory()
{
try
{
return (File) super.get( BASE_DIRECTORY_KEY );
}
catch( ContextException e )
{
final String error = "Unexpected exception while retrieving base
directory.";
throw new PipelineRuntimeException( error, e );
}
}
/**
* Returns the disposal policy to apply if the target component implements
* the <code>Startable</code> interface.
* @return boolean the target component disposal policy
*/
public boolean getDisposalPolicy()
{
try
{
return ((Boolean)super.get( DISPOSAL_POLICY_KEY )).booleanValue();
}
catch( ContextException e )
{
final String error = "Unexpected exception while retrieving
disposal policy.";
throw new PipelineRuntimeException( error, e );
}
}
/**
* Class name of a target object.
* @return String the class name of the target
*/
public String getTarget()
{
try
{
return (String) super.get( TARGET_KEY );
}
catch( ContextException e )
{
final String error = "Unexpected exception while retrieving
include files.";
throw new PipelineRuntimeException( error, e );
}
}
/**
* Returns the set of supporting jar files.
* @return File[] an array of jar files supporting target composition and
execution
*/
public File[] getIncludes()
{
try
{
return (File[]) super.get( INCLUDES_KEY );
}
catch( ContextException e )
{
final String error = "Unexpected exception while retrieving
include files.";
throw new PipelineRuntimeException( error, e );
}
}
/**
* Returns the logging priority.
* @return Priority the logging priority to apply.
*/
public Priority getLoggingPriority()
{
try
{
return (Priority) super.get( LOGGING_PRIORITY_KEY );
}
catch( ContextException e )
{
final String error = "Unexpected exception while retrieving
logging priority.";
throw new PipelineRuntimeException( error, e );
}
}
/**
* Returns the verbose policy - if true, logging entries from the pipeline
* will be inclued, otherwise, logging entries will be restricted to the
* target component.
* @return boolean the verbose policy
*/
public boolean getVerbose()
{
try
{
return ((Boolean)super.get( VERBOSE_POLICY_KEY )).booleanValue();
}
catch( ContextException e )
{
final String error = "Unexpected exception while retrieving
verbose policy.";
throw new PipelineRuntimeException( error, e );
}
}
}
1.1
jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/service/ServiceProvider.java
Index: ServiceProvider.java
===================================================================
/*
* File: ServiceProvider.java
* License: etc/LICENSE.TXT
* Copyright: Copyright (C) The Apache Software Foundation. All rights
reserved.
* Copyright: OSM SARL 2002, All Rights Reserved.
*/
package org.apache.avalon.excalibur.service;
import org.apache.avalon.framework.activity.Disposable;
/**
* <code>ServiceProvider</code> is an abstract base class to services
* factories supporting TRANSIENT, POOLED, and SINGLETON services. The
* abstract class provides the single <code>getRole</code> method that
* can be used by a <code>ServiceManager</code> implementation to identity
* a service provider for a particular role.
* A concrete <code>ServiceProvider</code> instance will typically be
* created by an implementation when constructing a
<code>ServiceManager</code>
* for a target component.
*/
abstract class ServiceProvider implements Disposable
{
private String m_role;
/**
* Creation of a new <code>ServiceProvider</code> instance based on
* a supplied role.
* @param role the role of the service provided by the provider relative
* to a <code>ServiceManager</code> that is exposing provider services.
*/
public ServiceProvider( String role )
{
m_role = role;
}
/**
* Returns the role of this provider within the scope of a
<code>ServiceManager</code>.
* @return String the role of the service provider by the provider.
*/
public String getRole()
{
return m_role;
}
/**
* Disposal of the provider and release of related resources.
*/
public void dispose()
{
m_role = null;
}
}
1.1
jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/service/ServiceRegistry.java
Index: ServiceRegistry.java
===================================================================
/*
* File: ServiceRegistry.java
* License: etc/LICENSE.TXT
* Copyright: Copyright (C) The Apache Software Foundation. All rights
reserved.
* Copyright: OSM SARL 2002, All Rights Reserved.
*/
package org.apache.avalon.excalibur.service;
import java.io.File;
import java.io.InputStream;
import java.io.IOException;
import java.util.Vector;
import java.util.Iterator;
import java.util.Map;
import java.util.Hashtable;
import java.util.Enumeration;
import java.util.jar.JarFile;
import java.util.jar.Attributes;
import java.util.jar.Attributes.Name;
import org.apache.avalon.framework.logger.Logger;
import org.apache.avalon.framework.logger.AbstractLogEnabled;
import org.apache.avalon.framework.CascadingRuntimeException;
import org.apache.avalon.framework.CascadingException;
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;
/**
* Internal class supporting registration of available services.
*/
class ServiceRegistry extends AbstractLogEnabled
{
private Vector m_repository = new Vector();
private UnitInfo[] m_block_info_set;
private Hashtable m_table;
private boolean m_verbose = false;
public ServiceRegistry( boolean verbose )
{
m_verbose = verbose;
}
public void register( UnitInfo info )
{
m_repository.add( info );
}
/**
* Populates the set of available services based on a supplied
* vector of jar files.
* @param list a list of jar files
*/
public void register( Vector list ) throws PipelineException
{
Vector registry = new Vector();
Enumeration enum = list.elements();
while( enum.hasMoreElements() )
{
File target = (File) enum.nextElement();
UnitInfo[] blocks = register( target );
if( m_verbose ) if( blocks.length > 0 ) if(
getLogger().isDebugEnabled() )
getLogger().debug(
"file: " + target + " has " + blocks.length + " block
declarations" );
}
}
/**
* Register a jar file with the registry.
* @param file the jar file to register
*
*/
public UnitInfo[] register( File target ) throws PipelineException
{
UnitInfo[] blocks = getUnitInfo( target );
for( int i=0; i<blocks.length; i++ )
{
UnitInfo info = blocks[i];
m_repository.add( info );
}
return blocks;
}
public UnitInfo lookup( ServiceInfo info )
{
Enumeration enum = m_repository.elements();
while( enum.hasMoreElements() )
{
UnitInfo block_info = (UnitInfo) enum.nextElement();
if( block_info.provides( info ) ) return block_info;
}
return null;
}
/**
* Returns an array of block infos provided by the jar file.
* @return a <code>UnitInfo[]<code> provided by the jar file
*/
private UnitInfo[] getUnitInfo( File file ) throws PipelineException
{
Vector vector = new Vector();
try
{
//
// if the file contains block declarations, then pipeline and
// blocks as a supporting service that will be provided to the
// target server
//
String[] blocks = getBlocks( file );
for( int i=0; i<blocks.length; i++ )
{
//
// get the block implementation class and check for
// dependecies - if dependencies > 0 then ignore it
// otherwise pipeline the block and add it as a
// service
//
final String path = blocks[i];
Configuration config = loadConfiguration( path + ".conf",
true );
Configuration xinfo = loadConfiguration( path + ".xinfo",
false );
if( xinfo == null ) throw new IllegalStateException(
"Could not locate <class>.xinfo resource for: " + path
);
final String classname = path.replace('/','.');
Class block =
Thread.currentThread().getContextClassLoader().loadClass( classname );
vector.add( new UnitInfo( block, xinfo, config ));
}
}
catch( Throwable e )
{
throw new CascadingRuntimeException(
"Unexpected error while attempting to load file: " + file, e );
}
return (UnitInfo[]) vector.toArray( new UnitInfo[0] );
}
/**
* Returns a single block info relative to a supplied class.
* @return a <code>UnitInfo<code> for the class.
*/
public static UnitInfo createUnitInfo( Class block ) throws
PipelineException
{
try
{
String path = block.getName().replace('.','/');
Configuration config = loadConfiguration( path + ".config", true
);
Configuration xinfo = loadConfiguration( path + ".xinfo", false );
if( xinfo == null ) xinfo = new DefaultConfiguration("", null);
return new UnitInfo( block, xinfo, config );
}
catch( Throwable e )
{
throw new CascadingRuntimeException(
"Unexpected error while attempting to resolve block info for a
class: " + block.getName(), e );
}
}
//===============================================================================
// utilities
//===============================================================================
/**
* Returns an array of <code>String</code>s corresponding to the set of
classnames
* where each classname is a declared block within the supplied jar file.
* @param file a jar file
*/
private String[] getBlocks( File file )
throws CascadingException, IllegalArgumentException
{
final Vector vector = new Vector();
try
{
JarFile jar = new JarFile( file );
Map map = jar.getManifest().getEntries();
Iterator iterator = map.keySet().iterator();
while( iterator.hasNext() )
{
String name = (String) iterator.next();
Attributes attributes = (Attributes) map.get( name );
Iterator it = attributes.keySet().iterator();
while( it.hasNext() )
{
Object entry = it.next();
if( entry.toString().equals("Avalon-Block") )
{
if( attributes.get( entry ).equals("true") )
{
vector.add(
name.substring(0,name.indexOf(".class")));
}
}
}
}
}
catch( IOException e )
{
final String error = "IO exception while attempt to read block
manifest.";
throw new CascadingException( error, e );
}
catch( Throwable e )
{
final String error = "Unexpected exception while inspecting
manifest on file: ";
throw new CascadingException( error + file, e );
}
finally
{
return (String[]) vector.toArray( new String[0] );
}
}
/**
* Returns a configuration resource form a jar file.
* @param path the package path to the resource e.g. net/osm/config.xml
* @param create if TRUE and no configuration found, return an empty
* configuration, else, return null
* @exception ConfigurationException if there is a problem
*/
private static Configuration loadConfiguration( String path, boolean
create )
throws ConfigurationException
{
try
{
DefaultConfigurationBuilder builder = new
DefaultConfigurationBuilder( );
InputStream is =
Thread.currentThread().getContextClassLoader().getResourceAsStream( path );
if( is != null ) return builder.build( is );
if( create ) return new DefaultConfiguration("-",null);
return null;
}
catch( Throwable e )
{
final String error = "Unexpected exception while attempting to
load a configuration from path: ";
throw new ConfigurationException( error, e );
}
}
}
1.1
jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/service/SingletonProvider.java
Index: SingletonProvider.java
===================================================================
/*
* File: SingletonProvider.java
* License: etc/LICENSE.TXT
* Copyright: Copyright (C) The Apache Software Foundation. All rights
reserved.
* Copyright: OSM SARL 2002, All Rights Reserved.
*/
package org.apache.avalon.excalibur.service;
import org.apache.avalon.framework.activity.Disposable;
/**
* A <code>SingletonProvider</code> is a provider of singleton services. A
* singleton service is a service that is managed by the container providing
the
* the service. The container providing the service is reposible for all
lifecycle
* actions including service decommissioning.
*/
class SingletonProvider extends ServiceProvider
{
private Object m_object;
/**
* Creation of a new <code>SingletonProvider</code>.
* @param object the singleton object to provide.
*/
public SingletonProvider( Object object, String role )
{
super( role );
m_object = object;
}
/**
* Provides a reference to a singleton service.
* @return Object the singleton service
*/
public Object provide()
{
return m_object;
}
/**
* Disposal of the provider and release of related resources.
*/
public void dispose()
{
if(( m_object != null ) && ( m_object instanceof Disposable )) try
{
((Disposable)m_object).dispose();
}
catch( Throwable anything )
{
// ignore it
}
finally
{
super.dispose();
}
}
}
1.1
jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/service/TransientProvider.java
Index: TransientProvider.java
===================================================================
/*
* File: TransientProvider.java
* License: etc/LICENSE.TXT
* Copyright: Copyright (C) The Apache Software Foundation. All rights
reserved.
* Copyright: OSM SARL 2002, All Rights Reserved.
*/
package org.apache.avalon.excalibur.service;
import org.apache.avalon.framework.CascadingRuntimeException;
/**
* A <code>TransientProvider</code> is a provider of transient services. A
* transient service is a service where total responsibility for the lifetime
* of the service rests with client aquiring the service.
*/
class TransientProvider extends ServiceProvider
{
private ServiceFactory m_factory;
private UnitInfo m_info;
private boolean m_disposed;
public TransientProvider( ServiceFactory factory, UnitInfo info, String
role )
{
super( role );
m_info = info;
m_factory = factory;
}
/**
* Create a new instance of a transient service.
* @return Object a new instance of a transient service
*/
public Object create()
{
if( m_disposed ) throw new IllegalStateException( "Transient provider
has been disposed.");
try
{
return m_factory.pipeline( m_info, super.getRole() );
}
catch( Throwable e )
{
final String error = "Unexpected error while creating transient
service.";
throw new CascadingRuntimeException( error, e );
}
}
/**
* Disposal of the provider and release of related resources.
*/
public void dispose()
{
m_disposed = true;
m_factory = null;
m_info = null;
super.dispose();
}
}
1.1
jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/service/UnitInfo.java
Index: UnitInfo.java
===================================================================
/*
* File: UnitInfo.java
* License: etc/LICENSE.TXT
* Copyright: Copyright (C) The Apache Software Foundation. All rights
reserved.
* Copyright: OSM SARL 2002, All Rights Reserved.
*/
package org.apache.avalon.excalibur.service;
import java.util.Vector;
import org.apache.avalon.framework.CascadingException;
import org.apache.avalon.framework.configuration.Configuration;
/**
* Meta information about a <code>Serviceable</code> component.
*/
class UnitInfo
{
public static final int SINGLETON_LIFETIME_POLICY = 0;
public static final int POOLED_LIFETIME_POLICY = 1;
public static final int TRANSIENT_LIFETIME_POLICY = 2;
private Class m_block;
private Configuration m_xinfo;
private Configuration m_config;
private ServiceInfo[] m_services;
private DependencyInfo[] m_dependencies;
private boolean m_installed = false;
private Object m_object;
private int m_policy = SINGLETON_LIFETIME_POLICY;
/**
* Creation of a new UnitInfo based a xinfo configuration
* derived from the supplied class name.
* The UnitInfo recognises the the following structures within
* the xinfo configuration:
* <pre>
* <blockinfo>
*
* <block>
* <version>1.0</version>
* </block>
*
* <!--
* services that are offered by this block
* -->
*
* <services>
* <service name="hello.Hello" version="1.0"/>
* </services>
*
* <!--
* Implementation policy may one of the following enumerations:
* (a) SINGLETON, service is available for the lifetime of the manager
* (b) POOLED, the implementation implements Pool
* (c) TRANSIENT, manager is a factory of transient service instances
* -->
*
* <implementation policy="SINGLETON" />
*
* </blockinfo>
* </pre>
*
* @param block the implementation class
* @param the xinfo meta-information in the form of a Configuration
* @param the default component configuration
*/
public UnitInfo( Class block, Configuration xinfo, Configuration config )
throws Exception
{
m_block = block;
m_xinfo = xinfo;
m_config = config;
try
{
Configuration[] services =
xinfo.getChild("services").getChildren("service");
Vector vector = new Vector();
for( int i=0; i<services.length; i++ )
{
vector.add( new ServiceInfo( services[i] ));
}
m_services = (ServiceInfo[]) vector.toArray( new ServiceInfo[
vector.size() ] );
}
catch( Throwable e )
{
throw new CascadingException( "Could not construct service
information.", e );
}
try
{
Configuration[] dependencies =
xinfo.getChild("dependencies").getChildren("dependency");
Vector vector = new Vector();
for( int i=0; i<dependencies.length; i++ )
{
vector.add( new DependencyInfo( dependencies[i] ));
}
m_dependencies = (DependencyInfo[]) vector.toArray( new
DependencyInfo[ vector.size() ] );
}
catch( Throwable e )
{
throw new CascadingException( "Could not construct dependency
information.", e );
}
String policy =
xinfo.getChild("implementation").getAttribute("policy","TRANSIENT");
if( policy.equalsIgnoreCase( "SINGLETON" ) )
{
m_policy = SINGLETON_LIFETIME_POLICY;
}
else if( policy.equalsIgnoreCase( "POOLED" ) )
{
m_policy = POOLED_LIFETIME_POLICY;
}
else
{
m_policy = TRANSIENT_LIFETIME_POLICY;
}
}
/**
* Returns the name of the class implemenmting the service.
* @return the class name
*/
public String getClassName()
{
return m_block.getName();
}
/**
* Returns the set of services provided by the component.
* @return an array of <code>ServiceInfo</code> instance
*/
public ServiceInfo[] getServices()
{
return m_services;
}
/**
* Returns a <code>ServiceInfo</code> from the <code>UnitInfo</code>
matching
* the supplied service descriptor.
* @param info a <code>ServiceInfo</code> to locate with the
<code>UnitInfo</code> instance
* @return a <code>ServiceInfo</code> instance matching the supplied
service descriptor
*/
public ServiceInfo getService( ServiceInfo info )
{
ServiceInfo[] services = getServices();
for( int i=0; i<services.length; i++ )
{
if( services[i].equals( info )) return services[i];
}
return null;
}
/**
* Returns a <code>TRUE</code> if the supplied <code>ServiceInfo</code>
can be
* provided by the implementation.
* @param service the requested service
* @return boolean TRUE if the service is available otherwise FALSE
*/
public boolean provides( ServiceInfo service )
{
return ( getService( service ) != null );
}
/**
* Returns a array of <code>DependencyInfo</code> descriptors that detail
* the dependencies that this component has on other components.
* @return component dependencies
*/
public DependencyInfo[] getDependencies()
{
return m_dependencies;
}
/**
* Returns the implemetation policy. The value returned shall be one of
* SINGLETON_LIFETIME_POLICY, POOLED_LIFETIME_POLICY, or
TRANSIENT_LIFETIME_POLICY.
* @return implementation policy
*/
public int getPolicy()
{
return m_policy;
}
/**
* The component implementation class.
* @return the component implementation class
*/
public Class getBaseClass()
{
return m_block;
}
/**
* Return the default configuration.
* @return the default configuration
*/
public Configuration getDefaultConfiguration()
{
return m_config;
}
/**
* Returns a string representation of the descriptor.
* @return stringified representation
*/
public String toString()
{
final StringBuffer buffer = new StringBuffer();
buffer.append( " block: " + getClassName() );
ServiceInfo[] services = getServices();
for( int i=0; i<services.length; i++ )
{
buffer.append( "\n service: " +
services[i].getInterface().getName()
+ ", version: " + services[i].getVersion() );
}
DependencyInfo[] dependencies = getDependencies();
for( int i=0; i<dependencies.length; i++ )
{
buffer.append( "\n dependecy: "
+ "role: " + dependencies[i].getRole()
+ ", service: " + dependencies[i].getInterface().getName()
+ ", version: " + dependencies[i].getVersion() );
}
return buffer.toString();
}
}
1.1
jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/service/package.html
Index: package.html
===================================================================
<body>
<p>Resource supporting the management of components using dynamic service
composition.</p>
<table border="1" cellpadding="3" cellspacing="0" width="100%">
<tr bgcolor="#ccccff">
<td colspan="2"><b>Features Summary</b></td>
<tr><td width="20%"><b>Feature</b></td>
<td><b>Description</b></td></tr>
<tr><td width="20%" valign="top">Service Composition</td>
<td>Handles automated composition of services based on declared component
dependencies.</td></tr>
<tr><td>Default Configurations</td>
<td>Provides support for configuration using component default configurations
together
with supplied configuration information using the CascadingConfiguration
model.</td></tr>
<tr><td>Mixed execution modes</td>
<td>Supports execution from the command line (main method), as a jar file, as
an Ant task,
and as an embedded component.</td></tr>
</table>
<h3>Component Creation</h3>
The pipeline class manages the creation and deployment of a target component
and dynamic
resolution of dependencies based on meta-information contained in a jar
manifest file. Entries
in the manifest file reference class names that are used to load the
component implementation
and configuration the component based on meta-information contained in .xinfo
and .conf files
of the same name as the implementation class.
<h4>The Manifest File</h4>
<p>Reusable components are published through a manifest declaration show
below. When
a jar file is supplied to the Pipeline the manifest is checked for any block
declarations.
Blocks declared within the manifest are registered by the pipeline as
potentially available
services that can be used in service dependency resolution. Each
Avalon-block declaration
in the manifest refers to a component implementation class and its associated
.xinfo file.</p>
<pre>
Manifest-Version: 1.0
Name: org/apache/DirectoryBlock.class
Avalon-Block: true
Name: org/apache/ReferralBlock.class
Avalon-Block: true
</pre>
<h4>The <classname>.xinfo File</h4>
<p>For each component declared in a manifest, the implementation establishes
the available services
and dependencies based on information contained in an <code>.xinfo</code>
file. Given a component
implementation class
<strong><code>org/apache/DirectoryBlock.class</code></strong> the
<code>Pipeline</code> processor
will attempt to locate a
<strong><code>org/apache/DirectoryBlock.xinfo</code></strong> resource in the
jar file.</P>
<pre>
<?xml version="1.0"?>
<blockinfo>
<font color="blue"><i><!--
Block version.
--></i></font>
<block>
<version>1.0</version>
</block>
<font color="blue"><i><!--
Services that are offered by the component are declared under the
services element. A services element may contain multiple service
declarations.
--></i></font>
<services>
<font color="blue"><i><!--
A service declaration includes a class name (typically an interface)
and a service version identifier. Service versions may have up to
three fiels (e.g. 1.1.3).
--></i></font>
<service name="org.apache.ReferralService" version="1.0" />
</services>
<font color="blue"><i><!--
A component declares the dependencies it has with other components
under
the dependencies element. A dependencies element may contain multiple
dependency statements.
--></i></font>
<dependencies>
<font color="blue"><i><!--
A dependency element contains the declaration of a role and a
service
that the component is dependent on. The pipeline processor will
attempt
to resolve dependencies based on registered services and
dynamically
establish and provide the dependencies via the component
Serviceable
implementation. The role name corresponds to the identifying string
that the component implementation will use to lookup a service from
a service manger during the serviceable lifecycle phase.
--></i></font>
<dependency>
<role>directory</role>
<service name="org.apache.DirectoryService"
version="1.0"/>
</dependency>
</dependencies>
<font color="blue"><i><!--
Component implementation policy may one of the following:
(a) SINGLETON, service is available for the lifetime of the manager
(b) POOLED, the implementation implements Pool (implementation pending)
(c) TRANSIENT, manager is a factory of transient service instances
--></i></font>
<implementation policy="SINGLETON" />
</blockinfo>
</pre>
<h4>The <classname>.conf File</h4>
<p>The pipeline processor provides support for the automated retrieval of
default
configurations for a component. During lifecycle processing, the pipeline
processor
will attempt to locate a configuration resource with the same path and name
as
the component implementation class. For example, for the component
<strong><code>org/apache/DirectoryBlock.class</code></strong>, the
implementation will look for
a default configuration under the resource path
<strong><code>org/apache/DirectoryBlock.conf</code></strong>.
During the configuration stage, the pipeline processor will supply the
component with
a <code>CascadingConfiguration</code> where the primary configuration is
configuration
derived from the pipeline processor configuration, and a default
configuration corresponding
to the .conf configuration.
</p>
<pre>
</body>
--
To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>