AndreaNobili
AndreaNobili

Reputation: 42957

How can I correctly manage the WSO2 ESB error case? Can I centralize all the errors situation in the faultSequence?

I am working on a WSO2 ESB project and I have some doubts about how to correctly handle the fault situation.

So for example I have this API:

<?xml version="1.0" encoding="UTF-8"?>
<api context="/weather/crop_calendar" name="crop_calendar" xmlns="http://ws.apache.org/ns/synapse">
    <resource methods="GET" uri-template="/country/{countryId}/crops?lang={lang_id}">
        <inSequence>
            <log level="full"/>
            <property expression="get-property('uri.var.countryId')" name="countryId" scope="default" type="STRING"/>
            <property expression="$url:lang" name="langId" scope="default" type="STRING"/>
            <log level="custom">
                <property expression="$ctx:countryId" name="countryId"/>
                <property expression="$ctx:langId" name="langId"/>
            </log>
            <property name="messageType" scope="axis2" type="STRING" value="application/xml"/>
            <payloadFactory media-type="xml">
                <format>
                    <ds:GetCropCalendarCommoditiesList xmlns:ds="http://ws.wso2.org/dataservice">
                        <country_id>$1</country_id>
                        <ds:language_id>$2</ds:language_id>
                    </ds:GetCropCalendarCommoditiesList>
                </format>
                <args>
                    <arg evaluator="xml" expression="$ctx:countryId"/>
                    <arg evaluator="xml" expression="$ctx:langId"/>
                </args>
            </payloadFactory>
            <header name="Action" scope="default" value="urn:SelectCropCalendarCommoditiesList"/>
            <call>
                <endpoint key="cropCalendarEndpoint"/>
            </call>
            <property name="messageType" scope="axis2" type="STRING" value="application/json"/>
            <property expression="json-eval($.)" name="JSONPayload" scope="default" type="STRING"/>
            <script language="js"><![CDATA[var log = mc.getServiceLog();

                function scan(obj) {
                    var k;
                    if (obj instanceof Object) {
                        for (k in obj){
                            if (obj.hasOwnProperty(k)){

                                obj[k] = checkForNull(obj[k]);
                                //recursive call to scan property
                                scan(obj[k]);  
                            }                
                        }
                    } else {
                        //not an Object so obj[k] here is a value
                    }

                }

                function checkForNull(value) {
                    if (value instanceof Object && "@nil" in value) {
                        return null;
                    }

                    return value;
                }

                log.info("----------------------------- CROP CALENDARD COMMODITIS LISTING SCRIPT START ------------------------------------------------");

                var lang_id = mc.getProperty('langId'); 

                // stange workaround for getting JSON Payload. getPayloadJSON returned null. 
                var pl_string = mc.getProperty("JSONPayload"); 
                log.info("PAYLOAD STRING: " + pl_string);
                var payload = JSON.parse(pl_string);

                // Create new response:          
                var response = payload;

                log.info("RESPONSE: " + JSON.stringify(response));

                scan(response);

                // Fix the single element problem:
                if (!(response.CropCalendarCommodities.CommoditiesList instanceof Array)) {
                    log.info("It is not an array, convert it into an array");

                    singleCommodity = response.CropCalendarCommodities.CommoditiesList;

                    response.CropCalendarCommodities.CommoditiesList = [];
                    response.CropCalendarCommodities.CommoditiesList.push(singleCommodity);
                }

                // Convert array of crops into required HATEOS format
                var cropsList = new Array();

                for (i = 0; i < response.CropCalendarCommodities.CommoditiesList.length; i++) {
                    log.info("Crop: " + i + " CROP NAME: " + response.CropCalendarCommodities.CommoditiesList[i].commodity_name);

                    commodityCropCalendarDetailsLinks = [];

                    commodityCropCalendarDetailsRefObj = {};

                    commodityCropCalendarDetailsRefObj.rel = "commodity_enutrifood_details";
                    commodityCropCalendarDetailsRefObj.href = "http://5.249.148.180:8280/weather/crop_calendar/commodity/" + checkForNull(response.CropCalendarCommodities.CommoditiesList[i].commodity_id) +  "?lang=" + lang_id; 
                    commodityCropCalendarDetailsRefObj.type = "GET";

                    commodityCropCalendarDetailsLinks.push(commodityCropCalendarDetailsRefObj);

                    response.CropCalendarCommodities.CommoditiesList[i].links = commodityCropCalendarDetailsLinks;

                }


                log.info("----------------------------- CROP CALENDARD COMMODITIS LISTING SCRIPT END ------------------------------------------------");
                // put payload back
                mc.setPayloadJSON(response);]]></script>
            <property name="RESPONSE" scope="default" type="STRING" value="true"/>
            <header action="remove" name="To" scope="default"/>
            <send/>
        </inSequence>
        <outSequence/>
        <faultSequence>
            // HERE HANDLE THE ERROR CASES
        </faultSequence>
    </resource>
</api>

This API take 2 input parameters in the URL:

and uses both to perform a DSS query, then the query output is converted in a JSON document and then Script mediator process this JSON document.

Error can take place during this mediators chain.

For example passing a countryId related to a country so there are no data in the database, the DSS query return something like this (after thge JSON conversion):

{"CropCalendarCommodities":null}

The problem is that when this document is processed by the Script mediator, WSO2 go into error because can't access to this element into the previous JSON:

CropCalendarCommodities.CommoditiesList

and I am obtaining an error like this:

TID: [-1234] [] [2018-06-13 10:26:58,527] ERROR {org.apache.synapse.mediators.bsf.ScriptMediator} -  The script engine returned an error executing the inlined js script function mediate {org.apache.synapse.mediators.bsf.ScriptMediator}
com.sun.phobos.script.util.ExtendedScriptException: org.mozilla.javascript.EcmaError: TypeError: Cannot read property "CommoditiesList" from null (<Unknown Source>#45) in <Unknown Source> at line number 45
        at com.sun.phobos.script.javascript.RhinoCompiledScript.eval(RhinoCompiledScript.java:68)
        at javax.script.CompiledScript.eval(CompiledScript.java:92)
        at org.apache.synapse.mediators.bsf.ScriptMediator.mediateForInlineScript(ScriptMediator.java:345)
        at org.apache.synapse.mediators.bsf.ScriptMediator.invokeScript(ScriptMediator.java:265)
        at org.apache.synapse.mediators.bsf.ScriptMediator.mediate(ScriptMediator.java:233)
        at org.apache.synapse.mediators.AbstractListMediator.mediate(AbstractListMediator.java:97)
        at org.apache.synapse.mediators.base.SequenceMediator.mediate(SequenceMediator.java:260)
        at org.apache.synapse.core.axis2.Axis2SynapseEnvironment.mediateFromContinuationStateStack(Axis2SynapseEnvironment.java:775)
        at org.apache.synapse.core.axis2.Axis2SynapseEnvironment.injectMessage(Axis2SynapseEnvironment.java:282)
        at org.apache.synapse.core.axis2.SynapseCallbackReceiver.handleMessage(SynapseCallbackReceiver.java:554)
        at org.apache.synapse.core.axis2.SynapseCallbackReceiver.receive(SynapseCallbackReceiver.java:188)
        at org.apache.axis2.engine.AxisEngine.receive(AxisEngine.java:180)
        at org.apache.synapse.transport.passthru.ClientWorker.run(ClientWorker.java:262)
        at org.apache.axis2.transport.base.threads.NativeWorkerPool$1.run(NativeWorkerPool.java:172)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at java.lang.Thread.run(Thread.java:745)
Caused by: org.mozilla.javascript.EcmaError: TypeError: Cannot read property "CommoditiesList" from null (<Unknown Source>#45)

My doubt are:

what are the best way to handle situation like this?

I know that I can handle the situation inside th script mediator (and it is pretty easy: I check if the CropCalendarCommodities.CommoditiesList is null. If it is null I create a propper JSON response that will be returned to th user (something containing a 404 status message).

But my doubt is: can I centralize the management of all the possile error case in the ... sequence.

If yes in this case I need a way to retrieve the type of error occured into my .

In this case if the error is into the script mediator I can build a 404 status message that will be returned to the user. But if occurred another tyoe of error (for example DSS can't access to the DB) I will build a different error message that will be returned to the user.

What I am trying to do is to centralize the error management and handle the error situation in a manner more similar to the classical concept of exception.

Can I do this in some way?

Upvotes: 0

Views: 1176

Answers (2)

Riverchimp
Riverchimp

Reputation: 419

The fault sequence will be called when an error occurs, it is a good idea to put something there otherwise the client will keep waiting for a response. See the error handling docs for some more info.

One thing to remember is that in the DSS, if a error is returned by the Database this will not cause the faultsequence to get invoked. You need to set the FORCE_ERROR_ON_SOAP_FAULT property.

If you have a group of mediators that you want to handle errors for differently, consider moving them to their on Sequence (lets call it CodeSeq). You then create another sequence that contains the error handling (lets call it ErrorSeq). Then in the CodeSeq you can specify the on error attribute onError="ErrorSeq".

Upvotes: 1

Saad Sahibjan
Saad Sahibjan

Reputation: 300

Yes fault sequence is the place to handle errors. You can use,

  • get-property('ERROR_CODE')
  • get-property('ERROR_MESSAGE')

You can create a custom error payload with an appropriate HTTP_SC and respond to the client.

https://docs.wso2.com/display/ESB481/Error+Handling

Upvotes: 1

Related Questions