In classic Metro we use the composition api as the framework for dynamic
management. In Metro FT this is replaced by a Parts
inner interface
and another set of method patterns supporting instance management, deployment and
release.
The following source code is from the class ExampleContainer. It is basically the same as ExampleComponent except that we have included the Dimension as a part. As a part the Dimension context is accessible to the containing component (ExampleContainer) and we can get in there are do things based on current state.
package net.dpml.composition.testing; import java.util.logging.Logger; public class ExampleContainer implements Example { private final Logger m_logger; private final Context m_context; private final Parts m_parts; public ExampleContainer( final Logger logger, final Context context, final Parts parts ) { m_logger = logger; m_context = context; m_parts = parts; } public void doMyStuff() { // // configure the dimension component's context map // Map map = m_parts.getDimensionContextMap(); int width = m_context.getWidth( 9 ); int height = m_context.getHeight( 7 ); map.put( "height", new Integer( height ) ); map.put( "width", new Integer( width ) ); // // get the dimension instance // Dimension dimension = m_parts.getDimension(); int size = dimension.getSize(); m_logger.info( "Creating a widget with a area of " + size ); } public interface Context extends DimensionalContext{} public interface Parts { Map getDimensionContextMap(); Dimension getDimension(); } }
The parts of component are distinct from context in that parts are exclusivly managed by the containing component. In the above example the Dimension component model is declared in XML and the component is reaching into the context model of the Dimension component and modifying height and width values based on it's own context information. After modification of the context the component acquires an instance of Dimension and proceeds with service execution.
The formal patterns associated with a Parts inner interface deal with default and identifiable instances.
Methods dealing with the default instance include:
[type] get[key](); // return the default service instance [manager] get[key]ContextManager(); // return the context manager for the default instance Map get[key]ContextMap(); // return the context map for the default instance Component get[key]Component() // return a model of a Component instance Model get[key]Model() // return the Model of a component type
Methods dealing with the identifiable instance include:
[type] get[key]( [id] ); // return an identified service instance [type] get[key]( [id], [policy] ); // same as above with control over proxy creation [manager] get[key]ContextManager( [id] ); // return the context manager for the identified instance Map get[key]ContextMap( [id] ); // return the context map for the identified instance Component get[key]Component( [id] ) // return a model of an identified Component instance Model get[key]Model( [id] ) // return the Model of an identified component type
In addition to the above the following optional release method signature is supported:
void release[key]( [instance] ) // release of a proxy or implementation instance
In the above examples we are interacting with the context model of the
Dimension component via the java.util.Map
interface. As suggested
by the [manager] get[key]ContextManager( [id] );
a type safe
management strategy is also provided as demonstrated in the following code
fragment:
public void doMyStuff() { DimensionalContext.Manager manager = m_parts.getDimensionContextManager(); manager.setHeight( height ); manager.setWidth( width ); Dimension dimension = m_parts.getDimension(); int size = dimension.getSize(); m_logger.info( "Creating a widget with a area of " + size ); } public interface Parts { DimensionalContext.Manager getDimensionContextManager(); Dimension getDimension(); }
Where DimensionContext and the associated inner Manager are defined as:
public interface DimensionalContext { int getHeight( int h ); int getWidth( int w ); interface Manager extends DimensionalContext { void setWidth( int width ); void setHeight( int height ); } }
In effect the Parts interface and its associated patterns provide a framework for management of internal parts with minimal API intrusion (significantly reducing the OS risk factor).
The following part descriptor demonstrates the creation of a part
containing another part within itself. Nested parts are declared within
the <parts>
element. Support for custom part handlers
is provided at the build level, allowing for example the inclusion of a
completely foreign part as a intrinsic part of the containing part. This
feature will enable 'radical' development of the metro platform while
retaining runtime compatibility with prior releases.
The part descriptor:
<component dest="target/test/acme-example-three.part" xmlns="plugin:dpml/composition/dpml-composition-builder" type="net.dpml.composition.testing.ExampleContainer" name="demo"> <parts> <component key="dimension" type="net.dpml.composition.testing.DimensionComponent"/> </parts> </component>
In addition to the classic declaration of parts - support has been included
for parts-by-reference. The following <component>
delcaration
has a <parts>
element that is using the <part>
element to include a foreign part by reference to a Transit artifact uri.
<x:property key="dpml-composition-testing-acme" feature="uri" type="part" name="acme.uri"/> <component dest="target/test/acme-example-four.part" xmlns="plugin:dpml/composition/dpml-composition-builder" type="net.dpml.composition.testing.ExampleContainer" name="demo"> <parts> <part key="dimension" uri="${acme.uri}"/> </parts> </component>
While the current development effort is looking promising there are number of issues still to addressed. These topics are covered in the following section.