Discover our knowledge. Read our blogs!

Learn more

We build all our solutions with WSO2 and we are proud that we are WSO2 Premier Certified Integration Partner and Value-Added Reseller.

Learn more

Working with Endpoints in the WSO2 Enterprise Integrator - part two

12 min read

Working with endpointsIn a previous blog, we looked into error handling from an API where the endpoint is not available. To recap: in the full sequence of the API we added two elements, one that shows the error code an error message on the console and the other elements created a custom message and set the error code to an HTTP  404 (not found). In this blog we are going to use WireMock to create an endpoint.

The reason why we use the fault sequence is because this is the sequence that is triggered when something goes wrong. But it’s not only when the endpoint cannot be found that the fault sequence is triggered there are also other situations that will trigger the fault sequence. One of those examples is an endpoint that does not respond within time. To test this, we are going to use WireMock to create an endpoint that will always respond a couple of seconds later than the timeout we set on the endpoint.

Defining an endpoint

When you define an endpoint, there is actually more to it than just the URL or address. When you define the endpoint, it is just the URI template and the methods (in case of an HTTP endpoint) but when you zoom in you can see that there is endpoint error handling that you can configure with things like timeouts, suspend codes and so on.

We will leave the endpoint configuration for now and simply create an endpoint as shown above. In WireMock we define a new service called servicedelay.json and again deployed to the mappings directory of WireMock.

{
          "request": {
"method": "GET",
                "url": "/yenlo/api/testdelay"
          },
          "response": {
                 "status": 200,
                        "fixedDelayMilliseconds": 31000,
                 "body": "Service is responding\n"
          }
}

We test the endpoint using SoapUI and after 31 seconds we get a response pretty much like the previous service. So, what we will do is create a new API that calls this specific endpoint and see what the response is going to be. We will make a verbatim copy of the previous API and just change the endpoint and rename it, nothing more.

Add the API to the Composite Application and deploy the car file to the enterprise integrator that should be running of course.

Check to see whether the API is deployed on the enterprise integrator using the management UI.

Test the API using soap UI with the API invocation URL that you can see in the deployed APIs overview. After 31 seconds, we actually get a response from the service, a 200 code and the message.

What we need to do now is to change the endpoint configuration so that it will actually timeout when a response is not given within a certain timeframe. We will do that by defining two important configurations: endpoints timeout state and timeout configuration.

When we do not get a response, we will tell the enterprise integrator to try three times to make a connection with the delay of 500 ms between each retry. Before we go to suspension, we have a duration of 10,000 ms as well as the execution of the full sequence in case of suspension.

From the source code perspective, it looks something like this (we were only showing the endpoint configuration here).

<send>
      <endpoint>
             <http method="get" uri-template="http://localhost:8080/yenlo/api/testdelay">
                   <timeout>
                       <duration>10000</duration>
                       <responseAction>fault</responseAction>
                    </timeout>
                    <markForSuspension>
                            <retriesBeforeSuspension>3</retriesBeforeSuspension>
                            <retryDelay>500</retryDelay>
                    </markForSuspension>
             </http>
             <description/>
       </endpoint>
</send>

The result is that when we call the API with this configuration it will try a number of times and then trigger the fault sequence.

[2019-05-10 08:50:22,758] [EI-Core]  WARN - EndpointContext Endpoint : AnonymousEndpoint with address http://localhost:8080/yenlo/api/testdelay is marked as TIMEOUT and will be retried : 2 more time/s after : Fri May 10 08:50:23 CEST 2019 until its marked SUSPENDED for failure
[2019-05-10 08:50:22,759] [EI-Core] INFO - LogMediator Error = 101504, MSG = Send timeout, Trace = null, exception = null
[2019-05-10 08:50:22,773] [EI-Core] WARN - TimeoutHandler Expiring message ID : urn:uuid:073c955c-a893-4cf3-b0fd-2f8c076c3e27; dropping message after ENDPOINT_TIMEOUT of : 10 seconds for AnonymousEndpoint, URI : http://localhost:8080/yenlo/api/testdelay, Received through API : EPTest2
[2019-05-10 08:50:30,822] [EI-Core] WARN - SynapseCallbackReceiver Synapse received a response for the request with message Id : urn:uuid:073c955c-a893-4cf3-b0fd-2f8c076c3e27 But a callback is not registered (anymore) to process this response

As you can see the full sequence is triggered and the error code 101504 is shown. When we look up this code we find that it means: ‘Connection timed out (no input was detected on this connection over the maximum period of inactivity)’.

We programmed the fault sequence for one situation, namely a 404 not found. But as we see now the full sequence also gets triggered when we have a timeout.

HTTP/1.1 404 Not Found
Host: localhost:8280
Accept-Encoding: gzip,deflate
Content-Type: application/json; charset=UTF-8
Date: Fri, 10 May 2019 06:50:22 GMT
Transfer-Encoding: chunked
Connection: Keep-Alive

{ 'Error':'Connection cannot be made',
                          'Error Code' : 101504,
                          'Error Message' : ,
                          'Error Detail' : Send timeout}

The solution is of course to look at the error code and then determine the right error message to accompany the response.

Switch Mediator

The best way to do this is using a switch mediator in the fault sequence. The switch mediator allows you to branch based on the error code to a specific section to create the response as well as set the status code.

So, let’s make the changes in the fault sequence. The switch mediator uses cases that correspond with an error code and also has a default case that will be triggered when none of the other cases match. This is an easy way to define a generic error without having to specify all of the error codes. We will start by defining two cases, one for the situation that the endpoint cannot be found in one for the situation that we get an error on suspension.

We are going to copy a complete configuration in each of the case statements as well as create a generic error for the default case.

However, this results in quite some duplicate code. So what we are going to do is we’re going to remove the elements that are exactly the same in each of the cases and move them outside of the switch mediator.  This of course means the log mediator that will put the error code on the console. This saves a considerable amount of code but there is more that we can do! The HTTP_SC is available so that needs to be inside the switch mediator. Our own defined ‘error’ message is now hardcoded but we can turn it into a variable and also set that variable inside the switch mediator. When we make all the changes this is the resulting code:

<faultSequence>
  <log description="" level="custom">
    <property expression="get-property('ERROR_CODE')" name="Error"/>
    <property expression="get-property('ERROR_MESSAGE')" name="MSG"/>
    <property expression="get-property('ERROR_DETAIL')" name="Trace"/>
    <property expression="$ctx:ERROR_EXCEPTION" name="exception"/>
  </log>
  <switch description="" source="get-property('ERROR_CODE')">
    <case regex="101503">
      <property name="HTTP_SC" scope="axis2" type="STRING" value="404"/>
      <property name="OUR_ERROR" scope="axis2" type="STRING" value="Error':'Connection cannot be made"/>
    </case>
   <case regex="101504">
       <property name="HTTP_SC" scope="axis2" type="STRING" value="408"/>
       <property name="OUR_ERROR" scope="axis2" type="STRING" value="Error':'Connection timed out into suspension"/>
    </case>
    <default>
      <property name="HTTP_SC" scope="axis2" type="STRING" value="500"/>
      <property name="OUR_ERROR" scope="axis2" type="STRING" value="Error': Generic fault handler, code not specified"/>
    </default>
  </switch>
  <payloadFactory media-type="json">
     <format>{ 'Error':$1,
    'Error Code' : $2,
    'Error Message' : $3,
    'Error Detail' : $4}</format>
  <args>
    <arg evaluator="xml" expression="get-property('ERROR_CODE')"/>
    <arg evaluator="xml" expression="get-property('ERROR_EXCEPTION')"/>
    <arg evaluator="xml" expression="get-property('ERROR_MESSAGE')"/>
  </args>
  </payloadFactory>
  <respond/>
 </faultSequence>

We now have a versatile fault sequence that will catch two errors, a 404,408 and the generic one that we called 500.

Trying out in soap UI will give us this message.

I hope this blog was helpful. Please leave a comment if you have any questions or feedback. Interested to learn more about for example WSO2 Enterprise Integrator or WSO2 API Manager? Join one of our WSO2 trainings.

Care to share?
   
Picture of Rob Blaauboer
Published September 5, 2019

Rob Blaauboer

Rob is a Senior Business Consultant and Solution Architect with more than twenty years experience. In addition to his work he is an active blogger working on a number of articles on the ‘Internet of Things’ and a WSO2 ‘Getting Started with …’ series (WSO2 tutorial) in which he talks about WSO2 components and their purpose especially aimed at non technical readers. Rob is a WSO2 expert and official WSO2 trainer.

Responses

Stay up to date with the latest articles