Enterprise Integration Zone is brought to you in partnership with:

OpenSource has posted 2 posts at DZone. View Full User Profile

Pattern Based Development with Mule 2.0

05.28.2008
| 36150 views |
  • submit to reddit

Are you still working with integration products from one of the typical large integration vendors such as IBM and Tibco and looking for a more lightweight approach? Then these two articles on Mule and Apache ServiceMix can serve as an eye-opener to the potential of Open Source ESBs. If you are already familiar with Mule or Apache ServiceMix, these articles will provide some insight in the new features of the latest versions of these Open Source ESBs and show you how to approach the design of your integration solution.

These two articles will also try to demystify the discussion between a Java Business Integration (JBI) based ESB implementation (Apache ServiceMix) and a custom developed ESB implementation (Mule). In these two articles we will implement an integration solution with both Mule and Apache ServiceMix, to show that both approaches each have their merits and that there is actually quite a bit of overlap between these two approaches.

In this first article we’ll focus on Mule, Mule 2.0 to be more specific, which is the latest release of the Mule project. The second article, on Apache ServiceMix, will be available in about two weeks.

A short introduction into Mule 2.0

Before we’ll take a look at a pattern based development approach, let’s dive into the Mule 2.0 architecture. If you’re already familiar with Mule you should note that we’re discussing the architecture of Mule 2.0 here, which was recently released as the successor to the Mule 1.4.x version and that the architecture has changed quite a bit between these two versions. At the bottom of this article you can find a link to a page on the MuleSource site which briefly explains the differences between these two versions.

A good way to start with the Mule 2.0 architecture is by looking at how a message is processed by Mule. This is shown in figure 1.

Figure 1 An overview of a typical Mule message flow that receives a message on a particular message channel, performs integration logic with the component and inbound and outbound routers, and passes the message on to its target destination message channel.

Figure 1 shows that for a typical Mule message flow, we have to define an inbound router which listens on a specific message channel. A message channel is a pattern described in the well-known Enterprise Integration Patterns book of Hohpe and Woolf. Basically a message channel just transfers a message. A message channel can for example be a JMS queue, a file directory or a HTTP connection. When a message is consumed via the inbound router, it’s passed on to a component implementation, which usually is a POJO class developed by yourself, but can also be a Spring bean, a scripting implementation or even a BPEL process. The response of this component will be sent to the output message channel through the outbound router.

Build a Mule 2.0 message flow

So far we’ve just talked theory. To make it a bit more practical and fun, let’s look at some Mule configuration that shows how to consume a message, which contains your name, from a JMS queue, adds a “Hello “ prefix and then forwards the message to another JMS queue.

The Mule configuration, shown in listing 1, uses the new Mule 2.0 notation. To use this configuration you need a Mule 2.0 broker which can be downloaded from the MuleSource website.

Listing 1. Simple message flow configured for Mule 2.0

<mule xmlns="http://www.mulesource.org/schema/mule/core/2.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:spring="http://www.springframework.org/schema/beans"
xmlns:jms="http://www.mulesource.org/schema/mule/jms/2.0"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.mulesource.org/schema/mule/core/2.0
http://www.mulesource.org/schema/mule/core/2.0/mule.xsd
http://www.mulesource.org/schema/mule/jms/2.0
http://www.mulesource.org/schema/mule/jms/2.0/mule-jms.xsd”>

<jms:activemq-connector name=”jmsCon” brokerURL=”tcp://localhost:61616”

<spring:bean id=”helloBean” class=”esb.example.HelloBean”>
<spring:property name=”prefix” value=”hello “ />
</spring:bean>

<model name=”helloModel”>
<service name=”helloService”>
<inbound>
<jms:inbound-endpoint queue=”in-queue” />
</inbound>
<component>
<spring-object bean=”helloBean” />
</component>
<outbound>
<outbound-pass-through-router>
<jms:outbound-endpoint queue=”out-queue” />
</outbound-pass-through-router>
</outbound>
</service>
</model>
</mule>

The example from listing 1 provides a good introduction into the functionality that Mule provides. First you can see that it’s very easy to connect Mule to a running ActiveMQ broker so you can exchange JMS messages. This is done with the jms:activemq-connector element. In addition, we can see that the inbound part of a message flow is defined with the inbound element. Within this element we can define an inbound endpoint or specific routers. In this example we just configured a inbound endpoint which listens to a JMS queue. Notice that the inbound-endpoint has a specific jms namespace. This prefix points to the mule-jms schema. This schema defines the queue attribute for the JMS inbound endpoint, so you can use your IDE’s code completion to create these elements. A standard ,not specific to a technology, inbound endpoint (as Mule users of version 1.x will know) defines an address attribute where you have to define a JMS queue with a JMS protocol prefix, like jms://in-queue. Using the schema based approach you get a much more descriptive and easier way to define an inbound endpoint for queues, topics and all the other supported technologies.

The example also shows the out-of-the-box integration with the Spring framework. While we can also specify a POJO class for a Mule component by using it’s classname, this example shows the use of a spring-object child element. When you use this element you tell Mule to use Spring to resolve the specified name as a Spring bean. In this case we reference the HelloBean which is also defined in listing 1. The implementation of the HelloBean class is just a basic POJO as you can see in listing 2.

Listing 2. HelloBean which is used as component in the example flow

public class HelloBean {

private String prefix;

public String hello(String name) {
return prefix + name;
}

public void setPrefix(String prefix) {
this.prefix = prefix;
}
}

The HelloBean defines a prefix attribute whose value is injected from the Mule configuration file. What’s interesting about this component implementation is how does Mule determine which method to invoke? Well, by default the Mule container inspects the component implementation for public methods with an input type that matches the payload type of the message. In this example, the payload will be your name as a String instance so the two methods of the HelloBean, the hello and setPrefix methods, will be found by the Mule container. However, by default the Mule container will first look at methods that return a value instead of void methods. In this case that means that the hello method will be invoked by the Mule container. In the Mule architecture this is call entry point resolving and by default the method entry point resolver is used. There are also other entry point resolving mechanisms available, you can even write your own implementation if you should need more complex logic.

The last part of the Mule configuration routes the return value of the HelloBean component to the out-queue JMS queue. In this example we have used the simple outbound-pass-through-router that just passes the message on to a specific endpoint. Mule also provides content-based routing routers, and many others, to implement more complex routing logic, as we will see in the larger example later on.

This example is a very basic example. It however does show use the same architectural components that can be used to implement complex integration logic. In the next section we’ll show you some more features of Mule by implementing a more complex case.

Case study: an insurance broker

A new insurance company, EasyInsurance, wants to provide potential customers with a web site which can be used to get quotes from various insurance companies based on the type of insurance you want. When the website will be put online only request for travel and car insurances are available, but in the future EasyInsurance intends to also provide the same functionality for home insurance.

When an insurance request is entered via the website, the request is forwarded to insurance providers depending on the type of insurance request. The number of insurance providers should also be easily expandable. The responses from the insurance provider are shown on the website and an insurance contract can be requested from the provider. Figure 2 shows an overview of the website’s functionality to send request to insurance providers and how the responses will be sent back to the website.

Figure 2 An overview of the insurance broker case study showing the messages sent to the insurance providers depending on the insurance request type.

An added problem that we need to solve in our integration solution is that the budgetCar insurance company requires the requests to be sent in CSV format over FTP (to keep it simple we’ll use a file connector here) and the LuxuryCar insurance wants an XML file over JMS. The Resort Travel Insurance company has the requirement that all calls are done using webservices.

Pattern based design approach

Most people who have worked in integration projects are probably familiar with the Enterprise Integration Patterns (EIP) book of Gregor Hohpe and Bobby Woolf. This book shows a number of design patterns that you can use to document, describe and solve your integration challenges. In the next couple of paragraphs we’ll show how you can use the patterns described in the EIP book to describe the insurance broker case study.

Let’s first look at figure 3, which describes the request part of the case we presented.

Figure 3 This figure shows the request flow of the insurance broker integration solution described using enterprise integration patterns from the EIP book.

Table 1. Used patterns for the request flow of the insurance case study.

Pattern
Description
Message channel  A message channel allows applications to communicate with each other.
Channel Adapter  A channel adapter defines how you can connect to the messaging system (e.g. a JMS broker) so you can receive and send messages.
Content based router  As the name implies a content based router, routes messages based on the content of the message.

Recipient list

  Sometimes you want to send a message to multiple channels at the same time. A recipient list provides in this.

Transformer

  A transformer is used when the format of the message needs to be changed before it can be sent to a recipient or when a standard format is defined.
Message endpoint  A message endpoint defines a connection from an application to a messaging channel.

 

Now let’s see how these patterns work together to solve the request part of our insurance website. We won’t show all the front-end stuff, but we’ll start with a insurance request that is received from the EasyInsurance website.

  1. The first thing we see is that the website uses a “message endpoint” to send a message to a specific channel (a JMS queue in this case) which is managed by a broker.
  2. This queue is then read by the ESB using a JMS “channel adapter”.
  3. The message is now routed internally by the ESB. The first router is the “content based router” which picks up the message and based on the type of request, car or travel, routes it to the next component.
  4. If the message needs to be sent to multiple insurance companies a recipient list is used and before the message is actually sent it’s first transformed to the target message format using a “transformer”.
  5. The “transformer” has made sure that the message is in a format the recipient can work with, so now all that is left to do is use another “channel adaptor” to send the message to a “message channel”, and the insurance companies can use a “message endpoint” on their side to read the request from the “message channel”.

Besides the use of the Enterprise Integration Patterns, the request flow of figure 3 also shows a clear separation between the different logical boundaries of the integration solution. Of course the ESB is shown as a logical boundary, but also the message broker (the JMS provider), the website and the endpoints of the insurance companies are separated in different boundaries. Also notice that figure 3 only shows the request flow of the insurance case study solution. To keep the design clean and comprehensible it’s often better to separate the request and response flows in different design diagrams. Figure 4 shows the response of the insurance broker integration solution.

Figure 4 This figure shows the response flow of the insurance integration solution described using enterprise integration patterns from the EIP book.

The design diagram of the response flow shown in figure 4 contains a lot of the patterns that were already used in the design of the request flow, only the aggregator pattern is new here. The aggregator is used to combine the insurance response messages from the BudgetCar and LuxuryCar insurance companies into one response that can be shown on the EasyInsurance website. To be able to aggregate the response messages from the two car insurance companies, we need some kind of correlation identifier that relates that response messages to the original insurance request message. In this example we use a request identifier in the insurance request message that is also available in the insurance response message.

This introduces another important step in the design phase of an integration solution: the message design. Because Mule can also use Java objects as message payload type, we will use Java based messages to communicate between the EasyInsurance website and the JMS broker and Enterprise Service Bus. In listing 3 the elements of the 3 message types involved in this integration solution are shown.

Listing 3. An overview of the Java classes that represents the insurance requests and response messages used in the integration broker implementation.

public class CarInsuranceRequest implements Serializable {

private String requestID;
private String numberPlate;
private String carType;
private int buildYear;
private boolean companyCar;
private Date startDate;
}

public class TravelInsuranceRequest implements Serializable {

private String requestID;
private String destinationCountry;
private int numberOfPersons;
private Date startDate;
private Date endDate;
}

public class InsuranceResponse implements Serializable {
private String requestID;
private String responseID;
private String insuranceCompanyName;
private float price;
}

As can be seen in listing 3, the messages are kept really simple for this example. The important parts of the message design for the integration solution are the distinction between a CarInsuranceRequest and a TravelInsuranceRequest that’s used for content-based routing and the requestID attribute that will be used for aggregating the car insurance response messages.

Notice that so far we haven’t talked about specific tools yet. In the design we made we just described what needs to be done. We can now hand over the integration flow and message design to a developer or integration specialist who can implement it using a specific technology. In this article we’ll implement it ourselves and we’ll use Mule 2.0 for this.

Implement the insurance broker with Mule: the request part

As you’ve seen it’s a fairly complex integration problem we need to solve. We have different types of formats we need to support, different technologies that we need to connect to, and even aggregate the events from the car insurance back to a single message before returning it to the website. We’ll start with the request part of this scenario and walk you through this step-by-step.

The first thing we need to do is read a message from a JMS queue and then use a content based router to select the message’s its next destination. This code is shown in listing 4.

Listing 4. Read from a JMS queue and use a content based router to decide the next destination

<!--omitted the Mule namespaces -->
<mule>
<jms:activemq-connector name="jmsCon" brokerURL="tcp://localhost:61616"/>

<model name=”InsuranceModel”>
<service name="JMSInService">
<inbound>
<jms:inbound-endpoint queue="insurance.in" />
</inbound>
<outbound>
<forwarding-catch-all-strategy>
<jms:outbound-endpoint queue="insurance.invalid"/>
</forwarding-catch-all-strategy>
<filtering-router>
<vm:outbound-endpoint path="car.insurances"/>
<expression-filter evaluator="payload-type"
expression="dzone.mule.model.CarInsuranceRequest"/>
</filtering-router>
<filtering-router>
<vm:outbound-endpoint path="travel.insurances"/>
<expression-filter evaluator="payload-type"
expression="dzone.mule.model.TravelInsuranceRequest"/>
</filtering-router>
</outbound>
</service>
</model>
</mule>

In listing 4 you can see a single mule service and the configuration on how to connect to activeMQ. The inbound part of this mule service reads messages from a jms queue named “insurance.in”, and uses the connector to activeMQ, since this is the only connector we’ve specified.


Once a message is read from the queue it get’s processed by the outbound routers. In this case we check the class of the request by using a “payload-type” expression filter. If the filter matches the request gets sent to the specified internal (VM) endpoint. If none of the filters match, the message gets sent to the “insurance-invalid” queue, which serves as a dead message queue in our scenario.

We’ve determined the type of message and sent it to its specific internal endpoint, so now we can use the message to invoke the two car insurance services and the single travel insurance service.

Lets first look at how to invoke the two car insurance companies. If you look back at figure 3, you can see that we want to use file and JMS for this. Listing 5 shows the configuration of this service and the required transformers. Notice that we split up the Mule configuration to explain it in chunks, but the whole Mule configuration shown in this article is implemented in one Mule configuration, the insurance-config.xml file.

Listing 5. Send the car insurance request to the two car insurance services.

<mule>	
<jms:object-to-jmsmessage-transformer name="ObjectToJMS"/>
<xm:object-to-xml-transformer name="InsuranceToXML"/>
<xm:xml-to-object-transformer name="XMLToInsurance"/>
<custom-transformer name="InsuranceToCSV"
class="dzone.mule.transformer.CSVTransformer"/>

<!-- repeated the model element to make it comprehensible -->
<model name=”InsuranceModel”>
<service name="SendCarInsurancesService">
<inbound>
<vm:inbound-endpoint path="car.insurances"/>
</inbound>
<outbound>
<multicasting-router>
<file:outbound-endpoint path="budgetCarIn"
outputPattern="${DATE:HH-mm-ss}-carinsurance.csv">
<transformer ref="InsuranceToCSV"/>
</file:outbound-endpoint>
<jms:outbound-endpoint queue="luxurycar.send">
<transformer ref="InsuranceToXML"/>
<transformer ref="ObjectToJMS"/>
</jms:outbound-endpoint>
</multicasting-router>
</outbound>
</service>
</model>
</mule>

In Listing 5. you can see that we’ve first configured the transformers we need. In the beginning of this article we mentioned that the BudgetCar company required the message to be in CSV format and the LuxuryCar company required XML. For this we’ve create a custom transformer that takes the java CarInsuranceRequest and transforms the content to CSV. This transformer is shown in Listing 6.

Listing 6. Custom transformer to transform a CarInsuranceRequest to a CSV String.

public class CSVTransformer extends AbstractTransformer {

protected Object doTransform(Object payload, String encoding)
throws TransformerException {
if(!(payload instanceof CarInsuranceRequest)) {
throw new TransformerException(this, new IllegalArgumentException(
"only car insurance requests can be transformed"));
}
CarInsuranceRequest request = (CarInsuranceRequest) payload;
return new StringBuffer()
.append(request.getRequestID())
.append(",")
.append(request.getNumberPlate())
.append(",")
.append(request.getCarType())
.append(",")
.append(request.getBuildYear())
.append(",")
.append(request.getStartDate())
.toString();
}
}

For the budgetCar insuranceCompany this means that with the configuration from Listing 5. the incoming java message is transformed to CVS and then sent to a directory on the filesystem.

For the luxuryCar Insurance company we need to transform the message to XML. For this we’ve just used the standard transformers mule provides. This will inspect the Java bean and use that to create an XML message. The last thing we do before we sent the message to the JMS queue is make sure the message is a JMSObject. If we don’t use the XML transformer we don’t have to take this last step, since then Mule will use the JMS default transformer to transform the message to a JMSObject. But since we defined transformers on the endpoint we override the default configuration, so we need to explicitly also add the ObjectToJMS transformer.

Well we’re almost done with the request part. The only thing we need to do is configure the webservice call for the Resort Travel Insurance. This configuration is shown in Listing 7.

Listing 7. Mule configuration which calls a webservice

<service name="travelService">
<inbound>
<vm:inbound-endpoint path="travel.insurances"/>
</inbound>
<outbound>
<chaining-router>
<outbound-endpoint
address="wsdl-cxf:http://localhost:8080/services/TravelInsuranceService?
wsdl&method=getTravelInsurance">
<custom-transformer
class="dzone.mule.custom.TravelRequestToWebserviceRequestTransformer"/>
<properties>
<spring:entry key="service"
value="{http://dzone.com/travelInsurance}TravelInsuranceServiceImplService">
</spring:entry>
<spring:entry key="port"
value="{http://dzone.com/travelInsurance}TravelInsuranceServiceImplPort">
</spring:entry>
</properties>
</outbound-endpoint>
<jms:outbound-endpoint queue="insurance.out">
<transformer ref="WSToResponseTransformer"/>
<transformer ref="ObjectToJMS"/>
</jms:outbound-endpoint>
</chaining-router>
</outbound>
</service>

In the configuration from Listing 7 you can see that we once again created a Mule service. This service listens to the internal vm endpoint “travel.insurances” to which the requests for travel insurance get routed by the router from Listing 4. When a message is received here the outbound part of this service get’s executed. In this case we’ve used a chaining-router. This router will first invoke the first outbound endpoint that is specified and will use the result of the call to this endpoint as the parameter for the next endpoint. So in this case we first call a webservice, and then sent the result from this webservice call to the specified JMS endpoint, where the website is listening on. For the webservice call we make the call based on the specified WSDL file. Besides that we also configure the service and the port from that WSDL that we’d like to invoke. The last thing we need to configure is the method which we want to call. We’ve done this as an attribute on the outbound-endpoint’s address. We won’t go into detail here how the webservice was created etc. if you’re interested in the code, see the resource section at the bottom of this article where you can download all the sources and resources of this scenario.

Well that wraps up the request part. We’ve shown you so far how we can use a content based router to determine where to send the request to. We’ve used a recipient list to send a car insurance request to two companies over two different transport in two different formats. We’ve also shown you how a the travel insurance webservice can be called, and how the response of this webservice is routed back to a JMS queue where the website is listening on. So we’ve already done a very small section of the response part, since we already routed the response from the webservice back to the queue the website is listening on.

Now let’s look at how we can implement the responses of the car insurance companies.

Implement the insurance broker with Mule: the response part

To complete the response part we only need to receive the responses from the two car insurance companies, aggregate them and send them back to the queue the website is listening on. As we’ve already mentioned the BudgetCar company works with CSV files over a file/ftp connection and the LuxuryCar uses XML over a JMS queue. So besides the aggregation we need to transform the incoming responses to java objects.

Let’s look at how the service looks which will receive the requests from these two insurance companies.

Listing 8. Mule configuration which receives requests from the budget car insurance company and from the luxury car insurance company.

<service name="ReceiveCarResponseService">
<inbound>
<jms:inbound-endpoint queue="luxurycar.receive">
<transformer ref="JMSToObject"/>
<transformer ref="XMLToInsurance"/>
<transformer ref="CorrelationTransformer" />
</jms:inbound-endpoint>
<file:inbound-endpoint path="budgetCarOut">
<file:file-to-string-transformer />
<transformer ref="CSVToResponse" />
<transformer ref="CorrelationTransformer" />
</file:inbound-endpoint>
</inbound>
<outbound>
<outbound-pass-through-router>
<vm:outbound-endpoint path="aggregator.in" />
</outbound-pass-through-router>
</outbound>
</service>

What you see in listing 8 is a service which listens on two inbound endpoints. The jms endpoint receives messages from the luxury car insurance company and the file endpoint from the budget one. For both the received messages we do a couple of transformations. For the luxury responses we first make a java object from the JMS object (remember we’ve overridden the default behaviour so we must explicitely transform from JMS to an Object), and since the object is an XML one we transform the XML to a java insurance object. For the budget messages we do the same, only this time we come from a CSV file which needs to be transformed.

The last transformation is a special one. We add this transformer since we want to correlate the messages received from the budget and the luxury company into one single message. For this we require some id that Mule can use to determine what to aggregate. Let’s look at the code for this transformer.

Listing 9. Correlation transformer which sets the Mule correlation properties

public class ResponseCorrelationTransformer 
extends AbstractMessageAwareTransformer {

@Override
public Object transform(MuleMessage message, String outputEncoding)
throws TransformerException {
message.setCorrelationGroupSize(2);
Object obj = message.getPayload();
InsuranceResponse response = (InsuranceResponse) message.getPayload();
String requestID = response.getRequestID();
message.setCorrelationId(requestID);
return message;
}
}

As you can see in Listing 9, we get the requestID from the message and set the Mule correlationID to this requestID. At the same time we also set that when two responses with this ID are received we’re done correlating. This is set through the correlationGroupSize property on the message.
So whenever a response is received this transformer will make sure that the above mentioned properties are set. Now why did we do this?

Well we did this so that we can use Mule’s standard aggregator to aggregate the messages for us. This is shown in listing 10.

Listing 10. Mule aggregator configuration

<service name="insuranceAggregator">
<inbound>
<vm:inbound-endpoint path="aggregator.in" />
<collection-aggregator-router />
</inbound>
<outbound>
<outbound-pass-through-router>
<jms:outbound-endpoint queue="insurance.out" />
</outbound-pass-through-router>
</outbound>
</service>

This small code fragment from listing 10, is the complete aggregator configuration. We tell mule to listen to the internal endpoint the responses are sent to (see listing 7). After a message is received it will be passed on to the collection-aggregator router. This standard mule component uses the two properties we set in listing 9. So when the first message is received this aggregator will check the expected size (2 in this case) and the requestID. It will then check how many messages with this ID he has already received. Since this is the first one, the aggregator will just wait. For the second message the same steps are repeated. But since we now have both our messages the aggregator puts them in a List and passes them on to the outbound router.
This outbound router simply sends the aggregated data to the configured endpoint, which is a JMS queue.

And that’s it for the response part!

Implement the insurance broker with Mule: summary

Let’s just quickly summarize what we’ve done. For the request part we received messages over JMS. Based on the type of message received we used a content based router to determine where to send the message to. If the message was a request for a travel insurance, the message was sent to the webservice of the travel agency. If the request was a car insurance request, the message got sent to two car insurance companies.
We also applied a couple of transformation steps to the messages. We’ve used a couple of Mule built-in transformers but also created a couple transformers ourselves. This was needed since our car insurance companies required the information in a different manner then we used internally.
The response from the travel webservice was sent directly back to the response queue on which the website was listening. The responses from the car insurance companies however were aggregated based on their requestID. For this we used Mule’s standard collection aggregator and used a transformer to set the correct message properties.

If you want to test this out download the sources for this article. This also contains a small JUnit test which you can use to simulate the website and trigger the integration solution. Be aware that you will need to download ActiveMQ separately to get the whole example working.

Conclusion

In this article we’ve shown how you can implement an integration design with Mule. We’ve first shown you a general description of the problem we faced, and how you can use EIPs to describe this problem in a technology independent manner. We’ve also shown how easy it is with Mule to implement this example. As you’ve seen the Mule configuration is pretty much a one-on-one copy of figure 3 and figure 4. Mule uses the same terminology as is used for the EIPs which makes implementing these scenarios easy. An additional advantage is that the Mule configuration is very readable and self-explaining.

We hope you enjoyed this first introduction into Mule 2.0 and in the pattern based integration development approach. In a couple of weeks we’ll also show how to implement this same scenario using Java Business Integration (JBI) with Apache ServiceMix.

Additional resources

1. Mule – http://mule.mulesource.org
2. Mule 2.x vs Mule 1.x - http://mule.mulesource.org/display/MULE2INTRO/Whats+New+in+Mule+2.0
3. Enterprise Integration Patterns – http://www.enterpriseintegrationpatterns.com
4. Mule 2.0.1 download – http://mule.mulesource.org/display/MULE/Download
5. Download article source code – http://www.esbinaction.com/files/dzonemule.zip

Jos Dirksen

Jos is a software architect working for Atos Origin and specializing in enterprise integration. Jos is the co-author of the upcoming Manning book “Open Source ESBs in Action” (http://www.manning.com/rademakers). He speaks frequently at Java conferences like JavaPolis, JavaZone, JavaOne and NL-JUG about Open Source enterprise integration projects like Mule, ServiceMix, jBPM and Axis2.

Tijs Rademakers

Tijs is a software architect working for Atos Origin and specializing in enterprise integration. Tijs is the co-author of the upcoming Manning book “Open Source ESBs in Action” (http://www.manning.com/rademakers). He speaks frequently at Java conferences like JavaPolis, JavaZone, JavaOne and NL-JUG about Open Source enterprise integration projects like Mule, ServiceMix, Apache Synapse and Apache Tuscany.

Legacy
Article Resources: 
Published at DZone with permission of its author, OpenSource ESB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)