For one of my personal projects I need to expose some Spring services for a web application, but I don’t want to pollute my model and service classes with annotations, so here’s how I might use Mule ESB for this task.
First I configure my pom file with Mule’s dependencies like this:
muleforge MuleForge Repository http://repository.muleforge.org ... org.mule.modules mule-module-builders 2.2.1 org.mule.transports mule-transport-servlet 2.2.1 org.mule.modules mule-module-json 2.2 commons-collections commons-collections 3.2-osgi
I’m going to use the servlet transport and the json module. The servlet transport will handle the HTTP request and redirect those calls to my services, and the json module will perform the conversions between objects and json (called transformations in Mule’s idiom). So far, I create/update my Eclipse project files with:
mvn eclipse:eclipse
The first step is to load Mule and Spring application contexts when your web application loads. If you have already loaded your spring application context then you can skip this step. For Spring I have added a context loader listener in the web.xml file:
contextConfigLocation classpath://applicationContext.xml org.springframework.web.context.ContextLoaderListener
And for Mule I have added its context listener which automatically composes with the existing Spring’s application context:
org.mule.config mule-config.xml org.mule.config.builders.MuleXmlBuilderContextListener
Also we need to expose the servlet who is going to handle the requests (e.g. under /services/ uri path) in the web.xml file:
muleServlet org.mule.transport.servlet.MuleReceiverServlet 2 muleServlet /services/*
Then you can write a minimum empty mule-config.xml file like this:
Notice that we imported Spring and servlet schemas in the previous lines.
So far so good — you can start your web application to check if everything is working fine and check that you don’t get any errors in the console. I’m using Jetty right now, so:
mvn jetty:run
Before we start writing the Mule configuration, I will briefly show my Spring configuration which contains only one transactional service and a repository:
PROPAGATION_REQUIRED
My project Service is quite simple. It only has one method called addProject which creates a Project with startDate today and returns it:
public class ProjectService { private ProjectRepository projectRepository; protected ProjectService() { } public ProjectService(ProjectRepository projectRepository) { Validate.notNull(projectRepository, "The project repository should not be null"); this.projectRepository = projectRepository; } public Project addProject(String name) { Validate.notNull(name, "The name should not be null"); Project newProject = new Project(name, Calendar.getInstance().getTime()); this.projectRepository.save(newProject); return newProject; } }
We would like to expose that method under the services URI. Let’s start with our Mule configuration; we create a model for our Project service, configuring its path (relative to the one we configure in web.xml) and the Spring bean, which is going to receive the invocations (projectService bean). Because of how the ReflectionEntryPointResolver works, it needs an array of objects to invoke the method, but we will receive an HTTP request and also we need to sort the method’s arguments (which are unsorted in the request). So, I create a general transformer which converts and sorts the map values according to its configuration.
The MapToArrayTransformer transformer receives a Map and according to its keyOrder property it creates an Array of arguments so that method invocation can successfully be done.
Also, notice that we have already configured the response transformer that will transform the response of the method invocation to a Json object. The Object to Json transformer takes an Object (in this case a Project class specified in its sourceClass property) and returns the Json string.
You can now start your application using Jetty and try the following URL:
https://docs.mulesoft.com
In a future post, I will explain how to make this solution a little more general.