Sie sind auf Seite 1von 34

The OpenCOMJ

Handbook

Lancaster University
February 2007

1
Table of Contents
1. Introduction ....................................................................................................... 4
1.1 A brief description of OpenCOM ............................................................... 4
1.2 Component Frameworks in OpenCOMJ..................................................... 5
1.2.1 Overview ........................................................................................... 5
1.2.2 The Component Framework as a composite component ..................... 6
1.2.3 Integrity Maintenance of Component Frameworks ............................. 6
1.2.4 The Component Framework Lock ...................................................... 7
1.2.5 Document Structure............................................................................ 7
2. The OpenCOM component Specification in Java ............................................... 8
3. The OpenCOMJ tutorial .................................................................................... 9
3.1 Installing OpenCOMJ ................................................................................ 9
3.1.1 Requirements ..................................................................................... 9
3.1.2 Installing ANT .................................................................................... 9
3.1.3 Installing the OpenCOM source ......................................................... 9
3.2 Developing a Basic OpenCOM component .............................................. 11
3.3 Creating an OpenCOM Component with Receptacles .............................. 13
3.4 Creating Systems and Applications Using OpenCOM Components.......... 15
3.4.1 Creating the Runtime Kernel & QueryInterface ................................ 15
3.4.2 Creating a New Component Instance ................................................ 16
3.4.3 Connecting two components............................................................. 17
3.4.4 Disconnecting Two Components ...................................................... 17
3.4.5 Deleting a component....................................................................... 17
3.5 Performing Reflective Operations ............................................................ 19
3.5.1 Architecture Meta Model Operations................................................ 19
3.5.2 Interface Meta Model Operations ..................................................... 20
3.5.3 The Interception Meta Model ........................................................... 23
3.6 Alternative Receptacle Styles ................................................................... 26
3.6.1 A Parallel Receptacle ....................................................................... 26
3.6.2 Sequential Receptacles ..................................................................... 28
3.6.3 Context Receptacles ......................................................................... 29
3.7 Developing Component Frameworks in OpenCOMJ ................................ 30
3.7.1 Creating a new Component Framework ............................................ 30
3.7.2 Reflection Operations on Component Frameworks ........................... 31
3.7.3 Using the ICFMetaInterface ............................................................. 32

2
Table of Figures
Figure 1.1 Overview of the basic OpenCOM kernel .................................................. 4
Figure 1.2 An OpenCOM component framework. ..................................................... 6
Figure 1.3 The IAccept Interface ............................................................................... 7
Figure 3.1 An example OpenCOMJ provides interface ............................................ 11
Figure 3.2 A simple OpenCOM component ............................................................. 11
Figure 3.3 Example ILifeCycle Interface implementation ........................................ 12
Figure 3.4 Implementation of the IAdd (provides) interface ..................................... 12
Figure 3.5 Adding IConnections to the component class .......................................... 13
Figure 3.6 The calculator component with the receptacles defined ........................... 14
Figure 3.7 IConnections Interface Implementation .................................................. 14
Figure 3.8 Using a Single Receptacle ...................................................................... 15
Figure 3.9 Creating the kernel instance .................................................................... 15
Table 3.1 The key operations in IOpenCOM ........................................................... 16
Figure 3.10 Creating and starting a component ........................................................ 16
Figure 3.11 Connecting two components ................................................................. 17
Figure 3.12 Disconnecting two components ............................................................ 17
Figure 3.13 Deleting a component instance ............................................................. 18
Table 3.2 Data fields in the connection information meta-data ................................. 19
Figure 3.14 Example use of enumConnsToInterface ................................................ 19
Figure 3.15 Example use of enumConnsFromRecp ................................................. 20
Figure 3.16 Example of how to enumerate the interfaces of a component ................ 21
Figure 3.17 Example of how to view the receptacles of a component....................... 21
Figure 3.18 Adding meta data to the IAdd interface of the Adder component .......... 22
Figure 3.19 Retrieving meta-data from the IAdd interface ....................................... 22
Figure 3.20 Example pre method interceptor ........................................................... 23
Figure 3.21 Example post method interceptor .......................................................... 24
Figure 3.22 Adding a pre method ............................................................................ 25
Table 3.3. Meta Operations available from each delegated interface ........................ 25
Figure 3.23 Connecting multiple components to a receptacle ................................... 26
Figure 3.24 Example method that utilises a context receptacle. ................................ 29
Figure 3.25 Implementing a new component framework.......................................... 31
Table 3.4 Operations for inspection of the internal CF structure .............................. 31
Table 3.5 Operations for dynamic reconfiguration ................................................... 32

3
1. Introduction
This documents contains all the information about the OpenCOM version 1
implementation in Java (from now on termed OpenCOMJ). That is, the
implementation of the component specification defined in the following paper by
researchers at Lancaster University: “Clark, M., Blair, G.S., Coulson, G.,
Parlavantzas, N., “An Efficient Component Model for the Construction of Adaptive
Middleware”, Proc. IFIP Middleware 2001, Heidelberg, Germany, November 2001”.

1.1 A brief description of OpenCOM


OpenCOM is a lightweight, efficient and reflective component model. The
fundamental concepts of OpenCOM are interfaces, receptacles and connections
(bindings between interface and receptacles).

An interface expresses a unit of service provision.


A receptacle describes a unit of service requirement.

OpenCOM deploys a standard runtime kernel (illustrated in figure 1.1) that manages
the creation and deletion of components, and acts upon requests to connect and
disconnect components. Furthermore, a system graph of the components currently in
use is maintained to support the introspection of a platform’s structure

IUnknown

IMetaInterface Custom
IConnections receptacles
OpenCOM
ILifeCycle Component

MetaInterface
Custom interface

IUnknown

IMetaArchitecture System Graph

IMetaInterception
IOpenCOM OpenCOM
Runtime
Kernel

Figure 1.1 Overview of the basic OpenCOM kernel

4
Every OpenCOM component now implements four interfaces:
• ILifeCycle provides operations called startup and shutdown that are called
when a component is created or destroyed.
• IConnections (OPTIONAL) offers methods to modify the interfaces
connected to a component’s receptacles.
• IMetaInterface supports inspection of the types of interfaces and receptacles
declared by the component.
• IUnknown is equivalent to the interface of the same name in Microsoft COM;
that is, it is used to get the reference to the requested interface on that
component instance.

In addition, the OpenCOM kernel provides an interception meta model


(IMetaInterception) and an architecture meta model (IMetaArchitecture). Interception
enables pre and post method behaviour to be added to a given interface of a
component; these are then invoked before and after every method invocation. The
architecture meta model supports inspection of the underlying component architecture
i.e. information about connections made between components.

OpenCOM is typically used to construct families of middleware, and other systems


software. More specifically, each middleware is constructed as a set of (configurable)
component frameworks and reflection is used to discover the current structure and
behaviour, and to enable selected changes at run-time. The end result is flexible
middleware that can be specialised to domains including multimedia and real-time
systems or in our case mobile computing. More detail on the component framework
concept is provided in section 1.2.

1.2 Component Frameworks in OpenCOMJ

1.2.1 Overview
OpenCOM provides no kernel-level support for creating composite components (the
key constituent of a component framework); only ad-hoc “architectures” of
connected, primitive components can be created per OpenCOM runtime instance.
Rather than a disadvantage, this allows specialised component frameworks per
application domain to be developed atop. Furthermore, OpenCOM’s reflective
capabilities are limited to simple operations; therefore code to dynamically view and
make elaborate changes to the system graph is repetitive and difficult to program.
Therefore, this section documents a general component framework model, which aims
to simplify the process of developing and maintaining architectures of components.

The design of this component framework model is based upon the concept of
composite components and promotes the following key properties:

1. A component framework in OpenCOM is a composite OpenCOM component.


2. A component framework provides an additional meta-interface for inspection
and dynamic adaptation of the local architecture of the composite component.
3. The integrity of each component framework is maintained in the face of
dynamic change, using developer specified architectural rules plugged into the
component framework.

5
Therefore, component frameworks in OpenCOM are implemented as OpenCOM
components; that is, they implement the same base interfaces (IMetaInterface,
ILifeCycle and IConnections), custom service interfaces and receptacles. However, a
CF contains its own internal structure (a configuration of components) that
implements its functional capabilities, together with an additional meta-interface
(ICFMetaArchitecture) to manipulate it. The diagram in figure 1.2 demonstrates the
general architecture of a CF. The benefit of this design is that an OpenCOM CF can
be treated as an OpenCOM component, simplifying the composition of hierarchical
architectures and promoting re-use.
ILifeCycle
IMetaInterface IConnections

OpenCOM component ICFMetaArchitecture


framework
CF Service Interfaces
(Can be exposed
interfaces of internal CF receptacles
components) (Can be exposed)

Lock
Interceptor
Graph of internal
IAccept components

Figure 1.2 An OpenCOM component framework.

1.2.2 The Component Framework as a composite component


To be subject to introspection and dynamic reconfiguration, each component
framework maintains a local graph of its internal structure. To reduce data
duplication, this is simply a view of the information held in the OpenCOM system
graph. Therefore, each CF maintains a list of Component Identifiers that point to their
corresponding position in the system graph.

This local graph can be used for integrity checking of the framework after each
reconfiguration, by ensuring that it meets the criteria for the particular domain of
concern. Given that integrity checks apply per component framework the following
rule for component composition must be followed: “No individual component
instance may exist in more than one component framework instance”; if a
component were to be changed in one framework it would also be changed in the
other. The second framework has no knowledge of the change; hence its integrity
would be compromised. However, the component framework model allows
composition of component frameworks; therefore, hierarchical component structures
can be created to resolve dependencies of this type.

1.2.3 Integrity Maintenance of Component Frameworks


A component framework must constrain the configuration of components to a valid
implementation within its domain. Therefore, after a CF is configured or reconfigured
it must be checked to ensure that it provides the correct functionality. To do this, each

6
component framework provides a receptacle named IAccept into which developers
can plug their own checking implementation. Figure 1.3 illustrates the interface of this
receptacle; this consists of a single operation that takes as parameters the local graph
of the component framework and the list of interfaces that expose the structure’s
functionality. When executed it returns a Boolean value to indicate if the structure is
valid; if true, the component framework continues its operation. Otherwise, if false,
the component framework rolls back to the previous known good configuration
(stored prior to the change) and generates a message to indicate a failed
reconfiguration. The complexity of checking depends upon the implementation of the
Accept component, which can be dynamically changed by plugging in a new
component.

/**
* This method performs validation checks on CF graphs.
* @param graph A Vector containing the graph of the composite component framework to check
* @param Intfs A Vector describing the list of interfaces exposed by the component framework
* @param cComps An integer representing the number of components in the graph
* @param cIntfs An integer describing the number of exposed interfaces
* @return A boolean indicating whether the CF contains a valid or invalid configuration
**/
boolean isValid(Vector<IUnknown> graph, Vector<CFMetaInterface.ExposedInterface> Intfs,
int cComps, int cIntfs);
Figure 1.3 The IAccept Interface

1.2.4 The Component Framework Lock


The previous section demonstrates how the structure of a component framework is
checked for validity. However, it does not ensure that reconfigurations are made at an
appropriate time. If a change to the configuration was made while one or more service
calls of the component framework were executing, then the results of these
invocations could be compromised or lost. Therefore, each component framework
provides a readers/writers lock to access the local CF graph. Each service call through
any of the interfaces other than ICFMetaInterface accesses the lock as a reader (there
can be n readers using the lock at any time). Any call to change the configuration of
the CF, accesses the lock as a writer (a single writer can access the lock when there
are no readers). The algorithm to implement this property is a standard readers/writers
solution with priority for readers.

Interceptors are used to ensure that all exposed configurations access the lock as a
reader before a service call is executed. Each interface exposed by a CF automatically
has an interceptor attached with pre and post method behaviour to implement the
reader role of a readers/writers solution. That is, the pre method accesses the lock and
increments the readers count, while the post method decrements the count and if it is
the last reader the lock is released for writers.

1.2.5 Document Structure


There are three main sections to the documentation: 1) we define the component
specification related to the Java language; 2) a tutorial about how to program
OpenCOMJ based systems; and finally, 3) information about the technical
implementation of the component kernel.

7
2. The OpenCOM component Specification in Java
Here we describe how the information describing OpenCOM is applied in the Java
programming language. Hence, the following set of statements defines the component
specification of OpenCOMJ.

o Each component is made up from a set of Java classes.

o One class from this set defines the component type; we refer to this as the
foundation OpenCOMJ class. For example, a component of type A includes
a foundation class named A.

o The foundation class must implement a set of OpenCOM specific


interfaces:
 IMetaInterface
 ILifeCycle
 IUnknown

o The component must be third party deployable; hence each component


must be available within a JAR; a single JAR can contain one or more
OpenCOMJ components.

o Each OpenCOMJ component lists one or more provides interfaces. Each


interface must be an OpenCOM interface, which inherits the IUnknown
interface.
This is opposed to a Java interface (although Java interfaces can still be
used in component implementation classes they cannot be used to connect components
together, or be queried by IUnknown).

o An OpenCOMJ component optionally lists a set of required interfaces. This


consists as a set of receptacles (that must inherit from the OCM_Receptacle
class). Each receptacle is a publicly accessible field of the foundation class.

o Apart from receptacles, no java field must be publicly accessible from


classes external to the component classes.

o A composite component or component framework must implement the


ICFMetaInterface interface, and define a single receptacle of type IAccept.

8
3. The OpenCOMJ tutorial
In this chapter we describe how to install and use the OpenCOMJ component kernel,
and also how to program new OpenCOMJ systems.

3.1 Installing OpenCOMJ

3.1.1 Requirements

To use OpenCOMJ you require the following:

 A Java 2 Platform; we recommend 1.5 and later.


 The ant toolkit for building java applications; we recommend 1.6 and greater.
 The OpenCOMJ download (available at http://gridkit.sourceforge.net)

3.1.2 Installing ANT

You can download ANT from: http://ant.apache.org/. To install ANT you must set
the “ANT_HOME” environment variable to point to the root of the ANT installation
e.g. c:\program files\ant-1.6.1 on Windows. You must also add the ant bin directory
to the system path.

Installing more than one version of ANT on your machine can cause conflicts to arise.

3.1.3 Installing the OpenCOM source

Download the latest version of the OpenCOM package and extract it to your chosen
location. This location is the OPENCOMROOT. You must then create 2 new
environment variables: OpenCOM and JRE. The OpenCOM variable points to your
OPENCOMROOT. The JRE variable points to the location of your Java Runtime
Environment; typically this is located in a folder by the name jre1.xx.xx e.g.
“C:\Program Files\Java\jre1.5.0_04” in Windows.

Windows:
set OpenCOM=OPENCOMROOT
(e.g. set OpenCOM=c:\OpenCOMJ; if extracted to root directory)
set JRE= JAVA_RUNTIME_ENVIRONMENT_ROOT
(e.g. set JRE= C:\Program Files\Java\jre1.5.0_04;)

To install OpenCOM in full type at OPENCOMROOT:

C:\OpenCOMJ> ant register

9
This will create a set of JAR files and install them in Java’s extension loader. In
OpenCOM each component is a third party deployable entity represented by a JAR
file. Hence, they can be loaded dynamically on demand.

You can also treat OpenCOM as a normal Java library, which consists of a set
of java classes. To install in this fashion:

C:\OpenCOMJ> ant compile

You can then use the OpenCOM library by pointing the java classpath to
include the ../classes folder that is created; e.g.

set classpath = %classpath%;c:\opencomj\classes;

10
3.2 Developing a Basic OpenCOM component

We now describe in a step-by-step fashion, how to implement OpenCOM components


in Java. First, we look at a basic OpenCOM without receptacles (i.e. no required
interfaces).

Step 1 – Define your provides interfaces

For each component interface you wish to provide from the component, create an
OpenCOM interface class. The type of the interface is equivalent to the java interface
class therefore a class of type A corresponds to an interface type of A, and so on. Here
we show an example of creating an interface of type Samples.AdderComponent IAdd
in figure 3.1. It is important to identify that this is a Java interface, but it extends
IUnknown to identify it as an OpenCOM interface.

package Samples.AdderComponent;
import OpenCOM.*;
public interface IAdd extends IUnknown{
/**
* Add two integers together.
* @param x Operand X.
* @param y Operand Y.
* @return The added values.
*/
public int add(int x, int y);
}
Figure 3.1 An example OpenCOMJ provides interface

Hence: To create an OpenCOMJ interface you create a Java interface class that
extends the IUnknown interface from OpenCOM.

Step 2 – Create the foundation class

Next, create a new Java class with the same name as the type of component you wish
to create. In this instance we are creating a component of type “Adder”; therefore, we
create a Java class named Adder. Figure 3.2 illustrates the template outline of an
example OpenCOMJ foundation class.

package Samples.AdderComponent;
import OpenCOM.*;
public class Adder extends OpenCOMComponent implements IAdd,
IUnknown, IMetaInterface, ILifeCycle {

/** Creates a new instance of Adder */


public Adder (IUnknown pRuntime) {
super(pRuntime);
}
...
}
Figure 3.2 A simple OpenCOM component
There are four important points in creating the OpenCOM component:

11
 The foundation class extends the OpenCOMComponent abstract class.
 The foundation class constructor must have the following syntax:
classname(IUnknown) otherwise the component cannot be created.
 You must call the super() method (passing the constructor’s parameter) of the
abstract class to initialise the component.
 The foundation class must implement three interfaces: IUnknown,
IMetaInterface, and ILifeCycle

Step 3 – Implementing ILifeCycle

Of the three interfaces, you only need to write operation implementations for
ILifeCycle, as the others are implemented within the abstract class. This is because
you place the application specific code you want to execute when the component is
started (not constructed, these are deliberately separate), and when it is stopped.
Hence, you can create components that are executable over time. Figure 3.3 shows an
example implementation of the ILifeCycle interface

// ILifeCycle Interface Implementation


public boolean shutdown() {
System.out.println(“Stopping Component”);
return true;
}
public boolean startup(Object pIOCM) {
System.out.println(“Starting Component”);
return true;
}
Figure 3.3 Example ILifeCycle Interface implementation

Step 4 – Implement the provides interfaces

The final step is to implement the methods of each of the provides interface. In the
case of the example Adder component there is one interface to implement (IAdd).
Figure 3.4 shows the example implemented interface.

/**
* Add two integers together.
* @param a Operand X.
* @param b Operand Y.
* @return The added values.
*/
public int add(int a, int b) {
return a+b;
}
Figure 3.4 Implementation of the IAdd (provides) interface

We can now create simple components; the next step is to examine how to create
components that can be connected to other components using receptacles.

12
3.3 Creating an OpenCOM Component with Receptacles

We now see how to add receptacles to a component implementation in a step by step


fashion. Through this section we will use the example of a calculator component
connected to the adder component’s IAdd interface.

Step 1 – Defining the receptacle interface


For each component interface you require to bind to another component, you must
implement the corresponding OpenCOM interface class. Although this will typically
already have been implemented in creating the provides interface. For, example our
component will have one receptacle of type Samples.AdderComponent IAdd so we do
not need to implement the interface, we use the one from figure 3.1.

Step 2 – Adding the IConnections interface to the component implementation

When there are receptacles to be connected a component must implement the


OpenCOM IConnections interface in addition to the other core OpenCOM interfaces.
Hence, we add it to the implements list of the foundation class. We can see this in the
sample calculator component in figure 3.5 below.

public class Calculator extends OpenCOMComponent implements


ICalculator, IConnections, ILifeCycle, IUnknown,
IMetaInterface {

Figure 3.5 Adding IConnections to the component class

Step 3 – Creating the receptacles

The next step is to define your receptacles in the foundation class. To do this you list
the receptacles as public fields of the foundation class. There are different types of
receptacle (see later) each with a separate class type and constructor; at this stage we
will concentrate on the simplest receptacle, known as the single receptacle. This
allows you to connect the receptacle to one interface instance only.

To define a single receptacle you create a field with the following syntax:

• public OCM_SingleReceptacle<IAdd> m_PSR_IAdd;

Notably, this uses Java generics to define the interface type of the receptacle (placed
in the <> angled brackets of the class). Hence, the value in the brackets must
correspond directly with the class type of the previously defined interface.

The next step is to then construct the receptacles within the component constructor.
This is done with the following syntax:

• m_PSR_IAdd = new OCM_SingleReceptacle<IAdd>(IAdd.class);

The class type is passed as a parameter, so you add .class to the class name.

13
Figure 3.6 shows the outline of the calculator component with the receptacle elements
now fully added.

// OpenCOM and Java


import OpenCOM.*;
import java.util.*;
// Interfaces
import Samples.AdderComponent.IAdd;

public class Calculator extends OpenCOMComponent implements


ICalculator, IConnections, ILifeCycle, IUnknown, IMetaInterface {

/**
* Requires Interface of type IAdd.
*/
public OCM_SingleReceptacle<IAdd> m_PSR_IAdd;
/** Creates a new instance of Calculator */
public Calculator(IUnknown binder) {
super(binder);

// Initiate the receptacles


m_PSR_IAdd = new OCM_SingleReceptacle<IAdd>(IAdd.class);
}
}
Figure 3.6 The calculator component with the receptacles defined

Step 4 – Implementing the IConnections Interface

The IConnections interface must be implemented following a consistent syntax to


ensure that your components are connected correctly. Figure 3.7 shows the example
code that you should always follow.

// IConnections Interface
public boolean connect(IUnknown pSink, String riid, long ConnID) {
if(riid.toString().equalsIgnoreCase("Samples.AdderComponent.IAdd")){
return m_PSR_IAdd.connectToRecp(pSinkIntf, riid, provConnID);
}
return false;
}
public boolean disconnect(String riid, long connID) {
if(riid.toString().equalsIgnoreCase("Samples.AdderComponent.IAdd")){
return m_PSR_IAdd.disconnectFromRecp(connID);
}
return false;
}
Figure 3.7 IConnections Interface Implementation

For each of the receptacles check if the riid parameter matches the type of the
receptacle; if it does, either call connectToRecp on the receptacle or
disconnectFromRecp. The bold statements are the ones changed specific to the
receptacle, the rest can be copied directly.

Step 5– Invoking a Receptacle

14
In order to use another component’s provided functionality, you invoke a connected
receptacle. To do this you call the method name from the interface on the
receptacle.mpIntf field of your receptacle. For example, in figure 2.8 we wish to call
add on the adder component, therefore we call the add operation preceded by
m_PSR_IAdd.m_pIntf. This method is specific to single receptacles, there are
different invocation approaches for alternative receptacle types (see later).

//Interface ICalculator implementation


/**
* Add two integers together.
* @param a Operand X.
* @param b Operand Y.
* @return The added values.
*/
public int add(int a, int b) {
/*
* Standard single receptacle invocation.
*/
return m_PSR_IAdd.m_pIntf.add(a, b);
}
Figure 3.8 Using a Single Receptacle

3.4 Creating Systems and Applications Using OpenCOM Components

This section describes how to use components once they have been developed. That
is, you can create component instances at runtime, and connect them together. Hence,
we illustrate the basic concepts about using the runtime kernel, interface programming
and using the IOpenCOM interface operations.

3.4.1 Creating the Runtime Kernel & QueryInterface


To use OpenCOM you must create an instance of the kernel in the local jvm address
space. This procedure is shown in figure 3.9.

// Create the OpenCOM runtime & Get the IOpenCOM interface reference
OpenCOM runtime = new OpenCOM();
IOpenCOM pIOC=(IOpenCOM) runtime.QueryInterface("OpenCOM.IOpenCOM");

Figure 3.9 Creating the kernel instance

The second line displays an important programming procedure in OpenCOMJ known


as QueryInterface. Every component (and the runtime) has an IUnknown interface
with a single operation: QueryInterface. This operation allows you to get a reference
to the interface you wish to invoke operations on. Here, we want to use the core
IOpenCOM interface (the key operations of which are listed in table 3.1) therefore,
we query from the runtime the interface type: “OpenCOM.IOpenCOM” and this
returns a java reference, or null if the interface isn’t available on this element.

Operation Name Description


createInstance Create a new instance of a component and insert it into

15
the OpenCOM runtime.
DeleteInstance Deletes a component instance which has been previously
created.
Disconnect Disconnect two components on an interface
connect Connects a receptacle on the Source component to an
interface on the Sink component.
GetComponentName Returns the registered unique component name for a
given component reference
getComponentPIUnknown Returns the registered component reference of a named
component
getComponentCLSID Returns the registered component type of a given
component instantiation
isContained Find out if a component is primitive or it resides within a
framework
Table 3.1 The key operations in IOpenCOM

3.4.2 Creating a New Component Instance


To create a new component you use the createInstance operation on the previous
IOpenCOM interface reference. Note, you are returned an IUnknown reference of the
component; this uniquely identifies the component in the address space and can be
used to find further interfaces. The syntax of the operation is as follows:

Name: createInstance
Param 1: Type String: Class name of the foundation class of the component
to create.
Param 2: Type String: Your chosen unique name for the component. If you
pass a name that is already in use then the component isn’t created and you get
a reference to the existing component.
Return: The IUnknown object reference of the created component. If the
procedure fails (typically because the OpenCOM specification isn’t followed
in development) then a null value is returned.

Figure 3.10 shows how this operation is used to create an instance of the sample adder
component with the name “adder”.

// Create the Adder component


IUnknown pAdderIUnk = (IUnknown) pIOCM.createInstance
(“Samples.AdderComponent.Adder”, “Adder”);
ILifeCycle pILife = (ILifeCycle)
pAdderIUnk.QueryInterface(“OpenCOM.ILifeCycle”);
pILife.startup(pIOCM);
Figure 3.10 Creating and starting a component

If the component needs to begin executing you call the startup operation on the
ILifeCycle interface. When this is done is dependent on the component. If there is no
execution then you don’t need to call it. You may also want to wait until the
component is fully connected. To startup you perform QueryInterface on the new
component reference and then invoke startup. The parameter of startup can be any

16
object, so you can pass any specific information to a component at this point in time.
Figure 3.10 illustrates how to startup a component; here we pass the runtime reference
as a parameter for illustrative purposes only.

3.4.3 Connecting two components


Components with required interfaces need to be connected, otherwise their operations
will fail when called. In our example, the calculator component must be connected to
the adder component on the IAdd interface. Therefore, after first creating an instance
of each of the two components we connect them together using the connect operation
from IOpenCOM. This operation has the following syntax:

Name: connect
Param 1: Type IUnknown: The reference of the component with the
receptacle to connect
Param 2: Type IUnknown: The reference of the component with the interface
to connect.
Param 3: Type String: The class type of the interface to connect
Return: A positive long reference uniquely identifying the connection. Every
connection gets a unique reference (that is valid for the lifetime of the kernel
operation). –1 is returned if the connection failed.

Figure 3.11 demonstrates how to connect two components together. Here we take the
calculator instance and connect it to the adder reference.

long ConnID1 = pIOCM.connect(pCalcIUnk, pAdderIUnk,


"Samples.AdderComponent.IAdd");
Figure 3.11 Connecting two components

3.4.4 Disconnecting Two Components


To disconnect two components you invoke the disconnect operation from
IOpenCOM; you pass the previously stored reference number of the connection as a
parameter. The syntax of the operations is as follows:

Name: disconnect
Param 1: Type long: The reference of connection
Return: A Boolean; true if the components are disconnected otherwise false.

Figure 3.12 illustrates an example disconnect operation. Here we use the previous
ConnID1 value to disconnect the calculator and adder components.

pIOCM.disconnect(ConnID1);
Figure 3.12 Disconnecting two components

3.4.5 Deleting a component


To delete a previously created component instance you use the deleteInstance
operation from the IOpenCOM interface. This will safely shutdown and disconnect
the component (if you haven’t already done it) and remove the instance from the

17
meta-level information. However, because the component is implemented in Java it
may not be removed from memory immediately. The syntax for the delete operation is
as follows:

Name: deleteInstance
Param 1: Type IUnknown: The component reference to delete.
Return: A Boolean; true if the delete completed otherwise false.

Figure 3.13 illustrates an example that deletes the previously created adder
component.

pIOCM.deleteInstance(pAdderIUnk);

Figure 3.13 Deleting a component instance

18
3.5 Performing Reflective Operations

3.5.1 Architecture Meta Model Operations


The architecture meta-model allows you to discover the architecture of components in
terms of what components are connected to another. For this there are two operations
that are available from the IMetaArchitecture interface that is provided by the
OpenCOM runtime: enumConnsToIntf examines a particular interface of a
component and then describes who is connected to that interface. Alternatively,
enumConnsFromRecp examines a particular receptacle and then describes who is
connected to that receptacle. The syntax of these operations is as follows:

Name: enumConnsToIntf
Param 1: Type IUnknown: The reference of the component with the interface
to inspect
Param 2: Type String: Interface type
Param 3: Type Vector<Long>: The vector list to fill with the connection
identifiers that are connected to this interface
Return: An integer describing the number of connections.

Figure 3.14 shows sample code for inspecting what is connected to the IAdd interface
on the adder component. First we call enumConnsToIntf on the IMetaArchitecture
interface that gives us a vector of information. Next, we use each long identifier from
the list, calling getConnectionInfo() from the IOpenCOM interface to find out the
information about this connection (the structure of this data type is shown in table
3.2).

OCM_ConnInfo_t Fields Type Description


sourceComponentName String A string describing the unique name of the
component hosting the receptacle.

sinkComponentName String A string describing the unique name of the


component instance hosting the interface.
interfaceType String The interface type of this connection.
Table 3.2 Data fields in the connection information meta-data

IMetaArchitecture pIMetaArch = (IMetaArchitecture)


runtime.QueryInterface("OpenCOM.IMetaArchitecture");

Vector<Long> list = new Vector<Long>();


int NoConns = pIMetaArch.enumConnsToIntf(pAdderIUnk,
"Samples.AdderComponent.IAdd", list);
for(int index=0; index<NoConns; index++){
OCM_ConnInfo_t TempConnInfo =
pIOCM.getConnectionInfo(list.get(index).longValue());
System.out.println("Component "+
TempConnInfo.sinkComponentName + " is connected to " +
TempConnInfo.sourceComponentName + " on interface " +
TempConnInfo.interfaceType);
}
Figure 3.14 Example use of enumConnsToInterface
Name: enumConnsFromRecp

19
Param 1: Type IUnknown: The reference of the component with the
receptacle to inspect
Param 2: Type String: Interface type
Param 3: Type Vector<Long>: The vector list to fill with the connection
identifiers that are connected to this receptacle
Return: An integer describing the number of connections.

Figure 3.15 shows sample code for inspecting what is connected to the IAdd
receptacle on the calculator component. First we call enumConnsFromRecp on the
IMetaArchitecture interface that gives us a vector of information. Next we use each
long identifier element of this list, calling getConnectionInfo from the IOpenCOM
interface, to find out the information about this connection.

Vector<Long> Recplist = new Vector<Long>();


int NoConns2 = pIMetaArch.enumConnsFromRecp(pCalcIUnk,
"Samples.AdderComponent.IAdd", Recplist);

for(int index=0; index<NoConns2; index++){


OCM_ConnInfo_t TempConnInfo =
pIOCM.getConnectionInfo(Recplist.get(index).longValue());
System.out.println("Component "+
TempConnInfo.sourceComponentName + " is connected to " +
TempConnInfo.sinkComponentName + " by receptacle of interface " +
TempConnInfo.interfaceType);
}
Figure 3.15 Example use of enumConnsFromRecp

3.5.2 Interface Meta Model Operations


The Interface meta model allows you to inspect the interfaces and receptacles
available on components. In addition, it allows you to add and inspect meta-data that
has been attached to interface elements (i.e. instances of a receptacle or interface). In
this section we demonstrate how to use the interface meta-model programmatically.
First, we examine interface inspection then we look at meta data.

Inspection of Receptacles and Interfaces

There are two operations available from the IMetaInterface of every OpenCOM
component for inspecting the current interface set. Hence, this meta model differs in
that you interact with the components rather than the run-time kernel to get
information. The two operations are enumIntfs and enumRecps; the syntax of the
operation to enumerate the interfaces of a component is as follows:

Name: enumIntfs
Param 1: Type Vector<Class>: The list to fill with the types of the interfaces
on this component
Return: An integer describing the number of interfaces on the component.

20
Figure 3.16 illustrates how to use the IMetaInterface to find the interfaces on a
component. First the component (in this case the adder component) is queried for the
IMetaInterface interface. Next the enumIntfs operation is called, and the vector ppIntf
is filled with class types of the interfaces; we convert these to strings to display them
to standard output.

IMetaInterface pMeta = (IMetaInterface)


pAdderIUnk.QueryInterface("OpenCOM.IMetaInterface");
Vector<Class> ppIntf = new Vector<Class>();
int length = pMeta.enumIntfs( ppIntf);
System.out.println("The number of Interfaces on Adder component is
"+length);

for (int y=0; y<length; y++){


Class temp = ppIntf.elementAt(y);
System.out.println(temp.toString());
}
Figure 3.16 Example of how to enumerate the interfaces of a component

The enumerate receptacles operation (enumRecps) follows a similar pattern, although


the meta-information about receptacles is different. The syntax for the enumRecps
operation is as follows:

Name: enumRecps
Param 1: Type Vector< OCM_RecpMetaInfo_t>: The list to fill with the
information about receptacles. OCM_RecpMetaInfo_t has two fields: a) String
iid, which is the class name representing the interface type, and b) String
recpType which is the type of the receptacle e.g. single, multiple, etc.
Return: An integer describing the number of receptacles on the component.

Figure 3.17 demonstrates how to programmatically discover the receptacles available


on a component at runtime. The calculator component is queried for the
IMetaInterface interface and the enumRecps() operation is invoked on the returned
reference, passing a vector to be filled with the results. We then extract the meta-
information from the list and display it to screen.

Vector<OCM_RecpMetaInfo_t> ppRecps=new Vector<OCM_RecpMetaInfo_t>();


pMeta = (IMetaInterface)
pCalcIUnk.QueryInterface("OpenCOM.IMetaInterface");
int length = pMeta.enumRecps( ppRecps);
System.out.println("The number of receptacles on Calculator
component is "+length);
for (int y=0; y<length2; y++){
OCM_RecpMetaInfo_t temp = ppRecps.elementAt(y);
System.out.println("Receptacle interface is : "+temp.iid);
System.out.println("Receptacle type is: "+temp.recpType);
}

Figure 3.17 Example of how to view the receptacles of a component.

21
Manipulating Receptacle and Interface Meta Data

The other feature of OpenCOMJ’s interface meta-model is that it supports the


addition of run-time meta data on interfaces and receptacles. That is you can add
information in the form of a name-value pair to either an individual interface element
or a receptacle element (note, this is an interface meta model so you cannot attach to a
component; the equivalent would be attaching meta data to the IUnknown interface
that represents the component). Here we now show examples of how to add data to
interfaces and receptacles.

IMetaInterface pMeta = (IMetaInterface)


pAdderIUnk.QueryInterface("OpenCOM.IMetaInterface");
pMeta.SetAttributeValue("Samples.AdderComponent.IAdd", "Interface",
"Variation", "int", new Integer(8));

Figure 3.18 Adding meta data to the IAdd interface of the Adder component

Figure 3.18 shows code that adds a name value pair to the IAdd interface. In this case,
the name is Variation, the type is “int” and the value is a new integer object 8. We call
the setAttributeValue() operation on the Adder component’s IMetaInterface, passing
the interface we want to add to, “Interface” states that it is the interface not receptacle
we are interested in (note components can have a receptacle and interface of the same
type), and then the data parameters.

Figure 3.19 then shows how we can read this information. The corresponding
GetAttributeValue() operation is called on IMetaInterface and we pass the 2 interface
description parameters as in 3.18, then we pass only the name of the data we want to
find out, which in this case is “Variation”. A TypedAttribute is returned; this is an
object that contains the name, and value in its fields. Here we extract the “Value”
field and get the integer value.

TypedAttribute vary = (TypedAttribute) pMeta.GetAttributeValue(


"Samples.AdderComponent.IAdd", "Interface", "Variation");

int value = ((Integer) vary.Value).intValue();

Figure 3.19 Retrieving meta-data from the IAdd interface

In the example, we have looked at only interfaces; however, the same process can be
applied identically to receptacles (i.e. use Set /GetAttributeValue()) changing the
“Interface” parameter input to “Receptacle”.

22
3.5.3 The Interception Meta Model

The interception meta model is an important feature of OpenCOMJ, as it allows you


to add new behaviour to a running system dynamically. It does this by allowing you to
add pre- and post- interceptors to any interface of an OpenCOM component. In this
section we will examine how to write a new interceptor, and then how to add it to an
interface instance.

Programming Interceptors

An interceptor is a Java method that follows a particular syntax that allows it to be


used in the interception meta model. Hence, you must create a class definition for the
method to reside (or use an existing class definition). The syntax of a pre method
(interceptor) is then as follows:

public int InterceptorName (String method, Object[] args){}

The return type is always int; this is to tell the meta model what action to take. The
return value must always be either 0 or –1. 0 indicates that the pre method execution
has encountered no problems and it is okay to continue onto the actual method. The
parameters are what you as the interceptor developer manipulate:

 The String “method” parameter contains the name of the method that is being
‘actually’ called on the interface. Typically you can use this to determine
which behaviour to use e.g. block all operations of a particular method name,
or perform behaviour on one method type, and alternative behaviour on
another method.

 The “Object[] args” parameter contains the list of parameters of the


operations for the real method call. For Add(x,y) the args list contains x and y
values. You can then manipulate these e.g. change their values before they are
used in the actual method invocation.

Figure 3.20 illustrates an example pre interceptor inside the class called
PreAndPostMethods. This pre- interceptor method is called “Pre0” and it simply takes
the second parameter and subtracts 8 from it.

Public class PreAndPostMethods {



public int Pre0(String method, Object[] args){
// Take 8 off the first integer parameter
// to correct the addition
Integer int1 = (Integer) args[1];
int val = int1.intValue()-8;
args[1] = new Integer(val);
return 0;
}

}
Figure 3.20 Example pre method interceptor

23
A post method is invoked after the actual method of the component has completed i.e.
it is invoked on the return back to the caller. A post interceptor has different syntax
from a pre method:

public Object Post0(String method, Object result, Object[] args, Exception e){}

This time the return type is of Object type; however you can match it explicitly with
the actual method you are post- intercepting, so if the method returns int, you can
return an int etc.
 The parameter String method is the name of the method that has been called.
 The second parameter is the important one: Object result is the result from the
actual method, and you can manipulate this directly i.e. change the value that
is returned back to the caller (remember the return value of this method will be
the result of the original invocation).
 The Object[] args are the original parameters of the invocation
 The Exception e parameter contains the value of any exception that was
thrown during the actual method operation. Hence, you can develop a post
interceptor that deals with exceptions in a particular manner.

For an example of a post interceptor we look at the framework locking mechanism,


which accesses a lock on the pre call and releases the lock on the post call. Figure
3.21 shows the code for the post call. Here we see that the operation of locks is
independent of the invocation (we aren’t doing any manipulation), so we simply
return the result parameter as the Object result.

public Object Post0(String method, Object result, Object[] args,


Exception e){
// access 1 means we want the readers count sempahore
pMeta.access_CF_graph_lock(1);
// Got it, now update the readers counter
int rc = pMeta.update_readers_count(-1);
// We are the last reader so we release the CF lock sempahore
if (rc==0){
pMeta.release_CF_graph_lock(0);
}
// We've finished so release the readers count semaphore
pMeta.release_CF_graph_lock(1);
return result;
}
Figure 3.21 Example post method interceptor

Adding Interceptors

The IMetaInterception interface provided by the runtime kernel is used to add and
remove interceptors from interfaces. Note, you can only add interceptors to interfaces
and not receptacles. Figure 3.22 illustrates how we add the Pre0 method we defined in
3.20 to the IAdd interface of the Adder component. The procedure for adding
interceptors is as follows:

1. Create the object that is hosting the method. That is, there must be a
reference to the object in memory where the method is hosted. In the
example, we create a new object of type PreAndPostMethods.
2. Query the IMetaInterception interface from the runtime kernel

24
3. Use IMetaInterception to Get the Delegator for the required interface. We
call GetDelegator() passing the component reference and the interface type
which pinpoints the correct interface and returns the IDelegator interface
to allow you to manipulate the delegator.
4. Call AddPreMethod/AddPostMethod on the IDelegator interface. You
always pass the object where the interceptor is hosted and the name of the
interceptor (in the example: Interceptors and “Pre0”).

IMetaInterception pIMeta = (IMetaInterception)


runtime.QueryInterface("OpenCOM.IMetaInterception");
IDelegator pIAdderDel = pIMeta.GetDelegator(pAdderIUnk,
"Samples.AdderComponent.IAdd");

// Add the new pre-method


PreAndPostMethods Interceptors = new PreAndPostMethods(pIOCM);
pIAdderDel.addPreMethod(Interceptors, "Pre0");
Figure 3.22 Adding a pre method

There are a series of reflective operations available from the IDelegator interface that
are programmed in a similar manner to the procedure for adding a pre interceptor.
These are listed in table 3.3 below.

Operation Description
DelPreMethod(String) Remove the named pre-interceptor
AddPostMethod(Object, String) Same procedure as AddPreMethod but this adds
post methods
DelPostMethod(String) Remove the named post-interceptor
ViewPreMethods(String[]) Fills the string list parameter with the names of
pre methods attached to this interface delegator.
ViewPostMethods(String[]) Fills the string list parameter with the names of
post methods attached to this interface delegator.

Table 3.3. Meta Operations available from each delegated interface

25
3.6 Alternative Receptacle Styles
So far we have only considered receptacles that are single i.e. every receptacle is
connected to only one interface. This is supported through the
OCM_SingleReceptacle class; however, OpenCOMJ is flexible to allow you to define
your own receptacle types. In this section we will examine how to program and use
new receptacle types using some of the alternate receptacle types that are already
available as part of the OpenCOMJ release.

3.6.1 A Parallel Receptacle


Here we define a programming abstraction for a multi-receptacle with parallel
invocations. Multiple components all implementing the same interface type can be
connected to this receptacle. This is exemplified in figure 3.23 where 3 adder
components are connected to the calculator receptacle of type IAdd. When the
receptacle is invoked, each connection executes in a separate thread. Importantly,
there are no return values; hence only void methods are appropriate. We advocate the
use of callbacks to handle the return of results from parallel calls.

Calculator
IAdd

Adder Adder Adder

Figure 3.23 Connecting multiple components to a receptacle

The code that follows shows how to implement a class for allowing parallel
receptacles. The full source code can be found in the OpenCOMJ release.

public class OCM_MultiReceptacleParallel<InterfaceType> extends


OCM_Receptacle implements IReceptacle{

 The new receptacle type must inherit from OCM_Receptacle (to support the
meta-models) and it must implement IReceptacle.

public InterfaceType m_pIntf;

/** List of interface pointers this receptacle is connected to. */


private Vector<Object> interfaceList;

/** List of connIDS for each connection of this receptacle. */


private Vector<Long> connIDS;

 We store a list of connections in the fields of the receptacle (rather than a single
one): interfaceList (reference pointers) and connIDS (the OpenCOM connection
identifiers).

public OCM_MultiReceptacleParallel(Class<InterfaceType> cls_type){

26
super();
interfaceList = new Vector<Object>();
connIDS = new Vector<Long>();
numberOfConnections = 0;
class_type = cls_type;

iid = cls_type.toString();
}

 We create a constructor for the programmer to use to create the receptacle; this is
a similar approach to single receptacles – the class type value is passed to type this
generic class e.g. we would pass IAdd.class to make the receptacle of type IAdd.
The constructor must: call super(), and set the value of the iid field from the
abstract class.

public boolean connectToRecp(IUnknown pIUnkSink, String riid, long


provConnID) {
// Get the reference to the component hosting the interface
try{
InterfaceType pIntfRef = (InterfaceType)
pIUnkSink.QueryInterface(riid);
interfaceList.add(pIntfRef);
}
catch(ClassCastException e){
return false;
}

// Add info to the receptacle object stores


connIDS.add(new Long( provConnID));
numberOfConnections++;
return true;
}

 We must implement the two operations of IReceptacle; here we implement


connectToRecp. We essentially take the information about the connection (which
is passed in the parameters): create the receptacle invocation reference, i.e. how to
call the operation through the receptacle, by querying the interface type on the
sink component. Finally, we add this to the interfaceList, and also store the
connection identifier
public boolean disconnectFromRecp(long connID) {
// Traverse the data looking for the required connection ID
for(int i = 0; i < numberOfConnections ; i++) {
Long vecConnID = connIDS.elementAt(i);
if(vecConnID.longValue() == connID) {
// remove information about that connection
numberOfConnections--;
interfaceList.remove(i);
connIDS.remove(i);
}
if(numberOfConnections ==0) {
m_pIntf = null;
return true;
}
return true;
}
return false;
}

 We finally implement the disconnectFromRecp operation of IReceptacle. This


cleans up the information from the receptacle to ensure that a disconnection
operation actually physically disconnects the components. Here, this involves

27
searching through the connection lists and removing the reference pointer and
connection identifier.

The remainder of the receptacle invocation involves ensuring that the invocation
behaves as it should. In this case, the programmer calls operations on the m_pIntf
field (see later, but it’s the same as OCM_SingleReceptacle) and these are then sent to
all the pointers in the interfaceList vector. To be parallel, we create a thread for each
member of list and invoke the operation within the separate thread; hence if add(x,y)
is called and there are 10 connections this class creates 10 threads that execute
add(x,y).

Using the Parallel Receptacle

To use the parallel receptacle in a component you define it in a similar way to


standard single receptacles. Hence, we define the receptacle as a public field of the
component:

public OCM_MultiReceptacleParallel<IOutput> m_PSR_IOutput;

Then we initialise it within the constructor (note we use IConnections in the same way
so the previous approach can be followed):

m_PSR_IOutput = new OCM_MultiReceptacleParallel


<IOutput>(IOutput.class);

Finally, when we want to invoke an operation on the receptacle:

m_PSR_IOutput.m_pIntf.DisplayMessage(message);

3.6.2 Sequential Receptacles


A sequential receptacle allows the programmer to choose any of the connections at a
time and then call them. For example, they can select each one in term and then
invoke an operation on it. Hence, you could keep trying until one returned the result
you required. The actual layout of the connections is identical to the topology seen in
figure 3.23.

The implementation of the sequential receptacle class: OCM_MultiReceptacle is


similar to the OCM_MultiReceptacleParallel. Except that there is no m_pIntf field
that acts as a single point to invoke operations on. Rather, the programmer interfaces
directly with the pointer list as follows:

m_PSR_IIntfType.interfaceList.get(index).foo(params);

Hence, you pass the index number of the connection to the get operation of the vector
in the interfaceList field, and you are returned the binding pointer reference (interface
reference) that the operation (foo) can be invoked upon.

28
3.6.3 Context Receptacles
The final receptacle type allows you to select a single connection from the set of
multiple connections based upon a stated context parameter. Note, only one operation
is called, and this is from the first connection that matches the context.

The following describes the procedure for using a context receptacle in one of your
components. First define the receptacle as a public field of the component and
initialise it with the component constructor.

/**
* Requires Interface of type IOutput. We want to connect this to
* multiple provides interfaces
* and select one to call based upon context.
*/
public OCM_MultiReceptacleDynamicContext<IOutput> m_PSR_IOutput;

public CalculatorDynamicContext(IUnknown binder) {


super(binder);

// Initiate receptacles
m_PSR_IOutput = new OCM_MultiReceptacleDynamicContext
<IOutput>(IOutput.class);

To invoke this receptacle you must first create a set of context rules; these are of the
format “context=value”. For example, in the code in figure 3.24 a context rule of
“Owner=Paul Grace” is created. This is added to a list of rules that will be used to
find a connection that matches the context. Hence, you call getContext(rules) on the
receptacle and it will return an integer index that matches the context. This is the
position of the connection in the interfaceList vector (used in both prior receptacle
types); if –1 is returned then no match was found. With the connection number you
can call the receptacle in the same way as for sequential; i.e. you get the reference
pointer (interface reference) from the interfaceList vector and then call the operation
on that reference.

public String display(String msg) {


String returnStr = message.concat(":: From Calculator");
// Create a context rule - our request for interface invoke
ContextRule context[] = new ContextRule[1];
context[0]=new ContextRule("Owner", "Paul Grace");
// Find out which interface has such a context match
int index = m_PSR_IOutput.getContext(context);
//Invoke the indexed interface that matches.
if(index>=0)
m_PSR_IOutput.interfaceList.get(index).DisplayMessage(msg);
return returnStr;
}
Figure 3.24 Example method that utilises a context receptacle.

29
3.7 Developing Component Frameworks in OpenCOMJ

A component framework is a component that is composed of other components (as


described in the introduction). Here we describe how to create your own component
framework.

3.7.1 Creating a new Component Framework

Developing a composite component is similar to a standard OpenCOM component


(this is because a framework must be able to be manipulated as an OpenCOMJ
component) :

• The framework implements IUnknown; QueryInterface will automatically find


dynamically exposed interfaces.
• The framework implements ILifeCycle and IMetaInterface
• The framework always implements IConnections. The framework will
automatically connect any receptacles that have been dynamically exposed
without affecting the IConnections of the framework. Therefore, you do not
need to implement the methods of IConnections, but it must be listed.
However, if the framework contains its own fixed receptacles IConnections is
followed normally with the addition of a call to the abstract class (as shown)
below rather than returning directly:

// IConnections:: Interface Implementation


public synchronized boolean connect(IUnknown pSinkIntf, String riid,
long provConnID) {
if(riid.toString().equalsIgnoreCase("IRepository")){
return m_PSR_Repo.connectToRecp(pSinkIntf, riid, provConnID);
}
else
return super.connect(pSinkIntf, riid, provConnID);
}

A framework differs in the following three features:


• A framework must implement ICFMetaInterface – this provides the core and
reflective functionality of the framework.
• A framework must inherit CFMetaInterface
• A framework must implement the QueryInterface class.
• You can/should implement a component that implements IAccept that is then
connected to this framework. All changes are then validated by this
component.

This amounts to a simple development process, but be careful with your IConnections
interface implementation. If you have no receptacle fields in the framework you do
not need to implement connect and disconnect, if you do follow the implementation
above. When you implement connect/disconnect you must call the super
methods.

We now use the calculator framework from the demo to demonstrate how to being
developing simple component frameworks. The code in figure 3.25 shows the

30
important features in bold. Note the QueryInterface implementation is always the
same, hence this can be copied from framework to framework.

public class CalculatorFramework extends CFMetaInterface implements


ICFMetaInterface, IConnections, IMetaInterface, IUnknown,
ILifeCycle{
/** Creates a new instance of CalculatorFramework */
public CalculatorFramework(IUnknown pRuntime) {
super(pRuntime);
}
public Object QueryInterface(String InterfaceName) {
// Test if its an exposed interface
return QueryInterface(InterfaceName, this);
}
Figure 3.25 Implementing a new component framework.

3.7.2 Reflection Operations on Component Frameworks


Every component framework is subject to the same reflective operations as a standard
OpenCOM component (because a CF is an extended component). For example, the
runtime IMetaArchitecture interface provides dynamic inspection of the external
structure of a CF (i.e. what it is connected to), and the local IMetaInterface interface
lists the interfaces and receptacles available from the CF. However, no OpenCOM
interface supports inspection or dynamic adaptation of the internal structure of a
component framework. Therefore, every CF implements its own additional meta-
interface, named the ICFMetaArchitecture interface that consists of methods for
introspection (table 3.4) and reconfiguration (table 3.5); the complete syntax of these
operations is available in the source. The implementation of these operations then
relies upon the local graph meta-representation.

We demonstrate how to use some of these operations in section 3.7.3.

Operation Name Description


get_internal_components Returns a list of the components that make up the current
component framework configuration.
get_Bound_Components Returns a list of all components bound to a particular
component.
get_internal_bindings Returns a list of all connections within the current
component framework configuration.
Table 3.4 Operations for inspection of the internal CF structure

Given that the component model offers a hierarchy of encapsulated (possibly


unconnected) graph structures, the corresponding meta-model must allow recursive
unfolding of these structures by introspection to allow reconfiguration to be applied at
the correct level. Therefore, the meta space is unfolded using the introspection
methods of the ICFMetaArchitecture to find the components and configurations
within a component, and IMetaInterface to view the interfaces and receptacles
(potentially exposed ones as well). Notably, primitive components only implement
IMetaInterface, therefore the base of recursion is the querying of the
ICFMetaArchitecture interface.

31
Operation Name Description
insert_component Create and insert a new component into this CF
configuration.
remove_component Delete a component from the configuration.
Replace_component Replace an instance of one component with another,
ensuring connections reconnected.
local_bind Establish a local binding between two components from
interface to receptacle.
break_local_bind Break a local binding between two Components in the
framework.
Expose_interface Map the interface of an internal component as a new
external interface of the CF.
unexpose_interface Remove an exposed external interface.
Expose_receptacle Map the receptacle of an internal component as a new
receptacle of
unexpose_receptacle Remove an exposed Receptacle.
Replace_configuration Replace the current graph of components with a new
component configuration.
init_arch_transaction Initiate a transaction for architecture reconfiguration.
commit_arch_transaction Completes the reconfiguration.
Rollback_arch_transaction Rolls back changes made during a transaction.
Table 3.5 Operations for dynamic reconfiguration

An additional feature of the meta object protocol for component frameworks is the
expose_interface and expose_receptacle operations; these allow inner component
functionality to be dynamically exported to create the component framework’s service
provision and requirements. Therefore, a CF becomes a dynamic entity, unlike the
fixed primitive components; this is especially important for frameworks that can
cover different styles of functionality, which may change over time.

3.7.3 Using the ICFMetaInterface


Here we create a sample calculator framework that consists of two components: a
calculator connected to an adder component. We do this in a step-by-step fashion.

Step 1 – Create the framework (same as for a standard component)

IUnknown pCFIUnk = (IUnknown) pIOCM.createInstance


("Samples.CalculatorFramework.CalculatorFramework", "Framework");
ILifeCycle pILife = (ILifeCycle)
pCFIUnk.QueryInterface("OpenCOM.ILifeCycle");
pILife.startup(pIOCM);

32
Step 2 – Create the validation component and connect it to the framework

// Create the checking component for the framework


IUnknown pAcceptIUnk = (IUnknown) pIOCM.createInstance
("Samples.AcceptComponent.Accept", "Accept");
pILife=(ILifeCycle)pAcceptIUnk.QueryInterface("OpenCOM.ILifeCycle");
pILife.startup(pIOCM);

// Connect the checking component to the framework


long cID = pIOCM.connect(pCFIUnk, pAcceptIUnk, "OpenCOM.IAccept");

Step 3 – Get the reference to the ICFMetaInterface

ICFMetaInterface pCF = (ICFMetaInterface)


pCFIUnk.QueryInterface("OpenCOM.ICFMetaInterface");

Step 4 – Begin a reconfiguration

To begin any change to the framework we must initiate a transaction; this tells the
framework to set itself to a quiescent state to allow changes to be made.

pCF.init_arch_transaction();

Step 5 – Create the configuration using internal creates and local binds
IUnknown pAdd = pCF.create_component
("Samples.AdderComponent.Adder", "Adder");
IUnknown pCal = pCF.create_component
("Samples.CalculatorComponent.Calculator", "Calculator");
// Connect the local components
long connid = pCF.local_bind(pCal, pAdd,
"Samples.AdderComponent.IAdd");

Step 6 – Expose an Interface

This is an important feature that differs from anything presented so far. For this
framework we want the behaviour to allow the ICalculator interface to be usable
directly from the framework instance (see step 8); that is, it can be found by querying
or enumerating interfaces on the framework (this will fail before exposure).
Therefore, we need to expose the internal ICalculator interface from the calculator
component using the expose_interface operation. We pass the interface type we wish
to expose and the component that it is physically located on (this must be inside the
component framework). Note a receptacle is exposed in a similar way.

pCF.expose_interface("Samples.CalculatorComponent.ICalculator", pCal);

Step 7 – Finish the Reconfiguration

We finally check the new configuration by terminating the transaction; this forces the
framework to validate the changes using the accept component connected earlier. If
true is returned then the change was successful, otherwise the change failed and the
framework remains in the state it was before init_arch_transaction();

boolean success = pCF.commit_arch_transaction();

33
Step 8 – Using the Framework

pICalc = (ICalculator) pCFIUnk.QueryInterface(


"Samples.CalculatorComponent.ICalculator");
// test the Add
System.out.println("The value of 18+19 = "+ pICalc.add(18,19));

Here we treat the framework like a standard component; we query for the ICalculator
interface and then call an operation upon it.

34

Das könnte Ihnen auch gefallen