






Implementing a New uMiddle Translator
Every translator is a subclass of edu.gatech.cc.realjin.umiddle.entities.ServiceEntity, since it must send or receive data through one or more uMiddle port that implements the uMiddle's communication API. There is no other constraints for translator implementations. In the following, we look into how to implement a translator. The choice between instance-based and type-based is yours. Though the initial design of uMiddle prefers the instance-based, in some cases it's anyhow impossible to distinguish native device instances in a translator, thereby leading to choose the latter.
1. Subclassing ServiceEntity
Every translator must subclass ServiceEntity, since uMiddle API allows to create ports only through protected methods contained by that class. The following is a null translator implementation that does nothing.
import edu.gatech.cc.realjin.umiddle.UMiddleEntityException;
import edu.gatech.cc.realjin.umiddle.directory.DirectoryException;
import edu.gatech.cc.realjin.umiddle.entity.ServiceEntity;
public class MyTranslator extends ServiceEntity{
public MyTranslator()
throws DirectoryException, UMiddleEntityException {
}
protected void init() throws Exception {
}
protected void start() throws Exception {
}
protected void stop() throws Exception {
}
}
This code defines an outline of a translator called MyTranslator. You can find a strange constructor that throws DirectoryException and UMiddleEntityException. They are necessary since the constructor of ServiceEntity, which is our superclass, is defined to throw these exceptions. Other three methods are invoked by uMiddle runtime as follows.
- init() is invoked after the constructor. You can put initialization code, such as creating a thread that receives data from a native device, here.
- start() is invoked after init(). You can start translation by, for example, starting the receipt thread.
- stop() is invoked when uMiddle runtime, which hosts our translation, finishes running. You have to release all the resources here.
In the current implementation, there is no big semantic difference between init() and start(), however, future implementations may differentiate these.
2. Constructor implementation
Since a translator instance has to communicate with a particular native device, it's natural to implement a constructor that inputs a parameter like a device address. You can do this, or actually can define any constructor as you like, since later, we will create/modify a mapper that instantiates our translator. For example, the Bluetooth HIDP-uMiddle translator found in mappers.bluetooth.HIDPService is instantiated by the Bluetooth mapper defined in mappers.bluetooth.BluetoothMapper as follows.
0: public int map(BluetoothMappedDevice device, BluetoothDevice btDevice)
1: throws DirectoryException, UMiddleEntityException, <snipped> {
2:
3: ...snipped...
4:
5: String btProfile = <Bluetooth profile of the native device>;
6: BluetoothMappedService service = null;
7: if(btProfile.toLowerCase().startsWith("hid")){
8: HumanInterfaceDevice hid = (HumanInterfaceDevice)btDevice;
9: service = new HIDPService(hid);
10: }else if(btProfile.toLowerCase().startsWith("spp")){
11: SerialDevice spp = (SerialDevice)btDevice;
12: service = new SPPService(spp);
13: }else{
14: throw new MapperException("Unsupported profile: "+btProfile);
15: }
16:
17: ...snipped...
18:
19: return DONE;
20: }
The lines 9 and 12 invoke the constructor of the Bluetooth Human Interface Device Profile (HIDP) translator and the Serial Port Profile (SPP) translators, respectively. Our new translator will be instantiated similarly by adding a block of code that looks like the following.
}else if(<need our new translator?>){
service = new MyTranslator(<arbitrary parameters>);
}
3. init() implementation
There are three important things we have to do in this method; creating one or more output/input uMiddle port to send/receive data to/from other uMiddle entities, connecting to the native device, and initializing a thread to receive data from uMiddle entities (in an outbound translator) or a native device (in an inbound translator).
3.1 Creating a port
Our superclass ServiceEntity provides the following six methods to create different types and directions of ports.
- addDataInputPort(String name, String dataType) : creates a port that receives bulk data such as images.
- addDataOutputPort(String name, String dataType) : creates a port that sends bulk data.
- addStreamInputPort(String name, String dataType) : creates a port that receives continuous media such as audio and video.
- addStreamOutputPort(String name, String dataType) : creates a port that sends continueous media.
- addEventInputPort(String name) : creates a port that receives zero-bytes event data.
- addEventOutputPort(String name) : creates a port that sends zero-bytes event data.
You can specify a name of the port to create as you like, however, any two ports contained by a service cannot have the same name. The parameter dataType represents a MIME-type that is sent/received through the port. Event ports have a fixed data type, so that the last two methods do not have the parameter for the data type specification. Note that, a uMiddle port can be connected to other ports if and only if their data type match. Therefore, for example, a port that is defined to input "image/jpeg" can only be connected to an output port that is defined to send "image/jpg".
The followings show the examples of the port creation.
- addDataInputPort("imageOut", "image/jpg") : creates a port that receives JPEG images.
- addDataOutputPort("htmlOut", "text/html") : creates a port that receives an HTML document.
- addStreamInputPort("videoIn", "video/mpeg") : creates a port that receives MPEG video.
- addStreamOutputPort("videoOut", "video/mpeg") : creates a port that sends MPEG video.
- addEventInputPort("switchOn") : creates a port that waits for commands to turn the native device on.
- addEventOutputPort("switchedOn") : creates a port that notifies when the native davice is switched on.
3.2 Connecting to a native device
Connection to a native device will be made through an arbitrary library/protocol you're using, such as CyberLink UPnP library, or a class you created, such as a specific Bluetooth device handling class.
3.3 Creating a thread
A typical translator contains a thread that loops for data arrival either from other uMiddle entities (in outbound translator) or from the native device (in inbound translator). You can define such thread as you like, but it'll be easy if you implement it as an inner class having an access to the port we created in init(). While data receipt from the native device depends on the library/protocol/class you are using, those from uMiddle entities can be achived via uMiddle's communication API defined in ports. See DataInputPort, DataOutputPort, EventInputPort, EventOutputPort, StreamInputPort, and StreamOutputPort for details.
4. start() implementation
You should start the receiver thread, and probably need some work to start communicating with your native device.
5. stop() implementation
You should stop the receiver thread, and probably need some work to release other resources.
6. So...?
MyTranslator.java (template)
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