Search code examples
javaapache-axis

Axis2: retry web service call after AxisFault


Environment: Ubuntu x86_64, JDK 1.7.0_80, Tomcat 7.0.64

I'm working on some Axis2 web services and I want to be able to catch an AxisFault exception after a web service call (e.g., when a HTTP 404, 500 or SSL error is thrown), perform some tasks (in order to try to automatically solve any configuration issues) and retry just once the same web service request. I'm aware of Axis2 phases and handlers and how we can trigger a handler in the InFault- or OutFaultFlow Phases, but I think that it does not fit the purposes because I want to perform the retry just one time, e.g., if I receive another AxisFault upon the retry, I want it to escalate and let the whole request fail (it'd mean that the effort of the system to self-configure after the first failure wasn't enough to solve the problem and therefore sysadmin intervention is needed). I think that using handlers would bring the request into an infinite loop in case the self-configuration doesn't manage to fix the issue. Nevertheless, I tried it but couldn't find a way to perform the retry. As such, I'm trying to do the usual try/catch inside the code that triggers the request.

Inside my Stub class I create the ServiceClient with its Options, necessary headers and then I do the request. Here's the relevant part of the code:

public class RespondingGateway_ServiceStub extends org.apache.axis2.client.Stub {


       private void populateAxisService() {

        // creating the Service with a unique name
        _service = new org.apache.axis2.description.AxisService("RespondingGateway_Service" + getUniqueSuffix());
        addAnonymousOperations();

        // creating the operations
        org.apache.axis2.description.AxisOperation __operation;
        _operations = new org.apache.axis2.description.AxisOperation[1];
        __operation = new org.apache.axis2.description.OutInAxisOperation();
        __operation.setName(new javax.xml.namespace.QName(XCPDConstants.SOAP_HEADERS.NAMESPACE_URI, XCPDConstants.SOAP_HEADERS.NAMESPACE_REQUEST_LOCAL_PART));
        _service.addOperation(__operation);
        _operations[0] = __operation;

        }

        // populates the faults
        private void populateFaults() {
        }

        /**
         * Constructor that takes in a configContext
         */
        public RespondingGateway_ServiceStub(org.apache.axis2.context.ConfigurationContext configurationContext, String targetEndpoint) {
            this(configurationContext, targetEndpoint, false);
        }

        /**
         * Constructor that takes in a configContext and useseperate listner
         */
        public RespondingGateway_ServiceStub(org.apache.axis2.context.ConfigurationContext configurationContext, String targetEndpoint, boolean useSeparateListener) {
            // To populate AxisService
            populateAxisService();
            populateFaults();
            try {
                _serviceClient = new org.apache.axis2.client.ServiceClient(configurationContext, _service);
            } catch (AxisFault ex) {
                throw new RuntimeException(ex);
            }

            _serviceClient.getOptions().setTo(new org.apache.axis2.addressing.EndpointReference(targetEndpoint));
            _serviceClient.getOptions().setUseSeparateListener(useSeparateListener);

            _serviceClient.getOptions().setTimeOutInMilliSeconds(180000); //Wait time after which a client times out in a blocking scenario: 3 minutes

            // Set the soap version
            _serviceClient.getOptions().setSoapVersionURI(org.apache.axiom.soap.SOAP12Constants.SOAP_ENVELOPE_NAMESPACE_URI);

            MultiThreadedHttpConnectionManager multiThreadedHttpConnectionManager = new MultiThreadedHttpConnectionManager();

            HttpConnectionManagerParams params = new HttpConnectionManagerParams();
            params.setDefaultMaxConnectionsPerHost(20);
            multiThreadedHttpConnectionManager.setParams(params);
            HttpClient httpClient = new HttpClient(multiThreadedHttpConnectionManager);

            this._getServiceClient().getServiceContext().getConfigurationContext().setProperty(HTTPConstants.REUSE_HTTP_CLIENT, false);
            this._getServiceClient().getServiceContext().getConfigurationContext().setProperty(HTTPConstants.CACHED_HTTP_CLIENT, httpClient);
            this._getServiceClient().getServiceContext().getConfigurationContext().setProperty(HTTPConstants.AUTO_RELEASE_CONNECTION, false);
        }

    public org.hl7.v3.PRPAIN201306UV02 respondingGateway_PRPA_IN201305UV02(PRPAIN201305UV02 pRPA_IN201305UV02, Assertion idAssertion) {

        org.apache.axis2.client.OperationClient operationClient = _serviceClient.createClient(_operations[0].getName());

        /* set the message context with that soap envelope */
        org.apache.axis2.context.MessageContext messageContext = new org.apache.axis2.context.MessageContext();
        messageContext.setEnvelope(env);

        /* add the message contxt to the operation client */
        operationClient.addMessageContext(messageContext);

        try {
            operationClient.execute(true);
        } catch (AxisFault e) {
        // perform internal tasks
        // retry the request
        // if we get another AxisFault we just let it escalate
        }
    }

With this code I'm able to catch the AxisFault and perform the internal tasks. But then I don't know how to perform the retry. I tried to play with the Axis2 API, including calling again the operationClient.execute(true) inside the catch block but without success:

try {
        operationClient.execute(true);
    } catch (AxisFault e) {
       operationClient.complete(messageContext);
       LOG.debug("Adding message context again");
       messageContext.resetExecutedPhases();
       operationClient.addMessageContext(messageContext);
       LOG.debug("Executing");
       operationClient.execute(true);
       LOG.debug("Successfully retried the operation!");

I get the following error:

ERROR 2017-04-19 12:59:03,832 eu.epsos.pt.cc.ClientConnectorServiceMessageReceiverInOut invokeBusinessLogic.385  - A message was added that is not valid. However, the operation context was complete.
java.lang.RuntimeException: A message was added that is not valid. However, the operation context was complete.
    at tr.com.srdc.epsos.ws.xcpd.client.RespondingGateway_ServiceStub.respondingGateway_PRPA_IN201305UV02(RespondingGateway_ServiceStub.java:445)
    at tr.com.srdc.epsos.ws.xcpd.client.RespondingGateway_RequestSender.sendRequest(RespondingGateway_RequestSender.java:80)
    at tr.com.srdc.epsos.ws.xcpd.client.RespondingGateway_RequestSender.respondingGateway_PRPA_IN201305UV02(RespondingGateway_RequestSender.java:69)
    at eu.epsos.pt.ws.client.xcpd.XcpdInitGateway.patientDiscovery(XcpdInitGateway.java:59)
    at eu.epsos.pt.cc.stub.IdentificationService.findIdentityByTraits(IdentificationService.java:72)
    at eu.epsos.pt.cc.ClientConnectorServiceSkeleton.queryPatient(ClientConnectorServiceSkeleton.java:107)
    at eu.epsos.pt.cc.ClientConnectorServiceMessageReceiverInOut.invokeBusinessLogic(ClientConnectorServiceMessageReceiverInOut.java:215)
    at org.apache.axis2.receivers.AbstractInOutMessageReceiver.invokeBusinessLogic(AbstractInOutMessageReceiver.java:40)
    at org.apache.axis2.receivers.AbstractMessageReceiver.receive(AbstractMessageReceiver.java:114)
    at org.apache.axis2.engine.AxisEngine.receive(AxisEngine.java:181)
    at org.apache.axis2.transport.http.HTTPTransportUtils.processHTTPPostRequest(HTTPTransportUtils.java:172)
    at org.apache.axis2.transport.http.AxisServlet.doPost(AxisServlet.java:146)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:650)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:731)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:505)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:956)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:423)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1079)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:625)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:316)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)
Caused by: org.apache.axis2.AxisFault: A message was added that is not valid. However, the operation context was complete.
    at org.apache.axis2.description.OutInAxisOperation.addMessageContext(OutInAxisOperation.java:78)
    at org.apache.axis2.context.OperationContext.addMessageContext(OperationContext.java:218)
    at org.apache.axis2.description.AxisOperation.registerOperationContext(AxisOperation.java:286)
    at org.apache.axis2.description.OutInAxisOperationClient.addMessageContext(OutInAxisOperation.java:132)
    at tr.com.srdc.epsos.ws.xcpd.client.RespondingGateway_ServiceStub.respondingGateway_PRPA_IN201305UV02(RespondingGateway_ServiceStub.java:302)
    ... 33 more

I tried to search for solutions but without success. I think it should be a matter of resetting again the Axis2 internal web service state and phases, but I don't know how to solve this.


Solution

  • I found the solution. Basically, I've to create a new OperationClient, SOAPEnvelope and MessageContext and execute again the request:

    try {
        operationClient.execute(true);
    } catch (AxisFault e) {
        LOG.error("Axis Fault error: " + e.getMessage());
    
        /* we need a new OperationClient, otherwise we'll face the error "A message was added that is not valid. However, the operation context was complete." */
        OperationClient newOperationClient = _serviceClient.createClient(_operations[0].getName());
        newOperationClient.getOptions().setAction(XCPDConstants.SOAP_HEADERS.REQUEST_ACTION);
        newOperationClient.getOptions().setExceptionToBeThrownOnSOAPFault(true);
        addPropertyToOperationClient(newOperationClient, org.apache.axis2.description.WSDL2Constants.ATTR_WHTTP_QUERY_PARAMETER_SEPARATOR, "&");
    
        SOAPFactory newSoapFactory = getFactory(newOperationClient.getOptions().getSoapVersionURI());
    
        org.apache.axiom.soap.SOAPEnvelope newEnv;
        newEnv = toEnvelope(newSoapFactory,
                    pRPA_IN201305UV02,
                    optimizeContent(new javax.xml.namespace.QName(XCPDConstants.SOAP_HEADERS.NAMESPACE_URI, XCPDConstants.SOAP_HEADERS.NAMESPACE_REQUEST_LOCAL_PART)));
    
        _serviceClient.addHeadersToEnvelope(newEnv);
    
        /* we create a new Message Context with the new SOAP envelope */
        org.apache.axis2.context.MessageContext newMessageContext = new org.apache.axis2.context.MessageContext();
        newMessageContext.setEnvelope(newEnv);
    
        /* add the new message contxt to the new operation client */
        newOperationClient.addMessageContext(newMessageContext);
        /* we retry the request */
        newOperationClient.execute(true);
    }