Draft UPnP/Jini Specification

Jan Newmarch

Version 0.3, 1 August 2005

Decision points appear in this format

Device and service structure

A UPnP device contains a list of services and may also contain other devices (which in turn will contain services and possibly more devices). The root device has a number of properties such as friendly name, manufacturer, etc. Each service contains a set of actions. Actions take parameters of in or out direction, and each parameter is associated with a state variable. State variables are of a small number of types such as string and int, and may take on any values of the type or a restricted subset. Additionally, a state variable may optionally generate events on state change.

Data types

UPnP has defined a set of standard data types: ui1, ui2, ui4, i1, i2, i4, int, r4, r8, number, fixed.14.4, float, char string, date, dateTime, dateTime.tz, time, time.tz, boolean, bin.base64, bin.hex, uri, and uuid. State variables must be one of these types.

Some of these types may be represented directly, such as i1, i2 and i4. These could be represented by primitive types such as byte or by objects such as Byte. Data types represented on the wire by SOAP have a string format, so the string has to be converted to the primitive type. In restricted types of numeric types (see later), there are optional bounds and step size. It is not possible to represent the absence of a bound or step by any particular primitive value, but the absence can be represented by a null value of an object.

1. Some UPnP types can be represented by Java primitive types or by corresponding objects. The type mapping chosen is as objects, with a constructor that takes a string. These are standard Java classes such as java.lang.Integer

There are no unsigned numeric types in Java. These are represented by objects such as UByte.

2. These types (like java.lang.Integer) have a constructor that takes a string. The reference implementation of UByte holds the value in a signed primitive type of the next size up, such as short

The UPnP boolean data type has preferred values of "0" and "1". Values of "yes", "no", "true" and "false" may be used but are deprecated. The Java object java.lang.Boolean has a constructor which only recognises "true" as String parameter - everything else evaluates to false. Similarly, Boolean.toString() returns "true" or "false".

3. A new type to represent boolean values is used, which uses the preferred UPnP string values of "0" and "1".
Other types.
4. Other types are defined to have constructors and toString() methods which correspond to the UPnP SOAP wire formats of these types.

Parameters and state variables

Parameters are not directly given a type. Instead, they are associated with state variables and the state variables have types. The type of a parameter is thus given by indirection. While the types are as above, some state variables may have restricted values (see later), thus effectively forming a subtype. These subtypes cannot be reprsented by primitive Java types.

5. This is a change from version 0.1 The type of a parameter is represented by a class with the same name as the associated state variable. This class contains an object containing the underlying primitive Java type.
For example, the WANCommonInterfaceConfig has a method GetTotalBytesSent with a parameter NewTotalBytesSent with related state variable TotalBytesSent. This state variable is of type ui4. The parameter type is out, so a holder type is used (see next section). The method signature is
    void GetTotalBytesSent(TotalBytesSentHolder NewTotalBytesSent)
                                      throws RemoteException;
where the class TotalBytesSentHolder contains a UInteger object.

Holder types

Parameters to actions can be in or out. In variables can be passed by value. There can be many out variables for a method call, so either out variables need to be passed by reference or collected together into a single return structure.

6. This is a change from version 0.1 In parameters are passed using the state variable data-types. These are object types such as TotalBytesSent.
For example, the TotalBytesSent class is

package jiniupnp.service.urn_schemas_upnp_org_service_WANCommonInterfaceConfig_1;

public class TotalBytesSent {
    private UInteger value;

    public TotalBytesSent(UInteger value) {
        this.value = value;
    }

    public UInteger getValue() {
        return value;
    }

    public String toString() {
        return value.toString();
    }
}
7. Out parameters are passed by reference, using a class XXXHolder for type XXX. These are object types such as TotalBytesSentHolder

A holder class contains a value that can be set by someone else. All classes can have a public method setValue(). This method could take a string or a value of the type. The SOAP wire format is as a string.

8. All holder classes have a public method setValue(String value) This differs from the holder classes in e.g. the CORBA and Apache Axis packages, which expose a public field of the primitive type which can be changed, Our choice leaves conversion from string to the primitive type to the holder object.
For example, the TotalBytesSentHolder class is

package jiniupnp.service.urn_schemas_upnp_org_service_WANCommonInterfaceConfig_1;

public class TotalBytesSentHolder {
    private UInteger value;

    public void setValue(String val) {
        value = new UInteger(val);
    }
    public UInteger getValue() {
        return value;
    }

    public String toString() {
        return value.toString();
    }
}

Restricted types

State variables with string values can have be given an enumeration of allowed values. For example, the WANAccessType state variable in the WANCommonInterfaceConfig service is restricted to possible values of "DLS", "POTS", "Cable" and "Ethernet". This could be handled by keeping the datatype as String and adding constants such as WANAccessType_POTS defined in the service, or by a Java 1.5 enumeration. To use constants, a suitable naming convention is required, and there is no clear link between parameter names in actions and state variable names. To use an enumeration commits to Java 1.5 or later.

9. This is a change from version 0.1 The state variable type is a class that contains a string and a set of possible values

State variables of numeric type can have their values restricted by minimum, maximum and step values. For example, the Dimmable service state variable LoadLevelTarget has a minimum of 0 and maximum of 100. These could be handled by setting constants such as LoadLevelTarget_MINIMUM. To use constants, a suitable naming convention is required.

10. This is a change from version 0.1 The state variable type is a class that contains a numeric value and maximum, minimum and step values. If the specification does not give one of these values, then it is represented by null

Table of data types

The current table of data-type mappings is

UPnP type Java in parameter Java out parameter
i1 Byte ByteHolder
i2 Short ShortHolder
i4 Integer IntegerHolder
ui1 UByte UByteHolder
ui2 UShort UShortHodler
ui4 UInteger UIntegerHolder
boolean jiniupnp.types.Boolean BooleanHolder
string String StringHolder
number
fixed.14.4
float
char
date
dateTime
dateTime.tz
time
time.tz
bin.base64
bin.hex
uri
uuid
r4
r8

Control actions

Querying state variables

A control point may query a state variable for its value. However, this has been deprecated by the UPnP consortium.

11. A Java interface for a service could include methods to query state variables. The Java interfaces defined here do not include any methods to query state variables.

Actions

Actions are remote procedure calls from a control point to a service on a device. Actions are defined in service descriptions by XML. A typical action description (from a Dimming service) is


<action>
 <name>GetOnEffectParameters</name>
  <argumentList>
    <argument>
      <name>retOnEffect</name>
      <direction>out</direction>
      <relatedStateVariable>OnEffect</relatedStateVariable>
    </argument>
    <argument>
      <name>retOnEffectLevel</name>
      <direction>out</direction>
      <relatedStateVariable>OnEffectLevel</relatedStateVariable>
    </argument>
  </argumentList>
</action>
Each action has a name and is followed by a list of named parameters. Java does not have named parameters, only positional ones.
12. The name of the Java method is the name of the UPnP action, even if it does not conform to normal Java naming conventions.
13. The Java method has positional parameters in the same order as they appear in the UPnP action specification. The name of each parameter is its UPnP name.
14. The type of each parameter is the type of the related state variable, with Holder appended if it is an out parameter.
15. Each method throws a RemoteException

Since OnEffect has type string and OnEffectLevel has UPnP type ui1, and both are of type out the corresponding Java method is


    void GetOnEffectParameters(StringHolder retOnEffect,
                               ByteHolder retOnEffectLevel)
         throws RemoteException;

Events

Changes of value for state variables may generate events. Event listening is defined at the service level: a listener subscribes to event changes for all state variables of a service. each service consequently needs a way to add and remove listeners.

16. The following methods are added to any service which generates events

    import net.jini.core.event.RemoteEventListener;
    import net.jini.core.event.EventRegistration;

    EventRegistration addEventListener(RemoteEventListener listener)
                          throws RemoteException;
    void removeEventListener(RemoteEventListener listener)
                          throws RemoteException;

When an event occurs, the method notify(RemoteEvent evt) will be called on any listener.

Jini remote events carry a minimum of information. A listener is usually expected to use this information to make further queries on the service - if it is interested - to establish what the change was. UPnP events on the other hand carry a large amount of information: the name of the state variable that changed, and the new value of this variable. Since the information is available and it is far from clear how to make a separate query to get a state variable value (since direct variable queries are deprecated), it may be better for event notification to include a subclass of RemoteEvent

17. The following subclass of RemoteEvent is the parameter to notify():

import net.jini.core.event.RemoteEvent;
import java.rmi.MarshalledObject;

public class UPnPEvent extends RemoteEvent {

    private String vblName;
    private Object value;

    public UPnPEvent(Object source, long eventID, long seqNum,
                     MarshalledObject handback,
                     String vblName, Object value) {
        super(source, eventID, seqNum, handback);
        this.vblName = vblName;
        this.value = value;
    } // UPnPEvent constructor

    public String getVariableName() {
        return vblName;
    }

    /**
     * The type is the type of the state variable e.g. Integer, UByte, etc
     */
    public Object getValue() {
        return value;
    }
} // UPnPEvent

Service description

Each UPnP service is described in an XML document. This contains a set of actions and a state table containing all of the state variables. It does not contain the name of the service. This XML file is sufficient to generate all of the service's methods as outlined above, but not the name of the interface.

Device description

Service information

Each device contains a list of the names of services that it contains, and a URL for each service description. In conjunction with the above, this is enough to generate an interface for each UPnP service. The names of services are strings containing "-" and ":" as separators.

18. The Java name of each service is obtained by replacing all "-" and ":" in the UPnP service name by the underscore "_".

As an example of this, the UPnP BinaryLight device contains a SwitchPower service:


      ...
      <service>
        <serviceType>urn:schemas-upnp-org:service:SwitchPower:1</serviceType>
        <serviceId>urn:upnp-org:serviceId:SwitchPower:1</serviceId>
        <SCPDURL>URL for description</SCPDURL>
        <controlURL>URL for control</controlURL>
        <eventSubURL>URL for eventing</eventSubURL>
      </service>
      ...

The service description is


    <?xml version="1.0"?>
    <scpd xmlns="urn:schemas-upnp-org:service-1-0">
      <specVersion>
	<major>1</major>
	<minor>0</minor>
      </specVersion>
      <actionList>
	<action>
	<name>SetTarget</name>
	  <argumentList>
	    <argument>
	      <name>newTargetValue</name>
	      <relatedStateVariable>Target</relatedStateVariable>
	      <direction>in</direction>
	    </argument>
	  </argumentList>
	</action>
	<action>
	<name>GetTarget</name>
	  <argumentList>
	    <argument>
	      <name>RetTargetValue</name>
	      <relatedStateVariable>Target</relatedStateVariable>
	      <direction>out</direction>
	    </argument>
	  </argumentList>
	</action>
	<action>
	<name>GetStatus</name>
	  <argumentList>
	    <argument>
	      <name>ResultStatus</name>
	      <relatedStateVariable>Status</relatedStateVariable>
	      <direction>out</direction>
	    </argument>
	  </argumentList>
	</action>
	Declarations for other actions added by UPnP vendor (if any) go here
      </actionList>
      <serviceStateTable>
	<stateVariable sendEvents="no">
	  <name>Target</name>
	  <dataType>boolean</dataType>
	  <defaultValue>0</defaultValue>
	</stateVariable>
	<stateVariable sendEvents="yes">
	  <name>Status</name>
	  <dataType>boolean</dataType>
	  <defaultValue>0</defaultValue>
	</stateVariable>
	Declarations for other state variables added by UPnP vendor (if any)
	go here
      </serviceStateTable>
    </scpd>

The Java interface for this service is


    package jiniupnp.service;

    import java.rmi.Remote;
    import java.rmi.RemoteException;
    import jiniupnp.types.*;
    import net.jini.core.event.RemoteEventListener;

    public interface urn_schemas_upnp_org_service_SwitchPower_1 extends Remote {
	void SetTarget(jiniupnp.types.Boolean newTargetValue)
                       throws RemoteException;
	void GetTarget(jiniupnp.types.BooleanHolder RetTargetValue)
                       throws RemoteException;
	void GetStatus(jiniupnp.types.BooleanHolder ResultStatus)
                       throws RemoteException;
	net.jini.core.event.EventRegistration addEventListener(RemoteEventListener listener)
					      throws RemoteException;
        void removeEventListener(RemoteEventListener listener)
					      throws RemoteException;
    }

Device information

Each device can contain nested devices, so that a root device can export not only its direct services but also the services of all contained devices. The root device has a large number of attributes such as friendly name, model number, manufacturer, etc. It is not clear to me if the nested devices also have these attributes or simply inherit them from the root device. I assume inheritance.

Each service has associated device information. While this is not part of the service specification, it is additional information about the environment of the service.

19. Define a subclass of Entry to contain this information

import net.jini.entry.AbstractEntry;
import java.net.URL;
import java.net.URI;

public class UPnPEntry extends AbstractEntry {

    public String friendlyName;

    public String manufacturer;
    public URL manufacturerURL;

    public String modelDescription;
    public String modelName;
    public String modelNumber;
    public URL modelURL;

    public String serialNumber;
    public URI UDN; // begins with "uuid:"
    public String UPC; // 12 digit number

    // public String IconList;

    public URL presentationURL;

    public UPnPEntry(String friendlyName,
                     String manufacturer,
                     URL manufacturerURL,

                     String modelDescription,
                     String modelName,
                     String modelNumber,
                     URL modelURL,

                     String serialNumber,
                     URI UDN,
                     String UPC,
                     URL presentationURL) {
        this.friendlyName = friendlyName;
        this.modelDescription = modelDescription;
        this.manufacturer = manufacturer;
        this.manufacturerURL = manufacturerURL;

        this.modelDescription = modelDescription;
        this.modelName = modelName;
        this.modelNumber = modelNumber;
        this.modelURL = modelURL;

        this.serialNumber = serialNumber;
        this.UDN = UDN;
        this.UPC = UPC;
        this.presentationURL = presentationURL;
    } // UPnPEntry constructor

    public String toString() {
        return "Friendly name: " + friendlyName + "\n" +
            "Manufacturer: " + manufacturer + "\n" +
            "Manufacturer URL: " + manufacturerURL + "\n" +

            "Model Description: " + modelDescription + "\n" +
            "Model name: " + modelName + "\n" +
            "Model number: " + modelNumber + "\n" +
            "Model URL: " + modelURL + "\n" +

            "Serial number: " + serialNumber + "\n" +
            "UDN: " + UDN + "\n" +
            "UPC: " + UPC + "\n" +
            "Presentation URL: " + presentationURL + "\n";
    }
} // UPnPEntry

UPnP devices as Jini services

Each UPnP device advertises itself and its services using UPnP procotols. These may be heard by a client that registers a Jini proxy onto Jini lookup services and from there to Jini clients, or by a custom lookup service that makes a Jini proxy directly available to Jini clients. There may be additional systems. We do not wish to be prescriptive over this, just to discuss the form of the resultant Jini proxy.

A Jini proxy may implement one or more Jini services. It does so by implementing one or more service interfaces. In the case of a device which contains several UPnP services, each service could have a separate proxy or there could be one proxy for all services.

20. A single Jini proxy implements all of the interfaces for all of the services on a UPnP device.
21. The proxy is advertised along with a UPnPEntry containing information about the root device.
22. The Jini ServiceID is derived from the UPnP device UUID as represented in the device UDN attribute.

JavaDoc

JavaDoc supporting this specification is available from http://jan.newmarch.name/java/jini/upnpLUS/doc/index.html

Changes

Version 0.3 (1 August)

Version 0.2 (25 July)


Jan Newmarch (http://jan.newmarch.name)
jan@newmarch.name
Last modified: Mon Aug 1 20:53:02 EST 2005
Copyright ©Jan Newmarch
Access count since May 2005 is:
This page is http://jan.newmarch.name:443/java/jini/upnpLUS/specification.html