Monday, June 29, 2015

How to generate a custom Error Message with Custom HTTP Status Code for unavailable Resources in WSO2 ESB


WSO2 ESB 4.8.1  does not throw any exception or error message when an API defined is access with incorrect HTTP method and it will just respond with 202.  In this blog post , i am explaining on how we can get a custom HTTP status code for the above.


In order to get a custom error message , you need to add following sequence to ESB which is not there by default.


<?xml version="1.0" encoding="UTF-8"?>
<sequence xmlns="http://ws.apache.org/ns/synapse" name="_resource_mismatch_handler_">
   <payloadFactory media-type="xml">
      <format>
         <tp:fault xmlns:tp="http://test.com">
            <tp:code>405</tp:code>
            <tp:type>Status report</tp:type>
            <tp:message>Method not allowed</tp:message>
            <tp:description>The requested HTTP method for resource (/$1) is not allowed.</tp:description>
         </tp:fault>
      </format>
      <args>
         <arg xmlns:ns="http://org.apache.synapse/xsd"
              xmlns:ns3="http://org.apache.synapse/xsd"
              evaluator="xml"
              expression="$axis2:REST_URL_POSTFIX"/>
      </args>
   </payloadFactory>
   <property name="NO_ENTITY_BODY" scope="axis2" action="remove"/>
   <property name="HTTP_SC" value="405" scope="axis2"/>
   <respond/>
   <drop/>
</sequence>

In ESB documentation [1] , it has explained that in order to handle non-matching resources, it is needed to define this sequence _resource_mismatch_handler_


[1] https://docs.wso2.com/display/ESB481/Configuring+Specific+Use+Cases#ConfiguringSpecificUseCases-Handlingnon-matchingresources


How to generate a custom Error Message with Custom HTTP Status Code for unavailable Resources in WSO2 API Manager

We are going to explain on how we can generate a custom HTTP Status code for a request which is addressed to a un-matching resource of an API.

Problem :

When an API exposed with resource "GET" , if the client invoke the API with "POST","PUT" or any other which is not "GET", By default API manager returns following.

{  
   "fault":{  
      "code":"900906",
      "type":"Status report",
      "message":"Runtime Error",
      "description":"No matching resource found in the API for the given request"
   }
}

In the RAW level you ll see it as follows

HTTP/1.1 403 Forbidden
Access-Control-Allow-Headers: authorization,Access-Control-Allow-Origin,Content-Type
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET,PUT,POST,DELETE,OPTIONS
Content-Type: application/xml; charset=UTF-8
Date: Mon, 29 Jun 2015 14:46:29 GMT
Server: WSO2-PassThrough-HTTP
Transfer-Encoding: chunked
Connection: Keep-Alive

<ams:fault xmlns:ams="http://wso2.org/apimanager/security">
   <ams:code>900906</ams:code>
   <ams:message>No matching resource found in the API for the given request</ams:message>
   <ams:description>Access failure for API: /sss, version: v1 with key: 4a33dc81be68d1b7a5b48aeffebe7e</ams:description>
</ams:fault>



Expected Solution :

We need to change this HTTP Response code 405 [1] with a custom error message.



Solution :

We need to create a sequence which builds the custom error message and the error code and deploy it in API manager's default sequences folder.


<?xml version="1.0" encoding="UTF-8"?>
<sequence xmlns="http://ws.apache.org/ns/synapse" name="converter">
    <payloadFactory media-type="xml">
        <format>
            <am:fault xmlns:am="http://wso2.org/apimanager">
                <am:message>Resource not found</am:message>
                <am:description>Wrong http method</am:description>
            </am:fault>
        </format>
    </payloadFactory>
    <property name="RESPONSE" value="true"/>
    <header name="To" action="remove"/>
    <property name="HTTP_SC" value="405" scope="axis2"/>
    <property name="messageType" value="application/xml" scope="axis2"/>
    <send/>
</sequence>

You can save this as converter.xml in wso2am-1.8.0/repository/deployment/server/synapse-configs/default/sequences folder.

Then we need to invoke this sequence in _auth_failure_handler_.xml which is located in the above sequences folder. In order to do that , we need to change it as follows.


<?xml version="1.0" encoding="UTF-8"?>
<sequence xmlns="http://ws.apache.org/ns/synapse" name="_auth_failure_handler_">
   <property name="error_message_type" value="application/xml"/>
    <filter source="get-property('ERROR_CODE')" regex="900906">
      <then>
          <sequence key="converter"/>
          <drop/>
      </then>
      <else>
      </else>
    </filter>
    <sequence key="_build_"/>
</sequence>


Once you done the above changes, save them. Then you can test your scenario. If you are successful with this , you ll be able see following response


HTTP/1.1 405 Method Not Allowed
Host: 10.210.1.202:8243
Content-Type: application/xml
Date: Mon, 29 Jun 2015 14:59:12 GMT
Server: WSO2-PassThrough-HTTP
Transfer-Encoding: chunked
Connection: Keep-Alive

<am:fault xmlns:am="http://wso2.org/apimanager">
   <am:message>Resource not found</am:message>
   <am:description>Wrong http method</am:description>
</am:fault>

Explanation : 

By default, when we invoke an non-existing resource it will send the default 403 error code with the message "No matching resource found in the API for the given request". If you check the log of the WSO2 AM, you can see that it has thrown following exception in the backend.


[2015-06-29 10:59:12,103] ERROR - APIAuthenticationHandler API authentication failure
org.wso2.carbon.apimgt.gateway.handlers.security.APISecurityException: Access failure for API: /sss, version: v1 with key: 4a33dc81be68d1b7a5b48aeffebe7e
    at org.wso2.carbon.apimgt.gateway.handlers.security.oauth.OAuthAuthenticator.authenticate(OAuthAuthenticator.java:212)
    at org.wso2.carbon.apimgt.gateway.handlers.security.APIAuthenticationHandler.handleRequest(APIAuthenticationHandler.java:94)
    at org.apache.synapse.rest.API.process(API.java:284)
    at org.apache.synapse.rest.RESTRequestHandler.dispatchToAPI(RESTRequestHandler.java:83)
    at org.apache.synapse.rest.RESTRequestHandler.process(RESTRequestHandler.java:64)
    at org.apache.synapse.core.axis2.Axis2SynapseEnvironment.injectMessage(Axis2SynapseEnvironment.java:220)
    at org.apache.synapse.core.axis2.SynapseMessageReceiver.receive(SynapseMessageReceiver.java:83)
    at org.apache.axis2.engine.AxisEngine.receive(AxisEngine.java:180)
    at org.apache.synapse.transport.passthru.ServerWorker.processNonEntityEnclosingRESTHandler(ServerWorker.java:344)
    at org.apache.synapse.transport.passthru.ServerWorker.processEntityEnclosingRequest(ServerWorker.java:385)
    at org.apache.synapse.transport.passthru.ServerWorker.run(ServerWorker.java:183)
    at org.apache.axis2.transport.base.threads.NativeWorkerPool$1.run(NativeWorkerPool.java:172)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:744)

When it throws above exception, the flow will hit the  _auth_failure_handler_.xml
sequence. So what we have done in this sequence, with using the filter mediator, we have filtered the error code "900906" and for that error code, we invoke our custom sequence and drop the message then.

In the custom sequence , we have used the payload factory mediator to create the payload and added required properties to make it as response. You can find the information further on each of those properties from [2][3][4]

Then after invoking the custom sequence, it will invoke the "_build_" sequence in the same folder which invoke the message builders to build the message.

I have used resources [4] on creating this blog post.


[1] http://www.checkupdown.com/status/E405.html#
[2] https://docs.wso2.com/display/ESB481/Generic+Properties#GenericProperties-RESPONSE
[3] https://docs.wso2.com/display/ESB481/Generic+Properties#GenericProperties-messageType
[4] http://sanjeewamalalgoda.blogspot.com/2015/04/how-to-generate-custom-error-message.html

Thursday, June 25, 2015

How to add a thread sleep to a Proxy Service

Here i am going to provide you a example on how we can create a mock service with WSO2 ESB and adding a sleep to that service.

In order to do that we need to use ;

  1. Payload Factory mediator to create the mock response
  2. script mediator to do a thread sleep
Here is the simple mock service proxy with a thread sleep.

<proxy xmlns="http://ws.apache.org/ns/synapse"
       name="MyMockProxy"
       transports="https,http"
       statistics="disable"
       trace="disable"
       startOnLoad="true">
   <target>
      <inSequence>
         <log>
            <property name="===Before sleep===" value="===Before sleep==="/>
         </log>
         <script language="js">java.lang.Thread.sleep(75000);</script>
         <log>
            <property name="===After sleep===" value="===After sleep==="/>
         </log>
         <payloadFactory media-type="xml">
            <format>
               <Response xmlns="">
                  <status>OK</status>
                  <code>1</code>
               </Response>
            </format>
            <args/>
         </payloadFactory>
         <header name="To" action="remove"/>
         <property name="RESPONSE" value="true" scope="default" type="STRING"/>
         <property name="NO_ENTITY_BODY" scope="axis2" action="remove"/>
         <send/>
      </inSequence>
   </target>
   <description/>
</proxy>

I have used the blog of miyuru [1] to create this.

[1] http://miyurudw.blogspot.com/2012/08/how-to-create-simple-mock-services.html

WSO2 IS User Store as ReadOnly/ReadWrite LDAP secondary user store

In most of the testing scenarios, we need to connect our products in to a secondary user store which is ReadOnly or ReadWrite Ldap User stores.

This is a simple way to get it done with WSO2 Identity Server.

Not as other WSO2 products, IS ships LDAP User store as it's primary user store. So if we need to point any of the other products in to a LDAP secondary user store, we can easily use WSO2 IS for that.



Case 01: Pointing WSO2 AM to a ReadOnlyLDAP Secondary user store


  • Download, Extract, Start WSO2 IS
  • Download, Extract WSO2 AM
  • If we are running both products in the same machine, we need to change the offset of the AM
  • Open the carbon.xml file located in "wso2am-1.9.0/repository/conf" folder and change the Offset value to "1". (By default it is "0")
  • Start AM
  • Browse url https://localhost:9444/carbon/
  • Login with credentials admin/admin
  • From the left menu , click on "Configure"

  • Click on "User Store Management"
  • Then click on "Add Secondary User Store" button 
  • From the drop down at the top, select "ReadOnlyLdapUserStoreManager" as the user store manager class.
  • Then provide parameters as follow
    • Domain Name : Any Name (wso2.com)
    • Connection Name : uid=admin,ou=system
    • Connection URL : ldap://localhost:10389
    • Connection Password : admin
    • User search base : ou=Users,dc=wso2,dc=org
    • User Object Class : (objectClass=person)
    • Username Attribute : uid
    • User search filter : (&(objectClass=person)(uid=?))
  • Then click on Add. 
  • After few seconds, it will be displayed in the user Store list 
  • You can find these configurations in user-mgt.xml file located in  "wso2am-1.9.0/repository/conf" folder. But you need to focus on the parameter "User search base".  By default it is given as "ou=system". But with that you ll not be able to view the users of the secondary user store. Here i have added the correct parameter value " ou=Users,dc=wso2,dc=org"




Case 02: Pointing WSO2 AM to a ReadWriteLDAP Secondary user store

Please follow the documentation https://docs.wso2.com/display/AM190/Configuring+Secondary+User+Stores


Tuesday, June 2, 2015

WSO2 APIManager - API is not visible to public

WSO2 API Manager  is releasing new versions time to time. So, people are migrating from old versions to new versions. In that situation, after the migration some times people are experiencing some problems like ;


  • API is not visible at all in API Store
  • API is not visible to public , but can see after logged in.

API is not visible at all in API Store


This can be due to the problem in indexing of APIs.  WSO2 APIM is providing the search capability of APIs with it's Solr based indexing feature.  Once there is a problem in indexing , it can cause to Not to displace the migrated APIs at all. 


How to fix ?

It is needed to allow the APIM to do the indexing again. In order to do that , it is needed to do the following steps.

1. Remove/Backup the solr directory located in WSO2AM directory
2. Change the value of "lastAccessTimeLocation" property in registry.xml file located in WSO2AM/repository/conf to an arbitrary value. 

Eg: By default the value of the above entry is as follows :

/_system/local/repository/components/org.wso2.carbon.registry/indexing/lastaccesstime

You can change it to 

/_system/local/repository/components/org.wso2.carbon.registry/indexing/lastaccesstime_1 



Note: About entry contains the last time it created the indexing on WSO2 AM in milliseconds. When we change it , if there is no resource available, APIM will create the indexing again and build the content for solr directory.

3. After the above step, restart the server and let it to be idle for 3-5 mins. Then you will be able to see the APIs if the problem was with the API indexing.




API is not visible to public, but can see after logged in

This can be caused due to a permission issue for the migrated API. By default, if we create an API with visibility as public,  APIM will create a resource for that API in registry with "system/wso2.anonymous.role" role with read permission. 

Eg: If i create an API called foo and with visibility set to public, i can see following permissions in registry.



So i can see my API with out log in to the API Store as bellow.



If i remove the anonymous permission from the registry resource, as bellow, It will not be visible to public. 



So, if you are experiencing a problem like this, You need to search for this API in registry and then check whether it has the read permission for the role "system/wso2.anonymous.role". If not just check by adding that permission. 

Then if it is working fine, You can check your migration script for the problem of not migrating the permissions correctly.