A container can declare services that it exports. In such a case the container declares one or more service directives that map components declared within itself as service providers. This mechanism enables a container to appear as a regular component relative to other components.
Content supporting this tutorial is available under the tutorials/composition directory.
The following container definition includes:
an application component
a location provider block included by reference
an application block included by reference
The application block declares dependencies on a location and publishing service. The location and publishing block declare that they provide these respective services. As a part of the general component assembly process - Merlin will assemble blocks relative to the services and dependencies they declare. When a block is deployed, it appears to other components as a component providing the services it exports. However in practice these services are provided by components defined within the container (i.e. the container represents the definition of a virtual component and the containers components and subcontainers represent the virtual component implementation).
The top level application block declaration (block.xml):
<container name="application"> <classloader> <classpath> <repository> <resource id="avalon-framework:avalon-framework-impl" version="4.1.5"/> <resource id="merlin-tutorial:locator-api" version="1.0"/> <resource id="merlin-tutorial:publisher-api" version="1.0"/> <resource id="merlin-tutorial:application-impl" version="1.0"/> </repository> </classpath> </classloader> <!-- note: each of the following include directives is equivalent to a nested container declaration - using include or declaraing a sub-container is semantically equivalent --> <include name="location" id="merlin-tutorial:locator-impl" version="1.0"/> <include name="publisher" id="merlin-tutorial:publisher-impl" version="1.0"/> <component name="application" class="tutorial.application.Application"> </component> </container>
If we look at the location block (as an example) we will see a corresponding service declaration. This declaration includes a source reference that is a relative component path that tells Merlin to use the sub-component named "location" as the component that will fulfil the service published by this block.
<container name="locator"> <services> <service type="tutorial.location.LocationService"> <source>info</source> </service> </services> <classloader> <classpath> <repository> <resource id="avalon-framework:avalon-framework-api" version="SNAPSHOT"/> <resource id="avalon-framework:avalon-framework-impl" version="SNAPSHOT"/> <resource id="merlin-tutorial:locator-api" version="1.0"/> <resource id="merlin-tutorial:locator-impl" version="1.0"/> </repository> </classpath> </classloader> <component name="info" class="tutorial.location.LocationComponent"> <configuration> <source>Paris</source> </configuration> </component> </container>
To run build and run the example please use the following commands:
$ cd application/impl $ maven build $ merlin target\*.jar -repository %MAVEN_HOME% -execute
The log output demonstrates the deployment by Merlin of the blocks in the correct order (based on dependency resolution) and the execution of the test application.
[INFO ] (application.publisher.publisher): created [INFO ] (application.location.info): location: Paris [INFO ] (application.application): servicing application [INFO ] (application.location.info): location: Paris [INFO ] (application.publisher.publisher): created [INFO ] (application.publisher.publisher): ****************** * Paris ****************** [INFO ] (application.application): done
The primary benefit of using block level services and dependencies is isolation of a block implementation from the public services it provides.