Writing UPnP Devices and Control Points
The OSGi UPnP Specification (v 1.1) dedicates section 111.6 "Working with a UPnP Device" to describe the details of implementing UPnP Devices on OSGi. Here we provide some hints on the main differences you may encounter working with OSGi with respect to the UDA 1.0 Specification .
The first peculiarity is that OSGi provides a centralized register for discovering of UPnP devices as opposed to the distributed mechanism of the UPnP protocol stack. Thus, while in the UPnP networks the steps for subscribing the services of some device are typically 1) discover the required device and 2) subscribe the service, within the OSGi platform a Control Point may register an interest in receiving notify events even before the device is really plugged on the network. This is possible because the subscription mechanism is based on the UPnPEventListenerinterface that is used for registering OSGi services, which ultimately handles the notify messages sent by the producers of the events. The base driver (importer) keeps track of such UPnPEventListener services and as soon as a matching service is discovered on the UPnP network, a subscription is made on behalf of the registered listeners.
On the other hand, even if it is enough to register a service implementing the UPnPDeviceinterface to expose it as UPnP devices on the network, the developers have to implement on their own the event management required by the UPnP technology. From this point of view, for each evented state variable declared by the UPnP device, the developers have to monitor UPnPEventListenerservices that is error prone[1]. The correct implementation of the UPnP eventing phase is left entirely to developers. In particular, in UDA 1.0, the first time a Control Point subscribes a service, the current value of its state variables should soon be delivered to it. To manage this situation in a standard way, the last OSGi UPnP specification defined the extended interface UPnPLocalStateVariable. In fact, the previous basic interface UPnPStateVariable provided only a descriptive interface which did not enable to get the value of a state variable without knowing the final implementation class. Every developer should use this new interface in order to allow the specification of helper classes that ease the subscription/notify management (see UPnPEventNotifier below).
We have factorized and released part of the code used by the UPnP examples with the org.apache.felix.upnp.extra bundle.
The Extra bundle and the driver interfaces
We provide some utility classes and services through the extra bundle and the services registered by the UPnP Base Driver.
In the Extra bundle the class org.apache.felix.upnp.extra.util.UPnPSubscriber can be instantiated to subscribe one or more services. The constructor takes two parameters a BundleContext reference and a UPnPEventListener reference. In this class the method subscribe(Filter aFilter) is a general and powerful way to subscribe to any service by using an LDAP filter. For example by using the string :
"(& (UPnP.device.type=urn:schemas-upnp-org:device:BinaryLight:1) (UPnP.service.type= urn:schemas-upnp-org:service:SwitchPower:1))"
we would subscribe to the SwitchPower service offered by any device implementing the BinaryLight profile. Looking at the Felix UPnP TV sample code, the UPnPSubscriber class is used in the file org.apache.felix.upnp.sample.tv.TVDevice to subscribe to the different service types offered by the Cyberlink sample devices. However, in this case, the utility method subscribeEveryServiceType is used to provide just the device and service types.
private final static String CLOCK_DEVICE_TYPE = "urn:schemas-upnp-org:device:clock:1"; private final static String TIME_SERVICE_TYPE = "urn:schemas-upnp-org:service:timer:1"; private final static String LIGHT_DEVICE_TYPE = "urn:schemas-upnp-org:device:light:1"; private final static String POWER_SERVICE_TYPE = "urn:schemas-upnp-org:service:power:1"; private final static String AIRCON_DEVICE_TYPE = "urn:schemas-upnp-org:device:aircon:1"; private final static String TEMP_SERVICE_TYPE = "urn:schemas-upnp-org:service:temp:1"; private final static String WASHER_DEVICE_TYPE = "urn:schemas-upnp-org:device:washer:1"; private final static String STATUS_SERVICE_TYPE = "urn:schemas-upnp-org:service:state:1"; public void doSubscribe() { subscriber = new UPnPSubscriber(Activator.context, this); subscriber.subscribeEveryServiceType(CLOCK_DEVICE_TYPE, TIME_SERVICE_TYPE); subscriber.subscribeEveryServiceType(AIRCON_DEVICE_TYPE, TEMP_SERVICE_TYPE); subscriber.subscribeEveryServiceType(LIGHT_DEVICE_TYPE, POWER_SERVICE_TYPE); subscriber.subscribeEveryServiceType(WASHER_DEVICE_TYPE, STATUS_SERVICE_TYPE); } public void undoSubscribe(){ subscriber.unsubscribeAll();}
The class org.apache.felix.upnp.extra.util.UPnPEventNotifier is a utility class that manages the delivery of notifications for you. There are two constructors. The first one takes a BundleContext, a UPnPDevice , and a UPnPService reference. They are internally used to keep trace of all the registered UPnPEvenListener that are interested in monitoring events generated by your UPnP service. UPnPEventNotifier implements the java beans PropertyChangeListener interface; once changes of the service state variables occurs you should call the method propertyChange(PropertyChangeEvent evt). Alternatively, you may use the second constructor to pass a reference to a model implementing the interface: EventSource defined in the Extra bundle. This model should use the PropertyChangeSupport to keep trace of PropertyChangeListener, and the related method firePropertyChange to notify changes. The EventSource interface is used internally by the UPnPEventNotifier to register itself as propertychangeListener of the model. Thus, in this case, you don't have to call propertyChange()directly: it is a duty of your model. As an example, take a look at LightModel class in the BinaryLight bundle.
The Felix UPnP base driver registers a non standard service implementing two interfaces:
org.apache.felix.upnp.basedriver.controller.DevicesInfo;
org.apache.felix.upnp.basedriver.controller.DriverController;
The former can be used to retrieve the XML description of both devices and services. Other than be used for debugging purpose, it allows access to the UPnP schema extensions defined by UPnP Vendors. According to the UDA 1.0 they consist of elements inserted in different points of the XML description and by convention starting with the prefix "X_". This interface is used by the context menu handler of the UPnP Tester bundle.
The latter interface can be used to change the log messages of the base driver at runtime. Two different methods are available to modify the log level of the base driver or to enable the visualization of low level messages related to the UPnP stack protocol (CyberDomo). Furthermore, the interface allows developers to send an M-SEARCH discovery message to the UPnP networks, thus refreshing the list of imported devices.
The Felix UPnP Examples << | >> Known Issues
[1] Developers should monitor UPnpEventListener services with a filter matching either the own service Id or service type, either the own device Id or device type and even a empty filter which are usually used to express interest for every UPnP device.