First steps with Mule 3.0
Been a while since I looked at the new versions of Mule, so took some time this weekend to look at the new version of Mule. After a rather unexpected small download of only 66 MB, getting Mule up and running has stayed as simple as it was in the previous versions. For me, that is on linux, it was just extracting the tar.gz, set MULE_HOME and run ./bin/mule. From clicking the download to having a running ESB within a minute.
So what exactly is new to Mule 3 since the previouse versions. Mulesoft provides a nice overview on their website (http://blogs.mulesoft.org/say-hello-to-mule-3/), but for me personally I especially like the following three new features:
- Deployment model: One of the biggest gripes people had with previous versions of Mule, was the lack of support for hot deployment. In this version hot deployment is now supported.
- Annotations: With annotations it's been made easier to create your own custom service components. It allows you to define part of your service component's configuration using annotations, and makes it a lot easier to define transformers.
- Flow: Another issue I've heard a lot of complaints about was that for complex message flows, the use of services with explicit inbound and outbound phases wasn't the most flexible and easy to use way of defining message flows. In Mule 3, the concept of Flows is introduces which makes working with large and complex message flows a lot easier.
Let’s look somewhat closer at these three new features.
Deployment model In older Mule versions you were forces to restart the Mule container whenever you made changes to your routing configuration. Running multiple mule instances in a distributed manner could help you in minimizing the impact of your change to a single mule configuration, but it always felt a little bit forced. With this new deployment model we can just simply create a folder in the $MULE_HOME/apps directory and add our libs and configuration file. Mule will pick up this file and startup your application. If you make changes to your config file, Mule will also see this and reload this configuration and libraries. You can also zip up the complete structure and add it to the apps directory.
You can see this when you start Mule:
INFO 2010-10-03 17:00:07,974 [WrapperListener_start_runner] org.mule.module.launcher.DeploymentService: Application directory check interval: 5000
Deploying one of Mule’s own supplied examples, is thus as easy as just copying the zip to the apps directory and done.
The required format:
/
\- classes // application-specific expanded resources (e.g. logging configuration files, properties, etc
|- lib // application-specific jars
|- mule-config.xml // Main Mule configuration file, also monitored for changes
|- mule-deploy.properties // Application deployment descriptor (optional)
|- mule-app.properties // custom properties to be added to the registry instance used by the application (optional)
No more restarting Mule and specifying the configuration you want to use. A nice addition is the “anchor” you get when an application is deployed. If you remove the anchor, Mule will cleanly remove your deployed application. So after deploying the “hello” example, I can instantly use it without having to shutdown and startup Mule.
So basically we can just keep the Mule instances running and add additional applications or flows as we please. This is also very handy when your creating your flow, you can just add the components and simply build up your integration scenario. Note though that for this you still need to make sure you as a developer think about how you deal with your classes and cleaning up of resources. Even though Mule has an excellent hot deployment model, careless use of resources, classes, statics etc. can still lead to OutOfMemory and PermGen errors.
Annotations
The best part with regards to annotations is that they make it very easy to follow the standard dependency injection patterns we’re used to from Spring and Guice. This time however we won’t inject beans into one another, but we can inject the message’s payload, message headers, attachments or by using XPath and Groovy even select part of the incoming payload to be injected. There isn’t much documentation and examples on this specific subject to be found yet, so let me show you how easy this is to do. Lets first look at how to configure a transformer:
public class MyTransformers {
@Transformer
public String reverseTransformer(String content) {
return StringUtils.reverse(content);
} }
Just by annotating this method with the @Transformer annotation will register this method automatically. Not that exciting I agree, but much better then extending some base class. Next up, some of the other annotations. First one we'll look at is the @Payload one. With this annotation you can implicitely tell Mule to transform the incoming payload to the type you've specified.
<code lang="java">
package org.smartjava.mule.tryout;
import org.mule.api.annotations.param.Payload;
public class SimpleGreeter {
public Object process(@Payload String content) {
System.out.println(content);
return content;
}
}
So when an message is received by this component, Mule will lookup a transformer to make sure the content is of type String. Oh, should you want to test this for yourself, I made the simplest Mule config to test this with:
By the way, if you don't want to use the Mule IDE, but still want to debug your flows without having to redeploy and add remote debugging. Mule still provides the old way of starting in embedded mode:
org.mule.MuleServer.main(new String[]{“-config”,”./config/mule-config.xml”});
So, on to the next one. The @function annotation. This annotation allows you to quickly inject the result from some simple functions. For example you could get the current time. On the function overview page (http://www.mulesoft.org/documentation/display/MULE3USER/Function+Annotation) you'll find a list of the possibilities. If we for instance want to get a timestamp and a counter as parameter to our component method. We can just change this method (from the previous example) to this:
<code lang="java">
public Object process(@Payload String content, @Function("date") Date date, @Function("count") Long count) {
System.out.println(content);
System.out.println(date);
System.out.println(count);
return content;
}
Note that, though I use these annotations on the component, you can just as easily use these in the transformer example I showed earlier. With the @Function annotation you can avoid having to create these helper methods for yourself, and they give you easy access to some generic functions. There are a lot of other annotations (http://www.mulesoft.org/documentation/display/MULE3USER/Annotations), I’d just like to show you one more, or better said two more: the @InboundHeaders and the @OutboundHeaders
When writing transformers or the more technical components, you often need access to message headers. You could do this by making your transformers and components aware of the mule context, or you could use these two annotations:
public Object process(@Payload String content, @InboundHeaders("*") Map headers) {
for (Object header : headers.keySet()) {
System.out.println(header + ":" + headers.get(header));
}
return content;
}
In this one, we get instant access to all the headers (because of the "*" wildcard). If we want to add some extra headers, or overwrite existing ones, we can use the @OutboundHeaders annotation.
<code lang="java">
public Object process(@Payload String content, @OutboundHeaders Map<String, Object> outHeaders) {
outHeaders.put("aNewHeaderKey", "aNewValue");
return content;
}
Well that was a short example of how to use some of the annotations provided by Mule. With these annotations it’s much easier to keep your own custom code nice and clean. So far we’ve quickly looked at how hot-deploy works in Mule, how you can use annotations.
Flow
The last item I’d like to quickly touch upon is the new flow based configuration. In the previous mule-config example, you’ve already seen an example of this. Basically what it is, is a shorthand way of wiring a set of components. It provides a basic pipeline where the output of one transport or component is used as input for the next one. Lets quickly look back at the mule-config I showed earlier:
Here you can see that we do the following things:
1. Read from a directory
2. Pass the message to our own custom component
3. Write the output to a file
Easy as that, and compared with the 'old' way of configuration very easy. To show you just what a change this is, I'll show, as a nice wrap up to this short introduction to Mule 3, the old style of configuration for the same scenario:
<code lang="xml">
<?xml version="1.0" encoding="UTF-8"?>
<mule xmlns="http://www.mulesoft.org/schema/mule/core"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:spring="http://www.springframework.org/schema/beans"
xmlns:file="http://www.mulesoft.org/schema/mule/file"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/3.0/mule.xsd
http://www.mulesoft.org/schema/mule/file http://www.mulesoft.org/schema/mule/file/3.0/mule-file.xsd">
<model name="simpeFlowModel">
<service name="fileIn">
<inbound>
<file:inbound-endpoint path="./myInputDirectory" >
<file:file-to-string-transformer/>
<file:filename-wildcard-filter pattern="*.xml"/>
</file:inbound-endpoint>
</inbound>
<component class="org.smartjava.mule.tryout.SimpleGreeter"/>
<outbound>
<pass-through-router>
<file:outbound-endpoint path="./myOutputDirectory"/>
</pass-through-router>
</outbound>
</service>
</model>
</mule>
Even in such a simple configuration you can see the advantages of using the flow model. Using the flow model (http://www.mulesoft.org/documentation/display/MULE3USER/Flows) or the patterns based approach (http://www.mulesoft.org/documentation/display/MULE3USER/Configuration+Patterns), should basically be enough for almost all scenarios. If not you can always fallback to the old service based model.
So that’s it for this short introduction into Mule 3. I personally think that with these improvements Mule has once again proved to be one of the best (if not the best) opensource ESBs. I especially like the continuing focus on making integration as easy as possible for the developer.