This document is a programmer's guide to using the features of the RM Component Tracker, part of the RM device management system.
Overview
In this way, all control units (device roots or components) ever known to the system can share common information which will be stored in a central place and will be usable by other modules through the Component Tracker. For more information about the functions of the Component Tracker, refer to the Device Management System Architecture document.
The Component Tracker sub-system has three layers with two interaction interfaces:
Application Layer – Includes applications (mostly front-end) accessing the content of the Component Tracker. Such applications can only perform monitoring without any destructive actions. See the "Using the Front-End Tracker API" section below for concrete details on using the tracker.
System Layer – Includes the Component Tracker itself. It implements storage for component track records to backend modules as well as provides monitoring capabilities to management applications.
Provider Layer – Contains providers of track interface definitions and providers of track records.
Track definition providers – They provide backend services that declare track interfaces and foreign variables for specific control unit types. Usually, Component CU Providers acts as track definition providers as well. The tracker interacts with the track definition providers only for gathering track interface deviations. See the "Defining a Track Interface and Foreign Variables" section below for more information about providing track definitions.
Track record providers – They use a system-provided backend service to pass control unit states to the tracker, which then stores appropriate records. Track record providers can use the backend service to monitor the tracker content, as well as to add and remove records from the tracker. By default, the system implements such a track record provider for those control units saved in the common Control Unit Database (see Device Management System Architecture).
The providers usually pass full control unit states regardless of the track interface declared, and the tracker cuts the opportune track template (regarding the track interface declaration) and stores it guaranteeing uniqueness of the track template. See the "Saving and Deleting Track Records"section below for more details on implementing a track record provider.
Using the Front-End Tracker API
The Component Tracker provides the Control Unit Tracker front-end API to retrieve the component control unit variants available on managed devices. With the Control Unit Tracker API you can a management application can get type-specific track interfaces, list track records and associated foreign state variables, and listen for changes in the tracker.
The Control Unit Tracker API is associated with the com.prosyst.mprm.admin.cutracker package. The API's main component is ControlUnitTracker, whose instance can be retrieved in one of the following ways:
On the RM backend as a service in the context of the control center or remote access server OSGi framework.
Through the remote access client (RAC) from a location remote to RM by using the
getServiceorgetRemoteReferencemethod ofcom.prosyst.mprm.rac.RemoteAccessClient.See the Remote Access to RM guide from the System Package document for more information about using a remote access client.The RAC class libraries to use are lib/rac/system-rac.jar and lib/rac/gdm-rac.jar.
Developers can also use the lib/api/gdm-api.jar archive for development and compilation of applications using the front-end API of the Component Tracker.
Getting a Track Interface
To get the track interface modeling the templates for a specific component control unit type, use the getTrackInterface method of the Control Unit Tracker. To get the control units types exporting track interfaces to the Component Tracker, use the getTrackedControlUnitTypes method.
import com.prosyst.mprm.admin.cutracker.ControlUnitTracker;import com.prosyst.mprm.common.ManagementException; public class CuTrackerTest{ CuTrackerTest() {// Getting the ControlUnitTracker service . . . String[] trackedTypes = tracker.getTrackedControlUnitTypes(); for (int i = 0; i < trackedTypes.length; i++) { printInterface(trackedTypes[i]); } }// Prints the state variables of a track interface private void printInterface(String cuType) { System.out.println("[CuTrackerTest] Track interface for " + cuType); String[] sVars = null; try { sVars = tracker.getTrackedInterface(cuType); } catch (ManagementException e) { e.printStackTrace(); } for (int i = 0; (sVars != null) && (i < sVars.length); i++) { System.out.println("[CuTrackerTest] \t#" + i + " " + sVars[i]); } }}Using Track Records
Track records are available to management applications as TrackedControlUnit instances. Each TrackedControlUnit contains its track ID, control unit type, track interface and tracked state variable values. You can retrieve a specific track record by its ID - use the getTrackedControlUnit method.
You can get the track records of a tracked control unit type, optionally filtered by specific features, as shown in the following couple of examples
To get the track records of a CU type altogether, use the
getTrackedControlUnitsmethod.To get the IDs of a CU type's records, use the
getTrackedControlUnitIdsmethod.
Using Foreign Variables
To get the tracked values of a foreign state variable associated with a track record, call the getForeignValues method of the Control Unit Tracker. You can get the list of foreign state variables associated with a tracked control unit type by using the getForeignVariableNames method.
The example below gets the values of the foreign state variables watched for all track records having attribute (i.e. a state variable participating in the track interface) "id" equal to "my.component.1".
import com.prosyst.mprm.admin.cutracker.ControlUnitTracker;import com.prosyst.mprm.admin.cutracker.TrackedControlUnit;import com.prosyst.mprm.common.ManagementException;import com.prosyst.mprm.data.Enumerator; public class CuTrackerTest{ CuTrackerTest() { // Getting the ControlUnitTracker service . . . String[] trackedTypes = tracker.getTrackedControlUnitTypes(); for (int i = 0; i < trackedTypes.length; i++) { printForeignVars(trackedTypes[i]); } }// Prints the foreign variables of specific track records private void printForeignVars(String trackedType) throws Exception { // Creating an CU filter for CUs with id equal to "my.component.1" String filter = "(\"id\" == \"my.component.1\")"; // Getting the track records matching the filter Enumerator trackRecords = tracker.getTrackedControlUnits(trackedType, filter); // Retrieving the values of the foreign variables saved for a matching track record while (trackRecords.hasMoreElements()) { TrackedControlUnit trackRecord = (TrackedControlUnit) trackRecords.nextElement(); System.out.println("[CuTrackerTest] trackRecord for " + trackedType + ": " + trackRecord.getTrackId()); String[] foreignVars = tracker.getForeignVariableNames(trackedType); for (int i = 0; i < foreignVars.length; i++) { Object[] foreignValues = tracker.getForeignValues(trackRecord.getTrackId(), foreignVars[i]); for (int j = 0; j < foreignValues.length; j++) { System.out.println("[CuTrackerTest] \tforeign var: " + foreignVars[i] + " = " + foreignValues[j]); } } } }}Tracing Track Records Inheritance
In case the tracker keeps information about a component type extending other super type, the Control Unit Tracker interface allows you to directly access the extension records attached to a track record of the super type.
To get the extension track records (TrackedControlUnit instances organized in an Enumerator), use the getExtensions method providing as arguments the track ID of the super track record, the type of the extending control unit type and optionally a control unit filter compliant with the RM Management Script.
You might as well use the getExtensionIds to retrieve only the track IDs of the extension records.
The code below gets the extending types from the Device Manager (see System-Wide Device Management), if existing, for all tracked CU types. Next, the example gets the extension track records related to each record for the super type.
import com.prosyst.mprm.admin.cutracker.ControlUnitTracker;import com.prosyst.mprm.admin.cutracker.TrackedControlUnit;import com.prosyst.mprm.admin.devices.DeviceManager;import com.prosyst.mprm.common.ManagementException;import com.prosyst.mprm.data.Enumerator; public class CuTrackerTest{ CuTrackerTest() { //Getting the ControlUnitTracker service . . . String[] trackedTypes = tracker.getTrackedControlUnitTypes(); for (int i = 0; i < trackedTypes.length; i++) { findExtensions(trackedTypes[i]); }} // Prints the extensions associated with the records of a specific super type private void findExtensions(String superCuType) { try { System.out.println("[CuTrackerTest] Super type " + superCuType); // Getting extending CU types from the Device Manager service String[] extTypes = getExtendingTypes(superCuType); if (extTypes == null) return; Enumerator trackIDs = tracker.getTrackedControlUnitIds(superCuType, null); while (trackIDs.hasMoreElements()) { // Getting extension track records for each super track record of the specified type String trackID = (String) trackIDs.nextElement(); System.out.println("[CuTrackerTest] \ttrack record: " + trackID); for (int i = 0; i < extTypes.length; i++) { System.out.println("[CuTrackerTest] \t\textension type " + extTypes[i]); Enumerator extensionIDs = tracker.getExtensionIds(trackID, extTypes[i], null); while (extensionIDs.hasMoreElements()) { String extensionID = (String) extensionIDs.nextElement(); System.out.println("[CuTrackerTest] \t\t\textension ID " + extensionID); } } }} catch (ManagementException e) { e.printStackTrace();} catch (Exception e) { e.printStackTrace(); }} // Gets the extending types of a specified CU type by calling the Device Manager private String[] getExtendingTypes(String cuType) throws ManagementException { DeviceManager devMngr = null; // Getting the DeviceManager service if (devMngr != null){ // Getting the extending types String[] extTypes = devMngr.getExtendingTypes(cuType, false); if (extTypes != null && extTypes.length > 0) { return extTypes; } } return null; }}Getting Notified of Changes in the Tracker
To receive events about newly-added track records, you have to implement a TrackListener and register it in the Control Unit Tracker by calling the addTrackListener method. When an event occurs, the tracker will call the trackedEvent method of the listener providing in a TrackEvent object the ID of the new track record, the control unit type of the record, etc.
The example below . It registers a track listener, which receives and prints information about three events and then exits. The snippet is intended mainly for use within a RAC-based standalone application.
import com.prosyst.mprm.admin.cutracker.ControlUnitTracker;import com.prosyst.mprm.admin.cutracker.TrackEvent;import com.prosyst.mprm.admin.cutracker.TrackListener;import com.prosyst.mprm.admin.cutracker.TrackedControlUnit;import com.prosyst.mprm.common.ManagementException; public class CuTrackerTest implements TrackListener{ private ControlUnitTracker tracker; private Object monitor = new Object(); private counter = 0; CuTrackerTest() {// Getting the ControlUnitTracker service . . . // Subscribing for tracker events tracker.addTrackListener(null, this); // Waiting for events synchronized (monitor) { monitor.wait(); } // Unsubscribing and exiting tracker.removeTrackListener(this); System.exit(1); } // Method inherited from TrackListener. Capable of processing three events. public void trackedEvent(TrackEvent trackEvent) { int eventType = -1; counter++; if (counter < 3) { eventType = trackEvent.getEventType(); if (eventType == TrackEvent.TRACKED_UNIT_ADDED) { System.out.println("[CuTrackerTest] Record for type " + trackEvent.getControlUnitType()); try { System.out.println("[CuTrackerTest] Track record is " + tracker.getTrackedControlUnit(trackEvent.getTrackId())); } catch (ManagementException e) { e.printStackTrace(); } } } else { synchronized (monitor) { monitor.notifyAll(); } }}Defining a Track Interface and Foreign Variables
To define a track interface for a component control unit type, you have to simply implement a com.prosyst.mprm.backend.cutracker.ControlUnitTrackDefinition service and register it in the backend OSGi frameworks of RM management servers with the following service properties:
org.mbs.services.cu.ControlConstants.TYPEequal to the component control unit type to be trackedControlUnitTrackDefinition.TRACK_INTERFACEequal to aString[]holding the IDs of the state variables of the corresponding track interfaceControlUnitTrackDefinition.FOREIGN_VARIABLESequal to aString[]holding the IDs of the foreign state variables.
Saving and Deleting Track Records
Overview
Adding and removing track records, as well as monitoring the content of the Component Tracker, can be performed by using the Backend Control Unit Tracker service (com.prosyst.mprm.backend.cutracker.BackendControlUnitTracker), available for all backend host roles.
A Component Control Unit Provider can easily access the Backend Control Unit Tracker from its ComponentSystemContext without the need of getting it from the OSGi service registry.
In addition, you can access the content of the Component Tracker conveniently straight from the Backend Control Unit Tracker by calling the methods inherited from the Front-End Control Unit Tracker service.
Adding and Removing Track Records
There are two approaches for adding and removing records to the Component Tracker:
Automatic (system-provided) – RM automatically saves track records for each tracked component control unit type, whose instances are stored in the system-provided Control Unit Database. In detail, when the relevant Component CU Provider calls the
saveControlUnitStatemethod of theComponentSystemContextobject allocated for the provider, the Device Manager provides to the Backend Control Unit Tracker service the corresponding information.Explicit – If the Component CU Provider should have its control units tracked but uses a custom persistent storage for saving control unit states, the provider has to explicitly call the Backend Control Unit Tracker in the way described in the next paragraphs.
To add track records, call the addTrackedControlUnit method with argument the control unit state for a new control unit or a modified one.
You can remove only specific track records by calling the removeTrackedControlUnits method or clean all records for a specific control unit type by calling the cleanTracks method.
Guidelines for Converting Track Records
Converting track records in case of a new track interface not fully compatible with track records of the previous interface stands for removing the old records from the Component Tracker and re-adding them with their new structure. For control units saved in the system Control Unit Database, RM converts the records automatically. For control units saved in a custom storage, the provider or another application should implement its own mechanism for record conversion. For example, there are two basic alternatives for converting records:
Delete all existing track records, list available control unit instances and re-add a correct record for each of them. This is the most reliable case - it is expected to work in all situations regardless of how the track interface is changed. Besides, the records will be updated according to the current state of deployed components. The flaw of this variant is that old format records not associated with a deployed component will not be available after the conversion.
List track records one by one, delete each of them and add a new record. The flaw of this variant is that it will not work when expanding the track interface, and that the converting application should keep a temporary buffer during the conversion.
