środa, 13 maja 2015

Web service in Mule 3.6 (producer)

I wouldn't call it a tutorial. This "article" depicts my first attempt to produce a web service using Mule 3.6. Probably (well, for sure) there is a better, more efficient approach, but this one works and for my purposes is sufficient. In next part, I'll show how to consume it.

We will start with WSDL file. I prefer contract first apporach, therefore in the very first step I have prepared WSDL contract.

Now, lets start Mule with a new project - I have called mine "SimpleWS". Nothing special to be set, just use default options. After project and its structure is created, copy hasher.wsdl to src/main/api:

First, we have to define HTTP connector and its configuration. By simply dragging HTTP icon from toolbox (?) we will create a new flow (assuming that there has been no flow yet):

Now configuration. Click on HTTP connector and in its General properties, General Settings tab, add new Connector Configuration:

First - give it a moreless meaningful name, then define a port that will be used by this connector. Next, in Basic Settings > Path enter the path to your WS:

It's high time we created a web service. Mule documentation encourages to use CXF component, so let it be:

CXF component requires a bit more effort to configure it. At the beginning add new CXF configuration (General > Generic> Config Reference). I personally don't find it necessary to change anything, default values are ok.
Next we choose the purpose of this component (General > Generic > Operation). In this case you should choose  "JAX-WS service".
Since we are applying contract-first apporach, we can now choose a WSDL file and generate java classes for our service:

In dialog window, enter a path to WSDL file and a package for java classes that will be generated from definition file:

After confirmation, you can see that a Service Class has been chosen automagically (in fact this is an interface, but that is not relevant at this momemnt).
We could run our application now, and call a service,  but we would end up with an error:

org.apache.cxf.interceptor.Fault: Marshalling Error: Instance of "pl.edu.pwr.isi.hasher.HashRequest" is substituting "pl.edu.pwr.isi.hasher.HashResponse"

Why did it happen? Well, after http request reaches HTTP connector it's being transformed and send to CXF component, which performs its magic, and in the end we have a payload set to an instance of a HashRequest class . Payload is not being changed and is treated as a response, which should be a HashResponse object (and that's the reason for substitution issue)

We should handle somehow this request and prepare a response. 
I have chosen a Spring bean for that purpose :)

In your first window choose "Global Elements" and create a new bean: (Beans > Bean):

We have to provide a bean definition (and add a new class implementing HashPort interface):

The only thing left is to call our newly defined bean. Add an Expression component to our flow:

and enter proper expression for this component:

payload = app.registry['requestHandler'].handleRequest(payload);

And that's all... What is happening in our flow?
  • ws client calls our service - HTTP connector receives the request and forwards it to CXF component
  • CXF analyses request and based on which operation is called, transforms request data into proper java object, which becomes now the payload in the flow
  • Expression component calls a method handleRequest with a payload as its parameter. Thanks to method overloading we can have one expression that could handle all possible operations.
  • Bean "requestHandler" creates a proper response object, which becomes now the payload in the flow.
  • CXF takes a response object and transforms it into a response data that are being sent back to client
If you don't know how to call this web service - use SoapUI. Create a new SOAP project, with WSDL at this location: http://localhost:8888/ws/hasher?wsdl. 

Whole project is here.