WireAdmin Bundle

The WireAdmin Bundle provides implementation of the OSGi Wire Admin Service. This service manages the wiring topology of the OSGi framework providing connections between producer and consumer services.

Contents:


Bundle Information

Bundle JAR

The JAR file of this bundle is wireadmin.jar, located in the bundles folder.

Import

Package Exporter Description
com.prosyst.util.hash System Bundle Hashtable utilities.
com.prosyst.util.time The time utility for high-resolution time measurement
com.prosyst.util.pcommands ProSyst Util Bundle /
ProSyst Util Full Bundle
Provides the API for creating a Pluggable Commands Service. This service is detected by the Parser Service for creating a new command group.
com.prosyst.util.ref Allows sending log messages to the Log Service.
org.osgi.service.wireadmin OSGi Library Bundle
The OSGi Wire Admin Service API.
org.osgi.service.event Contains the Event Admin service API whose management part is implemented by the Event Admin Bundle.
org.osgi.util.tracker Delivers the OSGi Service Tracker utility for enhanced tracking of services.
org.osgi.service.cm Enables configuration management through the OSGi Configuration Admin Service.
org.osgi.util.measurement Holds utilities for measuring states and units.

Note: When the bundle is installed on ProSyst mBS framework, the com.prosyst.util.ref and the com.prosyst.util.pcommands packages are exported by the ProSyst Util Bundle. If the bundle is installed on another vendor's framework, the packages are provided by the ProSyst Util Full Bundle.

Export

The WireAdmin bundle exports the org.mbs.util.wireadmin package, which delivers the ProSyst WireAdmin Utility API.


Overview of the Wire Admin Service

The goal of the OSGi Wire Admin Service is to enable services that generate some sort of data to send it to the services interested in the same data. The data can be updated dynamically so that the interested services can receive the new values regularly. The Wire Admin Service provides configuration data (in the OSGi Configuration Admin Service) through which new virtual connections (known as wires) can be established when a new service needs to receive the data output. Useless wires can easily be removed. The main advantage of using the Wire Admin service is that it decreases the need for wired bundles to have context-specific knowledge about the opposite party. They never need to communicate with each other directly but through the Wire Admin Service.

A typical data-producing service can be, for example, one that represents some physical device. It produces information about its current status. Typical data-consumers can be detectors, gauges, user interfaces, etc.

A typical wiring system in the OSGi framework
Figure 1: A typical wiring system in the OSGi framework

Each service which is supposed to produce some data output is referred to as producer. A producer must register the org.osgi.service.wireadmin.Producer interface. Details about creating producers and working with them are available in the Creating a Producer Service part.

The service which is supposed to consume the produced data is called consumer. A consumer must be registered under the org.osgi.service.wireadmin.Consumer interface. A consumer for some data type can also be a producer for another data type. More information about consumers is available in Creating a Consumer Service.

In the most common case, there should be a separate producer/consumer couple for each type of transferred information. However, when there are too many data flows to be handled, it is possible for a producer to be responsible for the generation of several data types, and for a consumer to receive more than one information type. These are called composite producer and composite consumer respectively. They are described in more details in the Creating Composite Producers and/or Consumers part of this document.

Note: Data type does not mean the Java object class of the transferred information! Data type refers to the logical description of the information, for example: door lock status, camera status, etc. The Java object classes that wrap the data are known as flavors.

The wire between a consumer and producer couple is represented by an org.osgi.service.wireadmin.Wire object, created by the Wire Admin. A wire is never opened automatically but when the user explicitly requests it (see Creating Wires for details). More than one wire may be opened between a producer/consumer couple. By default, the data sent across the wire is filtered by the Wire object itself. Filtering in this case implies the rate at which new data will be delivered (there could be time-based and value-based filters with LDAP syntax). The filtering can also be handled by the producer itself. In such case, the Wire object does not perform any filtering. Both filtering ways are described in the Filtering the Data Output part.

All wires are managed by the Wire Admin service. It is the sole party that can create, delete or update wires. Producers and consumers do not have the right to manage wires.

Producers, consumers and wires can have different registration properties which distinguish them and allow them to be obtained through LDAP filtering. These properties are defined as constants in org.osgi.service.wireadmin.WireConstants. In addition, both consumers and producers must have the org.osgi.framework.Constants.SERVICE_PID ("service.pid") property.


Services

Wire Admin Service

This service is registered with the framework under the org.osgi.service.wireadmin.WireAdmin interface. It manages the connections between consumer services and producer services as described in the OSGi Specification.

Creating a Producer Service

A producer service must implement and register the org.osgi.service.wireadmin.Producer interface. Its polled(Wire) method should return the data output sent across the wires. The consumersConnected(Wire[]) method actualizes the list of connected wires.

Each producer must be registered with the following properties:

The following example creates a producer for String output. This is indicated by the value of the org.osgi.service.wireadmin.WireConstants.WIREADMIN_PRODUCER_FLAVORS registration property. The service is also registered with the producer.all PID. The only output it sends is a single String. It will be received by consumers connected with wires to this producer.

import org.osgi.service.wireadmin.*;
import org.osgi.framework.*;
import java.util.Hashtable;


public class ProducerTest implements Producer, BundleActivator {
  private ServiceRegistration reg;
  Object output;
  
  public void start (BundleContext bc) throws BundleException {
    Hashtable props = new Hashtable();
    //the producer will be sending String data (flavors)
    Class[] flavors = new Class[] {String.class};
    props.put(WireConstants.WIREADMIN_PRODUCER_FLAVORS, flavors);
    //the producer PID property
    props.put("service.pid", "producer.all");
    reg = bc.registerService(Producer.class.getName(), this, props);    
  }
  
  public void stop (BundleContext bc) throws BundleException {
    reg.unregister();    
  }
  
  /** If there are connected wires, updates them with the produced values */
  public void consumersConnected(Wire[] wires) {
    if (wires != null) {
      for (int i = 0; i < wires.length; i++) {
        wires[i].update(polled(wires[i]));
      }
    }    
  }

  /** This method is responsible for creating the output */
  public Object polled(Wire wire) {
    String output = "Hello there! This is the producer speaking!";
    return output;    
  }
}
Listing 1.1: Creating a simple producer

Filtering the Data Output

The produced output can be filtered in two ways:

As mentioned earlier in this document, the data filtering can be time-based or value-based. Value-based filtering can be applied for Number data only.

For constructing value-based filters, the following wire constants can be used:

For constructing time-based filters, the WireConstants.WIREVALUE_ELAPSED ("wirevalue.elapsed") constant can be used. It represents the elapsed time in milliseconds between the current value and the previous value sent by the producer.

Listing 1.2 illustrates producer-made output filtering. It provides value-based filtering in the producer's polled method. It sends information about the current temperature only if it is higher than 25 degrees.

import org.osgi.framework.*;
import org.osgi.service.wireadmin.*;
 
import java.util.Hashtable;
  
public class ProducerThatFilters implements Producer {
    
    BundleContext bc;    
    private double currentTemperature;    
 
    public ProducerThatFilters(BundleContext bc) {
      this.bc = bc;
      Hashtable props = new Hashtable(3);
      Class[] flavors = new Class[] {Double.class};
      //the producer registers for Double data output
      props.put(WireConstants.WIREADMIN_PRODUCER_FLAVORS, flavors);
      props.put(WireConstants.SERVICE_PID, "producer.that.filters");
      // set this registration property with some value 
      //to indicate that filtering is performed by the producer
      props.put(WireConstants.WIREADMIN_PRODUCER_FILTERS, "some.value");
      bc.registerService(Producer.class.getName(), this, props); 
    }
 
    public void consumersConnected(Wire[] wires) {
        ...
    }
    
    //the data is sent only if it is no smaller than 25
    public Object polled(Wire wire) {
      return (currentTemperature >= 25.d) ? new Double(currentTemperature) : null;
    }
}
Listing 1.2: Producer-made data filtering

Filtering data through the Wire itself is illustrated in Listing 5 from the Getting the WireAdmin Service part.

Creating a Consumer Service

A consumer service must implement and register the org.osgi.service.wireadmin.Consumer interface. It must have the following list of registration properties:

The updated(Wire, Object) method of the consumer is responsible for actualizing the received data. It is called by the connected wires whenever new data is available. The producersConnected(Wire[]) method updates the list of connected wires if new wires are available, or already known wires are updated/removed.

Note: A consumer service will be able to receive data only if there is at least one connected wire with a producer!

The following simple example creates a consumer service ready to accept any data types. Its service PID is consumer.all.

import java.util.Hashtable;

import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.ServiceRegistration;
import org.osgi.framework.BundleException;
import org.osgi.service.wireadmin.*;

public class ConsumerImpl implements Consumer, BundleActivator {	           	
  private ServiceRegistration reg;

  public void start(BundleContext bc) throws BundleException {
    Hashtable prop = new Hashtable();
    //this property shows that the consumer will accept any data types
    prop.put(WireConstants.WIREADMIN_CONSUMER_FLAVORS, new Class[] {Object.class});
    //the identifier property of the consumer	
    prop.put("service.pid", "consumer.all");
    //registering the service on the framework
    reg = bc.registerService(Consumer.class.getName(), this, prop);    
  }
  
  public void stop(BundleContext bc) throws BundleException {
    reg.unregister();
  }
  
  /** Watches the list of wires*/
  public void producersConnected(Wire[] wires) {
    if (wires == null) {
	  System.out.println("Not connected to any wires");
    } else {
      System.out.println("Connected to " + wires.length + " wires");
    }
  }	
  
  /** Receives the new data whenever such are available */
  public void updated(Wire wire, Object value) { 
    System.out.println("Updated " + wire + " with value " + value);
  }
  
}	
Listing 2: Creating a simple consumer

The ways of creating wires are presented later in this document.

Creating Composite Producers and/or Consumers

Composite consumers and producers have additional registration properties besides those for ordinary producers and consumers. They were listed in the Creating a Producer Service and Creating a Consumer Service parts.

Such composite services exchange data in the form of org.osgi.service.wireadmin.Envelope objects (flavors). An Envelope wraps a number of data types, for example: "front left door status", "rear left door status" and "airbag status". The org.osgi.service.wireadmin package provides the following class implementing the Envelope interface:

The org.mbs.util.wireadmin package provides ProSyst's implementations of the Envelope interface. They serve for transferring primitive data.There are three implementation classes:

The following example illustrates implementing a composite producer service. The consumersConnected method is skipped because there is nothing interesting in its implementation.

      . . .
  private String[] scope = new String[] {"current.date", "hello", "bye"};
  private Class[] flavors = new Class[] {Envelope.class};
    . . .
	
    /** Registering the service with the necessary props */
    java.util.Hashtable props = new java.util.Hashtable();
    //the data types transmitted by this producer
    props.put(WireConstants.WIREADMIN_PRODUCER_SCOPE, scope);
    props.put(org.osgi.framework.Constants.SERVICE_PID, "test.producer");
    //this property indicates the PIDs of the consumers 
//that the producer will communicate with
props.put(WireConstants.WIREADMIN_PRODUCER_COMPOSITE, new String[] {"test.consumer"}); //for composite services, the value of this property must be Envelope props.put(WireConstants.WIREADMIN_PRODUCER_FLAVORS, flavors); bc.registerService(Producer.class.getName(), this, props); . . . public Object polled(Wire wire) { String date = new java.util.Date().toString(); BasicEnvelope envelope = new BasicEnvelope(date, "test.producer", "current.date"); System.out.println("Message from the producer: My current date is " +
envelope.getValue()); return envelope; }
Listing 3: Creating a composite producer service

Creating a WireAdmin Listener

The WireAdminListener interface allows you to receive WireAdminEvent-s notifying of changes in the state of the wire. The WireAdminEvent class provides the following types of events and their correspondent class fields:

WireAdminListener objects must be registered as a service by using the WireConstants.WIREADMIN_EVENTS service property. The value of this property represents a bitwise OR of all the event types the listener is interested in. If a WireAdminListener object is registered without a service property WireConstants.WIREADMIN_EVENTS, then the WireAdminListener will receive no events.

The following example registers a Wire Admin Listener for exceptions generated by producers and consumers.

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.wireadmin.*;

import java.util.Hashtable;

public class ExceptionsListener implements BundleActivator, WireAdminListener {

  private ServiceRegistration reg;

  public void start(BundleContext bc) {
    //bitwise OR of the event types
    Integer bitmask = new Integer(WireAdminEvent.PRODUCER_EXCEPTION | 
WireAdminEvent.CONSUMER_EXCEPTION); Hashtable props = new Hashtable(); props.put(WireConstants.WIREADMIN_EVENTS, bitmask); reg = bc.registerService(WireAdminListener.class.getName(), this, props); } public void stop(BundleContext bc) { reg.unregister(); } // WireAdminListener implementation public void wireAdminEvent(WireAdminEvent e) { switch (e.getType()) { case WireAdminEvent.PRODUCER_EXCEPTION: // do some work break; case WireAdminEvent.CONSUMER_EXCEPTION: // do some work break; } } }
Listing 4: Creating a Wire Admin Listener

Pluggable Commands Service

A com.prosyst.util.pcommands.PluggableCommands service is registered by the bundle. It serves for plugging a new console command group. This service cannot be used in a programming way. It is used through the console commands created by it. More information about them is available in Using the wa Console Command Group.

Managed Service Factory

The org.osgi.service.cm.ManagedServiceFactory interface is registered with the framework. It allows the Wire Admin Service to store configuration properties with the Configuration Admin Service. It could be obtained and used in a programming way or through its property editor in mConsole (MC) from the Framework Professional Edition Package .

The FPID of this Managed Service Factory is: mbs.wireadmin.fpid.


Creating Wires

A wire is represented by an org.osgi.service.wireadmin.Wire object. It can be used by producers to update the data objects available in it through its update(Object) method. Consumers can request the updated information by invoking the wire's poll() method. More than a single wire can connect a consumer and a producer.

With regard to the established connection, a wire has two states:

With regard to its validity, a wire can be:

There are three ways in which you can create a wire: using the server runtime console, using the property editor of the Wire Admin Service in the mConsole administrator, or programmatically by getting and using the Wire Admin Service.

Note: Initially, the Wire Admin service contains no default wires. Such have to be created by the developers needing them.

Note: For each created wire, no matter the way in which it is created, the Wire Admin creates a new configuration instance representing the wire.

Using the wa Console Command Group

The wa console command group allows managing wires available in the Wire Admin. To enter this group, type "wa/" in the server's runtime console.

The following commands are available:

Command Shortcut Description
list
ls

Prints the available wires, producers or consumers.

Usage:

list
Lists all available wires.
list -p
Lists all registered producers.
list -c
Lists all registered consumers.
list -p <producer_PID>
Lists all wires connected to the producer with the given PID.
list -c <consumer_PID>
Lists all wires connected to the consumer with the given PID.
list -d -p <producer_PID>
Shows a list of all wires connected to the producer with the given PID in which the consumer service is not registered.
list -d -c <consumer_PID>
Shows a list of all wires connected to the consumer with the given PID in which the producer service is not registered.
list -i -p <producer_PID>
Shows information about the producer with the given PID.
list -i -c <consumer_PID>
Shows information about the consumer with the given PID.
list -w <wire_PID>
Shows information about the wire with the given PID.

 

create [<data_filter>] <producer_PID> <consumer_PID> c Creates a new wire between the specified producer and the specified consumer. The <data_filter> is an optional part. It represents the filter for the data that will go through the wire. It must be constructed according to the requirements for constructing standard LDAP filers. The criteria for the filtering can be the values of the filtering constants of the WireConstants interface. See Filtering the Data Output for details.

For example, the command can be used in this way:

create consumer.test producer.test
This command would create a wire between the consumer with PID "consumer.test" and producer with PID "producer.test".

If we want the wire to handle data filtering, we could re-write the latter command line adding an LDAP filter like this:

c (wirevalue.current>10) consumer.test producer.test
delete <wire_ID> d Deletes the specified wire.

Using the Property Editor in mConsole

mConsole allows easy management of available wires through the GUI provided by the mConsole administrator. To enter the property editor of the WireAdmin Configuration, unfold the WireAdmin Bundle from the bundles tree and enter its node called "WireAdmin Configuration". The main pane contains the properties contained in this factory configuration and allows you to administer them. Description of the properties is available in the Configuration Resources part.


Figure 2: The property editor of the WireAdmin Bundle

Note: Initially, the WireAdmin Bundle contains no configuration instances because it contains no default wires.

To create a new wire, you need to create a new instance of the factory configuration. This is done with the following steps:

  1. Press the Create button.
  2. In the new small dialog that appears check all properties that you want to be included in the new wire. Required properties may not be excluded.
  3. When ready, press OK.


    Figure 3: Creating a new configuration (wire)

  4. The properties contained in the new configuration instance appear in the main pane. Type the desired values in the corresponding fields.
  5. When ready, press Set.

Getting the WireAdmin Service

To create a new wire in a programming way, get reference to the Wire Admin Service and invoke its createWire method. The parameters needed for this method are: producer PID, consumer PID and a Dictionary of the properties of this wire. The following properties are implicitly added to the Dictionary of properties by the Wire Admin Service:

Note:You should not include these properties when creating the wire because they are automatically overridden by the Wire Admin Service.

The following properties can optionally be supplied to create a wire filter that will be passed as a value to the WireConstants.WIREADMIN_FILTER property:

The following example invokes the Wire Admin Service to create a wire with a temperature filter based on both value and time criteria. It will allow sending data only if the following three conditions are true: the values are between 0 and 100, the time interval between two produced values is greater than 500 ms and the absolute delta between the current and the previous value is less than 20.

import org.osgi.framework.*;
import org.osgi.service.wireadmin.*;
import java.util.Hashtable;

public class WATest implements BundleActivator {

  private ServiceReference waRef;
  private WireAdmin wa;
  private Wire wire;

  public void start(BundleContext bc) throws BundleException {
    // getting a WireAdmin service reference
    waRef = bc.getServiceReference(WireAdmin.class.getName());
    
    if (waRef == null) {
      throw new BundleException("Unable to get WireAdmin service reference!");
    }

    WA = (WireAdmin) bc.getService(waRef);
    if (WA == null) {
      throw new BundleException("WireAdmin service has not been registered!");
    }

    Hashtable wireProps = new Hashtable();
    // A temperature filter (considering 
// that the Producer service produces numerical values) // that will evaluate to true if and only if the values send through this wire // are in the interval 0-100, the time interval // between two produced values is more than 500ms // and the absolute delta (|current - previous|) is less than 20
String wireFilter = "(&(" + WireConstants.WIREVALUE_ELAPSED + ">=500)" + "(" + WireConstants.WIREVALUE_CURRENT + ">=0)" + "(" + WireConstants.WIREVALUE_CURRENT + "<=100)" + "(" + WireConstants.WIREVALUE_DELTA_ABSOLUTE + "<=20))"; // setting the filter wireProps.put(WireConstants.WIREADMIN_FILTER, wireFilter); wire = wa.createWire("producer.pid", "consumer.pid", wireProps); } public void stop(BundleContext bc) { wa.deleteWire(wire); bc.ungetService(waRef); } }
Listing 5: Wire-made data filtering


Configuration

Configuration Resources

As mentioned in the beginning, the WireAdmin Bundle stores configuration resources with the OSGi Configuration Admin. It owns a factory configuration through which you can create and manage wires. Each single configuration produced from the factory corresponds to a separate wire. Thus, you can create a new wire by creating a new configuration instance.

A configuration (i.e. a wire) can contain the following properties:

Configuration Property Name Type Description
wireadmin.consumer.pid Consumer PID String Required. The PID under which the consumer service is registered.
wireadmin.producer.pid Producer PID String Required. The PID under which the producer service is registered.
wireadmin.filter filter String Optional. The LDAP filter for the data output. It is constructed according to the rules for constructing normal LDAP filter statements. The criteria for the filtering can be the values of the filtering constants of the WireConstants interface. See Filtering the Data Output for details.

Visual Administration

The above configuration properties can be administrated through the property editor of the WireAdmin Bundle in the mConsole application from the Framework package. See Using the Property Editor.

System Properties

The WireAdmin Bundle uses the following system properties:

System Property Description
mbs.wireadmin.persistencyDisabled If true, disables the use of the OSGi Configuration Admin for storing/loading persistent data of wires. In this way, the wires will not be recreated after an OSGi framework restart. Use this option when you want to manage the wires' lifecycle by yourself. Default is false.
mbs.services.wireadmin.debug Turns on/off generation of debug information about the runtime operation of the WireAdmin Bundle.
mbs.services.wireadmin.console Enables printing produced debug output into the framework's text console.

To use successfully the above system properties, you have to specify them:

For more information about using system properties, refer to the "System Properties" document from "Getting Started".


References


OSGi Bundles