Context Driven Development
Working with Context
Tutorial Objective

The purpose of this tutorial is to introduce the Metro Context model and the contract this represents between a component and its container.

Supporting classes:

Demo.java A component implementation incorporating a Context inner interface.
Asserting Operational Requirements

Components are typically established and deployed dynamically by a controlling system. To do this the controlling system needs to know about the operational requirements of the component and delivery mechanism. Within the Metro component model both the declaration of requirement and delivery mechanism are described using a Context interface referenced as a parameter within the component constructor.

For example ...

package org.acme;

import java.util.logging.Logger;
import java.util.logging.Level;

public class Demo
    // context
    public interface Context
        String getActivity();
        String getOwner();
        String getTarget();
        String getColor();
    // state
    private final Logger m_logger;
    private final Context m_context;

    // constructor
    * Creation of a new object using a supplied logging channel.
    * @param logger the logging channel
    * @param context the deployment context
    public Demo( final Logger logger, final Context context )
        m_logger = logger;
        m_context = context;
    public String getMessage()
        final String owner = m_context.getOwner();
        final String activity = m_context.getActivity();
        final String target = m_context.getTarget();
        final String color = m_context.getColor();
        final String message = 
            + " " 
            + owner 
            + "'s " 
            + target 
            + " " 
            + color 
            + ".";
        return message;

The Context interface is a component driven contract. The information disclosed in the interface can be analysed by a container. The container is any system acting in the role of component manager (which could be a dedicated container solution or a simple testcase).

Context Interface Semantics

Looking closer at the Context interface declared by the Demo class we have an inner class declaration:

public interface Context
    String getActivity();
    String getOwner();
    String getTarget();
    String getColor();

This contract is stating that an implementation of the Context interface must supply values for the features activity, owner, and target, and color - each of which (in this example) are instances of java.lang.String.

Noteable points:

  1. no exceptions in method declaration
  2. dependency type is the method return type (and any return type including primitives, arrays, custom interfaces and classes are valid return type)
  3. the dependency key is based on the method name applying the bean pattern get[key]
  4. default values may be declared as a single method argument
  5. if a default value is declared it must be assignable to the method return type
Building the deployment solution

The context inner interface is declaring the operation requirements - however, we need to declare somewhere the solution strategy. We do this by updating our component definition to include a <context> block.

The following lines are example of the creation of a component deployment solution. Markup up of the XML is used to show changes from our initial component definition.

<component xmlns="dpml:metro" class="org.acme.Demo" name="demo">
    <entry key="owner" value="${user.name}"/>
    <entry key="activity" value="Painting"/>
    <entry key="target" value="bike"/>
    <entry key="color" value="silver"/>
Testing the component using a Strategy handler

During execution of the testcases we see the result of logging the a message reflecting configuration and runtime information. In the DemoTestCase the output is shows the result of concatination of values resolved from the context implementation supplied by the container to the component instance.

    [junit] Executing forked test.
    [junit] Running org.acme.test.DemoTestCase
    [junit] [16245] [INFO   ] (demo): Painting mcconnell's bike silver.
    [junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0.89 sec
Testing with a Mock Context

An alternative to the above is to use a mock context object as demonstrated in the MockTestCase.

    [junit] Running org.acme.test.MockTestCase
    [junit] [17497] [INFO   ] (test): Painting mcconnell's house red.
    [junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0.203 sec
Testing via Context Proxy

Another solution is to use a proxy invocation handler as the context implementation. This approach is demonstrated in the in the ProxyTestCase.

    [junit] [20148] [INFO   ] (test): Painting mcconnell's GTV white.
    [junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0.204 sec
Summary ...

The container will make best efforts to assemble a viable deployment scenario. If that scenario cannot be established then your component will not be deployed. During deployment the container will evaluate all runtime requirements declaring within the context interface, build an context implementation fullfilling the contract (and any other recognized contructor arguments) and deploy the component taking into account lifecyle, lifestyle and collection policies (but more of these subject later in the tutorial).

The next tutorial provides a demonstration of the context contract within which we use primitive and complex types together with examples of optional context entries.