






Implementing a New Mapper
The goal of a mapper for a middleware platform like Bluetooth, UPnP, and others is to wrap native devices by translators based on the platform based on their availability. This means that the mapper is supposed to instantiate a translator when a native device appears, and destroy it when the device disappears. Therefore, a typical mapper runs a thread to discover the device. For example, the Bluetooth mapper discovers Bluetooth devices in a thread by contacting Bluetooth Service Discovery Protocol (SDP) library. The UPnP mapper discovers UPnP devices in a thread by contacting UPnP Simple Service Discovery Protocol (SSDP) library. Once a device is found, the mapper instantiates a translator based on the device's type and possibly its identifier to differentiate translators for different device instances. For differentiation of translators, please refer to Mapper and Translator Basics. The following is a step-by-step guideline to implement a new uMiddle mapper; we take a iButton reader and actual iButton devices as an example.
1. What's iButton? What does iButton mapper do?
Please refer to iButton.com (http://www.maxim-ic.com/products/ibutton/). It is a tiny storage device that can contain some amount of Kbytes. The followings are images of an iButton and its reader devices.
 | An iButton. In a practical use, this device is embedded into rings, cards, etc. |
 | A iButton reader called Bluedot. This device is connected to a computers serial port. |
A user can write a data to and read the data from the iButton by putting it into a Bluedot. So a mapper for iButtons behaves like below.
With instance-based translators...
- It instantiates a translator when an iButton is put into the Bluedot. (This corresponds to a Bluetooth device appearing in a Bluetooth domain.)
- The translator contains a data input port to read a data from uMiddle entities and write it to the iButton. It also contains a data output port to read a data from iButton and write it to uMiddle entities.
- It destroys the translator when the iButton is taken off from the Bluedot. (This corresponds to the Bluetooth device leaving from the Bluetooth domain.)
The above is an instance-based translator scenario where the mapper creates translator objects for each iButton instance. Actually, creating type-based translator is also possible in this case. In that case, the translator will abstract the iButton reader device in uMiddle as follows.
With type-based translators...
- The mapper instantiates a translator when it is started by uMiddle runtime.
- The translator contains the same set of ports as the instance-based translator case, but keep existing even while an iButton is not put into the Bluedot.
- the mapper destroys the translator when it is stopped by uMiddle runtime, namely when the runtime stops running.
2. Creating a blank mapper
Let's start from creating a new class for our iButton mapper. Every mapper must be a subclass of edu.gatech.cc.realjin.umiddle.mapper.Mapper requiring us to implement several methods: init(), stop(), map(DeviceMappingPolicy), and getNamespaceURI(). Therefore, the initial blank implementation of any mapper seems like below. In init() and stop(), you can start and stop a discovery thread if it's necessary. If you are using a type-based translator, these methods are the places you instantiate/discard it.
0: import edu.gatech.cc.realjin.umiddle.mapper.DeviceMappingPolicy;
1: import edu.gatech.cc.realjin.umiddle.mapper.Mapper;
2: import edu.gatech.cc.realjin.umiddle.mapper.MapperException;
3:
4: public class IButtonMapper extends Mapper{
5: public IButtonMapper() {
6: }
7:
8: protected void init() {
9: }
10:
11: protected void stop() {
12: }
13:
14: public int map(DeviceMappingPolicy policy) throws MapperException {
15: return 0;
16: }
17:
18: protected String getNamespaceURI() {
19: return null;
20: }
21: }
Note: in the following, iButton-related classes and methods are not based on any actual iButton library.
Robert: please change the following description to reflect the actual library
3. map(DeviceMappingPolicy)
This method is the gateway to this mapper through which the uMiddle runtime passes USDL documents defined for this mapper. The runtime invokes this method only once for each USDL document just after invoking init() method of this. The DeviceMappingPolicy class contains information acquired by parsing the USDL document, and the mapper is supposed to conduct mapping based on the information although devices dynamically appear and disappear. Therefore, the mapper needs to keep this object in memory until it finishes running. In our iButton mapper, we need to keep the policy objects since iButtons will be put into and taken off from the Bluedots while the mapper is running. Now, our translator code is like IButtonMapper.java (stage 2).
4. Defining an XML schema for the iButton mapper
The DeviceMappingPolicy class contains pre-parsed generic attributes for a translator, such as the name, description, services to include, etc., as well as middleware-specific unparsed XML node. This middleware-specific XML node contains tags required by the mapper to instantiate translators. For example, the USDL document shown in upnp-light.xml and bluetooth-hidp.xml contain such tags in <map> tags, and the mappers parse them to conduct mapping. The UPnP mapper parses the <map> tags to acquire UPnP actions and states that should be handled in a translator. The Bluetooth mapper parses them to related Bluetooth data to uMiddle ports. To enable such a middleware-specific definition, these mappers define their own XML schemas applied only inside the <map> tags: USDL UPnP-specific Schema and USDL Bluetooth-specific Schema.
Our iButton mapper will also need to input iButton-specific information to instantiate translators. Here, let's support two different kinds of information: iButton 64bit ID and iButton device type. The ID is useful to create instance-based translators for different iButton devices. The type is useful to change the translator to create based on iButton's capability, such as storage-only, storage plus real time clock, storage plus thermometer, and so on. For this purpose, the following is a minimum XML schema that defines one XML node called device with two selectable child elements type and id.
0: <?xml version='1.0'?>
1: <schema xmlns='http://www.w3.org/2001/XMLSchema'
2: targetNamespace='//edu.gatech.cc.umiddle.ibutton'
3: xmlns:ibutton='//edu.gatech.cc.umiddle.ibutton'
4: elementFormDefault='qualified'
5: version='1.0'>
6: <element name='device'>
7: <complexType>
8: <choice>
9: <element name="id" type='string'/> <!-- iButton id -->
10: <element name="type" type='string'/> <!-- iButton product type -->
11: </choice>
12: </complexType>
13: </element>
14: </schema>
In an actual USDL document, our device will go into map tag contained by device tag as follows. In line 2, we attach a nickname "ibutton" for our XML schema, and this is used in lines 9 to 11 to declare that device and id tags in map tag are from our iButton schema. The lines 8 to 12 are stored in the DeviceMappingPolicy class, and our mapper can get this by calling its getPolicy() method.
0: <?xml version="1.0"?>
1: <device xmlns="//edu.gatech.cc.realjin.umiddle.usdl"
2: xmlns:ibutton="//edu.gatech.cc.umiddle.ibutton"
3: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4: xsi:schemaLocation="http://www.ht.sfc.keio.ac.jp/~jin/umiddle usdl.xsd http://www.gatech.edu/~someone/ibutton ibutton.xsd">
5:
6: <name>MyOwnIButton</name>
7: <description>the iButton device I have</description>
8: <map>
9: <ibutton:device>
10: <ibutton:id>0x00000000</ibutton:id>
11: </ibutton:device>
<OR>
9: <ibutton:device>
10: <ibutton:type>DS1991</ibutton:type> //MultiKey iButton
11: </ibutton:device>
12: </map>
13:
14: <services>
15: <ports/>
16: </services>
17: </device>
Based on these, let's modify our mapper to create a list of iButton devices we are interested in, in the map method. The modified one is IButtonMapper.java (stage 3). Also this time, getNamespaceURI returns the string that identifies our iButton XML schema. This string is used to determine the mapper to pass a DeviceMappingPolicy instance based on the namespace URI included in the USDL document.
5. Instantiating iButton translator
Now, it's time to instantiate our tanslator. Here we suppose for simplicity that the id and type of iButton can be acquired by calling an iButton's getID() and getType() methods. Also, suppose methods iButtonInserted(IButtonDevice) and iButtonPulledOut(IButtonDevice) are invoked when corresponding events occur. The former method is the place where we create translator instances, and the latter is where we destroy them.
32: private Map devices = new HashMap();
33: public void iButtonInserted(IButtonDevice button){
34: if(this ibutton is already mapped)
35: return;
36:
37: String id = button.getID();
38: String type = button.getType();
39: DeviceMappingPolicy policy = (DeviceMappingPolicy)policies.get(id);
40:
41: if(policy == null){ ////we don't have USDL document for this paraticular iButton
42: policy = (DeviceMappingPolicy)get(type);
43: if(policy == null) ////we don't have USDL document for this iButton type
44: return; ////don't map this device.
45: }
46:
47: try{
48: //export container device entity to uMiddle space
49: Device dev = new DeviceEntity(policy.getName()); //DeviceEntity is in edu.gatech.cc.realjin.umiddle.entity
50: IButtonTranslator t = <create translator for this (id, type)>; // IButtonTranslator is supposed to be a subclass of ServiceEntity
51: //add this translator to the container device entity
52: dev.addService(dev.getServiceMappingPolicy().getName(), t);
devices.put(button, dev);
53: }catch(Exception e){
54: System.err.println("iButtonMapper: failed to map "+id);
55: e.printStackTrace();
56: }
57: }
58:
59: public void iButtonPulledOut(IButtonDevice button){
60: //assuming there is only one Bluedot connected to this PC, the current translator
61: //is for this iButton.
62: translator.unexport(); //this makes the translator stop operation
Device dev = (Device)devices.get(button);
dev.unexport();
63: }
The whole class is in IButtonMapper.java (stage 4).
6. Register the mapper to uMiddle runtime
After creating a new mapper, you need to register the mapper to the uMiddle instance so that your mapper is instantiated properly. The registration is done in a modular way as follows.
- A mapper must be stored in a single Java Archive (jar) file.
- The mapper jar file must contain the mapper class in its top directory. If the mapper class is packages, the directory corresponding to the top package must reside in the archives top directory. (ex: xxx.yyy.IButtonMapper must be in /xxx/yyy/IButtonMapper.class in a jar file.)
- The name of the mapper jar file must be equal to the mapper class name. (ex: xxx.yyy.IButtonMapper must be in xxx.yyy.IButtonMapper.jar.)
- The mapper jar file must be included in Java's CLASSPATH.
Link to this Page
- uMiddle HOWTOs last edited on 24 June 2005 at 1:16 pm by lawn-199-77-211-58.lawn.gatech.edu