Friday, October 11, 2013

Exposing RESTful interface with Mule pt.1

Update (20.10.2013) - When not supported HTTP method is used, return more appropriate HTTP status (405 instead of 400).

As it's my first blog entry I'd like to welcome everyone. If you'd like to know more about me click on the tab above. If not.. I'll go straight to the point.

Recently, I had to create Mule's application, which exposes itself via a simple RESTful API. However, when it comes to REST, Mule ESB seems to be quite limited in viable options.
The only, official approach is to use the REST Component, which relies on Jersey, which is the Reference Implementation of JAX-RS. Sounds good, but it ties you to the Java code instead of having fun with the Mule's message processing :) This can be overcome as well, but still some Java needs to be written.

Fortunately, there are some other possibilites, which would make simple REST API creation easier. Nevertheless, I'd like to present you all the options (of which I'm aware of) with description and a working example.

All presented examples were tested against Mule ESB 3.4.0 EE. However, they should run without any problems on CE and even on previous versions.

1. "Poor man's" REST:

First approach is the most straightforward one - handling HTTP properties available in Mule manually. Quick overview over HTTP properties is available here - (1.4.7 Use Case) in archived Mule Developer Resources.

In that case, we need to take care of detecting the HTTP method, URL and fetching URL variable parameters by ourselves. We can accomplish that using Mule's choice component and setting flow variables (if needed).
An example will accept URL (on 8088 port) as follows: /client/{accountID}/{userID}/get on both GET and POST HTTP methods, where {accountID} and {userID} are the variable parameters.

General overview of the flow:


First step is not necessary, but helpful for quick suppresion of favicon.ico requests when accesing service via browsers:
<message-filter doc:name="Filter favicon">
  <not-filter>
    <wildcard-filter casesensitive="true" pattern="/favicon.ico">
  </wildcard-filter></not-filter>
</message-filter>
To match URL let's use choice component with regular expression:
<choice doc:name="Choice">
  <when expression="#[regex('/client/\\w+/\\w+/get/?', message.inboundProperties['http.request.path'])]">
   ...
 </when>
  <otherwise>
    <http:response-builder status="400" doc:name="Return 400 For bad URL">
      <set-payload value="Unknown resource: #[message.inboundProperties['http.request.path']]" />
    </http:response-builder>
  </otherwise>
</choice>
We retrieve URL path using Mule's inbound property - 'http.request.path', then match it with the regexp '/client/\\w+/\\w+/get/?'. If we don't have a match we just simply return 400 HTTP status.
Otherwise, we can proceed and determine which HTTP method was used to send the request:
<choice doc:name="Choice">
  <when expression="message.inboundProperties['http.method'] == 'GET'">
    <flow-ref name="RetrievingParameters" doc:name="Retrieve Parameters"/>
    <flow-ref name="ProcessGET" doc:name="Process GET"/>
  </when>
  <when expression="message.inboundProperties['http.method'] == 'POST'">
    <flow-ref name="RetrievingParameters" doc:name="Retrieve Parameters"/>
    <flow-ref name="ProcessPOST" doc:name="Process POST"/>
  </when>
  <otherwise>
    <http:response-builder status="405" doc:name="Return 405 For bad HTTP method">
      <set-payload value="Unknown HTTP method: #[message.inboundProperties['http.method']]" />
    </http:response-builder>
  </otherwise>
</choice>
This time we are checking another inbound parameter - 'http.method'. If it's not the method we want to proceed with, we return 405 HTTP status (Method Not Allowed).

For all supported methods we want to fetch the URL parameters. Hence, this functionality is extracted to a reusable sub-flow:
<sub-flow name="RetrievingParameters" doc:name="RetrievingParameters">
  <set-variable variableName="accountID" value="#[StringUtils.splitAndTrim(message.inboundProperties['http.request.path'], '/')[1]]" doc:name="Set Account ID"/>
  <set-variable variableName="userID" value="#[StringUtils.splitAndTrim(message.inboundProperties['http.request.path'], '/')[2]]" doc:name="Set User ID"/>
</sub-flow>
As I'm not good at regular expressions, I decided to go with Mule's StringUtils method, which splits the URL and gets me the parameters I'm looking for.

To avoid providing fully qualified name of the class over and over again, we can define global import for Mule expressions:
<configuration doc:name="Configuration">
  <expression-language>
    <import class="org.mule.util.StringUtils" />
  </expression-language>
</configuration>
That is basically everything we needed. We can now process GET / POST requests. For readability and maintainability reasons we will do that in separate flows:
<flow name="ProcessPOST" doc:name="ProcessPOST">
  <http:response-builder status="200"doc:name="Return OK">
    <set-payload value="Processing POST with account id: #[accountID] and user id: #[userID] and body: #[payload]" doc:name="Set Payload"/>
  </http:response-builder>
</flow>
<flow name="ProcessGET" doc:name="ProcessGET">
  <http:response-builder status="200"doc:name="Return OK">
    <set-payload value="Processing GET with account id: #[accountID] and user id: #[userID]" doc:name="Set Payload"/>
  </http:response-builder>
</flow>
 
To test my service I've prepared a test class with couple of Mule functional tests. Instead of using default FunctionalTestCases, I suggest using MUnit. From what I know, it is supposed to replace current Mule testing solution in future. With MUnit you can write your tests using Java (JUnit) or XML (Mule code). It has features like mocking endpoints, processors and ability to call flow directly, skipping inbound endpoints. However, development of MUnit seemed to slow down recently.
To keep the test concise, I used JUnitParams extension, as I wanted to use same test methods, but with different input (URLs).

Additionally, we could've checked for content-type etc., but I didn't want to clutter the code with insignificant details.
Having in mind next example, I'm not exactly sure, if anyone would opt for this approach. But still, there it is, probably for demonstration purposes only ;)

Full example at GitHub: PoorMansREST

2. REST Router

To make our lives easier Mule REST Router Module has been developed. Code is available at GitHub.
Almost everything we did in previous approach is wrapped into one message processor. Installation and configuration is well explained in the links provided.

Flow overview:

Basically, to implement same behaviour, we need to configure rest-router as follows:
<rest-router:router templateUri="/client/{accountID}/{userID}/get">
  <rest-router:get>
    <flow-ref name="ProcessGET" doc:name="Process GET" />
  </rest-router:get>
  <rest-router:post>
    <flow-ref name="ProcessPOST" doc:name="Process POST" />
  </rest-router:post>
</rest-router:router>

REST Router automatically assign parameters specified in templateUri to flow variables. However, I found out that it passes through empty parameters, so it is advisable to validate them explicitly in your code.
Outstanding part is to cover invalid URLs and return something meaningful. For requests not matching our template we return standard 400 HTTP status (placed just after </rest-router:router>) :

<http:response-builder status="400" doc:name="Return 400 For bad URL">
  <set-payload value="Unknown resource: #[message.inboundProperties['http.request.path']]" />
</http:response-builder>

For HTTP methods not supported REST Router should (per documentation) throw UnsupportedHttpVerbException. However, there is an issue I submitted here. Unfortunately, the project looks abandoned as nobody cared :) I did some more research and it appears that the issue is not in the REST Router itself, but how the DevKit java code is generated.
There are two solutions for that:
1) Always implement whole set of methods in REST Router and return appriopriate message / status / throw exception in those you don't want to support.
2) As I don't like to have redundancy in my code I used a dirty hack of replacing compiled class with a fixed one directly in the jar file. Amended version of the module is available here. Just install it in your local Maven repository and switch module version in your pom.xml file to 1.2-fixed.

Fix description:
HTTP method recognition is implemented as in the following snippet: (RestRouterModule.java)
if (get != null && method.equalsIgnoreCase("get")) {
 return get.processWithExtraProperties(properties);
} else if (put != null && method.equalsIgnoreCase("put")) {
 return put.processWithExtraProperties(properties);
} else if (post != null && method.equalsIgnoreCase("post")) {
 return post.processWithExtraProperties(properties);
} else if (delete != null && method.equalsIgnoreCase("delete")) {
 return delete.processWithExtraProperties(properties);
} else if (patch != null && method.equalsIgnoreCase("patch")) {
 return patch.processWithExtraProperties(properties);
} else {
 throw new UnsupportedHttpVerbException(method);
}
Variables: get, put, post... are instances of NestedProcessor class, marked @Optional.
However, code generated by DevKit always propagates those variables to the RestRouterModule as instances of NestedProcessorChain (RouterMessageProcessor.java):
final NestedProcessor _transformedGet = new NestedProcessorChain(event, getMuleContext(), ((MessageProcessor) get));
final NestedProcessor _transformedPut = new NestedProcessorChain(event, getMuleContext(), ((MessageProcessor) put));
final NestedProcessor _transformedPost = new NestedProcessorChain(event, getMuleContext(), ((MessageProcessor) post));
final NestedProcessor _transformedDelete = new NestedProcessorChain(event, getMuleContext(), ((MessageProcessor) delete));
final NestedProcessor _transformedPatch = new NestedProcessorChain(event, getMuleContext(), ((MessageProcessor) patch));
Those _transformed method variables are passed into the RestRouterModule class, hence it always tries to process the processWithExtraProperties(properties) method and eventually finishes with NullPointerException as the last argument in NestedProcessorChain contructor is null.

My fixed version of RouterMessageProcessor.java includes following change:
final NestedProcessor _transformedGet = (get == null) ? null : new NestedProcessorChain(event, getMuleContext(), ((MessageProcessor) get));
final NestedProcessor _transformedPut = (put == null) ? null : new NestedProcessorChain(event, getMuleContext(), ((MessageProcessor) put));
final NestedProcessor _transformedPost = (post == null) ? null : new NestedProcessorChain(event, getMuleContext(), ((MessageProcessor) post));
final NestedProcessor _transformedDelete = (delete == null) ? null : new NestedProcessorChain(event, getMuleContext(), ((MessageProcessor) delete));
final NestedProcessor _transformedPatch = (patch == null) ? null : new NestedProcessorChain(event, getMuleContext(), ((MessageProcessor) patch));
Not much to explain here. Now, if a REST module HTTP method is not defined it will propagate null to RestRouterModule.java and throw UnsupportedHttpVerbException as expected.

In the flow we use standard choice exception strategy and catch the UnsupportedHttpVerbException to return 405 HTTP status:

  
    
      
    
  


For testing I used the same set of tests as in previous example. As you can see, it's much quicker and cleaner approach that saves you a lot of time and lines of code :)

Full example (using modified version of the module) at GitHub: RestWithRouter

In the next part I will present how to achieve the same, but using Jersey with writing as little Java as possible. Go here for the second part!

1 comment: