Previous Topic

Next Topic

Book Contents

Book Index

Abstract Device API

The Abstract Device API facilitates developers in exporting devices to a UPnP network and adapting them to the requirements of UPnP Device Specification. In particular, you can export a device in the UPnP community by extending the abstract classes, located in the com.prosyst.mbs.services.upnp.abstractdevice package, as they implement the interfaces from the UPnP Guide for you.

UPnP Device

In the Abstract Device API, each UPnP-supporting device should extend the com.prosyst.mbs.services.upnp.abstractdevice.AbstractUPnPDevice class, which implements the org.osgi.service.upnp.UPnPDevice interface. You pass the UPnP services and embedded devices in the constructor of AbstractUPnPDevice.

Override the getIcons and getDescriptions methods to provide the localized icons and descriptions of the device.

Optionally, if your device will export some HTML resources, such as a presentation page, implement the com.prosyst.mbs.services.upnp.export.UPnPHTMLResourceEx interface in the class implementing UPnPDevice. In the UPnPHTMLResourceEx methods you specify the resource name, its MIME type and the input stream to retrieve it from. The setRootURL method provides you with access to the root URL assigned for your device, including host, port, etc.

The listing below shows a virtual UPnP-enabled printer, which is realized with the Abstract Device API. Some of the used components are discussed in the paragraphs below.

import org.osgi.service.upnp.*;
import org.osgi.service.device.Constants;
import com.prosyst.mbs.services.upnp.export.UPnPHTMLResourceEx;
import com.prosyst.mbs.services.upnp.abstractdevice.*;
import java.util.Dictionary;
import java.util.Hashtable;
import java.io.InputStream;

public class UPnPPrinterDevice extends AbstractUPnPDevice
                               implements UPnPHTMLResourceEx {

  static final String PRINTING_SV = "Printing";
  static final String PRINTMSG_SV = "PrintMessage";
  static final String DEVICE_ID = "urn:bosch-com:serviceId:PrinterDevice";
  static final String DEVICE_TYPE = "urn:schemas-bosch-com:service:Printer";
  static final String DEVICE_VER = "1";
  static final String SERVICE_ID = "urn:bosch-com:serviceId:PrinterDevice";
  static final String SERVICE_TYPE = "urn:schemas-bosch-com:service:Printer";
  static final String SERVICE_VERSION = "1";

  private Hashtable props;

  public UPnPPrinterDevice(AbstractUPnPService[] services) {
    super(services, null);

    // init properties
    this.props = new Hashtable(14);
    props.put(Constants.DEVICE_CATEGORY,
              new String[]{UPnPDevice.DEVICE_CATEGORY});
    String ip = "unknown ip";

    try {
      ip = java.net.InetAddress.getLocalHost().getHostName();
    } catch (Exception uhe) {}
    
    props.put(UPnPDevice.UDN, "uuid:PrinterType-" + ip);
    props.put(UPnPDevice.TYPE, "urn:schemas-bosch-com:device:UPnPPrinterType:1");
    props.put(UPnPDevice.FRIENDLY_NAME, "UPnP Printer");
    props.put(UPnPDevice.MANUFACTURER, "Acme Corp.");
    props.put(UPnPDevice.MANUFACTURER_URL, "http://acme.com");
    props.put(UPnPDevice.MODEL_DESCRIPTION, "UPnPPrinter device");
    props.put(UPnPDevice.MODEL_NAME, "upnpprinter");
    props.put(UPnPDevice.MODEL_NUMBER, "1");
    props.put(UPnPDevice.MODEL_URL, "http://www.upnpprinter.com/model.html");
    props.put(UPnPDevice.UPC, "12345");
    props.put(UPnPDevice.SERIAL_NUMBER, "123");
    props.put(UPnPDevice.UPNP_EXPORT, "Yes");
    props.put(UPnPDevice.PRESENTATION_URL, "");
  }

  public static UPnPPrinterDevice newUPnPPrinterDevice() {
    // init state var Printing    
    StateVariable printingSV =
      new StateVariable(PRINTING_SV, Boolean.class,
                        UPnPStateVariable.TYPE_BOOLEAN, Boolean.FALSE,
                        null, null, null, null, true);

    // init state var PrintMessage
    StateVariable printMsg =
      new StateVariable(PRINTMSG_SV, String.class,
                        UPnPStateVariable.TYPE_STRING, "",
                        null, null, null, null, false);
        
    StateVariable[] vars = new StateVariable[] {printingSV, printMsg};

    // init action Print
    Hashtable nameVar = new Hashtable(1, 1F);
    nameVar.put(printMsg.getName(), printMsg);

    PrintAction printAct =
       new PrintAction("print",
                       null,
                       new String[]{PRINTMSG_SV},
                       null,
                       nameVar);
    AbstractAction[] actions = new AbstractAction[] {printAct};

    // init printer service
    AbstractUPnPService[] services = new AbstractUPnPService[] {
      new AbstractUPnPService(vars, actions) {
        public String getId() {
          return SERVICE_ID;
        }
        
        public String getType() {
          return SERVICE_TYPE;
        }
        
        public String getVersion() {
          return SERVICE_VERSION;
        }
      }
    };
        
    printAct.setParent(services[0]);

    // init printer device
    UPnPPrinterDevice upnpPrinter = new UPnPPrinterDevice(services);
    return upnpPrinter;
  }      

  // return an icon for the device
  public UPnPIcon[] getIcons(String locale) {
    UPnPIcon icon =  new UPnPIcon() {

      public String getMimeType() {
        return "image/png";
      }

      public int getWidth() {
        return 16;
      }

      public int getHeight() {
        return 16;
      }

      public int getSize() {
        return 359;
      }

      public int getDepth() {
        return 8;
      }

      public InputStream getInputStream() throws IOException {
        return this.getClass().getResourceAsStream("/osgiupnpprinter.png");
      }
    };
    return new UPnPIcon[] {icon};
  }  
      
  public Dictionary getDescriptions(String locale) {
    return props;
  }

  // methods from UPnPHTMLResourceEx
  public String getPresentationalHTMLResourceName() {
    return "presentation.html";
  }

  public InputStream getResourceInputStream(String s, String locale) {
    return this.getClass().getResourceAsStream(s);
  }

  public String getResourceMIMEType(String name, String locale) {
    return "text/html";
  }
  
  public long getResourceSize(String name, String locale) {
    return -1;                          
  }  

  public void setRootURL(java.lang.String rootUrl) {
    System.out.println("root URL for printer device is " + rootUrl);
  }
}

UPnP Service

In the Abstract Device API, each UPnP service extends the com.prosyst.mbs.services.upnp.abstractdevice.AbstractUPnPService class, which implements org.osgi.service.UPnPService. The actions and state variables of a UPnP service are specified in AbstractUPnPService constructor.

The abstract methods of AbstractUPnPService to override are getId, getType and getVersion. They should return fully qualified UPnP-compatible parameters as defined in the "UPnP Device Architecture 1.1".

You can call the generateEvent method to notify all interested listeners about changes in the value of a state variable. This method saves you a lot of efforts - you need not care about registering and unregistering OSGi service listeners and checking if listeners are interested in your device.

See the example in the listing above for to get clarified about extending AbstractUPnPService.

UPnP Action

An action of a UPnP service (org.osgi.service.upnp.UPnPAction) can be easily implemented by extending the com.prosyst.mbs.services.upnp.abstractdevice.AbstractAction class. Extending classes should only override the invoke method. The name and arguments of the action are set at instantiating AbstractAction. The action arguments and the state variables to represent them are associated through the Dictionary parameter of the AbstractAction constructor.

The listing below represents the print action of the UPnP-enabled printer from this listing.

import com.prosyst.mbs.services.upnp.abstractdevice.AbstractAction;
import com.prosyst.mbs.services.upnp.abstractdevice.AbstractUPnPService;

import java.util.Hashtable;
import java.util.Dictionary;
        
public class PrintAction extends AbstractAction {
        
  AbstractUPnPService parent;
        
  public PrintAction(String name, String raName, String[] iaNames,
    String[] oaNames, Hashtable argSSV) {
    super(name, raName, iaNames, oaNames, argSSV);
  }
        
  void setParent(AbstractUPnPService parent) {
    this.parent = parent;
  }
        
  public Dictionary invoke(Dictionary d) throws Exception {
    String msg = (String) d.get(iaNames[0]);
    if (msg != null) {
      // send printing=true event
      Hashtable events = new Hashtable(2);
      events.put(UPnPPrinterDevice.PRINTING_SV, Boolean.TRUE);
      parent.generateEvent(events);
        
      // printing
      System.out.println("Printing: \n" + msg);
        
      // send printing=false event
      events.put(UPnPPrinterDevice.PRINTING_SV, Boolean.FALSE);
      parent.generateEvent(events);
    }
    return null;
  }
}

UPnP State Variable

You can use the com.prosyst.mbs.services.upnp.abstractdevice.StateVariable to implement a state variable (org.osgi.service.upnp.UPnPStateVariable) of a UPnP service. The attributes of a state variable are passed to the StateVariable constructor.

Regarding the example printer device in this document, the Java code of its "Printer" service is included in this listing as an anonymous class instance.

Exporting the Device

You export an AbstractUPnPDevice as a UPnP-supporting device by simply calling its registerDevice method, which takes as input argument the org.osgi.framework.BundleContext currently allocated for the exporting bundle in the OSGi framework.

Information about the exact content of the Abstract Device API is available in the API documentation of the module.

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.upnp.UPnPDevice;

public class PrinterActivator implements BundleActivator {  
  private ServiceRegistration sReg;
  private UPnPPrinterDevice device;  
  public void start(BundleContext context) throws Exception {
    device = UPnPPrinterDevice.newUPnPPrinterDevice();
    device.registerDevice(context);  
  }

  public void stop(BundleContext context) throws Exception {    
    device.unregisterDevice();
  }
}