Beruflich Dokumente
Kultur Dokumente
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”.
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
IMetaInterception
IOpenCOM OpenCOM
Runtime
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.
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:
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
Lock
Interceptor
Graph of internal
IAccept components
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.
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
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.
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 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.
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.1 Requirements
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.
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;)
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:
You can then use the OpenCOM library by pointing the java classpath to
include the ../classes folder that is created; e.g.
10
3.2 Developing a Basic OpenCOM component
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.
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 {
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
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
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
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:
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:
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.
/**
* Requires Interface of type IAdd.
*/
public OCM_SingleReceptacle<IAdd> m_PSR_IAdd;
/** Creates a new instance of Calculator */
public Calculator(IUnknown binder) {
super(binder);
// 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.
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).
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.
// Create the OpenCOM runtime & Get the IOpenCOM interface reference
OpenCOM runtime = new OpenCOM();
IOpenCOM pIOC=(IOpenCOM) runtime.QueryInterface("OpenCOM.IOpenCOM");
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
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”.
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.
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.
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
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);
18
3.5 Performing Reflective Operations
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).
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.
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.
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.
21
Manipulating Receptacle and Interface Meta Data
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.
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
Programming Interceptors
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.
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.
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.
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”).
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.
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.
Calculator
IAdd
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.
The new receptacle type must inherit from OCM_Receptacle (to support the
meta-models) and it must implement IReceptacle.
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).
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.
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).
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.m_pIntf.DisplayMessage(message);
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;
// 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.
29
3.7 Developing Component Frameworks in OpenCOMJ
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.
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.
32
Step 2 – Create the validation component and connect it to the framework
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");
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);
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();
33
Step 8 – Using the Framework
Here we treat the framework like a standard component; we query for the ICalculator
interface and then call an operation upon it.
34