This document describes the main principles in providing new types of device components to the RM device management tree.
Component CU Provider Interface
To plug a new component type, usually reflecting the state and capabilities of a specific resource, to a device, you have to implement a Component Control Unit Provider, represented by the com.prosyst.mprm.backend.ms.cu.spi.component.ComponentControlUnitProvider interface.
The Component Control Unit Provider SPI is supported only on a backend framework having the management server role. It is exported by the RM Control Unit Lib bundle (packages/gdm/gdmlib.jar) and is handled by the Control Unit Manager bundle (packages/gdm/cu.jar).
During development, you can also use the lib/api/gdm-api.jar JAR file for compilation and for code complete in a Java editor.
Example of a Component CU Provider
Most of the code examples in this document are taken from a Component Control Unit Provider with the following features:
The provider supports control units of type "my.component.cu" with friendly type name "My Component".
Each component control unit has state variables "ID", "name" and "number of messages", and an action for echoing a message.
Component control units are created with arguments "ID" and "name".
Some sections contain examples, which are extracts from a Component Control Unit Provider implementation for components children of "my.component.cu" instances. The sub component type is "my.subcomponent.cu".
The example Component Control Unit Provider is available as a part of the Control Units Demo (demo/controlunits) distributed with RM. The Component Control Unit Provider is packed in a separate bundle componentproviderdemo.jar located in demo/controlunits/bundles, and its source code available in the demo/controlunits/src under the demo.controlunits.component Java package.
Communicate with the Generic Device Manager
Like in Device Root Control Unit Providers, to be able to use the control unit storage of RM as well as to notify the system of changes in a component control unit, it is recommended that you save the ComponentSystemContext instance passed to the init method of your ComponentControlUnitProvider object.
ComponentSystemContext. The transactional storage is in the form of a TransactionalComponentStorage instance, obtained by calling the begin method of ComponentSystemContext. Once having accessed the transactional storage, you can commit or rollback data read/write requests to the storage.The code below shows an implementation of the init method, which simply saves the ComponentSystemContext instance from the system, and initializes and saves the metatype for the managed component control unit type. Metatype loading is done from an XML within the provider bundle by using the com.prosyst.mprm.util.metatype.MetaTypeProviderInfo utility (refer to the "Provide Component Metadata" section below).
import java.io.IOException; import java.io.InputStream; import com.prosyst.mprm.backend.ms.cu.spi.component.ComponentControlUnitProvider; import com.prosyst.mprm.backend.ms.cu.spi.component.ComponentSystemContext; import com.prosyst.mprm.common.ManagementException; import com.prosyst.mprm.util.metatype.MetaTypeProviderInfo; public class MyComponentCUProvider implements ComponentControlUnitProvider { ComponentSystemContext ctx; static final String CU_TYPE = "my.component.cu"; static final String CU_VERSION = "1.0.0"; private MetaTypeProviderInfo metatype; . . . public void init(ComponentSystemContext ctx) {// Save the ComponentSystemContext for later use this.ctx = ctx; // Load the metatype from an XML try { if (metatype == null) { InputStream xml = MyComponentCUProvider.class.getResourceAsStream("/cu.xml"); if (xml != null) { metatype = new MetaTypeProviderInfo(xml, true);// Save persistently the metatype ctx.addMetadataRecord(CU_VERSION, metatype); } } } catch (ManagementException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } . . .}Provide a Factory for Device Components
In detail, the RM Component Control Unit Provider SPI uses the concept of per-device component factories, which are responsible for handling the component control units within the scope of a single device.
The device type a component type is associated with is indicated by means of a service property when registering the Component Control Unit Provider as a service. See the link from the same topic "Register the Component CU Provider as a Service" section.
A device-specific factory instance should implement the com.prosyst.mprm.backend.ms.cu.spi.component.DeviceControlUnitFactory interface and the Component Control Unit Provider should provide it to RM when the provider's getDeviceFactory method is called.
The following example returns device factories, saved in a factory cache. The factories are instances of a class, called MyDeviceFactory, whose methods are further available in the next listings on implementing a device factory.
import com.prosyst.mprm.backend.ms.cu.spi.component.ComponentControlUnitProvider; import com.prosyst.mprm.backend.ms.cu.spi.component.DeviceControlUnitFactory;import com.prosyst.mprm.common.ManagementException;public class MyComponentCUProvider implements ComponentControlUnitProvider { . . . public DeviceControlUnitFactory getDeviceFactory(String deviceType, String deviceId) throws ManagementException { MyDeviceFactory fact = new MyDeviceFactory(deviceType, deviceId, ctx); return fact; } . . .}Implementing a device factory is shown below:
import com.prosyst.mprm.backend.ms.cu.spi.component.ComponentSystemContext;import com.prosyst.mprm.backend.ms.cu.spi.component.DeviceControlUnitFactory; public class MyDeviceFactory implements DeviceControlUnitFactory { String deviceType; String deviceId; private ComponentSystemContext ctx; MyDeviceFactory(String deviceType, String deviceId, ComponentSystemContext ctx) { this.deviceType = deviceType; this.deviceId = deviceId; this.ctx = ctx; } . . . }A device factory can provide the following features:
Create/destroy components on request from a management application through the Generic Device Manager
Provide the current state of a component and synchronize this state if required
Define parent/child relations with other components
Handle queries for the value of a specific state variable
Handle invocation of actions
Find a specific control unit(s)
Create a Component Control Unit
Creating a component control unit is similar to creating a device control unit. If the provider is going to support user-initiated registration of resources, it should implement the createControlUnit method of the device-specific DeviceControlUnitFactory. If it is not possible to create a component at the moment of request, for example the device is offline or it does not have capacity for new resources, you should indicate an error to the ProviderResult argument.
Optionally, you can benefit from the RM persistent storage to avoid data losses on system restarts. Save the ControlUnitState in the Control Unit Database by calling the saveControlUnitState method of the ComponentSystemContext instance, previously provided by RM in the call to the init method (see "Communicate with the Generic Device Manager" section below)) of the ComponentControlUnitProvider.
If your component control unit is a child of another component unit, consider the following guidelines:
Make sure you have defined the parent control unit type in the metatype (see "Provide Component Metadata" section below).
To get the ID of the parent unit define it as an argument of the constructor in the control unit metatype so that a management application, such as the console, can pass it to the provider through the Generic Device Manager.
In addition, it is recommended that you add a hierarchy record at creating the unit. This is done by using
addHierarchyRecordof theComponentSystemContextinstance.
AttributeDefinition of the argument add a <key> tag with name attribute "argument.parent" and value "parent.id"<attribute modifier="in"> <name>Parent ID</name> <id>parentId</id> <description/> <type>&string;</type> <cardinality>0</cardinality> <key name="argument.parent" value="parent.id"/></attribute>In addition, you can also use a <key> tag with name "invisible" and value="true", which will make the console hide the parent ID argument from the user when creating a component control unit.
Finally, to be able to interact efficiently with the RM device management subsystem, it is recommended to notify the system of the newly-created component control unit by calling the controlUnitEvent method of the ComponentSystemContext instance.
The code below implements the createControlUnit method to register new components. It illustrates creating a new component control unit with ID and name as constructor arguments. Next, the unit's state is saved in the Control Unit Database and an event is fired to the RM system. The component state is implemented as a MyComponent object from the code after it.
import com.prosyst.mprm.backend.ms.cu.spi.ProviderResult;import com.prosyst.mprm.backend.ms.cu.spi.component.ComponentSystemContext;import com.prosyst.mprm.admin.devices.event.ControlUnitEvent; import com.prosyst.mprm.backend.ms.cu.spi.component.DeviceControlUnitFactory;import com.prosyst.mprm.common.ManagementException; public class MyDeviceFactory implements DeviceControlUnitFactory { ComponentSystemContext ctx; . . . public void createControlUnit(String constructor, Object arguments, ProviderResult result) throws ManagementException { if (!(arguments instanceof Object[]) || ((Object[]) arguments).length != 2) { result.setResult(null, new Exception("Arguments are not valid!")); } Object[] arguments1 = (Object[]) arguments; String id = (String) arguments1[0]; String name = (String) arguments1[1]; // Create a new component state and save it MyComponent state = new MyComponent(deviceType, deviceId); state.setID(id); state.setName(name); state.setNumberOfMsgs(0); ctx.saveControlUnitState(state); // Fire an event about a new component control unit ControlUnitEvent createEvent = new ControlUnitEvent(ControlUnitEvent.CONTROL_UNIT_ADDED, MyComponentCUProvider.CU_TYPE, id, deviceType, deviceId); ctx.controlUnitEvent(createEvent); // Return the state of the new component to RM result.setResult(id, null); } . . .}This is the example for implementing a control unit state of a component:
import java.util.Vector;import com.prosyst.mprm.admin.devices.ControlUnitID; import com.prosyst.mprm.backend.ms.cu.spi.ControlUnitState; public class MyComponent implements ControlUnitState { private String name = ""; static final String NAME_VAR_ID = "name"; private String id = ""; static final String ID_VAR_ID = "id"; private int numberOfMsgs = 0; static final String COUNT_VAR_ID = "msgCount"; private String deviceId; private String deviceType; private Vector stateInterface; MyComponent(deviceType, deviceId) { this.deviceType = deviceType; this.deviceId = deviceId; } // Methods inherited from ControlUnitState public ControlUnitID getControlUnitID() { return new ControlUnitID(MyComponentCUProvider.CU_TYPE, id, deviceType, deviceId); } public String[] getStateInterface() { String[] result = new String[stateInterface.size()]; stateInterface.copyInto(result); return result; } public Object getStateVariable(String stateVarId) { if (stateVarId.equals(COUNT_VAR_ID)) { return new Integer(numberOfMsgs); } else if (stateVarId.equals(ID_VAR_ID)) { return id; } else if (stateVarId.equals(NAME_VAR_ID)) { return name; } return null; } // Collects the state variable IDs of the component when some of them is set private void addInStateInterface(String sv) { if (stateInterface == null) { stateInterface = new Vector(); } if (!stateInterface.contains(sv)) { stateInterface.addElement(sv); } } // Setter methods for the component's state void setNumberOfMsgs(int numberOfMsgs) { this.numberOfMsgs = numberOfMsgs; addInStateInterface(COUNT_VAR_ID); } void setID(String id) { this.id = id; addInStateInterface(ID_VAR_ID); } void setName(String name) { this.name = name; addInStateInterface(NAME_VAR_ID); }}The code below illustrates creating a component, which has a parent control unit:
import com.prosyst.mprm.admin.devices.ControlUnitID;import com.prosyst.mprm.admin.devices.event.ControlUnitEvent;import com.prosyst.mprm.backend.ms.cu.spi.ControlUnitState;import com.prosyst.mprm.backend.ms.cu.spi.ProviderResult;import com.prosyst.mprm.backend.ms.cu.spi.component.ComponentSystemContext;import com.prosyst.mprm.backend.ms.cu.spi.component.DeviceControlUnitFactoryimport com.prosyst.mprm.common.ManagementException; public class MySubDeviceFactory implements DeviceControlUnitFactory { ComponentSystemContext ctx; . . . public void createControlUnit(String constructorId, Object arguments, final ProviderResult result) throws ManagementException { Object[] arguments1 = (Object[]) arguments; // Get the ID of the parent component as the first constructor argument final String parentId = (String) arguments1[0]; final String id = (String) arguments1[1]; // Create a state for the new component ControlUnitState cuState = new ControlUnitState() { public ControlUnitID getControlUnitID() { return new ControlUnitID(MySubComponentProvider.CU_TYPE, id, deviceType, deviceId); } public String[] getStateInterface() { return new String[]{"id"}; } public Object getStateVariable(String sVarId) { return id; } }; // Save persistently the state of the new component ctx.saveControlUnitState(cuState); // Save the relation parent component - child component ctx.addHierarchyRecord(deviceType, deviceId, PARENT_CU_TYPE, parentId, id); // Fire an event about the newly-created component control unit ctx.controlUnitEvent(new ControlUnitEvent(ControlUnitEvent.CONTROL_UNIT_ADDED, MySubComponentProvider.CU_TYPE, id, deviceType, deviceId)); // Return the component ID to RM result.setResult(id, null); } . . .}Destroy a Component Control Unit
The destroyControlUnit method is called when a request to delete a component has been made through the Generic Device Manager. The Component Control Unit Provider is expected to remove the component from the RM storage, to remove added hierarchy records and to fire an event. Finally, set the control unit ID as a string to the ProviderResult object.
Below is provided a simple example of the steps to take when deleting a component control unit. It implements the destroyControlUnit method to delete components from RM.
import com.prosyst.mprm.admin.devices.ControlUnitID;import com.prosyst.mprm.backend.ms.cu.spi.ControlUnitState;import com.prosyst.mprm.backend.ms.cu.spi.ProviderResult;import com.prosyst.mprm.backend.ms.cu.spi.component.ComponentSystemContext; import com.prosyst.mprm.backend.ms.cu.spi.component.DeviceControlUnitFactory;import com.prosyst.mprm.common.ManagementException; public class MyDeviceFactory implements DeviceControlUnitFactory { String deviceType; String deviceId; private ComponentSystemContext ctx; . . . public void destroyControlUnit(String cuId, ProviderResult result) throws ManagementException { // Delete the state of the component control unit ctx.deleteControlUnitState(new ControlUnitID(MyComponentCUProvider.CU_TYPE, cuId, deviceType, deviceId)); // Fire an event about destroying the component control unit ControlUnitEvent destroyEvent = new ControlUnitEvent(ControlUnitEvent.CONTROL_UNIT_REMOVED, MyComponentCUProvider.CU_TYPE, cuId, deviceType, deviceId); ctx.controlUnitEvent(destroyEvent); // Indicate that the operation is complete result.setResult(null, null); } . . .}List All Managed Control Units
The listControlUnits method should list all component control units existing in the scope of the device. The result should be in the form of a String[] object and should be set to the ProviderResult method argument.
The following example implements the listControlUnits methods by simply calling the getControlUnits method, implemented in the code above. Providing null for the parent type and ID will make the getControlUnits method return all components in the device's scope.
import com.prosyst.mprm.backend.ms.cu.spi.ProviderResult;import com.prosyst.mprm.backend.ms.cu.spi.component.DeviceControlUnitFactory;import com.prosyst.mprm.common.ManagementException; public class MyDeviceFactory implements DeviceControlUnitFactory { . . . public void listControlUnits(ProviderResult result) throws ManagementException { getControlUnits(null, null, result); } . . .} Provide the Proper Component State
When a request from a management application comes for getting the actual state of a device component through the Generic Device Manager, the getControlUnit method of the device factory is called. The provider should return a ControlUnitState instance reflecting the state of the device component.
The code below contains a simple implementation of the getControlUnitState method, which retrieves the state from the RM Control Unit Database. The provider is implemented to save each state variable change, i.e. each state change, in the RM storage.
import com.prosyst.mprm.admin.devices.ControlUnitID;import com.prosyst.mprm.backend.ms.cu.spi.ControlUnitState;import com.prosyst.mprm.backend.ms.cu.spi.ProviderResult;import com.prosyst.mprm.backend.ms.cu.spi.component.ComponentSystemContext; import com.prosyst.mprm.backend.ms.cu.spi.component.DeviceControlUnitFactory; import com.prosyst.mprm.common.ManagementException;public class MyDeviceFactory implements DeviceControlUnitFactory { String deviceType; String deviceId; private ComponentSystemContext ctx; . . . public void getControlUnitState(String cuId, ProviderResult result) throws ManagementException { // Get the component's state from the RM database ControlUnitState cu = ctx.retrieveControlUnitState(new ControlUnitID(MyComponentCUProvider.CU_TYPE, cuId, deviceType, deviceId)); // Return the component's state to RM result.setResult(cu, null); } . . .}Define Hierarchy Relations
Device components that a Component Control Unit Provider implements may be defined as children of another component control units.
The factory should provide the following information regarding parent-child relations:
Provide the IDs of all control units, children of a specific control unit, managed by the provider. This feature can be implemented in the
getControlUnitsmethod of the device factory. You can simply call RM to get the child control units of the specified parent.For components which are directly attached to their parent devices, such as "my.component.cu", and do not have parent components, implement the
getControlUnitsmethods by simply listing available control units from the RM storage. For getting such "root" components the system will pass null in the providergetControlUnitsmethod as parent type and ID arguments.For components which can be roots as well as subcomponents, support hierarchy records for the root case (the system will call the
getControlUnitsmethod with null parent arguments) as well as for the "sub" case (the system will call thegetControlUnitsmethod with non-null parent arguments).importcom.prosyst.mprm.admin.devices.ControlUnitID;importcom.prosyst.mprm.backend.ms.cu.spi.component.DeviceControlUnitFactory;importcom.prosyst.mprm.backend.ms.cu.spi.ProviderResult;importcom.prosyst.mprm.backend.ms.cu.spi.component.ComponentSystemContext;importcom.prosyst.mprm.backend.ms.cu.spi.component.DeviceControlUnitFactory;importcom.prosyst.mprm.common.ManagementException;publicclassMySubDeviceFactoryimplementsDeviceControlUnitFactory {privatestaticfinalString PARENT_CU_TYPE ="my.component.cu";privateString deviceId;privateString deviceType;privateComponentSystemContext ctx;. . .publicvoidgetControlUnits(String parentCUType, String parentCUId,ProviderResult result)throwsManagementException {// Get the children of the specified parent from the RM storage.String[] ids = ctx.retrieveSubControlUnits(newControlUnitID(parentCUType,parentCUId, deviceType,deviceId),MySubComponentProvider.CU_TYPE);// Return the child IDs to RMresult.setResult(ids,null);}. . .}
Provide the IDs of the control units of a particular type, parents of a specific control unit. This feature can be implemented in the
getParentsmethod of the device factory. You can simply call theComponentSystemContextto retrieve the parents of the specified component.importcom.prosyst.mprm.admin.devices.ControlUnitID;importcom.prosyst.mprm.backend.ms.cu.spi.ProviderResult;importcom.prosyst.mprm.backend.ms.cu.spi.component.ComponentSystemContext;importcom.prosyst.mprm.backend.ms.cu.spi.component.DeviceControlUnitFactory;importcom.prosyst.mprm.common.ManagementException;publicclassMySubDeviceFactoryimplementsDeviceControlUnitFactory {. . .publicvoidgetParents(String childCUId, String parentCUType, ProviderResult result)throwsManagementException {// Get the parents of the specified component from RM.String[] parentIds = ctx.retrieveParentControlUnits(newControlUnitID(MySubComponentProvider.CU_TYPE,childCUId, deviceType, deviceId),parentCUType);// Return the parent IDs to RMresult.setResult(parentIds,null);}. . .}
Support State Variable Query
In the cases where a management application is interested only in the value of a particular component property, represented as a state variable, the Generic Device Manager will call the queryStateVariable method of the device-specific factory. The provider should return the value of the variable, obeying the correct value type, in the passed ProviderResult object.
The following example contains an implementation of the queryStateVariable method, which gets the state of the component from the RM Control Unit Database and calls the state's getStateVariable method to get the value of the requested state variable. The provider is implemented to save each state change in the RM database.
import com.prosyst.mprm.admin.devices.ControlUnitID;import com.prosyst.mprm.backend.ms.cu.spi.ControlUnitState;import com.prosyst.mprm.backend.ms.cu.spi.ProviderResult;import com.prosyst.mprm.backend.ms.cu.spi.component.ComponentSystemContext;import com.prosyst.mprm.backend.ms.cu.spi.component.DeviceControlUnitFactory;import com.prosyst.mprm.common.ManagementException;public class MyDeviceFactory implements DeviceControlUnitFactory { . . . public void queryStateVariable(String cuId, String sVarId, ProviderResult result) throws ManagementException { // Get the component's state from the RM database. ControlUnitState cu = (ControlUnitState) ctx.retrieveControlUnitState(new ControlUnitID(MyComponentProvider.CU_TYPE, cuId, deviceType, deviceId)); if (cu != null) { // Get the state variable's value and return it to RM result.setResult(cu.getStateVariable(sVarId), null); } else { result.setResult(null, new Exception("Component does not exist!")); } } . . .}Synchronize RM with the Actual Component State
The Generic Device Manager can force synchronization of a component's state in two cases:
When a management application wants to synchronize only the state of a specific component control unit. The Generic Device Manager will call the synchronizeState method of your device factory. In return, the provider should contact the device and get the required information. The
ProviderResultshould contain aControlUnitStatewith the synchronized device properties.When a management application wants to synchronize the state of the entire device including all its components. The Generic Device will call the
synchronizeDevicemethod of the device factory. The factory is expected to update all component states from the device and return synchronized information on later requests. Set null as result to theProviderResultargument.
Support Component Searching
If your provider will support looking for control units on part of a management application, that is, the provider will support one or more finder actions, implement the findControlUnits method. When the search operation is complete, set as a result in the ProviderResult argument the string IDs of the components satisfying the search criteria.
Support Action Invocation
From the Generic Device Manager
When a management application invokes an action on a component control unit by using the Generic Device Manager (see System-Wide Device Management), the RM system locates the proper Component Control Unit Provider and invokes the provider's invokeAction(String controlUnitId, String actionId, Object arguments, ProviderResult result) method. Hence, if the control units managed by your provider support some actions, in the body of this method implement the operations for sending the corresponding command to the device. If an action returns some output, set it to the passed ProviderResult instance. If the action does not have an output, set null as result in ProviderResult.
The example below supports action invocation from the Device Manager. It implements the invokeAction method, dedicated to calls from the Generic Device Manager, adding support of the echo action – the action increments the value of the "number of messages" state variable of the target component control unit, saves the change in the RM storage, fires a change event and returns the echoed message to RM.
import org.osgi.service.metatype.MetaTypeProvider;import com.prosyst.mprm.admin.devices.ControlUnitID;import com.prosyst.mprm.admin.devices.event.StateVariableEvent; import com.prosyst.mprm.backend.ms.cu.spi.ControlUnitState;import com.prosyst.mprm.backend.ms.cu.spi.ProviderResult;import com.prosyst.mprm.backend.ms.cu.spi.component.ComponentSystemContext;import com.prosyst.mprm.common.ManagementException;public class MyDeviceFactory implements DeviceControlUnitFactory { String deviceType; String deviceId; private ComponentSystemContext ctx; . . . public void invokeAction(String cuId, String actionId, Object arguments, ProviderResult result) { try { // Prepare an object to accept the echoed message StringBuffer output = new StringBuffer(); // Echo the message invokeAction0(cuId, actionId, arguments, output); // Return the echoed message to the system result.setResult(output.toString(), null); } catch (Exception e) { result.setResult(null, e); } } // Helper method for echoing a message private void invokeAction0(String cuId, String actionId, Object arguments, StringBuffer output) throws Exception { if (actionId.equals("printMsg")) { ControlUnitState stateDb = ctx.retrieveControlUnitState(new ControlUnitID(MyComponentCUProvider.CU_TYPE, cuId, deviceType, deviceId)); if (arguments instanceof String) { // Increment the number of echoed messages int numberOfMsgs = incrementNumberOfMessages(stateDb); // Prepare the echoed message String outputMsg = "Message processed : "; output.append(outputMsg); output.append((String) arguments); // Update the component's state with the changed number of messages MyComponent state = new MyComponent(deviceType, deviceId); state.setID(cuId); // Not setting the whole CU state, but only the changed variable state.setNumberOfMsgs(numberOfMsgs); ctx.saveControlUnitState(state); // Fire an event that the number of messages has been changed ctx.stateVariableChanged(new StateVariableEvent( MyComponentCUProvider.CU_TYPE, cuId, deviceType, deviceId, MyComponent.COUNT_VAR_ID, new Integer(numberOfMsgs))); } else { throw new Exception("Invalid argument type"); } } else { throw new Exception("Action is not supported!"); } } // Helper method for incrementing the number of echoed messages int incrementNumberOfMessages(ControlUnitState stateDb) { // Get the number of messages echoed so far int numberOfMsgs = ((Integer)stateDb.getStateVariable(MyComponent.COUNT_VAR_ID)).intValue(); // Increase the number of messages numberOfMsgs++; return numberOfMsgs; } . . .} Provide Component Metadata
Implement a Metatype Provider
To describe the interface of the provided component control unit type to RM and management applications, the Component Control Unit Provider should return a Metatype Provider (org.osgi.service.metatype.MetaTypeProvider) in its getMetaType method.
The Metatype Provider should export metadata with AttributeDefinitions for state variables and extended ObjectClassDefinitions for actions.
Basically, there are two ways for providing a component Metatype Provider:
By implementing all required interfaces from the OSGi Metatype API
(org.osgi.service.metatype)and from the(org.mbs.services.metatype)Bosch Digital Metatype Extension API.By writing a metadata XML following the Bosch Digital defined DTD and converting it to a Metatype Provider through the
com.prosyst.mprm.util.metatype.MetaTypeProviderInfoutility (available in the System Package).
The code below returns the MetaTypeProvider instance generated from the metadata XML in the example following this one through the MetaTypeProviderInfo utility.
import java.io.IOException;import java.io.InputStream;import java.net.URL;import org.osgi.service.metatype.MetaTypeProvider; import com.prosyst.mprm.util.metatype.MetaTypeProviderInfo;import com.prosyst.mprm.backend.ms.cu.spi.ProviderResult;import com.prosyst.mprm.backend.ms.cu.spi.component.ComponentControlUnitProvider;import com.prosyst.mprm.backend.ms.cu.spi.component.ComponentSystemContext;import com.prosyst.mprm.common.ManagementException; public class MyComponentCUProvider implements ComponentControlUnitProvider { static final String CU_VERSION = "1.0.0"; static final String CU_TYPE = "my.component.cu"; private ComponentSystemContext ctx; . . . public void getMetaType(String cuVersion, ProviderResult result) throws ManagementException { // Get the metatype from the RM storage and return it to RM MetaTypeProvider metatype = ctx.getMetadataRecord(CU_TYPE, CU_VERSION); if (mtp == null) { mtp = loadMetatype(); } result.setResult(metatype, null); } private MetatypeProvider loadMetatype() throws ManagementException { try { URL mtpURL = bc.getBundle().getResource("component.xml"); InputStream in = mtpURL.openStream(); MetaTypeProviderInfo mtp = new MetaTypeProviderInfo(in); cuSystemContext.addMetadataRecord(CU_VERSION, mtp); return mtp; } catch (Exception e) { throw new ManagementException(e); } } . . .}Here is the code providing a metadata XML for a component control unit type.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><!DOCTYPE metatype-provider SYSTEM "metatype.dtd"><metatype-provider> <objectclass> <locale>en</locale> <name>My Component</name> <id>my.component.cu</id> <description/> <attribute modifier="req" load="true"> <name>Component ID</name> <id>id</id> <description/> <type>&string;</type> <cardinality>0</cardinality> </attribute> <attribute modifier="req"> <name>Component Display Name</name> <id>name</id> <description/> <type>&string;</type> <cardinality>0</cardinality> </attribute> <attribute modifier="req"> <name>Number of Printed Message</name> <id>msgCount</id> <description/> <type>∫</type> <cardinality>0</cardinality> <value> <scalar>0</scalar> </value> </attribute> <objectclass> <locale>en</locale> <name>Create My Component</name> <id>$create.</id> <description/> <attribute modifier="in"> <name>ID</name> <id>id</id> <description/> <type>&string;</type> <cardinality>0</cardinality> </attribute> <attribute modifier="in"> <name>Name</name> <id>name</id> <description/> <type>&string;</type> <cardinality>0</cardinality> </attribute> </objectclass> <objectclass> <locale>en</locale> <name>Delete Component</name> <id>$destroy</id> <description/> </objectclass> <objectclass> <locale>en</locale> <name>Print Test Message</name> <id>printMsg</id> <description/> <attribute modifier="in"> <name>Test Message</name> <id>inMsg</id> <description/> <type>&string;</type> <cardinality>0</cardinality> </attribute> <attribute modifier="out"> <name>Output Message</name> <id>outMsg</id> <description/> <type>&string;</type> <cardinality>0</cardinality> </attribute> </objectclass> </objectclass></metatype-provider>Define of a Parent
To define one or more parent types of a component, include an AttributeDefinition within the root ObjectClassDefinition for attribute with ID mbs.control.parent.type, type String or String[] and default value equal to the parent type(s).
The code below contains an XML AttributeDefinition of two parent types - "my.component1.cu" and "my.component2.cu".
<attribute modifier="req"> <name>ParentType</name> <id>mbs.control.parent.type</id> <description>Parent Types</description> <type>&string;</type> <cardinality>2</cardinality> <value> <array> <scalar>my.component1.cu</scalar> <scalar>my.component2.cu</scalar> </array> </value></attribute>Inherit a Superior Metatype
If a component control unit type is designed to inherit another type, that is, acquire all attributes of its "superior", the control unit metatype should contain an attribute with ID "super", type String and default value indicating the super type ID. If you want to exclude one or more actions of a super type, redefine the action with an "!" in front of the ID string.
The following example contains an XML AttributeDefinition of a super metatype called "my.generic.cu".
<attribute modifier="req"> <name>Super Type</name> <id>super</id> <description>Super Type</description> <type>&string;</type> <cardinality>1</cardinality> <value> <array> <scalar>my.generic.cu</scalar> </array> </value></attribute>Provide Icons for the Components
To provide icons that will represent each component of the same type and its actions, you need to implement the getIcon method. Depending on the passed object class ID and on the component control unit version, you can provide different icons.
Basically, you can retrieve icons from a file or from the system-provided control unit storage. To handle icons in the CU storage, use the addIcon, getIcon and deleteIcons methods of the provider's ComponentSystemContext.
The code below returns a single icon common to the component type and to all its actions. For a more detailed example on providing icons in a Control Unit Provider, check the example in the "Support Action Invocation" > "From the Generic Device Manager" subsections of the Extending RM with a New Device Type guide.
import java.io.IOException;import java.io.InputStream;import java.net.URL;import org.osgi.framework.BundleContext;import com.prosyst.mprm.backend.ms.cu.spi.ProviderResult;import com.prosyst.mprm.backend.ms.cu.spi.component.ComponentControlUnitProvider; public class MyComponentCUProvider implements ComponentControlUnitProvider { private BundleContext bc; . . . public void getIcon(String cuVersion, String ocdId, int size, ProviderResult result) throws ManagementException { // Get the icon's location in the provider's bundle JAR URL iconURL = bc.getBundle().getEntry("/component.png"); InputStream iconStream = null; try { iconStream = iconURL.openStream(); } catch (IOException e) { result.setResult(null, e); } // Return the stream to the icon to RM result.setResult(iconStream, null); } . . .}Destroy Control Units on Device Remove
As component control units are associated with a parent device, when a management application removes the device from RM, the system requests all involved Component Control Unit Providers to clean all related components. In this case, the system will calls the deviceRemoved method of your Component Control Unit Provider. The conventional actions to take are delete all control unit states, delete all parent-child relations within the scope of the deleted device and fire an event about each component's removal.
The couple if listings below contain an example of how to clear all components attached to a specific device. The deviceRemoved method of the example Component Control Unit Provider gets the factory for the device being removed and calls a specific method of the factory, which deletes the states of the components attached to the device and fires proper events:
import com.prosyst.mprm.backend.ms.cu.spi.component.ComponentControlUnitProvider; public class MyComponentCUProvider implements ComponentControlUnitProvider { . . . public void deviceRemoved(String deviceType, String deviceId) { MyDeviceFactory fact = new MyDeviceFactory(deviceType, deviceId, ctx); fact.destroyAll(); } . . .}Clearing all related components on device remove is shown in the following example:
import com.prosyst.mprm.admin.devices.ControlUnitID;import com.prosyst.mprm.admin.devices.event.ControlUnitEvent;import com.prosyst.mprm.backend.ms.cu.spi.component.ComponentSystemContext;import com.prosyst.mprm.backend.ms.cu.spi.component.DeviceControlUnitFactory;public class MyDeviceFactory implements DeviceControlUnitFactory { ComponentSystemContext ctx; String deviceType; String deviceId; . . . public void destroyAll() { try { // Get all component in the scope of the device from the RM storage String[] ids = ctx.getControlUnits(deviceType, deviceId, MyComponentCUProvider.CU_TYPE); // Delete the state of each component and fire an event for (int i = 0; i < ids.length; i++ ) { ctx.deleteControlUnitState(new ControlUnitID(MyComponentCUProvider.CU_TYPE, ids[i], deviceType, deviceId)); ControlUnitEvent destroyEvent = new ControlUnitEvent(ControlUnitEvent.CONTROL_UNIT_REMOVED, MyComponentCUProvider.CU_TYPE, ids[i], deviceType, deviceId); ctx.controlUnitEvent(destroyEvent); } } catch (ManagementException e) { e.printStackTrace(); } } . . .}Register the Component CU Provider as a Service
After implementing the ComponentControlUnitProvider interface, you need to register it as an OSGi-compliant service on the RM backend hosts with the management server role. The service should have the following properties:
"mbs.control.type" (org.mbs.services.cu.ControlConstants.TYPE)– Shows the type of components this Component Control Unit Provider service handles."mprm.cu.device.type" (ComponentControlUnitProvider.DEVICE_TYPE)– Shows the type of devices that the component control unit type is valid for."mbs.control.version" (org.mbs.services.cu.ControlConstants.VERSION)– Optional. Indicates the supported component type version in case versioning is supported.
The example belowis a bundle activator, which registers and unregisters the example Component Control Unit Provider as a service on a backend framework. The registration properties indicate that the provider handles components of type "my.component.cu" and version "1.0" for devices of type "my.device.cu" (the Extending RM with a New Device Type guide contains the code of the "my.device.cu" provider).
import java.util.Hashtable;import org.mbs.services.cu.ControlConstants;import org.osgi.framework.BundleActivator;import org.osgi.framework.BundleContext;import org.osgi.framework.ServiceRegistration;import com.prosyst.mprm.backend.ms.cu.spi.component.ComponentControlUnitProvider; public class MyComponentCUActivator implements BundleActivator { ServiceRegistration sReg; public void start(BundleContext bc) throws Exception { Hashtable props = new Hashtable(); props.put(ComponentControlUnitProvider.DEVICE_TYPE, "my.device.cu"); props.put(ControlConstants.TYPE, MyComponentCUProvider.CU_TYPE); props.put(ControlConstants.VERSION, MyComponentCUProvider.CU_VERSION); MyComponentCUProvider provider = new MyComponentCUProvider(bc); sReg = bc.registerService(ComponentControlUnitProvider.class.getName(), provider, props); } public void stop(BundleContext bc) throws Exception { if (sReg != null) { sReg.unregister(); sReg = null; } }}