link
Avalon
Merlin Runtime
Home PlanetProductsCentral
Avalon Repository - Example

The following example is taken from the the Merlin project. It demonstrates the creation of an embedded logging service using the Repository Facility builder, factory and criteria handling patterns.

Creating an InitialContext

The following code fragment defines a cache directory for the repository system to use when loading resources needed in your embedded application - and provides the directory as an argument when creating a new InitialContext object. The InitialContext is your hook into the repository system and the embedding machinery.

File cache = new File( "my-cache" );
InitialContext context = new DefaultInitialContext( cache );
          
Declare what you want to embed
String spec =
  "artifact:avalon-logging/avalon-logkit?version=1.0.0"
Artifact artifact =
  Artifact.createArtifact( spec );
          

An artifact is a logical reference to a jar file (or other type of resource) that the repository can access. The avalon-repository system uses artifact references as the key to locating meta data about embedded classloaders. The classloader meta data is maintained as a properties file with the .meta extension. For example the above artifact meta address translates to:

[host]/avalon-logging/jars/avalon-logkit-impl-1.0-SNAPSHOT.jar.meta 
          

The content of the meta file is automatically generated using the avalon-plugin artifact:install goal. Two examples of factory meta are provides, one concerning the logkit factory and a second describing the merlin factory.

The contents of the meta file includes:

  • an ordered list of jar files that are required to construct a classloader for the embedded application
  • the name of a factory class to be used as the embedded instance creator
Create the factory

Using the initial context and the artifact you now have everything you need to create you embedded instance.

Builder builder = m_context.newBuilder( artifact );
Factory factory = builder.getFactory();
          

Behind the scenes the avalon-repository system has gone off, pulled down the meta data, downloaded and cached all of the classloader entries, constructed a new classloader, and instantiated the factory.

Parameterizing the factory

The factory object is the central entry point of the embedded application - it is responsible for instantiation of the embedded instance based on a supplied criteria. The initial criteria (the defaults) are established by the factory in response to the following operation:

Map criteria = factory.createDefaultCriteria();
          

Based on the documentation about the facility you are embedding you can update the criteria using application specific keys. All of the Merlin related criteria instances use the avalon-util Criteria as the abstract base class for the map implementations. This provides support for key and type validation under the put operations and type coercion on get operations.

For example:

String key = "avalon.logging.configuration";
File file = new File( "logging.xml" );
criteria.put( key, file );
          

Parameterization of the criteria is typically different for each embedding scenario. A CLI handler will for example adapt to the command line operations and set criteria values accordingly. A web application may set the criteria based on parameters declared in a web.xml file. Typically the embedding class acts as the adapter between the embedded context and the factory.

Embedded instance creation

Creation of the embedded instance is now a simple one line operation:

Object object = factory.create( criteria );
          

The object that is created is the embedded application. In this example its a logging manager that uses the logkit implementation. However, it could have been the Merlin kernel. The only difference between the Merlin scenario and the logging manager scenario is the initial artifact and the actions taken to parameterize the criteria.

Putting it all together
//
// create the initial referecne
//

File cache = new File( "my-cache" );
InitialContext context = new DefaultInitialContext( cache );

// 
// define the artifact 
//

String spec =
  "artifact:avalon-logging/avalon-logkit?version=1.0-SNAPSHOT"
Artifact artifact =
  Artifact.createArtifact( spec );

//
// create the builder, get the factory and the initial criteria
//

Builder builder = m_context.newBuilder( artifact );
Factory factory = builder.getFactory();
Map criteria = factory.createDefaultCriteria();

//
// customize the crieria
//

String key = "avalon.logging.configuration";
File file = new File( "logging.xml" );
criteria.put( key, file );

//
// create the embedded instance
//

LoggingManager manager = 
  (LoggingManager) factory.create( criteria );

          
Special Notes

The avalon-repository API provides special support for initial context propergation to the factory that is being created. The allows the factory to embed additional applications within itself using the classloader it is constructed under, together with the original initial context.

The following code snipet demonstrates a factory that caches references to the classloader and initial context so that it can use these is subsequent embedding of services during its own create method implementation.

public class WidgetFactory implements Factory
{
    private final InitialContext m_context;
    private final ClassLoader m_classloader;

    public WidgetFactory( 
      InitialContext context, ClassLoader classloader )
    {
        m_context = context;
        m_classloader = classloader;
    }

    public Map getDefaultCriteria()
    {
        return new WidgetContext();
    }

    public Object create()
    {
        return create( getDefaultCriteria() );
    }

    public Object create( Map criteria )
    {
        //
        // embed something into this factory using 
        // the classloader that established this factory 
        // as the parent classloader of the thing we are
        // embedding
        //

        Artifact artifact = 
          (Artifact) criteria.get( "gizmo.artifact" );
        Builder builder = m_context.newBuilder( m_classloader, gizmo );
        Gizmo gizmo = builder.getFactory().create();

        //
        // do other stuff
        //

        return new DefaultWidget( gizmo );
    }
}