Wednesday 11 April 2007

Tutorial: HTTP Monitor


In this tutorial we're going to take a look at how to implement a simple HTTP/Web Services monitor using Mule.

Difficulty: Easy
Keywords: HTTP
Transports: http, vm
Modules: core, builder

A HTTP monitor needs to listen on a TCP port to HTTP requests, log them and then pass them on to the correct destination. For the sake of a simple example we're going to narrow the requirements down a little. Our monitor has a hard coded listening address and a hard coded destination address.

Step One


Lets sketch out our Mule configuration, starting with the means to log messages:
        <mule-descriptor name="Record"
implementation="RecordingService"
inboundEndpoint="vm://record"/>
Our descriptor creates a service called 'Record' which receives messages on the in-memory (vm) queue and then passes the input to the output unchanged. The result is also sent to System.out using the class RecordingService. This results simply in a service which logs to System.out all messages sent to it.
        <mule-descriptor name="Monitor" implementation="org.mule.components.simple.PassThroughComponent">

<inbound-router>
<endpoint address="http://localhost:7070" synchronous="true"/>
</inbound-router>

<outbound-router>
<router className="org.mule.routing.outbound.ChainingRouter">
<endpoint address="vm://record"/>
<endpoint address="http://dv304:18080" />
<endpoint address="vm://record"/>

</router>
</outbound-router>

</mule-descriptor>
In this configuration we're defining a service which does not change the data, hence we are using the PassThroughComponent which does exactly that, it passes through the input to the output without changing it.

We then need to specify that mule should listen for the input using the http connector on localhost port 7070, importantly we specify that this is synchronous (i.e. request/response) communication.

Any messages received by this service are then passed to a ChainingRouter. This router calls each endpoint in turn, using the output of one as the input to another. So we first call our recording service which will record the HTTP request string that was passed to our inbound-router. The result of this is then passed to the host we are sending the actual requests to, in this case the host is dv304 and the port is 8080. The result of sending our request is then passed back to our recording service which records the result and passes this back to the caller.

Step Two

We now have the basic config, so it is time to start working on the Java which for this example is simply our RecordingService.
public class RecordingService {

public void record(String requestString) throws IOException {
System.
out.println(requestString);
}

public void record(HttpResponse response) throws IOException {
final InputStream in = response.getBody();
response.setBody(
new InputStream() {

public int read() throws IOException {
int b = in.read();
if(b != -1) {
System.
out.write(b);
}
return b;
}
});

}
}
The recording service has two methods one will get called if it is passed a String, the other if it is passed a valid HttpResponse. The String recording method is straight forward, on receiving a string it prints it to System.out. The second method is a little more involved what this does is wrap the input stream from the response so that when it is consumed it's contents will be written to System.out; i.e. when the result is streamed back to the users browser. This is deliberate to allow the streaming characteristics of the HTTP connector to be unchanged.

Step Three

Finally we need to complete our mule configuration.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mule-configuration PUBLIC "-//SymphonySoft //DTD mule-configuration XML V1.0//EN" "http://dist.codehaus.org/mule/dtds/mule-configuration.dtd">

<mule-configuration id="WebRequestMonitor" version="1.0">

<description>Web request monitor.</description>

<mule-environment-properties synchronous="true"/>

<model name="Monitor">

<mule-descriptor name="Record"
implementation="RecordingService"
inboundEndpoint="vm://record"/>

<mule-descriptor name="Monitor" implementation="org.mule.components.simple.PassThroughComponent">

<inbound-router>
<endpoint address="http://localhost:7070" synchronous="true"/>
</inbound-router>

<outbound-router>
<router className="org.mule.routing.outbound.ChainingRouter">
<endpoint address="vm://record"/>
<endpoint address="http://dv304:18080"/>
<endpoint address="vm://record"/>

</router>
</outbound-router>

</mule-descriptor>
</model>

</
mule-configuration>