Decision points appear in this format
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.
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 (likejava.lang.Integer
) have a constructor that takes a string. The reference implementation ofUByte
holds the value in a signed primitive type of the next size up, such asshort
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 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.
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 classXXXHolder
for typeXXX
. These are object types such asTotalBytesSentHolder
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();
}
}
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
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 |
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 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, withHolder
appended if it is anout
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;
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 eventsimport 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 ofRemoteEvent
is the parameter tonotify()
: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
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.
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;
}
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 ofEntry
to contain this informationimport 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
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 supporting this specification is available from http://jan.newmarch.name/java/jini/upnpLUS/doc/index.html