ASG
IBM
Zystems
Cressida
Icon
Netflexity
 
  MQSeries.net
Search  Search       Tech Exchange      Education      Certifications      Library      Info Center      SupportPacs      LinkedIn  Search  Search                                                                   FAQ  FAQ   Usergroups  Usergroups
 
Register  ::  Log in Log in to check your private messages
 
RSS Feed - WebSphere MQ Support RSS Feed - Message Broker Support

MQSeries.net Forum Index » IBM MQ Java / JMS » JMS Client connection to MQ Server and reason code 2009

Post new topic  Reply to topic
 JMS Client connection to MQ Server and reason code 2009 « View previous topic :: View next topic » 
Author Message
rsinha
PostPosted: Sat Sep 13, 2003 2:12 pm    Post subject: JMS Client connection to MQ Server and reason code 2009 Reply with quote

Apprentice

Joined: 29 Aug 2003
Posts: 42

Hi,
My JMS application makes a client connection to MQ server thats on another machine. My application, on startup, creates connection and other necessary objects in following order -

- mConnection = factory.createQueueConnection();
- session = mConnection.createQueueSession(false, Session.CLIENT_ACKNOWLEDGE);
- mQueueSender = mSession.createSender(sendQ);

After the, the app uses following to send the message to the Q, whenever it detects a new file in a directory.

- mQueueSender.send(mOutMessage);

As part my test, after starting the JMS application and sending one msg to the Q, I forcibly ended the Q manager that was running on a different machine. Then I created a new message to be sent by JMS application. I got following exception -

Caught JMSException: javax.jms.JMSException: MQJMS2007: failed to send message to MQ queue
Caught Exception: com.ibm.mq.MQException: MQJE001: Completion Code 2, Reason 2009

THE MQ Mesaage doc gives the explanaton for 2009 as follwing -

"Connection to the queue manager has been lost. This can occur because the queue manager has ended. If the call is an MQGET call with the MQGMO_WAIT option, the wait has been canceled. All connection and object handles are now invalid."

This is fine since the q manager won't be ended like this and if it crashes on its own, then after restarting the q manager, my application can be rstarted, which establish a new connection.

But what about the network errors, TCP errors etc during send, does the client app get the same 2009 error or different one. If a different one, then, when the message is resent, if the error is resolved, is the client ble to send the msg, without reestablish a new connection, i.e restarting the jms application? I wouldn't want to restart my application everytime a network error occurs, to establish a new connection and recreate all the objects.

Has anybody experienced what's the behaviour during send/receive if this occurs? How should program my application so it can handle the network, tcp/ip errors, without having to restart the app?

Thanx
Back to top
View user's profile Send private message
leonwj
PostPosted: Wed Sep 17, 2003 6:10 am    Post subject: Re: JMS Client connection to MQ Server and reason code 2009 Reply with quote

Newbie

Joined: 16 Sep 2003
Posts: 4
Location: Boston, MA

Hi,

As you are aware a 2009 Exception (Broken Connection) is thrown once the connection to the remote QM is severed. In some cases (on differing platforms) and under differing scenarios it is also possible to get a 2195 Exception (Unexpected Error) returned.

The JMS spec states the following:

<spec extract>
If a JMS provider detects a problem with a connection, it will inform the connection’s ExceptionListener if one has been registered. It does this by calling the listener’s onException() method, passing it a JMSException describing the problem.

This allows a client to be asynchronously notified of a problem. Some connections only consume messages, so they would have no other way to learn their connection has failed. A Connection serializes execution of its ExceptionListener. A JMS provider should attempt to resolve connection problems itself prior to notifying the client of them.

The exceptions delivered to ExceptionListener are those that have no other place to be reported. If an exception is thrown on a JMS call it, by definition, must not be delivered to an ExceptionListener (in other words, ExceptionListener is not for the purpose of monitoring all exceptions thrown by a connection).
</spec extract>

In all cases of connectivity failures it is a prime responsibility of a ‘well-behaved’ JMS application to diligently try to recover and re-establish its connections to the services’ provider (i.e. WebSphereMQ).

In the case of a message listener where the onMessage is simply consuming messages, the ExceptionListener handler is the only way for the client to be made aware of a Broken/severed connection. Therefore by issuing the following you provide a way for your application to be informed on catatrosphic events:

// Set the ExceptionListener for this QueueConnection
qc.setExceptionListener(this);

....
....

/**
* If a JMS provider detects a serious problem with a
* Connection this method is invoked passing it a JMSException
* describing the problem.
* @param JMSException jmse
*/

public void onException (JMSException jmse) {

// Report the Error and take necessary Error handling measures
// clean-up connection resources and re-establish the connection

// Alternatively, for full resilience access the qcfSecondary connection
// factory created at startup in order to provide full 24/7 availability.
// Also need to ensure that clean-up of qcfPrimary takes place in order
// to re-access should qcfSecondary become unavailable down the line.

//
String error = jmse.getErrorCode();
System.out.println(error);
}

In the case of a queueSender.send operation (as you indicate in your post) your client code will actively be in control and therefore it will be able to perform the cleanup operations without the need for the ExceptionListener/onException interface.

Again you should note that in the case where you are connecting to a single QM you will need to wait for the QM to be brought back online/up for re-connection to be successful. This can be done by coding in a retry delay period (based on SLAs with the operations team), a retry attempt count or a combination of both.

Should you wish to facilitate full 24/7 resilience then it is possible for multiple providers (WebSphereMQ clustered QMs) to exist for the same service therefore allowing you to attempt to utilise an alternate provider should the primary become unavailable. As indicated in the code comments above, this would involve placing multiples QCFs in your LDAP directory(s) and 'rolling' over to the alternate QCF should the current connection become unavailable.

Should you wish to take resilience even further up the chain then Java 1.4 also allows you to specify multiple Provider URLs for your LDAP directory allowing JNDI to try alternate directories should the previous listed servers be unavailable.

i.e. var.put(Context.PROVIDER_URL, "ldap://uk-xxxxxx.dlogic.com ldap://uk-yyyyyy.dlogic.co.uk ldap://uk-zzzzzzz.3rdparty.com");

In versions prior to JDK 1.4 your client code would be responsible for rolling over to an alternate JNDI directory.

I do have some code snippets (of the above) lying around from solutions that we have architected for a few global institutions so please post back should you need additional details.

I hope the above helps you (and others who may come across the same problems )

Regards

Wayne
Back to top
View user's profile Send private message
rsinha
PostPosted: Wed Sep 17, 2003 8:58 am    Post subject: Reply with quote

Apprentice

Joined: 29 Aug 2003
Posts: 42

Thanx Wayne for such detailed explanation. One more question, so if the connection is lost due to network errors, not only does the app should recreate the connection to the Q Manager, but also all the objects that are created from it, like Session, QueueSender, QueueReceiver etc. Right? Currently, my program uses 1 connection to QCF, with two separate session for 2 send queues and two more sessions for 2 receive queues. The 2 receive queues are monitored by 2 threads. So, if I detect the connection problem during send on one particular queue, its a safe assumption to say that other queues , send or receive will also error out, thus I should destroy all 4 (2 send and 2 receive objects (with sessions, QueueSenders, QueueReceivers, the Receive threads etc.), then recreate the connection, followed by recreating all of these object. Right? If you have a code snippets of how you ar handling such problem, that'd help me a lot.
So, if there are prolonged network error, which the JMS provider is not able to recover from (even though the Q Manager is running), I should still get either 2009 or 2195 error?
Back to top
View user's profile Send private message
leonwj
PostPosted: Wed Sep 17, 2003 11:16 am    Post subject: Reply with quote

Newbie

Joined: 16 Sep 2003
Posts: 4
Location: Boston, MA

Exactly!!

As detailed, ALL the connection objects will need to be discarded and then re-created when the connection is back up again. There are a number of design patterns that could be utilised on this based upon the level of resilience/performance you are trying to achieve.

One pattern would be to create numerous QCFs upfront and to round-robin between them pushing the workload across multiple clustered QMs. In the event of failure to one (or more) QMs you would still have resilience but with reduced performance.

Another option would be to only create/utilise the secondary/alternate QCFs if and when you get a connection exception.

In the scenario that you mentioned you could also utilise the ASF (Application Server Facility) of JMS (from spec 1.02b onwards). This would allow you to connection pool your receivers (not senders however) and cleanup the threads in tandem.

I will try to dig up the production code snippets for the above patterns however below is some pseudo test code from my initial works in this area (not pretty but it does give the general idea of what needs to be accomplished):

Code:
public class testListener implements MessageListener {

   public static void main(String[] args) {

                try {
                  initConnections();
                  testListener app = new testListener();
                  app.startListener();

                  System.out.println("Terminating...");
                }
                catch (Exception _ex) {

                }
}

Code:
static void initConnections() {
      try {
         Hashtable p = new Hashtable();
         System.out.println("getting JNDI context");
         p.put(Context.PROVIDER_URL, "ldap://uk-xxxxxx/ou=development,cn=services,dc=dlogic,dc=com");
         p.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");

         Context ctx = new InitialContext(p);

         // Obtain the connection factory from JNDI
         System.out.println("Retrieving JMS objects via JNDI");
         factory = (QueueConnectionFactory) ctx.lookup(qcfLookup);
         requestQueue = (Queue) ctx.lookup(qInLookup);
         replyQueue = (Queue) ctx.lookup(qOutLookup);
      } catch (NamingException e) {
         System.err.println("Unable to obtain objects from JNDI");
         e.printStackTrace(System.err);
                        System.exit(0);
      }
   }


Code:
void startListener() {
      try {
         qCon = factory.createQueueConnection();
         qCon.start();    // get a connection and start it
         QueueSession qSession = qCon.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);

         qReceiver = qSession.createReceiver(requestQueue);
         qReceiver.setMessageListener(this);

                        // since message retrieval is asynchronous, ExceptionListener has to be used
                        qCon.setExceptionListener(new ExceptionHandler());

                      //*
                      //* let's do what we need to do to keep this Listener alive
                      //*
         }

      } catch (JMSException e) {
         System.out.println("Unable to start listener");
                        Exception linkedException = e.getLinkedException();
                        printException((com.ibm.mq.MQException) linkedException);
         e.printStackTrace(System.err);
      }
   }

Code:

void cleanUpListener() {
   try {
      System.out.println("Cleaning up JMS...");
                        if(qReceiver != null)
                            qReceiver.close(); // clean up qReceiver

                        if(qSession != null)
                            qSession.close(); // clean up qSession

                        if(qCon != null) {
               qCon.stop();
               qCon.close(); // clean up qConnection
                        }
   } catch (JMSException exception) {
      System.out.println("Exception whilst cleaning up listener...");
                        Exception linkedException = exception.getLinkedException();
                        printException((com.ibm.mq.MQException) linkedException);
           exception.printStackTrace(System.err);
   }
}


Code:
static void startJMSConnections() {
              app.startListener();
}

Code:
// The class that implements the ExceptionListener callback mechanism
class ExceptionHandler implements ExceptionListener {

     public void onException(JMSException exception) {
    // let's see what we have here now!!!
    // JMS spec dictates that what we have will be catatrosphic
    // in any case so we'll need to deal with it....
         Exception linkedException = exception.getLinkedException();
         printException((com.ibm.mq.MQException) linkedException);   // let's see what MQSeries thinks it is
         exception.printStackTrace();

         cleanUpListener();   // let's go clean up the mess

    //*
    //* need some kind of delay/retry mechanism since only single QCF in use over here
    //*

         initConnections();   // re-initialise ALL the JMS objects

         startJMSConnections();   // come on let's start it UP!!!!
     }

     public void printException(com.ibm.mq.MQException mqException) {
      System.out.println("WebSphereMQ Exception - Reason Code: " + mqException.reasonCode);
     }
}


In the case of a prolonged network failure/outage, once you have cleaned up the JMS objects, you will not be able to re-establish a connection to the QM due to the network being down. In this case you will get the standard error indicating that the QM is not available (i.e. 2059 Q_MGR_NOT_AVAILABLE).

Let me know if you need additional insight into the above.

Regards

Wayne
Back to top
View user's profile Send private message
rsinha
PostPosted: Wed Sep 17, 2003 11:37 am    Post subject: Reply with quote

Apprentice

Joined: 29 Aug 2003
Posts: 42

Thanx Wayne, That code snippets was really useful. We are not using the cluster Q manager environment yet, hence we just have to deal with one for the time being. The only ugly part is that since I'm using the same connection for all send Qs and recv Qs, if receive detects an error, I'll not only have to recreate the receive Listener for that Q, but also the other listeners for other Qs and all the Senders.

Anyway, I guess I'll just have to that, ugly or not.

Thanx again.
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic  Reply to topic Page 1 of 1

MQSeries.net Forum Index » IBM MQ Java / JMS » JMS Client connection to MQ Server and reason code 2009
Jump to:  



You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
Protected by Anti-Spam ACP
 
 


Theme by Dustin Baccetti
Powered by phpBB © 2001, 2002 phpBB Group

Copyright © MQSeries.net. All rights reserved.