1
votes

I am trying to add some logic in the WSO2 ESB that will attempt to call a service as normal and return the response, but if for whatever reason there is a problem with the service I would like to check another service to see if it is down due to maintenance and then show the error message accordingly.

I tried using a fault sequence added to the proxy target. And with in this sequence I make a send call to a servlet, but it errors out saying that the response is not JSON which I know it is because it is hard coded.

Is this the correct way of doing this? What is the correct way of achieving this functionality?

Here is the servlet that will return an error code or message, currently it is hard coded:

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    System.out.println("hit the servlet.......");
    response.setContentType("application/json");
    response.getWriter().append("{\"result\": \"1234\"}");
}

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    doGet(request, response);
}

Here is the Proxy:

<?xml version="1.0" encoding="UTF-8"?>
<proxy xmlns="http://ws.apache.org/ns/synapse" name="mjstest" transports="https http" startOnLoad="true" trace="disable">
<target faultSequence="json_fault_handler">
    <inSequence>
        <log level="full"/>
        <property name="messageType" value="application/json" scope="axis2" type="STRING"/>
        <log level="full" category="TRACE"/>
        <send>
            <endpoint>
                <address uri="http://10.180.63.195:9088/rest/badServiceThatWillFailForThisTest" format="rest">
                    <timeout>
                        <duration>30000</duration>
                        <responseAction>fault</responseAction>
                    </timeout>
                    <suspendOnFailure>
                        <errorCodes>-1</errorCodes>
                        <initialDuration>0</initialDuration>
                        <progressionFactor>1.0</progressionFactor>
                        <maximumDuration>0</maximumDuration>
                    </suspendOnFailure>
                    <markForSuspension>
                        <errorCodes>-1</errorCodes>
                    </markForSuspension>
                </address>
            </endpoint>
        </send>
    </inSequence>
    <outSequence>
        <log level="full" separator=","/>
        <send/>
    </outSequence>
</target>

Here is the sequence:

<sequence xmlns="http://ws.apache.org/ns/synapse" name="json_fault_handler">
 <log category="INFO" level="custom" separator=",">
     <property name="failS" value="=======False Sequence==========="/>
 </log>
 <send>
     <endpoint>
         <address uri="http://localhost:8080/UGC_Images/BACMaintananceWindow" ></address>
     </endpoint>
 </send>
 <property name="messageType" value="application/json" scope="axis2"></property>

Any help is greatly appreciated.

2

2 Answers

2
votes

So this is the solution I have working. It may not be the best of solutions but it does do what is needed. The solution flow is at follows:

  1. Attempt the original request
  2. If it fails, go to fault_handler, where
  3. goes to Java Class to read xml file to see if it is in maintenance block
  4. return values and show responce

Here is the fault_handler:

<sequence xmlns="http://ws.apache.org/ns/synapse" name="json_fault_handler">
<log category="INFO" level="full" separator="," />
<property xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope" xmlns:ns="http://org.apache.synapse/xsd" xmlns:ns3="http://org.apache.synapse/xsd" action="set" expression="get-property('ERROR_CODE')" name="ErrorCode" scope="default" type="INTEGER" />
<log category="INFO" level="custom" separator=",">
    <property name="failS" value="=======False Sequence===========" />
    <property xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope" xmlns:ns="http://org.apache.synapse/xsd" xmlns:ns3="http://org.apache.synapse/xsd" expression="$ctx:ErrorCode" name="testCode" />
    <property xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope" xmlns:ns="http://org.apache.synapse/xsd" xmlns:ns3="http://org.apache.synapse/xsd" expression="get-property('Action')" name="Action" />
</log>
<class name="com.comp.synapse.mediator.ServiceMediatorClass"></class>
<property action="set" name="HTTP_SC" scope="axis2" type="STRING" expression="$axis2:HTTP_SC" />
<payloadFactory media-type="json">
    <format>{
        "code": "$1",
        "error": "$2",
        "error_description": "$3"
        }
    </format>
    <args>
        <arg expression="$axis2:SystemErrorCode" />
        <arg expression="$axis2:SystemErrorShort" />
        <arg expression="$axis2:SystemErrorDescription" />
    </args>
</payloadFactory>
<property action="set" name="messageType" scope="axis2" type="STRING" value="application/json" />
<property action="remove" name="NO_ENTITY_BODY" scope="axis2" />
<property action="set" name="RESPONSE" scope="default" type="STRING" value="true" />
<header action="remove" name="To" />
<send />

Here is the mediator class method:

public boolean mediate(MessageContext mc) {     
    org.apache.axis2.context.MessageContext msgContext = ((Axis2MessageContext) mc).getAxis2MessageContext();
    String httpStatusCode = ((Integer) msgContext.getProperty("HTTP_SC")).toString();
    String directory = "C:\\Temp\\maintenance.xml";
    File file = new File(directory);
    boolean inMaintenanceWindow = false;
    String errorDescription = "";
    Date now = new Date();
    try {           
        DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
        Document doc = dBuilder.parse(file);

        NodeList nList = doc.getElementsByTagName("downTimePeriod");

        for (int temp = 0; temp < nList.getLength(); temp++) {
            Node nNode = nList.item(temp);

            Date fromDate = null;
            Date toDate = null;
            SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy HH:mm");
            if (nNode.getNodeType() == Node.ELEMENT_NODE) {
                Element eElement = (Element) nNode;
                System.out.println("downTimePeriod id : " + eElement.getAttribute("id"));
                String fromDateString = eElement.getElementsByTagName("FromDate").item(0).getTextContent();
                String fromTimeString = eElement.getElementsByTagName("FromTime").item(0).getTextContent();
                String toDateString = eElement.getElementsByTagName("ToDate").item(0).getTextContent();
                String toTimeString = eElement.getElementsByTagName("ToTime").item(0).getTextContent();
                fromDate = sdf.parse(fromDateString + " " + fromTimeString);
                toDate = sdf.parse(toDateString + " " + toTimeString);

                if(now.after(fromDate) && now.before(toDate)){
                    inMaintenanceWindow = true;
                    errorDescription = "Service is under maintenance until - " + toDateString + " " + toTimeString;
                    break;
                }
            }
        }
    } catch (Exception e) {         
        e.printStackTrace();
    }
    if(inMaintenanceWindow){
        msgContext.setProperty("HTTP_SC", "503");
        msgContext.setProperty("SystemErrorCode", "503");
        msgContext.setProperty("SystemErrorShort", "Service is under maintenance.");
        msgContext.setProperty("SystemErrorDescription", errorDescription);
    }else{
        msgContext.setProperty("HTTP_SC", httpStatusCode);
        msgContext.setProperty("SystemErrorCode", httpStatusCode);
        msgContext.setProperty("SystemErrorShort", "Error occurred.");
        msgContext.setProperty("SystemErrorDescription", errorDescription);         
    }

    return true;
}

Hope this helps someone.

0
votes

Here you go. As per your explanation what you are trying to achieve is service chaining. what i understand is you are trying to call service 1 and if it is fail you are going to call service 2. according to this service 2 get called only if service 1 failed. if that is the case what you should do is your fault sequence(s) should keep status of each and every call and finally raise that to caller. best option is keep separate sequence for each call and you can call those sequences from each fault sequence. again this is depends on use case. if when at least 1 service fail, if you want to treat as everything failed, you can return directly from fault sequence. if not you can follow as i suggested. but problem here what should be the status code. if you set as 200 that means success. can we say success if one service failed? again it depends on your use case.