mcconnell 2002/07/12 03:17:21
Added: assembly/src/java/org/apache/excalibur/merlin/container
UnresolvedProviderException.java
Resources.properties Registry.java
ProfileTable.java ProfileRegistry.java package.html
DefaultContainer.xinfo DefaultContainer.java
ContainerRuntimeException.java
ContainerException.java
AssemblyRuntimeException.java
AssemblyException.java
Log:
rationalization of kernel and registry container functionlity into the container
package
Revision Changes Path
1.1
jakarta-avalon-excalibur/assembly/src/java/org/apache/excalibur/merlin/container/UnresolvedProviderException.java
Index: UnresolvedProviderException.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.excalibur.merlin.container;
import org.apache.excalibur.meta.info.DependencyDescriptor;
import org.apache.avalon.framework.CascadingException;
/**
* Exception to indicate that a service provider could not be found.
*
* @author <a href="mailto:[EMAIL PROTECTED]">Stephen McConnell</a>
* @version $Revision: 1.1 $ $Date: 2002/07/12 10:17:20 $
*/
public final class UnresolvedProviderException
extends CascadingException
{
private DependencyDescriptor m_dependency;
/**
* Construct a new <code>UnresolvedProviderException</code> instance.
*
* @param dependency the unresolved dependency
*/
public UnresolvedProviderException( String message, DependencyDescriptor
dependency )
{
this( message, dependency, null );
}
/**
* Construct a new <code>UnresolvedProviderException</code> instance.
*
* @param dependency the unresolved dependency
* @param cause the causal exception
*/
public UnresolvedProviderException( String message, DependencyDescriptor
dependency, Throwable cause )
{
super( message, cause );
m_dependency = dependency;
}
public String getMessage()
{
return "Could not resolve provider for dependency "
+ m_dependency.getService()
+ " for role: " + m_dependency.getRole()
+ " due to " + super.getMessage();
}
}
1.1
jakarta-avalon-excalibur/assembly/src/java/org/apache/excalibur/merlin/container/Resources.properties
Index: Resources.properties
===================================================================
xinfo-load=Creating Type from {0}.
xinfo-missing=XINFO resource unavailable for class "{0}".
xinfo-parse-error=Error occured while parsing xinfo resource "{0}".
xinfo-nocreate=Failed to create Type from resource "{0}" (Reason: {1}).
xinfo-props-error=Unable to construct attributes using key "{0}" (Reason: {1}).
cinfo-nocreate=Failed to create DefaultType from resource "{0}" (Reason: {1}).
cinfo-properties-error=Failed to create Type attributes from resource "{0}" (Reason:
{1}).
sinfo-noname=Missing name attribute in service declaration from resource "{0}".
sinfo-version=Bad service version in resource "(Reason: {0})".
sinfo-nocreate=Failed to create ServiceInfo from resource "{0}" (Reason: {1}).
dinfo-service-error="Could not create dependecy service delcaration (Reason: {0}).
dinfo-nocreate="Could not create dependecy delcaration from resource "{0}" (Reason:
{1}).
missing.extension=Unable to locate an extension that is required by application.\n
Extension Name: {0}\n Specification Vendor: {1}\n Specification Version: {2}\n
Implementation Vendor: {3}\n Implementation Vendor-Id: {4}\n Implementation Version:
{5}\n Implementation URL: {6}
unsatisfied.extensions=Missing {0} extensions and thus can not build ClassLoader for
application.
bad-classpath-entry=There is a bad entry ("{0}") on classpath that made it
impossible to load a manifest.
available-extensions=Available extensions: {0}
required-extensions=The list of required extensions for application includes: {0}
optional-packages-added=The list of "Optional Packages" added to the application
include: {0}
classpath-entries=The list of classpath entrys for the application include: {0}
1.1
jakarta-avalon-excalibur/assembly/src/java/org/apache/excalibur/merlin/container/Registry.java
Index: Registry.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.excalibur.merlin.container;
import java.io.InputStream;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.JarURLConnection;
import java.net.URLClassLoader;
import java.util.List;
import java.util.LinkedList;
import java.util.Hashtable;
import java.util.Properties;
import java.util.Vector;
import java.util.Iterator;
import java.util.ArrayList;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
import java.util.Enumeration;
import java.security.Policy;
import java.io.FileInputStream;
import org.apache.log.output.io.StreamTarget;
import org.apache.log.Hierarchy;
import org.apache.log.Priority;
import org.apache.avalon.excalibur.i18n.ResourceManager;
import org.apache.avalon.excalibur.i18n.Resources;
import org.apache.excalibur.configuration.ConfigurationUtil;
import org.apache.avalon.framework.logger.Logger;
import org.apache.avalon.framework.logger.LogEnabled;
import org.apache.avalon.framework.logger.AbstractLogEnabled;
import org.apache.avalon.framework.logger.AvalonFormatter;
import org.apache.avalon.framework.logger.LogKitLogger;
import org.apache.avalon.framework.activity.Initializable;
import org.apache.avalon.framework.activity.Disposable;
import org.apache.avalon.framework.activity.Executable;
import org.apache.avalon.framework.activity.Startable;
import org.apache.avalon.framework.CascadingRuntimeException;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.DefaultConfiguration;
import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.configuration.Configurable;
import org.apache.avalon.framework.context.Context;
import org.apache.avalon.framework.context.Contextualizable;
import org.apache.avalon.framework.context.ContextException;
import org.apache.avalon.framework.context.DefaultContext;
import org.apache.avalon.framework.service.Serviceable;
import org.apache.avalon.framework.service.ServiceManager;
import org.apache.avalon.framework.service.DefaultServiceManager;
import org.apache.avalon.framework.service.ServiceException;
import org.apache.avalon.framework.parameters.Parameters;
import org.apache.avalon.framework.Version;
import org.apache.avalon.framework.ExceptionUtil;
import org.apache.avalon.excalibur.extension.PackageRepository;
import org.apache.avalon.excalibur.extension.Extension;
import org.apache.avalon.excalibur.extension.OptionalPackage;
import org.apache.avalon.excalibur.extension.DefaultPackageRepository;
import org.apache.avalon.excalibur.logger.LoggerManager;
import org.apache.excalibur.configuration.ContextFactory;
import org.apache.excalibur.meta.info.DefaultType;
import org.apache.excalibur.meta.info.Type;
import org.apache.excalibur.meta.info.ServiceDescriptor;
import org.apache.excalibur.meta.info.DependencyDescriptor;
import org.apache.excalibur.meta.info.ServiceDesignator;
import org.apache.excalibur.meta.verifier.VerifyException;
import org.apache.excalibur.merlin.meta.data.Profile;
import org.apache.excalibur.merlin.meta.data.ContainerDescriptor;
import org.apache.excalibur.merlin.meta.data.ComponentDescriptor;
import org.apache.excalibur.merlin.meta.data.DefaultProfile;
import org.apache.excalibur.merlin.meta.data.Association;
import org.apache.excalibur.merlin.meta.data.builder.TypeManager;
import org.apache.excalibur.merlin.meta.data.verifier.AssemblyVerifier;
import org.apache.excalibur.merlin.meta.data.verifier.MetaDataVerifier;
import org.apache.excalibur.merlin.meta.data.lifecycle.LifecycleHelper;
import org.apache.excalibur.merlin.meta.data.lifecycle.ResourceProvider;
import org.apache.excalibur.merlin.meta.data.lifecycle.LifecycleException;
import org.apache.excalibur.merlin.kernel.DependencyGraph;
import org.apache.excalibur.merlin.kernel.ContainerClassLoader;
import org.apache.excalibur.merlin.kernel.DefaultProvider;
import org.apache.excalibur.merlin.kernel.DefaultLoggerManager;
import org.apache.excalibur.merlin.Container;
import org.apache.excalibur.merlin.Verifiable;
/**
* Provides support for the maintenance of a registry of
* component type and profiles established within a classloader complimented by
* explicit container based assembly directives.
* <b>Package Structure (UML)</b>
* <p><img src=doc-files/RegistryUML.gif border=0></p>
*
* @author <a href="mailto:[EMAIL PROTECTED]">Stephen McConnell</a>
* @version $Revision: 1.1 $ $Date: 2002/07/12 10:17:20 $
*/
public class Registry extends AbstractLogEnabled implements Contextualizable,
Initializable, Executable, Disposable, Startable
{
//=======================================================================
// static
//=======================================================================
/**
* Context key used to locate the application classloader.
*/
public static final String CLASSLOADER_KEY = "classloader";
/**
* Context key used to locate the application classloader.
*/
public static final String CONTAINER_KEY = "container";
/**
* Context key used to locate the application classloader.
*/
public static final String MAP_KEY = "map";
/**
* Context key used to locate the logger manager.
*/
public static final String LOG_MANAGER_KEY = "logging";
/**
* Context key used to locate the logger manager.
*/
public static final String CATEGORY_PATH_KEY = "path";
/**
* Context key used to locate the resource provider.
*/
public static final String PROVIDER_KEY = "provider";
/**
* Context key used to locate the logger manager.
*/
public static final String CONTAINER_DESCRIPTOR_KEY = "descriptor";
private static final Resources REZ =
ResourceManager.getPackageResources( Registry.class );
//=======================================================================
// state
//=======================================================================
/**
* The parent registry to this registry.
* (not currently in use - will come into effect as registry hierachies as
* dealt with).
*/
private Container m_parent;
/**
* Classloader supplied by the parent.
*/
private ContainerClassLoader m_classloader;
/**
* Internal class that maintains information about profile types.
*/
private ProfileRegistry m_profiles;
/**
* The depenecy map supplied by the parent container.
*/
private DependencyGraph m_map;
/**
* The resource provider to handle resource creation during profile activation.
*/
private DefaultProvider m_provider;
/**
* The lifecycle helper to use to process startup and shudown of services.
*/
private LifecycleHelper m_helper = new LifecycleHelper();
private DefaultLoggerManager m_logging;
private String m_path;
private ContainerDescriptor m_descriptor;
//=======================================================================
// Contextualizable
//=======================================================================
/**
* Service context from which the registry classloader is resolved.
* @param context a context value containing the key 'classloader'
*/
public void contextualize( Context context ) throws ContextException
{
m_classloader = (ContainerClassLoader) context.get( CLASSLOADER_KEY );
m_logging = (DefaultLoggerManager) context.get( LOG_MANAGER_KEY );
m_map = (DependencyGraph) context.get( MAP_KEY );
m_provider = (DefaultProvider) context.get( PROVIDER_KEY );
m_descriptor = (ContainerDescriptor) context.get( CONTAINER_DESCRIPTOR_KEY );
try
{
m_parent = (Container) context.get( CONTAINER_KEY );
}
catch( ContextException e )
{
// root container
}
}
//=======================================================================
// Initializable
//=======================================================================
/**
* Initalization of a <code>Registry</code> during which an application
* scoped classloader is created.
* @exception Exception if an error occurs during initialization.
*/
public void initialize() throws Exception
{
getLogger().debug("registry initialization");
m_helper.enableLogging( getLogger().getChildLogger("lifecycle") );
//
// initiate profile creation
//
getLogger().debug("explicit profile registration");
m_profiles = new ProfileRegistry( m_descriptor, m_classloader,
m_classloader, m_parent, m_map );
m_profiles.enableLogging( getLogger().getChildLogger( "profiles") );
//
// explicit component profile registration
//
getLogger().debug("explicit profile registration");
ComponentDescriptor[] children = m_descriptor.getComponentDescriptors();
for( int i=0; i<children.length; i++ )
{
m_profiles.register( children[i] );
}
//
// packaged and implict profile registration
//
getLogger().debug("packaged and implicit profile registration");
Type[] types = m_classloader.getTypes();
for( int i=0; i<types.length; i++ )
{
m_profiles.register( types[i] );
}
try
{
getLogger().debug("profile assembly");
m_profiles.assembleProfiles();
}
catch( Throwable e )
{
final String error = "Assembly failure.";
throw new AssemblyException( error, e );
}
}
//=======================================================================
// Executable
//=======================================================================
/**
* Executes composition of a target component assembly options relative to a
target
* component classname within the scope of a classpath declaration containing jar
file
* references (refer contextualize), and generate a report to the logging channel.
*
* @exception Exception if an error occurs during execution
*/
public void execute() throws Exception
{
}
//======================================================================
// Startable
//======================================================================
public void start() throws Exception
{
getLogger().debug("start");
Profile[] startup = m_map.getStartupGraph();
for( int i=0; i<startup.length; i++ )
{
Profile profile = startup[i];
if( m_profiles.isLocalProfile( profile ) )
{
final String name = profile.getName();
try
{
if( getLogger().isDebugEnabled() )
getLogger().debug("starting: " + name );
m_helper.startup( name, profile, m_provider );
if( getLogger().isInfoEnabled() )
getLogger().info("started: " + name );
}
catch( LifecycleException le )
{
final String warning =
"Could not startup a service derived from profile: " + profile;
getLogger().warn( warning, le );
// need to retract all profiles dependent on this profile
}
}
}
getLogger().debug("started");
}
public void stop()
{
getLogger().debug("stop");
Profile[] shutdown = m_map.getShutdownGraph();
for( int i=0; i<shutdown.length; i++ )
{
Profile profile = shutdown[i];
if( m_profiles.isLocalProfile( profile ) )
{
final String name = profile.getName();
if( getLogger().isDebugEnabled() )
getLogger().debug("stopping: " + name );
Object object = m_provider.getSingletonInstance( profile );
if( object != null )
{
try
{
m_helper.shutdown( name, object );
m_provider.release( profile );
if( getLogger().isInfoEnabled() )
getLogger().info("stopped: " + name );
}
catch( LifecycleException le )
{
final String warning =
"Could not shutdown a service derived from profile: " +
profile;
getLogger().warn( warning, le );
}
}
}
}
getLogger().debug("stopped");
}
//=======================================================================
// Verifiable
//=======================================================================
/**
* Method invoked by a parent container to request type level validation of
* the container.
*
* @exception ValidationException if a validation failure occurs
*/
public void verify() throws VerifyException
{
getLogger().debug("Map listing");
Profile[] startup = m_map.getStartupGraph();
doVerify( startup );
}
//=======================================================================
// Container
//=======================================================================
public Profile[] getProviders( ServiceDesignator designator )
{
Profile[] local = m_profiles.getProviders( designator );
if( m_parent != null )
{
Profile[] facilities = m_parent.getProviders( designator );
ArrayList list = new ArrayList();
for( int i=0; i<local.length; i++ )
{
list.add( local[i] );
}
for( int i=0; i<facilities.length; i++ )
{
list.add( facilities[i] );
}
return (Profile[]) list.toArray( new Profile[0] );
}
return local;
}
//=======================================================================
// implementation
//=======================================================================
private void doVerify( Profile[] assembly ) throws VerifyException
{
MetaDataVerifier mdv = new MetaDataVerifier();
for( int i=0; i<assembly.length; i++ )
{
try
{
mdv.verifyType( assembly[i], m_classloader );
}
catch( Throwable e )
{
getLogger().error("verification failure", e );
}
}
AssemblyVerifier verifier = new AssemblyVerifier();
verifier.enableLogging( getLogger().getChildLogger("verifier") );
getLogger().debug("commencing assembly verification");
verifier.verifyAssembly( assembly );
listProfiles( assembly );
}
private void listProfiles( Profile[] profiles )
{
getLogger().debug("listing profiles");
List reported = new LinkedList();
for( int i=0; i<profiles.length; i++ )
{
listProfile( i+1, profiles[i], m_map, reported );
}
}
private void listProfile( int n, Profile profile, DependencyGraph map, List
reported )
{
if( !reported.contains( profile ) )
{
reported.add( profile );
getLogger().debug( "" + n + ": " + profile.toString() );
Profile[] suppliers = map.getProviderGraph( profile );
for( int j=0; j<suppliers.length; j++ )
{
Profile supplier = suppliers[j];
getLogger().debug(" supplier: " + supplier );
}
Profile[] consumers = map.getConsumerGraph( profile );
for( int j=0; j<consumers.length; j++ )
{
Profile consumer = consumers[j];
getLogger().debug(" consumer: " + consumer.getName() );
}
}
}
//=======================================================================
// Disposable
//=======================================================================
/**
* Disposal of this component.
*/
public void dispose()
{
}
}
1.1
jakarta-avalon-excalibur/assembly/src/java/org/apache/excalibur/merlin/container/ProfileTable.java
Index: ProfileTable.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.excalibur.merlin.container;
import java.util.List;
import java.util.LinkedList;
import org.apache.avalon.framework.logger.Logger;
import org.apache.avalon.framework.logger.AbstractLogEnabled;
import org.apache.excalibur.meta.info.ServiceDesignator;
import org.apache.excalibur.meta.info.Type;
import org.apache.excalibur.merlin.meta.data.Profile;
/**
* Internal table that holds references to the available component types
* that represent candidate providers for a single service type.
*
* @author <a href="mailto:[EMAIL PROTECTED]">Stephen McConnell</a>
* @version $Revision: 1.1 $ $Date: 2002/07/12 10:17:20 $
*/
final class ProfileTable extends AbstractLogEnabled
{
//=======================================================================
// state
//=======================================================================
/**
* Component type lists keyed by service designator.
*/
private List m_providers = new LinkedList();
/**
* Identification of the service type that this table is supporting.
*/
private ServiceDesignator m_designator;
public ProfileTable( ServiceDesignator designator, Logger logger )
{
m_designator = designator;
super.enableLogging( logger );
}
//=======================================================================
// ServiceTable
//=======================================================================
/**
* Add a service provider to the set of provider managed by this table.
*
* @param classname the component class name
* @return the component type
*/
public void add( Profile profile )
{
m_providers.add( profile );
}
/**
* Returns the set of providers currently registered in the table.
* @return the set of component types capable of acting as a provider for the
* service managed by the table
*/
public Profile[] getProfiles()
{
return (Profile[]) m_providers.toArray( new Profile[0] );
}
/**
* Return the service type for the table.
* @return the service designator
*/
public ServiceDesignator getService()
{
return m_designator;
}
/**
* Return the number of entries in the table.
* @return the number of providers
*/
public int getSize()
{
return m_providers.size();
}
/**
* Returns true if the table service designator matches the supplied designator.
* @param service a service type designator
* @return TRUE if the supplied service type matches the the service type for
* this table.
*/
public boolean matches( ServiceDesignator service )
{
return m_designator.matches( service );
}
public String toString()
{
return "ProfileTable:"
+ System.identityHashCode( this )
+ ", "
+ m_designator;
}
}
1.1
jakarta-avalon-excalibur/assembly/src/java/org/apache/excalibur/merlin/container/ProfileRegistry.java
Index: ProfileRegistry.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.excalibur.merlin.container;
import java.util.List;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Hashtable;
import java.util.ArrayList;
import java.util.Vector;
import java.util.Iterator;
import org.apache.avalon.framework.CascadingException;
import org.apache.avalon.framework.logger.Logger;
import org.apache.avalon.framework.logger.LogEnabled;
import org.apache.avalon.framework.logger.AbstractLogEnabled;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.activity.Startable;
import org.apache.avalon.framework.activity.Initializable;
import org.apache.avalon.framework.activity.Disposable;
import org.apache.excalibur.meta.info.ServiceDescriptor;
import org.apache.excalibur.meta.info.ServiceDesignator;
import org.apache.excalibur.meta.info.DependencyDescriptor;
import org.apache.excalibur.meta.info.Type;
import org.apache.excalibur.meta.info.builder.TypeBuilder;
import org.apache.excalibur.meta.verifier.ComponentVerifier;
import org.apache.excalibur.configuration.ConfigurationUtil;
import org.apache.excalibur.merlin.meta.data.builder.TypeManager;
import org.apache.excalibur.merlin.meta.data.Profile;
import org.apache.excalibur.merlin.meta.data.ContainerDescriptor;
import org.apache.excalibur.merlin.meta.data.ComponentDescriptor;
import org.apache.excalibur.merlin.meta.data.builder.ProfileBuilder;
import org.apache.excalibur.merlin.meta.data.Selector;
import org.apache.excalibur.merlin.kernel.DependencyGraph;
import org.apache.excalibur.merlin.Container;
/**
* Internal table that holds available component type keyed relative
* to the service it provides.
*
* @author <a href="mailto:[EMAIL PROTECTED]">Stephen McConnell</a>
* @version $Revision: 1.1 $ $Date: 2002/07/12 10:17:20 $
*/
final class ProfileRegistry extends AbstractLogEnabled
{
//=======================================================================
// state
//=======================================================================
private ProfileBuilder m_builder = new ProfileBuilder();
private ClassLoader m_classloader;
private TypeManager m_types;
private Container m_parent;
/**
* Component profiles keyed by name.
*/
private Hashtable m_profiles = new Hashtable();
/**
* List of ServiceTable instances.
*/
private List m_services = new LinkedList();
private final Configuration[] m_directives = new Configuration[0];
/**
* The set of local explicitly declared profiles.
*/
private Vector m_assembly = new Vector();
/**
* The set of locally installed profiles.
*/
private ArrayList m_installed = new ArrayList();
private DependencyGraph m_map;
private ContainerDescriptor m_descriptor;
//=======================================================================
// constructor
//=======================================================================
/**
* Creation of a new service registry.
* @param registry the registry that will be supplied to new component defintions
* @param loader the registry class loader
* @param profiles the configuration fragment containing explicit component
profiles
*/
public ProfileRegistry(
ContainerDescriptor descriptor, TypeManager registry, ClassLoader loader,
Container container, DependencyGraph map )
{
m_classloader = loader;
m_types = registry;
m_descriptor = descriptor;
m_parent = container;
m_map = map;
}
//=======================================================================
// PrifileRegistry
//=======================================================================
/**
* Return the set of explicit, packed and implict providers capable of handling
the
* supplied service.
* @param designator the service designator
* @return the set of candidate profiles
*/
public Profile[] getProviders( ServiceDesignator designator )
{
ArrayList list = new ArrayList();
ComponentDescriptor[] local = m_descriptor.getComponentDescriptors();
for( int i=0; i<local.length; i++ )
{
Profile profile = local[i];
ServiceDescriptor[] services = profile.getType().getServices();
for( int j=0; j<services.length; j++ )
{
if( services[j].getService().matches( designator ) )
{
list.add( profile );
break;
}
}
}
return (Profile[]) list.toArray( new Profile[0] );
}
/**
* Register a potential supplier component type. The implementation will
* create a component type instance for the entry if not already known and
* return the existing or new instance to the invoking client.
*
* @param classname the component class name
* @return the component type
*/
public Profile[] register( Type type ) throws Exception
{
Profile[] profiles = m_builder.build( type, m_classloader );
for( int i=0; i<profiles.length; i++ )
{
Profile profile = profiles[i];
register( profile );
}
return profiles;
}
/**
* For all of the explicity declared profiles, initiate dependency correlation.
*/
public void assembleProfiles() throws Exception
{
Profile[] profiles = m_descriptor.getComponentDescriptors();
for( int i=0; i<profiles.length; i++ )
{
Profile profile = profiles[i];
getLogger().debug("assembly target: " + profile );
ArrayList visited = new ArrayList();
assemble( profile, visited, "" );
m_map.add( profile );
m_installed.add( profile );
}
}
public boolean isLocalProfile( Profile profile )
{
return m_profiles.values().contains( profile );
}
/**
* Returns the set of component types know to the registry.
* @return the set of component types registered with the registry
*/
public Profile[] getProfiles()
{
return (Profile[]) m_profiles.values().toArray( new Profile[0] );
}
/**
* Returns the set of component types know to the registry that are capable of
* supporting the supplied service.
* @return the set of candidate component types
*/
public Profile[] getProfiles( ServiceDesignator service )
{
return getTable( service ).getProfiles();
}
/**
* Returns a registered component type.
* @return the component type from the registry or null if the type is unknown
*/
public Profile getProfile( String name )
{
return (Profile) m_profiles.get( name );
}
/**
* Register the type resulting in the cross-referencing of the type with the set
of
* service tables that the type is is capable of supporting.
*/
public Profile register( Profile profile )
{
String name = profile.getName();
m_profiles.put( name, profile );
getLogger().debug( "" + profile );
ServiceDescriptor[] services = profile.getType().getServices();
for( int i=0; i<services.length; i++ )
{
register( services[i].getService(), profile );
}
return profile;
}
private void assemble( Profile profile, List visited, String pad ) throws
Exception
{
getLogger().debug( pad + "assemble: " + profile );
String pad2 = pad + " ";
visited.add( profile );
DependencyDescriptor[] dependencies = profile.getType().getDependencies();
for( int i=0; i<dependencies.length; i++ )
{
DependencyDescriptor dependency = dependencies[i];
String role = dependency.getRole();
if( profile.getAssociation( role ) == null )
{
Profile[] candidates = assemble( profile, dependency, visited, pad2
);
Profile[] facilities = getFacilities( dependency.getService() );
Profile provider = selectProfile( dependency, facilities, candidates
);
if( provider == null )
throw new UnresolvedProviderException("no available provider",
dependency );
profile.addProvider( provider, role );
if( isLocalProfile( provider ) )
{
m_map.add( provider );
m_installed.add( provider );
}
}
}
}
private Profile[] assemble( Profile source, DependencyDescriptor dependency,
List visited, String pad )
throws Exception
{
getLogger().debug( pad + "dependency: " + dependency.getRole() );
Vector vector = new Vector();
String pad2 = pad + " ";
Profile[] profiles = getProfiles( dependency.getService() );
for( int i=0; i<profiles.length; i++ )
{
Profile provider = profiles[i];
if( !provider.equals( source ) )
{
try
{
assemble( provider, visited, pad2 );
vector.add( provider );
getLogger().debug( pad + "candidate: " + provider );
}
catch( Throwable e )
{
// solution is not resolvable
}
}
}
return (Profile[]) vector.toArray( new Profile[0] );
}
/**
* Register a type under a service cross-reference.
*/
private void register( ServiceDesignator service, Profile profile )
{
ProfileTable table = getTable( service );
table.add( profile );
}
/**
* Return a table holding table capable of supply the supplied service.
*/
private ProfileTable getTable( ServiceDesignator service )
{
ProfileTable table = null;
Iterator iterator = m_services.iterator();
while( iterator.hasNext() )
{
table = (ProfileTable) iterator.next();
if( table.matches( service ) )
return table;
}
// otherwise create a new table
table = new ProfileTable( service, getLogger().getChildLogger("table") );
m_services.add( table );
return table;
}
private Selector getSelector( DependencyDescriptor dependency )
{
// if the dependency declares a selector class then use that,
// otherwise, look for a default service type selector
String selectorName = dependency.getAttribute("avalon.service.selector");
if( selectorName == null )
selectorName = dependency.getService().getClassname() + "Selector";
try
{
Class clazz = m_classloader.loadClass( selectorName );
Selector selector = (Selector) clazz.newInstance();
if( selector instanceof LogEnabled )
{
((LogEnabled)selector).enableLogging(
getLogger().getChildLogger("selector") );
}
return selector;
}
catch( Throwable e )
{
return null;
}
}
private Profile[] getFacilities( ServiceDesignator service )
{
if( m_parent != null )
{
return m_parent.getProviders( service );
}
else
{
return new Profile[0];
}
}
private Profile selectProfile( DependencyDescriptor dependency, Profile[]
facilities, Profile[] profiles )
{
Selector selector = getSelector( dependency );
if( selector != null )
{
return selector.select( facilities, profiles );
}
else
{
// apply default selection policy
Profile profile = select( profiles, Profile.EXPLICIT );
if( profile == null )
{
profile = select( facilities );
}
if( profile == null )
{
profile = select( profiles );
}
return profile;
}
}
private Profile select( Profile[] profiles )
{
Profile profile = select( profiles, Profile.EXPLICIT );
if( profile == null ) profile = select( profiles, Profile.PACKAGED );
if( profile == null ) profile = select( profiles, Profile.IMPLICIT );
return profile;
}
private Profile select( Profile[] profiles, int mode )
{
for( int i=0; i<profiles.length; i++ )
{
if( profiles[i].getMode() == mode )
return profiles[i];
}
return null;
}
}
1.1
jakarta-avalon-excalibur/assembly/src/java/org/apache/excalibur/merlin/container/package.html
Index: package.html
===================================================================
<body>
<p>
A geneneric component type management service supporting automated component
assembly, service deployment and service decommissioning . The service acts as a
repository for component types and profiles. The repository enables resolution of
component types based on a classloader, extension directory set defintion (managemed
by the host kernel), a supplied classpath, and component deployment directives.
Components are established based on the publication of components across a set jar
file manifest together with explicit component profile directives included within the
registry configuration. Given a manifest declaration of a component implementation
class, a component type definition will be resolvable providing the registry can
resolve all type constraints (specifically the validation of the dependecies of
candidate supplier components).
</p>
<h3>Funtional Summary</h3>
<ul>
<li>Portable component type mangement.
<li>Automated service assembly.
</ul>
<h3>Key Features</h3>
<p>A primary objective of this service is to provide simple deployment of components
with minimal administration overhead. In many cases, no component assembly directives
are required as the container will apply assembly logic based on the meta-information
association with component types that are either explicity declared, or implicly
located via a classpath and extension directories.</p>
<ul>
<li>Automatic component profile generation.
<li>Automatic assembly of components based on dependency and service production
declarations.
<li>Service provider selection plug-in architecture.
<li>Customization of application content.
</ul>
<h3>Object Model</h3>
<p>The primary service is a type {@link
org.apache.excalibur.merlin.container.Registry}. The registry acts as a component
{@link org.apache.excalibur.meta.info.Type} repository and {@link
org.apache.excalibur.merlin.meta.data.Profile} manager. Each component type
represents a concrete component implementation class. For each component type, the
container associates at least one instantiation {@link
org.apache.excalibur.merlin.meta.data.Profile}. A profile is either a default profile
generated by the registry based on meta-information derived from the type, or, an
explicit profile declared by the component assembler via the registry configuration.
Multiple profiles for a particular component type can coexist in the same registry.
<h3>Package Structure (UML)</h3>
<p><img src=doc-files/RegistryUML.gif border=0></p>
</body>
1.1
jakarta-avalon-excalibur/assembly/src/java/org/apache/excalibur/merlin/container/DefaultContainer.xinfo
Index: DefaultContainer.xinfo
===================================================================
<?xml version="1.0"?>
<!--
Copyright (C) The Apache Software Foundation. All rights reserved.
This software is published under the terms of the Apache Software License
version 1.1, a copy of which has been included with this distribution in
the LICENSE.txt file.
@author Stephen McConnell
@version 1.0 12/03/2001
-->
<component-info>
<component>
<name>container</name>
<version>1.0</version>
</component>
<context>
<entry key="classloader"
type="org.apache.excalibur.merlin.kernel.ContainerClassLoader" optional="false"/>
</context>
<services>
<service>
<service-ref type="org.apache.excalibur.merlin.Container" version="1.0"/>
</service>
</services>
</component-info>
1.1
jakarta-avalon-excalibur/assembly/src/java/org/apache/excalibur/merlin/container/DefaultContainer.java
Index: DefaultContainer.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.excalibur.merlin.container;
import java.io.InputStream;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.JarURLConnection;
import java.net.URLClassLoader;
import java.util.List;
import java.util.LinkedList;
import java.util.Hashtable;
import java.util.Properties;
import java.util.Vector;
import java.util.Iterator;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
import java.security.Policy;
import java.io.FileInputStream;
import org.apache.avalon.excalibur.i18n.ResourceManager;
import org.apache.avalon.excalibur.i18n.Resources;
import org.apache.excalibur.configuration.ConfigurationUtil;
import org.apache.avalon.framework.logger.Logger;
import org.apache.avalon.framework.logger.AbstractLogEnabled;
import org.apache.avalon.framework.logger.AvalonFormatter;
import org.apache.avalon.framework.logger.LogKitLogger;
import org.apache.avalon.framework.activity.Initializable;
import org.apache.avalon.framework.activity.Disposable;
import org.apache.avalon.framework.activity.Executable;
import org.apache.avalon.framework.activity.Startable;
import org.apache.avalon.framework.CascadingRuntimeException;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.DefaultConfiguration;
import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.configuration.Configurable;
import org.apache.avalon.framework.context.Context;
import org.apache.avalon.framework.context.Contextualizable;
import org.apache.avalon.framework.context.ContextException;
import org.apache.avalon.framework.context.DefaultContext;
import org.apache.avalon.framework.service.Serviceable;
import org.apache.avalon.framework.service.ServiceManager;
import org.apache.avalon.framework.service.DefaultServiceManager;
import org.apache.avalon.framework.service.ServiceException;
import org.apache.avalon.framework.Version;
import org.apache.avalon.framework.ExceptionUtil;
import org.apache.avalon.excalibur.extension.PackageRepository;
import org.apache.avalon.excalibur.extension.Extension;
import org.apache.avalon.excalibur.extension.OptionalPackage;
import org.apache.avalon.excalibur.extension.DefaultPackageRepository;
import org.apache.avalon.excalibur.logger.LoggerManager;
import org.apache.excalibur.meta.info.DefaultType;
import org.apache.excalibur.meta.info.ServiceDescriptor;
import org.apache.excalibur.meta.info.DependencyDescriptor;
import org.apache.excalibur.meta.info.ServiceDesignator;
import org.apache.excalibur.meta.verifier.VerifyException;
import org.apache.excalibur.merlin.meta.data.Profile;
import org.apache.excalibur.merlin.meta.data.ContainerDescriptor;
import org.apache.excalibur.merlin.meta.data.CategoryDescriptor;
import org.apache.excalibur.merlin.container.Registry;
import org.apache.excalibur.merlin.kernel.ContainerClassLoader;
import org.apache.excalibur.merlin.kernel.DependencyGraph;
import org.apache.excalibur.merlin.kernel.DefaultProvider;
import org.apache.excalibur.merlin.kernel.DefaultLoggerManager;
import org.apache.excalibur.merlin.Container;
import org.apache.excalibur.merlin.Verifiable;
import org.apache.excalibur.merlin.Manageable;
import org.apache.log.Hierarchy;
import org.apache.log.Priority;
import org.apache.log.output.io.StreamTarget;
import org.apache.excalibur.merlin.container.UnresolvedProviderException;
/**
* Default container implementation that manages a registry of componet providers
and
* a registry of child containers.
* <p><b>UML</b></p>
* <p><image src="doc-files/DefaultContainer.gif" border="0"/></p>
*
* @author <a href="mailto:[EMAIL PROTECTED]">Stephen McConnell</a>
* @version $Revision: 1.1 $ $Date: 2002/07/12 10:17:20 $
*/
public class DefaultContainer extends Registry implements Container
{
//=======================================================================
// state
//=======================================================================
/**
* Configuration.
*/
//private Configuration m_config;
/**
* The registry of embedded containers.
*/
private List m_containers = new LinkedList();
/**
* Parent container.
*/
private Container m_parent;
private ContainerClassLoader m_classloader;
private Logger m_logger;
private DependencyGraph m_map;
private DefaultProvider m_provider;
private DefaultLoggerManager m_logging;
private String m_path;
private ContainerDescriptor m_descriptor;
//=======================================================================
// Contextualizable
//=======================================================================
/**
* Service context from which the registry classloader is resolved.
* @param context a context value containing the key 'classloader'
*/
public void contextualize( Context context ) throws ContextException
{
m_classloader = (ContainerClassLoader) context.get( CLASSLOADER_KEY );
m_logging = (DefaultLoggerManager) context.get( LOG_MANAGER_KEY );
m_descriptor = (ContainerDescriptor) context.get( CONTAINER_DESCRIPTOR_KEY );
try
{
m_parent = (Container) context.get( CONTAINER_KEY );
super.contextualize( context );
}
catch( ContextException e )
{
// root container
m_map = new DependencyGraph();
m_provider = new DefaultProvider( m_classloader, m_logging );
m_provider.enableLogging( getLogger() );
DefaultContext dc = new DefaultContext( context );
dc.put( MAP_KEY, m_map );
dc.put( PROVIDER_KEY, m_provider );
super.contextualize( dc );
}
}
//=======================================================================
// Configurable
//=======================================================================
/**
* Invoked by the container to establish the registry configuration.
* @param config a component configuration
*/
/*
public void configure( Configuration config)
{
getLogger().debug("container configuration");
super.configure( config );
m_config = config;
}
*/
//=======================================================================
// Initializable
//=======================================================================
/**
* Initalization of a <code>Container</code>. Initilization of types and
* associated provides (inplicit, explicit and packaged) is undertaken by the
* (@link Registry} supertype. Following the registry initialization of the
* containers primary propulation the container initialization continues through
* the establishment of subsidiary containers.
*
* @exception Exception if an error occurs during initialization.
*/
public void initialize() throws Exception
{
getLogger().debug("initilization");
super.initialize();
getLogger().debug("subsidiary container creation");
ContainerDescriptor[] containers = m_descriptor.getContainers();
for( int i=0; i<containers.length; i++ )
{
getLogger().debug("container: " + containers[i].getName() );
m_containers.add( createContainer( containers[i] ) );
}
}
//=======================================================================
// Verifiable
//=======================================================================
/**
* Method invoked by a parent container to request type and assembly validation
of
* this container.
*
* @exception ValidationException if a validation failure occurs
*/
public void verify() throws VerifyException
{
super.verify();
Iterator iterator = m_containers.iterator();
while( iterator.hasNext() )
{
((Verifiable)iterator.next()).verify();
}
}
//======================================================================
// Manageable
//======================================================================
public void startup() throws Exception
{
//
// startup all of the components in this container
// before starting up any of the nested containers
//
if( getLogger().isDebugEnabled() )
getLogger().debug("startup");
start();
Iterator iterator = m_containers.iterator();
while( iterator.hasNext() )
{
((Manageable)iterator.next()).startup();
}
}
public void shutdown()
{
//
// shutdown all of the nested containers before stopping
// the components in this container
//
if( getLogger().isDebugEnabled() )
getLogger().debug("shutdown");
Iterator iterator = m_containers.iterator();
while( iterator.hasNext() )
{
((Manageable)iterator.next()).shutdown();
}
stop();
}
//=======================================================================
// private
//=======================================================================
private DefaultContainer createContainer( ContainerDescriptor descriptor )
throws Exception
{
String name = descriptor.getName();
CategoryDescriptor loggers = descriptor.getCategoryDescriptor();
loggers.setParent( m_descriptor.getCategoryDescriptor() );
m_logging.addCategories( loggers );
Logger logger = getLogger().getChildLogger( name );
Logger loaderLogger = logger.getChildLogger( "loader" );
final ContainerClassLoader loader = new ContainerClassLoader( m_classloader
);
loader.enableLogging( loaderLogger );
DefaultContext loaderContext = new DefaultContext();
loaderContext.put(
ContainerClassLoader.CLASSPATH_DESCRIPTOR_KEY,
descriptor.getClasspathDescriptor() );
loaderContext.makeReadOnly();
loader.contextualize( loaderContext );
loader.initialize();
DependencyGraph map = new DependencyGraph( m_map );
m_map.addChild( map );
DefaultProvider provider = new DefaultProvider( m_provider, m_classloader,
m_logging );
provider.enableLogging( logger.getChildLogger( "provider" ) );
DefaultContext context = new DefaultContext();
context.put( CLASSLOADER_KEY, loader );
context.put( CONTAINER_KEY, this );
context.put( MAP_KEY, map );
context.put( PROVIDER_KEY, provider );
context.put( LOG_MANAGER_KEY, m_logging );
context.put( CONTAINER_DESCRIPTOR_KEY, descriptor );
context.makeReadOnly();
DefaultContainer container = new DefaultContainer();
container.enableLogging( logger );
container.contextualize( context );
//container.configure( config );
container.initialize( );
return container;
}
}
1.1
jakarta-avalon-excalibur/assembly/src/java/org/apache/excalibur/merlin/container/ContainerRuntimeException.java
Index: ContainerRuntimeException.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.excalibur.merlin.container;
import org.apache.avalon.framework.CascadingRuntimeException;
/**
* Exception to indicate that there was a repository related runtime error.
*
* @author <a href="mailto:[EMAIL PROTECTED]">Stephen McConnell</a>
* @version $Revision: 1.1 $ $Date: 2002/07/12 10:17:20 $
*/
public final class ContainerRuntimeException
extends CascadingRuntimeException
{
/**
* Construct a new <code>ContainerRuntimeException</code> instance.
*
* @param message The detail message for this exception.
*/
public ContainerRuntimeException( final String message )
{
this( message, null );
}
/**
* Construct a new <code>ContainerRuntimeException</code> instance.
*
* @param message The detail message for this exception.
* @param throwable the root cause of the exception
*/
public ContainerRuntimeException( final String message, final Throwable
throwable )
{
super( message, throwable );
}
}
1.1
jakarta-avalon-excalibur/assembly/src/java/org/apache/excalibur/merlin/container/ContainerException.java
Index: ContainerException.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.excalibur.merlin.container;
import org.apache.avalon.framework.CascadingException;
/**
* Exception to indicate that there was a repository related error.
*
* @author <a href="mailto:[EMAIL PROTECTED]">Stephen McConnell</a>
* @version $Revision: 1.1 $ $Date: 2002/07/12 10:17:20 $
*/
public final class ContainerException
extends CascadingException
{
/**
* Construct a new <code>ContainerException</code> instance.
*
* @param message The detail message for this exception.
*/
public ContainerException( final String message )
{
this( message, null );
}
/**
* Construct a new <code>ContainerException</code> instance.
*
* @param message The detail message for this exception.
* @param throwable the root cause of the exception
*/
public ContainerException( final String message, final Throwable throwable )
{
super( message, throwable );
}
}
1.1
jakarta-avalon-excalibur/assembly/src/java/org/apache/excalibur/merlin/container/AssemblyRuntimeException.java
Index: AssemblyRuntimeException.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.excalibur.merlin.container;
import java.util.Enumeration;
import java.util.Dictionary;
import java.util.Hashtable;
import org.apache.avalon.framework.CascadingRuntimeException;
/**
* Exception to indicate that there was an error during assembly.
*
* @author <a href="mailto:[EMAIL PROTECTED]">Stephen McConnell</a>
* @version $Revision: 1.1 $ $Date: 2002/07/12 10:17:20 $
*/
public final class AssemblyRuntimeException
extends CascadingRuntimeException
{
private static final Hashtable EMPTY_TABLE = new Hashtable();
private Dictionary m_errors;
/**
* Construct a new <code>AssemblyRuntimeException</code> instance.
*
* @param message The detail message for this exception.
*/
public AssemblyRuntimeException( final String message )
{
this( null, message, null );
}
/**
* Construct a new <code>AssemblyRuntimeException</code> instance.
*
* @param errors a list of warning messages related to the exception.
* @param message The detail message for this exception.
*/
public AssemblyRuntimeException( final Dictionary errors, final String message )
{
this( errors, message, null );
}
/**
* Construct a new <code>AssemblyRuntimeException</code> instance.
*
* @param message The detail message for this exception.
* @param throwable the root cause of the exception
*/
public AssemblyRuntimeException( final String message, final Throwable throwable
)
{
this( null, message, throwable );
}
/**
* Construct a new <code>AssemblyRuntimeException</code> instance.
*
* @param errors a list of warning messages related to the exception.
* @param message The detail message for this exception.
* @param throwable the root cause of the exception
*/
public AssemblyRuntimeException( final Dictionary errors, final String message,
final Throwable throwable )
{
super( message, throwable );
if( errors != null )
{
m_errors = errors;
}
else
{
m_errors = EMPTY_TABLE;
}
}
/**
* Return the table of supplimentary messages.
* @return the messages table
*/
public Dictionary getDictionary()
{
return m_errors;
}
/**
* Returns a stringified representation of the exception.
* @return the exception as a string.
*/
public String toString()
{
StringBuffer buffer = new StringBuffer();
buffer.append( super.toString() );
buffer.append( " Errors: " + m_errors.size() );
Enumeration keys = m_errors.keys();
while( keys.hasMoreElements() )
{
Object key = keys.nextElement();
buffer.append( "\n source: " + key.toString() + " cause: " +
m_errors.get( key ) );
}
return buffer.toString();
}
}
1.1
jakarta-avalon-excalibur/assembly/src/java/org/apache/excalibur/merlin/container/AssemblyException.java
Index: AssemblyException.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.excalibur.merlin.container;
import java.util.Enumeration;
import java.util.Dictionary;
import java.util.Hashtable;
import org.apache.avalon.framework.CascadingException;
/**
* Exception to indicate that there was an error Assembling a component model.
*
* @author <a href="mailto:[EMAIL PROTECTED]">Stephen McConnell</a>
* @version $Revision: 1.1 $ $Date: 2002/07/12 10:17:20 $
*/
public final class AssemblyException
extends CascadingException
{
private static final Hashtable EMPTY_TABLE = new Hashtable();
private Dictionary m_errors;
/**
* Construct a new <code>AssemblyException</code> instance.
*
* @param message The detail message for this exception.
*/
public AssemblyException( final String message )
{
this( null, message, null );
}
/**
* Construct a new <code>AssemblyRuntimeException</code> instance.
*
* @param errors a list of warning messages related to the exception.
* @param message The detail message for this exception.
*/
public AssemblyException( final Dictionary errors, final String message )
{
this( errors, message, null );
}
/**
* Construct a new <code>AssemblyException</code> instance.
*
* @param message The detail message for this exception.
* @param throwable the root cause of the exception
*/
public AssemblyException( final String message, final Throwable throwable )
{
this( null, message, throwable );
}
/**
* Construct a new <code>AssemblyException</code> instance.
*
* @param errors a list of warning messages related to the exception.
* @param message The detail message for this exception.
* @param throwable the root cause of the exception
*/
public AssemblyException( final Dictionary errors, final String message,
final Throwable throwable )
{
super( message, throwable );
if( errors != null )
{
m_errors = errors;
}
else
{
m_errors = EMPTY_TABLE;
}
}
/**
* Return the table of supplimentary messages.
* @return the messages table
*/
public Dictionary getDictionary()
{
return m_errors;
}
/**
* Returns a stringified representation of the exception.
* @return the exception as a string.
*/
public String toString()
{
StringBuffer buffer = new StringBuffer();
buffer.append( super.toString() );
buffer.append( " Errors: " + m_errors.size() );
Enumeration keys = m_errors.keys();
while( keys.hasMoreElements() )
{
Object key = keys.nextElement();
buffer.append( "\n source: " + key.toString() + " cause: " +
m_errors.get( key ) );
}
return buffer.toString();
}
}
--
To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>