Hume Integration has created a Java package for integrating Java
and web-based applications into a distributed system using the Distributed
Message Hub (DMH) message system. The Java package consists
of classes to use the DMH message system, a class to use text structured
as Tcl lists, a class to parse VFEI formatted strings, and a class that
provides interval timing. High-level methods are provided for
sending and receiving messages, with either send-and-reply synchronous-style
interactions, or higher performance asynchronous-style interactions.
Message exchanges are typically directed to application servers such as
the Hume Integration Datahub, or the dmh_SQLsrv persistent database interface.
The DMH message system is remarkably easy to use with these DMH Tcl processes
because the messages are typically SQL, Tcl, or VFEI text that is directly
interpreted by the receiver. To extend this ease of use to
the Java client, methods are provided to parse Tcl list text strings,
and to create Tcl list text strings from string elements. The package
has been recently enhanced to include DMH server capabilities.
The class files are all placed in a subdirectory structure as determined by the package name, "com.hume.DMH". The "com/hume/DMH" directory is usually located at the root of your http web server document directory for deploying Java applets on the web. You can place this entire subdirectory in another file system location if you use CODEBASE tags in your documents to specify the non-default location. The demonstration applets included with this DMH package, are coded with the package name "DMHApplet". For the simplest deployment, a directory of this name, DMHApplet, is created from the root directory of your web server, and the files are installed in that directory. For development, see your Java documentation about adding the directory to your CLASSPATH, or loading it into your integrated development environment.
There are alternatives for web server deployment. It is possible to create jar file archives that contain the class files and your application files. The startup of your application is sped up if the jar file is pre-installed at the client. When you use jar files, you need to specify the name of the jar file as one of the options to the HTML markups that identify your Applet.
As a developer, you should know how to display the Java console from your web browser. Microsoft's IE does in fact feature a console window, but it is not on the menu until the program options are set to make it available. There are usually informational messages on the console in error situations.
Your design will typically use one instance of the DmhClient class. You can declare the instance as part of your application class, and then make it public or pass references to your window and dialog classes. A single instance of the class can be used for sending and receiving with multiple mailboxes simultaneously. More than one instance of the class is only needed if you wish to communicate directly with multiple DMH servers. You should not use multiple instances of the class to communicate with a single DMH server.
As a developer, per the licensing terms, you are required to have the Tcl/Tk DMH software installed on your development system. One reason for this requirement is to insure that you have the online documentation for the DMH system. A second major reason is so that you can run your own DMH server process to test and debug against without affecting a production system.
If you are not familiar with the DMH message system, you may want to read the mbx document which is usually installed at \usr\local\htm84\mann\mbx.html. Your application acts a DMH message system client, and attaches to a running DMH server. Once you are connected to the DMH system, you can exchange messages with other attached processes.
To get started, you only need to know the hostname where the DMH server is running, and the DMH Groupname that has been assigned to the server. If you click on the Windows "Programs"/"Hume Datahub SDK 8.4"/"Datahub" program item, or start a Datahub process on your POSIX system ("datahub eof &"), you will start a DMH server running on your host, with the default DMH groupname of "mbx".
The usual application design, and the one that provides the best performance, is to connect to the DMH server during initialization, and to use this connection during the life of the application. The init( ) method is used to connect to the server. The server's hostname and groupname are provided as arguments to the init( ) method. If the application disconnects from the server, the init( ) command can be used again to restore the connection.
So in your application class you might have code that looks like:
import com.hume.DMH.*; // Hume Integration DMH message system
// class declaration
public class MyApp extends JApplet implements DmhLostServerItf,
... {
// ...
public static DmhClient dmh = null;
// ...
public void init() {
// ...
// construct a single
class instance if needed
if(dmh == null) { dmh
= new DmhClient(); }
// connect to the DMH
server on the web server host
// if not already connected
if( dmh.getState() ==
0) {
String serverhost;
try {
// throws exception if called from static main
java.net.URL thisURL = getCodeBase();
serverhost =thisURL.getHost();
} catch (Exception e) { ; }
if ( serverhost == null || serverhost.equals("")) {
serverhost = dmh.getHostname();
}
System.out.println("Connecting to the DMH Server, host=" + serverhost +
"group=mbx");
try {
dmh.init(serverhost, "mbx");
} catch (Exception e) {
String msg="Communication to the application server failed. Try reloading
this web page later.";
JOptionPane.showMessageDialog(null,
msg, "DMH Server Not Online", JOptionPane.WARNING_MESSAGE);
//setContentPane(offline_panel());
return;
}
}
// ...
}
public void dmhLostServer(DmhClientItf dmh) {
String msg="Communication
to the DMH server has been lost. The system has probably been shutdown";
msg = msg + " Reload this
web page to try reconnecting.";
JOptionPane.showMessageDialog(null,
msg, "Communication Lost", JOptionPane.WARNING_MESSAGE);
// now disable transaction buttons...
}
The first style of error handling is to avoid them by disabling buttons and window controls that make use of DMH features when there is not a server connection. You write the statements that enable your buttons after a successful init( ) call, and you write statements that disable the buttons in your dmhLostServer( ) event handler. During initialization, before you have a connection, call a procedure that you have written to disable the buttons. Also during initialization, call the init( ) method. The init( ) method is a synchronous call that does not return until you have setup a connection to the DMH server, or have failed. When the init( ) call succeeds, call a procedure that you have written to enable your buttons.
The second style of error handling is to add explicit error trapping to your procedures. The methods that involve network communication can throw exceptions which can be caught with a try{ } and catch( ) statement.
Here is a summary of the kinds of errors that the DMH control will report.
When you make a method call that requires a DMH server connection, and
you do not have one, an Exception will be thrown with the text "No DMH
server connection". If you use an improper mailbox name such as one
with whitespace in it, an Exception will be thrown with the text, "mailbox
name must contain only letters, digits, -, _, ., !, :, or @".
These are the two main errors when calling most of the methods.
Most of the DMH method calls are then processed asynchronously.
In other words, your method call returns, and the message communication
you initiated happens as events are processed. An error that occurs
during event processing results in the dmhLostServer( ) callback being
processed. You do not need to write a dmhLostServer( ) callback handler,
but you probably should to communicate to the users of your program if
an error occurs. You can place logic in your dmhLostServer( )
handler to initiate recovery and resume logic, exit the program, etc.
The init( ) can result in a wider variety of exception returns - see the
reference pages.
You can send messages without asking for replies using the put( ) method . For example, suppose you are integrating a barcode reader. When data is read from the barcode reader device, your code is supposed to update a record in a Datahub table. You will send messages without asking for reply messages - if there is a system shutdown or communication failure, your application will know from the DmhLostServer( ) callback. This is more efficient than asking for a reply message at every barcode read. When you send a message without waiting for a reply, it is referred to as an asynchronous send.
Often, you will want to send a message to obtain reply data. The most convenient method to use is the doXact( ) method. This method will take care of specifying and using a unique mailbox for your reply message, and it will take care of managing a timer in case a reply message is not forthcoming. Suppose you want to query a database table, and the DB variable is assigned the mailbox name of the dmh_SQLsrv process. The online documentation shows that the SQL standard "select" command sends multiple reply messages, but the "telect" command sends a single reply message with all of the requested data formatted as a Tcl list. The doXact( ) method is designed for a single reply message, so your code looks like:
String reply;
String msg;
msg = "telect device_id from barcode_config where display='" +
new hostname() +" '");
reply = dmh.doXact(DB, msg);
if (reply.equals("TIMEOUT")) {
// timeout or error
return;
}
// Parse the result- a Tcl List
// element(6) = rows of data, then (0) = first row, then (0) =
first item in row
String device_id = TclList.listElement(selection, 6, 0, 0);
The disarm( ) method can be used to unregister an existing whenMsg setup, or all whenMsg setups.
The whenMsg( ) method functions to receive only one message. If whenMsgAgain( ) is executed from your receiving event handler, then the logic is re-armed to receive the next message. So the combination of whenMsg( ) and whenMsgAgain( ) are used for ongoing asynchronous receiving.
Lets revisit the doXact( ) method. In some situations, you may want higher performance by sending messages and collecting the replies asynchronously, instead of waiting for each reply before sending the next message. You do this by setting up one or more reply mailboxes and arming them for receiving using the whenMsg( ) method. Instead of using doXact( ) use the putr( ) command and specify the reply mailbox argument. Typically a high performance application will create a small number of unique mailbox names for replies, and re-use them. If you are creating unique mailboxes for each reply message, use the close( ) method when you are done with each one, to recover resource usage.
Your application should not "live" inside of your message receive handling
code. Be sure to return in due course. Avoid performing long
running computations.
When declaring database tables to hold International text, base the VARCHAR( ) sizes on UTF-8 byte counts, not on the number of characters. In the most conservative case, you need to allow 3 bytes per displayed character.
To be successful with International applications you need to make sure
that you have installed fonts and chosen fonts in your application that
are capable of displaying the characters you require.
The programming model is that you will not have more than one connection to a particular DMH server. It is typical to have only one DMH connection per application process. It is possible to use multiple controls, each connected to a different DMH Group.
There should be only one reader per mailbox name in a given DMH group.
The DmhClient class is able to use and resolve DMH groupname aliases as described in the online Tcl documentation. Groupname aliases are resolved at the DMH server and not at the client.
Mailbox Naming rules:
| Event Callback and Set Method | Description |
| All callback methods - NOTES
|
You write your own callback class or classes, and you declare that
a class implements the callback interface. It is possible to implement
any or all of the interfaces in one class, or multiple classes.
// Demonstration of repeated receiving
dmh.whenMsgAgain(); // rearm for next message // process the received message
Then, you register instances of your class(es) to receive the event with the DmhClient: WheneverReceive wr=new WheneverReceive();
When the event happens, your callback function is called. A reference to the DmhClient is provided as an argument, in case you are using the same callback with multiple instances of the DmhClient class. When registering the LostServer or Trace callbacks, the previous value is returned. This lets you chain, or swap and restore callbacks. |
| public interface
DmhLostServerItf { public void dmhLostServer( DmhClientItf dmh); } // DmhClient set method
|
The LostServer event happens when the DMH connection has been closed from any circumstance such as remote closure, communication failure, error, or invocation of the Disconnect method. This event is similar to the Tcl lostserver procedure invocation. The event may happen more than once if multiple errors are being processed. |
|
public interface DmhReceiveItf { public void dmhReceive( DmhClientItf dmh, String data, String destinationBox, String replyMailbox) throws Exception; } |
The Receive callback is executed when a message has arrived.
You can register different callback objects for each destination mailbox
using the whenMsg method, described in the next
table.
The destinationBox parameter is a mailbox name that your application has specified when initiating receiving. If the sender of the message indicated a reply mailbox, it is passed as the replyMailbox. If no reply mailbox has been specified, the replyMailbox argument is an empty string. The data argument is the text of the sent message. The DmhClient logic protects you from receiving another message for the DestinationMailbox, and re-entering your handler logic until you have returned from the current callback execution. |
| public interface
DmhTraceItf { public void dmhTrace( DmhClientItf dmh, String traceMessage); } // DmhClient set method
|
This event provides diagnostic and debug information per the TraceBits property setting. Your application needs to avoid creating new DMH activity in the Trace event callback, that in return causes Trace events. A cycle of positive feedback is possible which will cause a software fission reaction. |
| METHOD | DESCRIPTION |
|
All methods - NOTES |
If the method throws Exceptions, it is generally true that if the method is called when there is not a connection to the DMH server, an Exception will be raised. In this situation, the text of the Exception is "No DMH server connection". |
| void close(String boxname) throws Exception; | Stop using a mailbox - Disarm receiving if listening, Flush if not empty, and remove from existence if it exists. The Tcl version of this call, differs because it will not flush existing messages. |
| int[] count(String boxname) throws Exception;
|
Returns an array of three numbers, the total count of messages that have been sent to the mailbox, the total count of messages that have been consumed from the mailbox, and last, the current count of pending messages. A pending message is one that exists in the queue associated with the mailbox, and has not been consumed by reading or flushing. |
| public void disarm(String boxname) throws Exception;
public void disarm() throws Exception; |
Un-register the listener from a specified mailbox. This
call may be used to cancel an earlier whenMsg( ) call.
If called with no arguments, all whenMsg( ) receiving registrations are canceled. |
| void disconnect(); | The counterpart of init( ); disconnects from the DMH server. The call also cancels all receiving. Has no effect if not connected. |
| String doXact(String destbox, String msg) throws Exception;
String doXact(String destbox, String msg, int seconds_timeout) throws Exception; String doXact(String destbox, String msg, String replybox) throws Exception; String doXact(String destbox, String msg, String replybox, int seconds_timeout) throws Exception;
|
Performs a complete send and reply transaction with timeout management.
Creates and manages a unique reply mailbox for the send and reply transaction
if the replybox argument is defaulted. If the timeout is not specified,
the DefaultTimeout value is used. The
usual reply is the text of the reply message. The String literal TIMEOUT
is returned in case of failure.
If you specify a ReplyMailbox, you need to insure that the name you specify is only used by your application. It is usual to create a unique reply mailbox name, perhaps based on the hostname, assign it to a variable, and use it repeatedly. If you are not connected, the call fails immediately with an Exception. |
| void flush(String boxname) throws Exception;
|
Empty a mailbox of any pending messages. A pending message is one that has been sent to the mailbox but has not been consumed. In other words, a pending message is waiting in a queue associated with the mailbox name. Messages are consumed by reading or flushing. |
| int groupnamePort(String Groupname)
|
Used to determine the TCP/IP port number that is used by the DMH server to listen for client connections. The method is equivalent to the mh_name_to_socket Tcl procedure. Most applications will not have a use for this method since the server socket port is managed by the DMH software. |
| String init(String hostname, String groupname) throws Exception;
String init(String hostname, int port) throws Exception;
|
Performs the initial connection to the DMH message server. The connection
will be setup or an exception result will be obtained before returning.
If the connection to the DMH server is ever lost, the LostServer( ) event is fired.
|
| void put(String mailbox, String message) throws Exception; | The put( ) method sends a message to a mailbox with no reply mailbox
indicated.
The doublebyte String characters are converted to UTF-8 encoding, so that you can safely send International characters. Sending to the mailbox name "TRACE" sends the message to the DMH Server Trace Facility. |
| void putr(String destbox, String replybox, String message) throws Exception; | Sends a message to a mailbox with a reply mailbox indicated.
By convention, when a reply mailbox is indicated for a command message
sent to a Datahub mailbox or equipment interface mailbox, the command is
processed, and a reply message is sent to the reply mailbox.
Sending to the mailbox name "TRACE" sends the message to the DMH Server Trace Facility. |
| String serverStatus() throws Exception;
|
Returns a Tcl list containing the information presented in the Tcl
DMH status window. The information can be parsed by the application
to determine status information on every mailbox that is currently
in use. This command may be useful for debugging, and is not used
by ordinary applications.
The first element of the list is a list of 5 elements:
Subsequent elements in the list are lists of four or five elements:
Additional elements may exist in the list
if there are DMH clients that are not currently waiting to receive messages.
These elements are formatted as:
|
| String timedReceive(String boxname, int secondsTimeout) throws Exception; | Waits for a message to be received in the specified mailbox.
If the call succeeds, the return value is the message data.
If the call fails, the return value is the literal string "TIMEOUT".
If you are not connected, the call fails immediately with an Exception. |
| void whenMsg(String boxname, DmhReceiveItf
handler) throws Exception;
|
Register for receiving the next available message directed to the specified mailbox. Calling whenMsgAgain( ) in your DmhReceive( ) event handling code re-arms the receive registration for the next message. |
| void whenMsgAgain() throws Exception; | The whenMsg( ) method functions as a one-shot. In other words, receiving is stopped after receiving one message. Calling the whenMsgAgain method from the receive handler re-registers to receive the next message. |
| String[][] whenMsgDump(); | The whenMsgDump method returns an array of string pairs (String [N][2]).
Each pair consists of [][0] a mailbox name, and [][1] the result of executing
the toString( ) method of the DmhReceiveItf object. This command
may be useful for debugging, and is not used by ordinary applications.
String reply[][]; try { reply = dmh.whenMsgDump(); OutputAppend("whenMsgDump:"); for(int i=0; i< reply.length; i++) { OutputAppend("box=" + reply[i][0] + " obj=" + reply[i][1]); } } catch (Exception e) { OutputAppend( "Exception:" + e); } |
| METHOD | DESCRIPTION |
| TclList(String s)
TclList() |
The constructors. To work with String text that is formatted as a Tcl list, pass the text as the constructor argument. For creating a new empty Tcl list, use the empty constructor. A new empty list can also be constructed by passing a null or empty string constructor argument. |
| String join() | Used to obtain the text of the constructed Tcl list. |
| void lappend(String element) throws Exception
void lappend(String e1, String e2) throws Exception void lappend(String e1, String e2, String e3) throws Exception void lappend(String e1, String e2, String e3, String e4) throws Exception |
List append - adds one to four string elements at the end of the list. |
| String lindex(int n) throws Exception | Obtain the string element at the specified index. Indexing starts with 0. An empty string is returned if the index is out of range. |
| static String lindex(String s, int n) throws Exception | Use this method to grab elements in a list without constructing a TclList object. The method caches a parsed representation of the last String argument, so successive calls to lindex on the same string are processed rapidly. |
| void linsert(String e, int index) throws Exception | List insert - add an element at index N, sliding existing elements +1. |
| static String listElement(String s, int index1) throws Exception
static String listElement(String s, int index1, int index2) throws Exception static String listElement(String s, int index1, int index2, int index3) throws Exception |
These are convenience methods to parse elements in a list without constructing a TclList object. If a single index is specified, the method is identical to the static lindex( ) method. Additional indexes may be specified to parse lists that are nested inside of lists. If a specified index is out of bounds, an empty string is returned. Similar methods are provided with the DMH Active-X control, and the DMH C++ client software. |
| int llength() throws Exception | Returns the number of elements in the list - the list length. |
| void remove(int index) throws Exception | Removes a single element at a given index, shortening the list. The index must be in the range of 0 to llength( )-1. |
| void remove(int indexes[]) throws Exception | Removes multiple elements at specified indexes, shortening the list. |
| void replace(int index, String element) throws Exception | Replaces a single element at a given index. |
| String[] split() throws Exception
static String [] split(String s) throws Exception |
Returns a string array representing the elements of the list. There is also a static version for parsing a list without creating a TclList instance. |
| String toString() | Returns the result of join( ). This method causes operations like string concatenation to work with TclList objects. |
| METHOD | DESCRIPTION |
| TclListModel(String s)
TclListModel() |
The constructors. To work with String text that is formatted as a Tcl list, pass the text as the constructor argument. For creating a new empty Tcl list, use the empty constructor. A new empty list can also be constructed by passing a null or empty string constructor argument. |
| void addListDataListener(ListDataListener l) | Supports the ListModel interface. |
| Object getElementAt(int i) | Similar to lindex( ). Returns null if the list is invalid. Supports the ListModel interface. |
| Object getSelectedItem() | Supports the ComboBoxModel interface. |
| int getSize() | Similar to llength(). Returns -1 if the list is invalid. Supports the ListModel interface. |
| String join() | Used to obtain the text of the constructed Tcl list. |
| void lappend(String element) throws Exception | List append - adds a string element at the end of the list. |
| String lindex(int n) throws Exception | Obtain the string element at the specified index. Indexing starts with 0. An empty string is returned if the index is out of range. |
| void linsert(String e, int index) throws Exception | List insert - add an element at index N, sliding existing elements +1. |
| int llength() throws Exception | Returns the number of elements in the list - the list length. |
| Object remove(int index) | Removes a single element at a given index, shortening the list. The index must be in the range of 0 to llength( )-1. |
| void removeListDataListener(ListDataListener l | Supports the ListModel interface. |
| void replace(int index, String element) throws Exception | Replaces a single element at a given index. |
| void setSelectedItem() | Supports the ComboBoxModel interface. |
| String[] split() throws Exception | Returns a string array representing the elements of the list. |
| String toString() | Returns the result of join( ). This method causes operations like string concatenation to work with TclList objects. |
| Event Callback | Description |
| package com.hume.DMH;
public interface HisTimeoutItf {
|
This is the callback interface that a timer uses to notify you that your interval has expired. The Object argument references the timer so you can see which timer has expired. We let you throw exceptions so you do not have to code everything in try blocks. |
| Method | Description |
| public HisTimer(long millisecs, HisTimeoutItf target); | The constructor. |
| Method | Description |
| public static int parse(String VFEItext, java.util.Map NameValues) throws Exception | The parse method is static which means that no instances of the class
are needed to call the method.
The Map interface is implemented by the java.util.Hashtable class, and instances of this class are usually passed as the second argument. The return value is the count of new entries that were added to the Map table. Usually, the caller empties the table before calling the method, in which case the count indicates the number of (name, value) pairs parsed from the string. Backslashes can be used to escape imbedded quotes. Other backslash escape sequences are left in the value strings as they are found. Otherwise, the parsing is the same as the Hume developed Tcl vfei_2_array command. The method can throw Exceptions for input text that is not proper VFEI. No Exception is thrown for blank text. See the com.hume.DMH.Vfei2MapTest.java file for example code that uses this class. |
The Hume DmhClient Java software is licensed for development and runtime use at no additional charge for computers that are licensed for development use of the Hume Integration Datahub SDK. Hume Integration is also pleased to offer separate runtime licenses for using the DmhClient Java software on systems that are not licensed as development systems. Runtime usage of the DMH client software is licensed separately from the Datahub SDK runtime license.
The Java DmhServer software is a new application which is separately licensed from the Datahub SDK Development or Runtime DMH client licenses. Contact Hume Integration directly for current terms.
Date of last revision: $Date: 2006/02/03 19:03:36 $