Beruflich Dokumente
Kultur Dokumente
qxd
7/16/2001
7:10 AM
Page 41
CHAPTER
TO P I C S I N T H I S C H A P T E R
M APPLICATIONS M HAVIJAVA APIS M AN EXAMPLE
Applications
Now that you know what HAVi is and have seen an overview of how it works, you can start looking at how to build applications. As you have seen, HAVi applications are based on software elements. Generally an application creates a single software element which then registers itself and uses the services of existing software elements on the network. It finds other software elements by querying the Registry service. It can be notified of events by subscribing to the Event Manager. By sending requests to DCM and FCM software elements, the application can control the operation of target devices. It can also request the services of the Resource Manager or Stream Manager. In designing a HAVi application, a key decision is whether the application is portable or not. Portable applications run on any full (FAV) controller. However, to assure portability, the application 1) must be written in Java, and 2) must confine itself to a set of packages identified by HAVi.
ch03.qxd
7/16/2001
7:10 AM
Page 42
42
Not all HAVi applications need be portable and, when developing an application intended for a specific controller, there may be advantages to dropping the restrictions imposed by portability. Nonportable applications need not be written in Java or, if written in Java can use additional packages to those allowed for portable applications. Nonportable applications are also free to use any of the native software on the controlleroperating system calls, windowing libraries, networking APIs, etc.
Havlets
A portable HAVi application is called a havlet. The analogy to the familiar applet is intentional. Like applets, havlets are downloaded from a remote entity and then installed and run locally. While applets are downloaded from HTTP servers, havlets are downloaded from special software elements. There are two types of software elements that provide this havlet download service: Device Control Modules (DCMs) and Application Modules. You have not yet encountered Application Modules in this book, but these are simple software elements whose sole purpose is to act as a source for havlets. Havlets are transferred to the controller in JAR file format. In addition to providing an API for the controller to download the havlet, DCMs and Application Modules also provide an API that returns the havlet profile. This is a description of the memory requirements of the havlet. By first examining a havlets profile, a controller can estimate whether it has sufficient memory to run the havlet and so avoid the potential confusion of having the havlet fail to launch.
Controller-Specific Applications
Java plays a clear role in developing havlets which, as explained above, need to be portable. You can also use Java to develop controller-specific (i.e., nonportable) HAVi applications. This type of application is not intended to be runnable on all HAVi controllers but rather on a particular controller (or family of controllers) chosen by the developer. The advantage to the developer is being free to use any of the Java packages known to be on the controller, rather than the restricted set available to havlets.
HJA Applications
The set of packages defined by HAVi for developing in Java is called HJAthe HAVi Java APIs. In this book we use the term HJA application to indicate a HAVI application written in Java. HJA applications include both havlets and controllerspecific applications. If you are writing a havlet, you can use only the packages appearing in HJA. Controller-specific applications can use HJA packages and other Java packages appearing on the controller.
ch03.qxd
7/16/2001
7:10 AM
Page 43
43
HAVi does not require controller devices to have a display and or to support UI functionality. Controllers without displays need not support the java.awt and havi.ui packages and cannot run havlets. However, displayless controllers can upload and run bytecode DCMs.
org.havi.constants
To assure interoperability, HAVi software elements must agree on the interpretation of messages. Many of the messages used by software elements contain parameters that take specific values defined by HAVi. Because there are a large number of such values, they have been collected together as interfaces in the org.havi.constants package.
*Java Development Kit (JDK) 1.1 Core API specification http://java.sun.com/products/ jdk/1.1/docs.html
ch03.qxd
7/16/2001
7:10 AM
Page 44
44
For example, in the description of the Registry, you saw that there are several Registry attributes defined by HAVi. To obtain the value of a specific attribute, you must provide a constant that identifies the attribute. The constants for HAVi-defined attributes are grouped together in the interface shown in Example 3-1.
Example 3-1
Constants for Registry Attribute Names ConstAttributeName { final int ATT_SE_TYPE final int ATT_VENDOR_ID; final int ATT_HUID final int ATT_TARGET_ID final int ATT_INTERFACE_ID final int ATT_DEVICE_CLASS final int ATT_GUI_REQ final int ATT_MEDIA_FORMAT_ID final int ATT_DEVICE_MANUF final int ATT_DEVICE_MODEL final int ATT_SE_MANUF final int ATT_SE_VERS final int ATT_AV_LANG final int ATT_USER_PREF_NAME
public interface public static public static public static public static public static public static public static public static public static public static public static public static public static public static }
= = = = = = = = = = = = = =
0x00; 0x01; 0x02; 0x03; 0x04; 0x05; 0x06; 0x07; 0x08; 0x09; 0x0a; 0x0b; 0x0c; 0x0d;
Using ConstAttributeName, application developers can refer to the symbolic names for Registry attributes and need not be aware of their actual values. However, to assure interoperability, it is essential that all implementations of the HAVi system use the same values.
org.havi.types
org.havi.types groups together a set of simple classes used by many software
elements. These classes primarily define get/set methods. The classes are: M Structured data types appearing in HAVi messages. For example, SEID represents software element identifiers and ResourceNegotiateRecord represents a structure used by the Resource Manager. M Holder classes used to store replies from asynchronous requests sent to software elements. M HaviException and its subclasses.
ch03.qxd
7/16/2001
7:10 AM
Page 45
45
org.havi.system
The org.havi.system package contains the core functionality of HAVi. Within this package are classes for accessing DCMs and the system software elements the Event Manager, Registry, and others. Two key classes in this package are: M SoftwareElementAllows you to create software elements and service requests sent by other software elements. M HaviClientProvides local proxies, or stubs, for sending requests to software elements.
org.havi.fcm.*
HAVi 1.0 specifies ten FCMs, nine of which have corresponding packages. These nine are called:
org.havi.fcm.tuner org.havi.fcm.vcr org.havi.fcm.clock org.havi.fcm.camera org.havi.fcm.avdisc org.havi.fcm.amplifier org.havi.fcm.display org.havi.fcm.modem org.havi.fcm.webproxy // // // // // // // // // access access access access access access access access access a Tuner FCM a VCR FCM a Clock FCM a Camera FCM an AV Disc FCM an Amplifier FCM a Display FCM a Modem FCM a Web Proxy FCM
The tenth FCM, the AV Display, provides display and amplifier services. Applications access the services of AV Display FCMs by using both the org.havi.fcm.display and org.havi.fcm.amplifier packages. Common data structures and constants used by FCMs are collected in the org.havi.fcm.constants and org.havi.fcm.types packages.
org.havi.iec61883
Audio, video, and other streamed data types are transferred over an IEEE 1394 (i.LINK) network using a packet format defined by a standard called IEC 61883. org.havi.iec61883 provides two classes that allow applications to read and write streaming data:
public class Iec61883InputStream extends java.io.InputStream {} public class Iec61883OutputStream extends java.io.OutputStream {}
ch03.qxd
7/16/2001
7:10 AM
Page 46
46
The read and write methods of these two classes do not take into account the timing requirements of transferring streaming data. It is the applications responsibility to assure that data is produced and consumed at the proper rate ; otherwise, buffers used by Iec61883OutputStream or Iec61883InputStream will underflow or overflow, respectively.
org.havi.ui and org.havi.ui.event are new additions to HAVi and are undergoing interoperability testing and revision. Since these packages are still being validated, we do not use them in our examples.
HJA Conventions
HAVi services are specified in IDL, the Interface Definition Language, as mentioned in Software Element APIs in Chapter 2. The HAViJava APIs have been generated automatically from the IDL form using an IDL to Java mapping. This mapping, described in the HAVi specification, introduces several conventions to the HJA. These conventions are described in the following subsections.
The IDL-to-Java mapping was designed for automatic generation. Unfortunately, the mapping introduces slightly idiosyncratic Java class definitions in some cases (e.g., when mapping IDL unions). An alternative would have been to handcraft the Java APIs. The HAVi Organization took the approach of automatically generating the HJA, since it was concerned about the difficulty of maintaining both an IDL and a Java version of the specification.
ch03.qxd
7/16/2001
7:10 AM
Page 47
47
Constant Naming
Java interfaces that specify constant values appear in the packages org.havi.constants and org.havi.fcm.constants. Their names are of the form Const<xx>. For instance:
org.havi.constants.ConstStreamType org.havi.constants.ConstSystemEventType org.havi.fcm.constants.ConstWriteProtectStatus
Event Information
HAVi events, i.e., those sent by the Event Manager, carry an event type and additional information associated with the event. This additional information is represented by a class in org.havi.types with a name of the form <EventType>EventInfo, as in Example 3-2.
Example 3-2
org.havi.types.ConnectionChangedEventInfo org.havi.types.NewSoftwareElementEventInfo
Example 3-3
ch03.qxd
7/16/2001
7:10 AM
Page 48
48
You should keep in mind that a synchronous request just blocks until a response is returnedit need not block until all actions initiated by the request are completed. For instance, the synchronous play requests supported by some FCMs just block until playout commences rather than completes.
Example 3-4
Timeout Parameter
int timeout = 0; // default timeout: 30 seconds myDcm.getDeviceManufacturer Sync(timeout, ); int timeout = 1000; // 1 second myDcm.getDeviceManufacturerSync(timeout, );
Holder Classes
The Interface Definition Language (IDL) used by HAVi allows service interfaces to specify multiple out parameters. When a service is invoked, the values of out parameters are returned by the service provider. Java methods, however, allow only a single return parameter. The IDL-to-Java mapping used by HAVi solves this problem by mapping out parameters to holder classes. These classes have getValue and setValue methods for accessing the encapsulated value. Holder classes have names of the form <DataType>Holder; they extend HaviHolder and are located in the org.havi.types package. Some holder classes are listed in Example 3-5.
Example 3-5
ch03.qxd
7/16/2001
7:10 AM
Page 49
49
HJA does not define holder classes for all IDL out and inout parameters. If the class used to represent an IDL data type is not immutable (does not extend HaviImmutableObject), then it can be used for an out parameter and a holder class is not necessary.
HAVi Messaging
HAVi is essentially a distributed programming environment and so provides mechanisms for applicationssoftware elements in HAVi terminologyto communicate with each other across the network. Now lets look at the HJA classes that play a key role in allowing software elements to exchange requests and replies.
HAVi RMI
Software elements communicate through the HAVi Messaging System. The Messaging System is divided into two levels: M A lower level that provides basic support for exchanging messages between software elements. This level deals with fragmentation and assembly of messages and supports two transfer modes: simple (unacknowledged) and reliable (acknowledged). M An upper level that supports a protocol called HAVi Remote Method Invocation (HAVi RMI). This protocol defines how requests for services are sent to software elements and how replies to service requests are returned. Figure 3-1 shows a software element on one device sending a request to a software element on a second device. The structure of the request and how it is placed in the payload of a message are defined by HAVi RMI. The receiver processes the request and sends a response. The structure of the response and how it is placed in a message payload are also defined by HAVi RMI. Figure 3-2 shows the message formats used by the Messaging System and for HAVi RMI. A message consists of a 28-byte header followed by a variable-length payload. The header contains the source and destination SEID, the message length, and other fields used by the Messaging System. The message header also includes a protocol, type field. If this field indicates the HAVi RMI protocol, then the message payload contains a service request or response and has the structure shown in the bottom of Figure 3-2. The HAVi RMI header contains three fields: M Operation code (3 bytes)Specifies the service being requested or that is responding.
ch03.qxd
7/16/2001
7:10 AM
Page 50
50
Service Response
Software Element
Software Element
Device #3 Device #1
M Control flag (one byte)Contains a flag indicating whether the message is a service request or service response. M Transaction ID (4 bytes)An integer allowing the Messaging System to match responses with requests.
0 to 64 Kbytes payload
8 bytes HAVi RMI request or response HAVi RMI header request/response parameters
ch03.qxd
7/16/2001
7:10 AM
Page 51
51
Marshalling
The parameters that appear in service requests and replies are marshalled into a bit stream before writing to the network. This process is similar to Java object serialization but produces a bit stream based on IDL data types rather than Java objects. Marshalling and unmarshalling is performed for you by HaviClient and its subclasses, so many applications do not need to worry about marshalling. The situations in which an application may have to perform explicit marshalling or unmarshalling are: M Applications that create software elements that offer their own services or that do not use HaviClient (subclasses) to send requests. M Applications that use asynchronous communication to invoke services. M Applications that register for Event Manager events or other asynchronous notifications. M Applications that use Registry attribute values. A marshalled bit stream is created using the HaviByteArrayInputStream. Any object that implements the Marshallable interface can then be placed in the bit stream (and removed from the bit stream). Marshallable is defined as:
public interface Marshallable { public void unmarshal(HaviByteArrayInputStream hbais) throws HaviUnmarshallingException; public void marshal(HaviByteArrayOutputStream hbaos) throws HaviMarshallingException; }
The Marshallable interface is implemented by two abstract classes: HaviObject and its subclass, HaviImmutableObject.
public abstract class HaviObject extends java.lang.Object implements java.lang.Cloneable, Marshallable {} public abstract class HaviImmutableObject extends HaviObject {}
Instances of the HaviImmutableObject class represent values that cannot be changed. Invocation of unmarshal() on these objects throws an exception.
ch03.qxd
7/16/2001
7:10 AM
Page 52
52
If you define a class that extends HaviObject and objects of this class have internal state, then you will need to override the default implementation of Marshallable and add explicit marshalling of the state variables used by your class.
In addition to classes that implement Marshallable, many of the primitive data types can be marshalled and unmarshalled using methods of HaviByteArrayInputStream and HaviByteArrayOutputStream. Example 3-6 shows how to create a bit stream containing marshalled values and how to extract the values from the bit stream. The example first marshals three values, an int, String, and SEID object, into a HaviByteArrayOutputStream. The marshalled bit stream is then obtained from the HaviByteArrayOutputStream object; this would typically form part of the payload of a message. However, this example just uses the bit stream to unmarshal the original values.
It is crucial to unmarshal objects in precisely the same order as they were marshalled; otherwise, their values are not likely to be preserved.
Example 3-6
Marshalling and Unmarshalling Values hbaos; hbais bitStream; myInt; myString; mySEID;
// assign values to myInt, myString and mySEID // marshall // hbaos = new HaviByteArrayOutputStream(); hbaos.writeInt(myInt); hbaos.writeHaviString(myString); mySEID.marshal(hbaos); // get the bit stream bitStream = hbaos.toByteArray();
ch03.qxd
7/16/2001
7:10 AM
Page 53
53
// unmarshall // USE SAME ORDER AS MARSHALLED // hbais = new HaviByteArrayInputStream(bitStream); myInt = hbais.readInt(); myString = hbais.readHaviString(); mySEID = new SEID(hbais);
Example 3-6, line , uses writeHaviString() to marshal the String value. Generally, HAVi uses UNICODE strings (two bytes per character). writeHaviString() marshals myString by writing the length of myString, followed by each two-byte character, followed by a two-byte null value \u0000. Line shows how to unmarshal an immutable object. The SEID class is a subclass of HaviImmutableObject and so does not support unmarshall(). Instead of attempting to change the value of mySeid using
mySeid.unmarshal(hbais); // Error
Service Requests
HAVi uses an operation code to uniquely identify each type of service request. If you write software elements that only request the services of HAVi software elements, then operation codes are constructed for you automatically by the HaviClient subclasses. However, you must explicitly construct and examine operation codes if you write either a software element that offers services to other software elements, or a software element that requests nonstandard services (i.e., services not specified by HAVi). An operation code contains two parts: M API codeIdentifies a family of software element APIs, e.g., the services provided by the Registry, the Event Manager, etc. API codes for the software elements defined by HAVi are in org.havi.constants.ConstApiCode. HAVi reserves API codes from 0x0000 to 0x7fff for system services. Applications are free to use API codes from 0x8000 to 0xffff. M Operation IDIdentifies a particular service within a family, e.g., the Registry query service or the Event Manger post event service. The OperationCode class allows you to create and examine operation codes as illustrated in Example 3-7.
ch03.qxd
7/16/2001
7:10 AM
Page 54
54
Example 3-7
import org.havi.types; import org.havi.constants; OperationCode opCode = new OperationCode( ConstApiCode.REGISTRY, ConstRegistryOperationId.GET_ELEMENT);
Once an OperationCode object has been created, the API code and operation ID values can be extracted using the getApiCode() and getOperationId() methods.
Service Responses
You can issue a service request via HaviClient methods or by accessing the Messaging System through the SoftwareElement class described in the next section. In either case, a HAVi message is sent to the software element providing the service. This message includes an operation code identifying the service and marshalled parameters. The response includes the marshalled return values plus a status value. A status value contains: M API codeIdentifies the interface to which the service request belongs. M Error codeIdentifies a particular error condition from the service provider. Each type of software element can define a set of error codes for the API codes that it implements. HJA provides a Status class for handling status values. Example 3-8 shows how a Registry software element could create a status value using the Status class.
When a client receives a status value, it can examine the API code and error code fields using the getApiCode() and getErrorCode() methods of the Status class. Some errors reflect conditions that can occur with many different software elements. HAVi calls these general errors, and reserves error codes in the range 0x0 to 0x7f for such values. Table 3-1 lists all general errors defined by HAVi. These values are defined in the ConstGeneralErrorCode interface.
ch03.qxd
7/16/2001
7:10 AM
Page 55
55
Description
Normal return status, no error. The destination software element does not support the requested service. The requested service is only available to trusted software elements and the client is not trusted. An error of unknown origin has occurred. The request is for an optional service and it is not implemented by the destination software element. The provider is reserved by another client. One or more parameters in the service request contain invalid values. The request failed due to resource limits at the destination. One or more parameters in the service request exceed size limits specified by HAVi. The request is incomplete. One or more parameters in the response are correct but incomplete. The requested service is only available locally but the client is not local (is on a different device). The destination software element is on a device in power standby state and cannot service the request.
Example 3-9
public class SoftwareElement extends java.lang.Object { public SoftwareElement(); public SoftwareElement(HaviListener hl); // public final void addHaviListener(HaviListener hl); public final void addHaviListener(HaviListener hl, SEID targetSeid); public final void removeHaviListener(HaviListener hl);
ch03.qxd
7/16/2001
7:10 AM
Page 56
56
close(); getSeid(); msgGetSystemSeid(SEID seid, int softwareElementType); public final boolean msgIsTrusted(SEID seid); public final void msgSendRequest(SEID destSeid, OperationCode opCode, HaviByteArrayOutputStream buffer, IntHolder transactionId); msgSendRequestSync(SEID destSeid, OperationCode opCode, int timeout, HaviByteArrayOutputStream bufferIn, HaviByteArrayInputStream bufferOut, StatusHolder returnCode); msgSendResponse(SEID destSeid, OperationCode opCode, int transferMode, Status returnCode, HaviByteArrayOutputStream buffer, int transactionId); msgSendSimple(byte protocolType, SEID[] destSeidList, HaviByteArrayOutputStream buffer); msgSendReliable( byte protocolType, SEID destSeid, HaviByteArrayOutputStream buffer); msgWatchOff(SEID destSeid); msgWatchOn(SEID destSeid);
The SoftwareElement class is the link between an HJA application and the HAVi Messaging System. Methods of the SoftwareElement class can be divided into several groups as summarized in Table 3-2.
ch03.qxd
7/16/2001
7:10 AM
Page 57
57
getSeid msgGetSystemSeid msgIsTrusted msgSendRequest msgSendRequestSync msgSendResponse msgSendSimple msgSendReliable msgWatchOn msgWatchOff
SEID operations
Direct access to HAVi messaging Software element supervision (low-level support for monitoring whether a software element is present, not typically used by applications)
The hl parameter in the second constructor is an instance of a class that extends HaviListener. A HaviListener object is installed for a software element either via the SoftwareElement constructor or via the addHaviListener() method. HAVi listeners determine how a software element handles incoming messages.
Since receiveMsg() of HaviListener is abstract, extensions of this class must implement this method.
ch03.qxd
7/16/2001
7:10 AM
Page 58
58
receiveMsg()returns true if it receives an incoming HAVi RMI service request that it will handle; in all other cases it returns false.
The haveReplied parameter shows whether some other listener has already replied to the message. If haveReply is true, then recieveMsg() should not also reply (and so should return false), although it is free to inspect the message. The protocolType parameter in receiveMsg() indicates whether the message uses the HAVi RMI protocol or some private (application-specific) protocol. If the message is a HAVi RMI service request or response, then protocolType will have the value ConstProtocolType.HAVi_RMI. The state parameter shows whether any error condition has arisen in the Messaging System during delivery of the message. The two SEID parameters indicate the sender (sourceId) and receiver (destId) of the message. destId will be the same as the SEID of the software element that installed the listener. payload is the message payload from which request parameters can be unmarshalled. Messages sent to a software element are passed to matching HaviListener objects installed on the associated SoftwareElement object. M Listeners installed by addHaviListener(HaviListener) match all messages. M Listeners installed by addHaviListener(HaviListener, SEID) match messages from the specified software element. An incoming message should be processed by only one listener. If you add several listeners to a software element, you should check the incoming message, for instance by examining its operation code, so that the message is processed by only one listener. This is shown in Example 3-10.
Example 3-10
A HaviListener
public class MyListener extends HaviListener { private short _myApiCode = 0x8000; private short _myOperationId = 0x00; private OperationCode _myOpCode = new OperationCode(_myApiCode, _myOperationId); public boolean receiveMsg(boolean haveReplied, byte protocolType, SEID sourceId, SEID destId, Status state, HaviByteArrayInputStream payload)
ch03.qxd
7/16/2001
7:10 AM
Page 59
59
{ // check haveReplied, protocolType, status and // the HAVi RMI header // see Example 3-15 for details OperationCode opCode = new OperationCode(payload); if(!opCode.equals(_myOpCode)) return false; // this message is for this listener, < process the message now and send reply, or do so in separate thread > return true; } } MyListener will process only messages containing the specific operation code created in line . The API code used to create _myOperationCode is 0x8000; this is in the range for applications rather than the predefined HAVi services. Before checking the operation code in the incoming message, MyListener first checks (as indicated by line ) that the message actually contains a HAVi RMI service request, in which case the first value in the message payload is the operation code. In Line , the operation code of the message can then be unmarshalled and used to create an OperationCode object.
A SoftwareElement object has its own thread to call the HaviListener.receiveMsg() callbacks; it does not block other SoftwareElement objects or the underlying Messaging System while performing these callbacks. Consequently, there are no restrictions on the implementations of the (application-provided) HaviListener.receiveMsg() method: The method does not have to be treated as an interrupt. However, you must be careful when using SoftwareElement.msgSendRequestSync() in the HaviListener.receiveMsg() method, since this will block the calling software element until the response to the request is received. While the msgSendRequestSync() is blocked, no other incoming messages can be processed by the software element. This may result in a deadlock.
ch03.qxd
7/16/2001
7:10 AM
Page 60
60
For instance, RegistryClient extends HaviClient and provides access to Registry services; EventMangerClient extends HaviClient and provides access to Event Manger services; and so on. See Example 3-11.
Example 3-11
A HaviClient Class
public class DcmClient extends HaviClient { public DcmClient(SoftwareElement se, SEID destSeid); // globally accessible DCM APIs }
The constructor in line identifies a software element that will be a client of DCM services; this is typically a software element created by the application. The second parameter identifies the SEID of the software element that will provide the services. This should be the SEID of a DCM; otherwise, an exception is thrown. To access a DCM, you first create an instance of DcmClient. Specific DCM services are then invoked via the methods of DcmClient:
dcm = new DcmClient(mySe, aDcmSeid); dcm.getDeviceIcon(< arguments omitted >);
In many cases, a software element may provide services that are accessible only to local software elements, i.e., software elements running on the same controller. This is reflected in HJA by subclassing the client class:
public class DcmLocalClient extends DcmClient { public DcmLocalClient(SoftwareElement se, SEID destSeid); // locally accessible DCM APIs }
The constructor of DcmLocalClient tests that destSeid (identifying the DCM) is on the same controller as se (identifying the client of the DCM). An exception is thrown if these do not match.
HaviException Classes
HJA maps HAVi status values to exception objects. This means that if you invoke services using a HaviClient subclass, then you can just catch exceptions rather
ch03.qxd
7/16/2001
7:10 AM
Page 61
61
than examine status values. HJA defines a large hierarchy of exceptions. Figure 3-3 shows the overall structure of this hierarchy. Exceptions that result from failed service requests derive from HaviException. For instance, all the general errors listed in Table 3-1 correspond to subclasses of HaviGeneralException. In addition, HJA defines exceptions classes that are related to: M marshallingHaviMarshallingException and HaviUnmarshallingException; M constructing HJA objects that correspond to IDL data structuresHaviInvalidValueException and HaviUnionException; and M Messaging System listenersHaviMsgListenerExistsException and HaviMsgListenerNotFoundException. Since HAVi errors are mapped to Java exceptions, you can make full use of Javas exception handling mechanisms. Also, the structure of the HaviException hierarchy helps in organizing exception handling code, as shown in Example 3-12.
Example 3-12
dcm = new DcmClient(mySe, aDcmSeid); try { dcm.getDeviceIcon(< arguments omitted >); } catch (HaviDcmException e) { // problem with the DCM } catch(HaviException e) { // some other failure System.err.println(HAVi Error: + e.getMessage()); System.err.println( API code = + e.getApiCode()); System.err.println( error code = + e.errCode()); }
Figure 3-3 shows the structure of the HaviException class hierarchy. For brevity, not all exceptions classes are shown.
SEID Access
When a SoftwareElement object is created it is assigned a SEID; the value can then be retrieved using getSeid(), i.e.,
SEID mySeid = mySe.getSeid();
ch03.qxd
7/16/2001
7:10 AM
Page 62
62
java.iang Exception
HaviException
HaviGeneralException
HaviMsgException
HaviRegistryException
HaviUnknownMessageException HaviUnidentifiedFailureException HaviNotImplementedException HaviReserveException HaviInvalidParameterException HaviResourceLimitException HaviResourceSizeLimitException HavicompleteMessageException HaviincompleteResultException HaviLocalException HaviStandbyException
HaviMsgAckException HaviMsgAllocException
...
elements on the same host as a specific SEID. Example 3-3 shows how to find the SEID of the local Registry.
Example 3-13
Using msgGetSystemSeid()
ch03.qxd
7/16/2001
7:10 AM
Page 63
63
The msgIsTrusted()method of SoftwareElement tests whether a software element is trusted (see Trusted and Untrusted Software Elements in Chapter 2); this is done by examining its SEID.
Example 3-14
HaviByteArrayOutputStream hbaos = new HaviByteArrayOutputStream(); OperationCode opCode = new OperationCode( ConstApiCode.REGISTRY, ConstRegistryOperationId.UNREGISTER_ELEMENT); SoftwareElement mySe = new SoftwareElement(); SEID mySeid = mySe.getSeid(); SEID regSeid = mySe.getSystemSeid(mySeid, ConstSoftwareElementType.REGISTRY); mySeid.marshal(hbaos); int timeout = 2000; try {
ch03.qxd
7/16/2001
7:10 AM
Page 64
64
StatusHolder returnCodeH = new StatusHolder(); mySe.msgSendRequestSync(regSeid, opCode, timeout, hbaos, null, returnCodeH); } catch (HaviGeneralException e) { // code for general exceptions } catch (HaviMsgException e) { // code for Messaging System exceptions }
The parameters that need to be marshaled into the payload of msgSendRequestSync() or msgSendRequest() are determined by examining the IDL specification of the service. For the unregister element, this is:
Status Registry::UnregisterElement(in SEID seid) // IDL
software element that is being removed from the Registry. Line shows this SEID being marshalled into the input byte array. Line sets the timeout for the request to 2000 milliseconds. The request is then sent in line , returnCodeH will hold the Status object that is returned. The fifth argument of msgSendRequestSync() is null, since unregister element does not return any values (other than the status value).
protocol directly on top of the HAVi Messaging System core. However, if your
ch03.qxd
7/16/2001
7:10 AM
Page 65
65
HJA app
HJA app
HaviClient classes
HAVi RMI
HAVi Messaging
IEEE 1394
software elements must interoperate with other software elements, then HAVi RMI is appropriate.
Example 3-15
import org.havi.system.*; import org.havi.types.*; import org.havi.constants.*; // an HJA application that creates a software element // with two services: // Helloprint hello // Goodbyeclose the software element
ch03.qxd
7/16/2001
7:10 AM
Page 66
66
// public interface ConstMyService { public static final short apiCode = 0x8123; public static final byte helloId = 0x00; public static final byte goodByeId = 0x01; } public class MyApp { private SoftwareElement private MyListener
_mySe; _myListener;
public MyApp() { try { _myListener = new MyListener(this); _mySe = new SoftwareElement(_myListener); _mySe.add HaviListener(_myListener); } catch(HaviException e) { System.err.println(HAVi Error: + e.getMessage()); } } protected SoftwareElement getSoftwareElement() { return _mySe; } protected void helloServiceHandler() { System.out.println(MyApp: hello); } protected void goodByeServiceHandler() { System.out.println(MyApp: goodbye); _mySe.removeListener(_myListener); _mySe.close(); }
// MyListener inner class // public class MyListener extends HaviListener { private MyApp _myApp; private SEID _destSeid; private OperationCode _opCode; private byte _controlFlags; private int _transactionId; public MyListener(MyApp myApp) {
ch03.qxd
7/16/2001
7:10 AM
Page 67
67
_myApp = myApp; } public boolean receiveMsg(boolean haveReplied, byte protocolType, SEID sourceId, SEID destId, Status state, HaviByteArrayInputStream payload) { _destId = destId; if(haveReplied) { // another listener has replied // just ignore return false; } if(state.getErrorCode() != ConstGeneralErrorCode.SUCCESS) { // Messaging System problem // just ignore return false; } if(protocolType != ConstProtocolType.HAVi_RMI) { // incoming message is not a // HAVi RMI service request or response, // just ignore return false; } // unmarshal the HAVi RMI header // _opCode = new OperationCode(payload); _controlFlags = payload.readByte(); _transactionId = payload.readInt(); if((_controlFlags & 0x01) == 1) { // incoming HAVi RMI message // is a service response, // just ignore return false; } if(_opCode.getApiCode() != ConstMyService.apiCode) { // incoming HAVi RMI service request
ch03.qxd
7/16/2001
7:10 AM
Page 68
68
// uses an unknown/unsupported API code return false; } switch(_opCode.getOperationId()) { case ConstMyService.helloId: _myApp.helloServiceHandler(); sendMsg(ConstGeneralErrorCode.SUCCESS); return true; case ConstMyService.goodByeId: // reply before calling handler since // it will close us down sendMsg(ConstGeneralErrorCode.SUCCESS); _myApp.goodbyeServiceHandler(); return true; default: // unknown operation id; return false; } } // sendMsg() sends a HAVi RMI response // to the software element that has // sent us a HAVi RMI service request // public void sendMsg(short err) { Status returnCode = new Status( _opCode.getApiCode(), err); SoftwareElement se = _myApp.getSoftwareElement(); se.msgSendResponse(_destSeid, _opCode, ConstTransferMode.SIMPLE, returnCode, null, _transactionId); } } }
In Example 3-15, the constants used for API code and operation IDs are defined in the public interface ConstMyService. This interface is public since software elements that want to request the hello or goodbye service would need to know these values. In line , the value for the API code is set to 0x8123; this is in a range available to application developers.
receiveMsg() handles incoming messages to the software element created by the MyApp constructor. It first checks haveReplied and state. If haveReplied is
ch03.qxd
7/16/2001
7:10 AM
Page 69
69
true, then another listener has replied to the message. This will not happen in MyApp since only one listener is installed, but its good programming style to make the check. The state parameter tells whether an error has been detected by
the Messaging System (such as the sender of the message terminating). If no error is present, state.getErrorCode() will return ConstGeneralErrorCode.SUCCESS. Next, receiveMsg() examines protocolType to determine whether the message is a HAVi RMI service request or response. receiveMsg() then unmarshals the HAVi RMI header using the code repeated in Example 3-16.
Example 3-16
Recall from HAVi RMI earlier in this chapter that the HAVi message payload will contain the HAVi RMI header followed by any service-specific parameters. The HAVi RMI header contains the operation code, the control flags field, and the transaction ID. After unmarshalling the operation code in Example 3-16, the control flags and transaction ID are unmarshalled in lines and . The control flags are needed to distinguish between a service request and a service response (MyListener is not expecting service responses, but they may be sent erroneously by other software elements). The transaction ID is needed by sendMsg() to send the response to the requested service. Referring back to the body of receiveMsg() in Example 3-15, the Hello and Goodbye services do not take any parameters, so after unmarshalling the HAVi RMI header, no further unmarshalling is needed. receiveMsg() then checks the control flags and API code. If these indicate a request for a supported service, it does a switch on the operation ID of the requested service and invokes the service handlers supplied by MyApp and sends a response using sendMsg().
Summary
The HAVi Java APIs, or HJA, allow you to construct software elements that communicate over an IEEE 1394 (i. LINK) network. You can write two types of HJA application: havlets and controller-specific applications. Havlets are portable; they run on any controller supporting HJA. Controller-specific applications make use of features found on specific controllers and so are not portable.
ch03.qxd
7/16/2001
7:10 AM
Page 70
70
High-level classes like HaviClient, HaviListener, and SoftwareElement abstract the underlying HAVi Messaging System. These classes support a protocol called HAVi Remote Method Invocation (HAVi RMI) that is used by software elements to send service requests and responses over the Messaging System.