3 Replies Latest reply on Dec 11, 2013 12:54 PM by patrickawilson

    JMS Error handling in Switchyard

    patrickawilson

      I am trying to get Switchyard to handle a JMS send failure.  The happy path is to have the message hit the GenericGSCIMServiceBean where it is processed and a message is sent to an external JMS queue.  This works. 

       

      In my test case I intentionally misconfigure the connectivity information to cause a failure. The error handling goal is to route the same message to an error handler upon failure of GenericGSCIMServiceBean.  My problem is that the underlying framework is swallowing the exception.  It is not propagated up through GenericGSCIMServiceBean (there would be no need for a separate error handler if it was) nor is the GenericGSCIMErrorHandlerBean being invoked.

       

          from("switchyard://GenericGSCIMService")
         .to("switchyard://GenericGSCIMServiceBean")
         .onException(java.lang.Exception.class)
         .to("switchyard://GenericGSCIMErrorHandlerBean");

       

      I would expect GenericGSCIMErrorHandlerBean to be invoked upon failure, but it is not.  What happens instead is that GenericGSCIMServiceBean simply completes as though the delivery was successful.

       

      The message trace is below:

       

      13:34:16,848 ERROR [org.switchyard.bus.camel.DefaultErrorListener] (http-localhost/127.0.0.1:8088-1)

      ------- Begin Message Trace -------

      Consumer -> {urn:com.example.switchyard:GSCIMSwitchyard:1.0}GenericGSCIMServiceBean/ConsertOpsCenterService

      Provider -> {urn:com.example.switchyard:GSCIMSwitchyard:1.0}ConsertOpsCenterService

      Operation -> send

      MEP -> IN_ONLY

      Phase -> IN

      State -> OK

      Exchange Context ->

          CamelCreatedTimestamp ............................: Sat Dec 07 13:34:15 CST 2013

          CamelExceptionCaught .............................: org.switchyard.HandlerException: SWITCHYARD037234: Failed to process JMS outbound interaction

          CamelFailureEndpoint .............................: direct://%7Burn:com.example.switchyard:GSCIMSwitchyard:1.0%7DGenericGSCIMServiceBean/ConsertOpsCenterService

          CamelFilterMatched ...............................: false

          CamelToEndpoint ..................................: direct://%7Burn:com.example.switchyard:GSCIMSwitchyard:1.0%7DGenericGSCIMServiceBean/ConsertOpsCenterService

          org.switchyard.bus.camel.consumer ................: ServiceReference [name={urn:com.example.switchyard:GSCIMSwitchyard:1.0}GenericGSCIMServiceBean/ConsertOpsCenterService, interface=SWITCHYARD010007: BaseServiceInterface [type=java, operations=[SWITCHYARD010008: send : IN_ONLY  : [java:byte[], null, null]]], domain=ServiceDomain [name=null]]

          org.switchyard.bus.camel.contract ................: org.switchyard.metadata.BaseExchangeContract@7fab60fc

          org.switchyard.bus.camel.dispatcher ..............: org.switchyard.bus.camel.ExchangeDispatcher@67c27eab

          org.switchyard.bus.camel.labels ..................: {org.switchyard.exchangeGatewayName=[org.switchyard.label.behavior.transient], org.switchyard.exchangeInitiatedNS=[org.switchyard.label.behavior.transient], org.switchyard.exchange.transaction.beforeInvoked=[org.switchyard.label.behavior.transient], org.switchyard.security.context.SecurityContext=[org.switchyard.label.behavior.transient]}

          org.switchyard.bus.camel.phase ...................: IN

          org.switchyard.bus.camel.provider ................: Service [name={urn:com.example.switchyard:GSCIMSwitchyard:1.0}ConsertOpsCenterService, interface=SWITCHYARD010007: BaseServiceInterface [type=java, operations=[SWITCHYARD010008: send : IN_ONLY  : [java:byte[], null, null]]], domain=ServiceDomain [name=null], metadata=org.switchyard.metadata.ServiceMetadataBuilder$ServiceMetadataImpl@1b9c0622]

          org.switchyard.exchange.transaction.beforeInvoked : true

          org.switchyard.exchangeGatewayName ...............: _ConsertOpsCenterService_jca_1

          org.switchyard.exchangeInitiatedNS ...............: 108928955949314

          org.switchyard.security.context.SecurityContext ..: DefaultSecurityContext@542552151[systemUUID=8b95a749-1954-4702-b154-80bc9c688561, expirationMillis=0, credentials=[ConfidentialityCredential@458436695[confidential=false]], securityDomainsToSubjects={}]

      Message Context ->

          breadcrumbId ..............................: ID-CONSERT-SA101-64859-1386444499824-4-6

          org.switchyard.bus.camel.labels ...........: {org.switchyard.contentType=[org.switchyard.label.behavior.transient], org.switchyard.bus.camel.messageSent=[org.switchyard.label.behavior.transient], org.switchyard.transform.TransformSequence=[org.switchyard.label.behavior.transient]}

          org.switchyard.bus.camel.messageSent ......: true

          org.switchyard.contentType ................: java:byte[]

          org.switchyard.messageId ..................: ID-CONSERT-SA101-64859-1386444499824-4-6

          org.switchyard.transform.TransformSequence : org.switchyard.transform.TransformSequence@728aadc4

      ------ End Message Trace -------

       

       

      The exceptions are exactly what one would expect given bad connectivity data - connection refused, etc.

       

      Caught exception of type org.switchyard.HandlerException with message: SWITCHYARD037234: Failed to process JMS outbound interaction

        Caused by exception of type com.ibm.msg.client.jms.DetailedJMSException, message: MQJCA1011: Failed to allocate a JMS connection.

          Caused by exception of type javax.resource.ResourceException, message: IJ000658: Unexpected throwable while trying to create a connection: null

            Caused by exception of type com.ibm.mq.connector.DetailedResourceException, message: MQJCA1011: Failed to allocate a JMS connection., error code: MQJCA1011

       

      etc. etc. etc.

       

      Caused by: com.ibm.msg.client.jms.DetailedJMSException: MQJCA1011: Failed to allocate a JMS connection.

      An internal error caused an attempt to allocate a connection to fail.

      See the linked exception for details of the failure.

          at com.ibm.mq.connector.services.JCAExceptionBuilder.buildException(JCAExceptionBuilder.java:146)

       

      Caused by: javax.resource.ResourceException: IJ000658: Unexpected throwable while trying to create a connection: null

       

      Caused by: com.ibm.mq.connector.DetailedResourceException: MQJCA1011: Failed to allocate a JMS connection., error code: MQJCA1011 An internal error caused an attempt to allocate a connection to fail. See the linked exception for details of the failure.

       

      Caused by: com.ibm.msg.client.jms.DetailedIllegalStateException: JMSWMQ0018: Failed to connect to queue manager 'LAB02OPSC01' with connection mode 'Client' and host name '10.18.20.136(12345)'.

       

      Caused by: com.ibm.mq.MQException: JMSCMQ0001: WebSphere MQ call failed with compcode '2' ('MQCC_FAILED') reason '2538' ('MQRC_HOST_NOT_AVAILABLE').

       

      Caused by: com.ibm.mq.jmqi.JmqiException: CC=2;RC=2538;AMQ9204: Connection to host '10.18.20.136(12345)' rejected. [1=com.ibm.mq.jmqi.JmqiException[CC=2;RC=2538;AMQ9213: A communications error for  occurred. [1=java.net.ConnectException[Connection refused: connect],3=vip-mq-lab02opsc01.fe.stg01.bwi01.consert.com]],3=10.18.20.136(12345),5=RemoteTCPConnection.connnectUsingLocalAddress]

       

      Caused by: com.ibm.mq.jmqi.JmqiException: CC=2;RC=2538;AMQ9213: A communications error for  occurred. [1=java.net.ConnectException[Connection refused: connect],3=vip-mq-lab02opsc01.fe.stg01.bwi01.consert.com]

       

      Caused by: java.net.ConnectException: Connection refused: connect

        • 1. Re: JMS Error handling in Switchyard
          kcbabo

          It is difficult to provide concrete guidance here without seeing the application as we don't know the structure of your application or what happens inside 'GenericGSCIMServiceBean'.  From the looks of things, you have an in-only MEP that's getting an exception.  By default, bean services will not return exceptions on one-way invocations since no exception is defined in the service contract. I'm assuming GenericGSCIMServiceBean is a bean service since I don't have knowledge of your application.  If you want visibility into exceptions on in-only invocations within a bean service, then consider injecting an instance of ReferenceInvoker for the 'ConsertOpsCenterService' reference in your bean instead:

           

          Bean - SwitchYard - Project Documentation Editor (search for ReferenceInvoker)

          • 2. Re: JMS Error handling in Switchyard
            patrickawilson

            Thanks for the response and apologies if the OP was not clear.  Been working with it some more today and hopefully I can clarify.

             

            The simple version is that I have to create and/or define an error handler.  Right now the error is being captured and swallowed by the DefaultErrorHandler, which I do not want. 

             

            Possibilities (in order of desirability)

            1. Define an ErrorHandler on GenericGSCIMServiceBean to replace DefaultErrorHandler.

            2. Define and invoke the error ahandler from the route.

            from("switchyard://GenericGSCIMService")
            .to("switchyard://GenericGSCIMServiceBean")
            .if-an-error-occurred-in-GenericGSCIMServiceBean
            .to("switchyard://GenericGSCIMErrorHandlerBean");

            3. ExchangeInterceptor that checks for FAULT status on GenericGSCIMServiceBean.

            I have been looking at some of your other responses where you seem to advocate setting up an ExchangeInterceptor. I have looked into this but I am not clear exactly how they work.

            4. Something else

            Thanks again.

            • 3. Re: JMS Error handling in Switchyard
              patrickawilson

              So I got the ExchangeInterceptor working and it is properly in the fault state when an error occurs.  I was looking for a way to link the ExchangeInterceptor to a service only to find that creating a ExchangeInterceptor automatically gets invoked after every service. 

               

              Now the next problem ...

               

              The logic flow should be ...

              1. Send JMS message to remote WebSphere queue.

              2. On failure, send the message to local HornetQ queue.

               

              @Service(value = GenericGSCIMService.class, name = "GenericGSCIMServiceBean")

              public class GenericGSCIMServiceBean implements GenericGSCIMService

              {

                  @Inject @Reference("ConsertOpsCenterService")

                  private IConsertMQService consertOpsCenterService;

               

                  @Inject @Reference("ConsertMQService")

                  private IConsertMQService consertMQService;

               

                  public void PublishEvent(ConsertPayload consertPayload)

                  {  

                      consertOpsCenterService.send(consertPayload);

               

                      if consertOpsCenterService fails I would like to:

                      {

                          consertMQService.send(consertPayload);

                      }

                  }

              }

               

               

              @Named("MQErrorHandler")

              public class GenericGSCIMFaultHandler implements ExchangeInterceptor

              {

                  @Override

                  public void after(String target, Exchange exchange) throws HandlerException

                  {

                      String serviceName = exchange.getProvider().getName().getLocalPart();

                      if (serviceName.equals("ConsertOpsCenterService"))

                      {

                          if (ExchangeState.FAULT.equals(exchange.getState()))

                          {

              Right here I know that a failure occurred in ConsertOpsCenterService.

              I need to do one of two things

               

              1. Propogate the error back up to GenericGSCIMServiceBean.PublishEvent

              There does not seem to be a way to do this.  This method is void and throwing an exception at this point is

              not allowed by the Switchyard framework (cannot throw an exception when the exchange is in a fault state).

               

              2. Invoke consertMQService from here.

              I have lost the handle on the consertPayload object and therefore have nothing to send.

                          }

                      }

                  }

              }