When the network
connection between the client and server is lost, a JMS provider must
make every reasonable attempt to re-establish the connection. In the
event that the JMS provider cannot automatically reconnect, the
provider must notify the client of this condition by throwing an
exception when the client invokes a method that would cause network
traffic to occur. However, it is reasonable to expect that a JMS
client may only be a receiver using the
MessageListener
interface, and not a producer that
makes any outbound publish( )
or send(
)
calls. In this case, the client is not invoking JMS
methods—it is just listening for messages—so a dropped
connection won't be detected.
JMS provides an
ExceptionListener
interface for
trapping a lost connection and
notifying the client of this condition. The
ExceptionListener
is bound to the
connection—unlike MessageListeners
, which
are bound to sessions. The definition of the
ExceptionListener
is:
public interface ExceptionListener{ void onException(JMSException exception); }
It is the responsibility of the JMS provider to call the
onException( )
method of all registered
ExceptionListener
s after making reasonable
attempts to reestablish the connection automatically. The JMS client
can implement the ExceptionListener
so that it can
be alerted to a lost connection, and possibly attempt to reestablish
the connection manually.
How can the JMS provider call an ExceptionListener
if the client has been disconnected? Every JMS provider has some
amount of functionality that resides in the client application. We
have been referring to this as the client
runtime
portion
of the JMS provider. It is the responsibility of the client runtime
to detect the loss of connection and call the
ExceptionListener
.
To make our
Wholesaler
into an
ExceptionListener
, we start by changing the
formal declaration of the class to implement the
javax.jms.ExceptionListener
interface:
public class Wholesaler implements
javax.jms.MessageListener,
javax.jms.ExceptionListener
{
...
Next, we remove the connection setup information from the constructor
and isolate it in its own method, establishConnection(
)
:
public Wholesaler(String broker, String username, String password){
mBroker = broker;
mUsername = username;
mPassword = password;
establishConnection( broker, username, password );
}
establishConnection( )
sets up the connection, the
publisher, and the subscriber. The new addition is the retry logic on
the TopicConnectionFactory
's
createTopicConnection(
)
method, which continually retries
the connection every ten seconds until it is established:
private void establishConnection(String broker, String username,
String password)
{
...
while (connect == null)
{
try {
connect =
factory.createTopicConnection (username, password);
} catch (javax.jms.JMSException jmse)
{
try {
Thread.sleep(10000);
} catch (java.lang.InterruptedException ie) { }
continue;
}
}
System.out.println("
Connection established");
...
}
The
establishConnection( )
method then registers the ExceptionListener
with
the connection via the setExceptionListener( )
method:
connect.setExceptionListener( (javax.jms.ExceptionListener) this);
Last, but not least, is the implementation of the
onException( )
listener method. Its task is to
call the establishConnection( )
method again to
re-establish a connection with the JMS provider:
public void onException ( javax.jms.JMSException jmse)
{
// Tell the user that there is a problem
System.err.println ("
There is a problem with the connection.");
System.err.println (" JMSException: " + jmse.getMessage( ));
System.err.println ("Please wait while the application tries to "+
"reestablish the connection...");
connect = null;
establishConnection(mBroker, mUsername, mPassword);
}
When a connection is dropped and reestablished, all of the sessions,
queues, publishers, and subscribers need to be reestablished in order
for the application to continue normal processing. This is why we
isolated all the connection logic in the
establishConnection( )
method, so that it can be
used during startup and reused if the connection is lost.
JMS does not define any reason codes for a dropped connection. However, a JMS provider may provide a finer level of granularity by defining reason codes. Depending on the host operating system's network settings, it may take a while for the provider to notice that a connection has been dropped. Some providers implement a ping capability as a configurable setting to detect a network loss.