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 formatted
as lists, 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 because the messages
are typically SQL, Tcl, or VFEI text that is
directly
interpreted by the receiver. The
package also includes DMH server capabilities.
The demonstration applets included with this DMH package, are coded with the package name "DMHApplet". For the simplest web 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 and the DMH.jar file 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.
Your application will typically use one instance of the DmhClient
class or the DmhServer 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
DmhClient class is used if you wish to communicate directly with
multiple
DMH servers.
If you are not familiar with the DMH message system, you may want to read the mbx Tcl command document which is usually installed at \usr\local\htm85\mann\mbx.html. If your application acts a DMH message system client, during initialization it attaches to a running DMH server. Once you are connected to the DMH system, you can exchange messages with other attached processes.
To initialize a DmhClient instance, 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.5"/"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 DMH client 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.
You will not usually 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. |
DmhClient() | The class constructor takes no
arguments. |
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
ListFormatException void lappend(String e1, String e2) throws ListFormatException void lappend(String e1, String e2, String e3) throws ListFormatException void lappend(String e1, String e2, String e3, String e4) throws ListFormatException |
List append - adds one to four string elements at the end of the list. |
String lindex(int n) throws ListFormatException | 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 ListFormatException | 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 ListFormatException | List insert - add an element at index N, sliding existing elements +1. |
static String listElement(String s, int index1)
throws ListFormatException
static String listElement(String s, int index1, int index2) throws ListFormatException static String listElement(String s, int index1, int index2, int index3) throws ListFormatException |
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 length() throws ListFormatException int llength() throws ListFormatException |
Returns the number of elements in the list - the list length. |
void remove(int index) throws ListFormatException | 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 ListFormatException | Removes multiple elements at specified indexes, shortening the list. |
void replace(int index, String element) throws ListFormatException | Replaces a single element at a given index. |
String[] split() throws Exception static String [] split(String s) throws ListFormatException |
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 DMH Java software is licensed for development and
runtime
use at no additional charge for computers that are licensed for
development
or runtime use
of the Hume Integration Datahub SDK. Hume Integration is also
pleased
to offer separate runtime licenses for using the Java
software
on systems that are not licensed for the Datahub SDK. Runtime
usage
of the DMH client software can be licensed separately from the Datahub
SDK
runtime license at a lower cost.
Contact Hume Integration directly for current terms.
Date of last revision: $Date: 2017/08/10 18:22:09 $