Apache Camel and SoapUI
Introduction
As the title indicates, this article describes a use case of Apache Camel and SoapUI. But first of all, what is Apache Camel and what is SoapUI.
Apache Camel
Jonathan Anstey described Apache Camel as “an open source Java framework that focuses on making integration easier and more accessible to developers. It does this by providing:
- concrete implementations of all the widely used Enterprise Integration Patterns (EIPs)
- connectivity to a great variety of transports and APIs
- easy to use Domain Specific Languages (DSLs) to wire EIPs and transports together”
Soap UI
SoapUI is a free and open source cross-platform functional testing solution. SoapUI allows you to easily and rapidly create and execute automated functional and compliance tests.
Use cases
Both these products have enough functionality and complexity to be main topics of various books and courses. Under this circumstances it is only possible to scratch a bit on the surface of these products within this article but we want to show a possible combination of these two products.
We want to dive into the following basic use cases of Camel within an example project:
- Define SOAP web service and publish this web service with Camel.
- Forward calls on this web services within the application.
- Consume an external web service with Camel.
The above uses cases are pretty much straight forward, nevertheless we want to put some meat on the bone. That’s why there are some more complex use cases:
- How can we test our example project? Here comes SoapUI into play.
- How can we test our project automatically?
- How can we configure Camel to react properly if something goes wrong? E.g. External web service not available.
Basic Use Cases
Define SOAP Web Service and Publish this Web Service with Camel
In Camel you will mostly define some routes in which you are routing exchanges through your application with processes and endpoints. There are various different types of endpoints and one possible endpoint type is a CXF endpoint. Such a CXF endpoint can be defined in spring like this.
<cxf:cxfEndpoint id="CXF.myProvidedWebservice" address="http://localhost:8181/webservices/myProvidedWebservice" serviceClass="com.noser.camel.cxf.webservices.myProvidedWebserviceEndpoint"> </cxf:cxfEndpoint>
This will create a CXF endpoint as a bean with the id CXF.myProvidedWebservice, the endpoint is bound to the address http://localhost:8081/webservices/myProvidedWebservice. The name of the SEI (Service Endpoint Interface) class is com.noser.camel.cxf.webservices.myProvidedWebserviceEndpoint.
Forward Calls on this Web Services within the Application
An endpoint itself does not help you too much, you want to forward the calls you are receiving on this endpoint to some process within your application. Here comes the beauty of Camel into play. You simply define a route from this CXF endpoint to some process within your application.
<camelContext id="myCamelContext" xmlns="http://camel.apache.org/schema/spring"> <route> <from uri="CXF:CXF.myProvidedWebservice" /> <to uri="log:com.noser.logging?showAll=true&level=INFO" /> <to uri="myProvidedWebserviceHandler" /> </route> </camelContext>
This Camel route in xml style would consume exchanges from the CXF endpoint and route them to a logger and then to the processor myProvidedWebserviceHandler.
In the bean myProvidedWebserviceHandler we could have a method which is doing something with the received exchange.
public class MyProvidedWebserviceHandler implements Processor { public void process(Exchange exchange) throws Exception { final MyProvidedWebserviceRequest request = (MyProvidedWebserviceRequest) exchange.getIn().getBody(); logger.info("received request with id: {}", request.getRequestId); //Do something with the request } }
We omitted the involved WSDL for the most part but in this definition, there should be a binding for our myProvidedWebserviceEndpoint.
<wsdl:portType name="<strong>myProvidedWebserviceEndpoint</strong>"> <wsdl:operation name="myProvidedWebservice"> <wsdl:input name="myProvidedWebserviceInputMessage" message="tns:myProvidedWebserviceInputMessage" /> <wsdl:output name="myProvidedWebserviceOutputMessage" message="tns:myProvidedWebserviceOutputMessage" /> </wsdl:operation> </wsdl:portType>
Consume an External Web Service with Camel
A CXF endpoint can also be configured to consume an external web service. For that we have to specify the endpointName which is the port name this service is implementing, it maps to the wsdl:port@name. We also have to specify the serviceName this maps to wsdl:service@name. And obviously the address of the web service has to match the external web service.
Such a CXF endpoint could look like this.
<cxf:cxfEndpoint id="CXF.theirProvidedService" address="$http://theirServer.com:8081/webservices/theirProvidedWebservice" endpointName="s:theirProvidedWebserviceEndpoint" serviceName="s:TheirProvidedWebservice" serviceClass="com.otherCompany.ws.theirProvidedWebserviceEndpoint" xmlns:s="http://www.otherCompany.com/WS"> </cxf:cxfEndpoint>
With the following Camel route we could send an exchange to this web service.
<camelContext id="myCamelContext" xmlns="http://camel.apache.org/schema/spring"> <route> <from uri="direct:callTheirWebService" /> <to uri="CXF:CXF.theirProvidedService" /> </route> </camelContext>
Advanced Use Cases
Now we dive a bit deeper into Camel and test our application with SoapUI.
How can we Test our Example Project?
In the Basic Use Cases we have created an application which is providing a web service and which is also consuming an external web service. At this point we could ship that application to our productive environment and pray that everything works just fine. With Camel this should obviously be the case but anyhow, sometimes you have to be sure :).
How could we test our example project? We could just write an “opposite” test-application which is calling the web service we are providing (myProvidedWebservice) and providing the web service we are consuming (theirProvidedWebservice). But this would be much overhead.
Here comes SoapUI into play. With SoapUI we can easily consume web services and create mocks which are publishing web services.
Consuming a Published Web Service with SoapUI
With a given WSDL we can create a new SoapUI project. In this project all defined operations are shown.
For such an operation (MyProvidedWebservice) a new request can be defined. SoapUI is then creating a skeleton request which contains all possible fields as defined in the WSDL.
In the request we just have to specify the URL of our provided web service as endpoint and we can start testing.
Provide a Mocked Web Service
The web service which is called from our application can easily be mocked with SoapUI. For that we have to create a new SoapUI Project with the WSDL of this web service. Again all defined operations are then shown. For every operation we need, we can create a MockService.
In the settings of the created mock web service we can specify the path, port and host of this web service. Also we can define a response when this mocked web service is called.
Now we are ready, we start this mocked web service and then configure our application to call that mocked web service and we can be ensured if the outgoing calls of our application are correct.
How can we Test our Project Automatically?
In the last chapter we saw, how we can easily execute requests over web services with SoapUI and also how to mock web services which are called by our application.
At some point we want to do testing a bit more serious and for that SoapUI provides us some tools.
The requests which are sent to web services can be executed within TestCases. In the following picture we created a TestSuite in which we have a test case “myProvidedWebServiceTestCase” there a Groovy script “Setup Request” and then a Soap request “Send Request” are executed.
Within such test case we can first prepare a request with some test vectors and then send this request. After the request was sent we can specify assertions which define the result of my test case.
This Test Suite can then be executed manually by a developer/tester or it can also be executed over a shell command and therefore easily be integrated into an existing build process.
How can we Configure Camel to React Properly if something goes Wrong?
When we are starting to test our web services with SoapUI we quickly face problems which may also occur in the real world. For example we forgot to start our mock services in SoapUI and then the external web service cannot be reached by our application. In this case we have two solutions to this problem.
- We blame it on the test and ensure that the mock services are always running.
- We reflect the situation and realize, this is a problem we have to solve within our application. We cannot be sure that an external web service is always available.
Of course we take option two.
How to handle this problem? Camel is providing so called Dead Letter Channels (DLC). The DLC lets you control behaviors including redelivery, whether to propagate the thrown Exception to the caller, and where the (failed) Exchange should now be routed to.
This can again be configured in XML.
<!-- Same Route as before but with errorHandlerRef --> <route errorHandlerRef="myDeadLetterErrorHandler"> <from uri="direct:callTheirWebService" /> <to uri="CXF:CXF.theirProvidedService" /> </route> <bean id="myDeadLetterErrorHandler" class="org.apache.camel.builder.DeadLetterChannelBuilder"> <property name="deadLetterUri" value="direct:myErrorHandlingRoute"/> <property name="redeliveryPolicy" ref="myRedeliveryPolicyConfig"/> </bean> <bean id="myRedeliveryPolicyConfig" class="org.apache.camel.processor.RedeliveryPolicy"> <property name="maximumRedeliveries" value="10"/> <property name="redeliveryDelay" value="5000"/> </bean>
In the example configuration above we have the following configuration. Camel is taking an exchange from the direct endpoint direct:callTheirWebService and tries to send it to the CXF endpoint CXF.theirProvidedService. This will be tried 11 times (One initial try and 10 redelivery tries). The delay between the redelivery tries is 5 seconds. If the redelivery policy is exhausted, the exchange is send to the Camel endpoint direct:myErrorHandlingRoute. From there on you have to define how to continue. Shall the exchange be discarded? Shall we store it as a file in your file system?
Further Configuration
Dead Letter channel is only one example how we could specify and optimize our Camel routing. Other tasks which can be delegated to Camel could be:
- Further Error handling.
- Message Validation
- Interception of messages
- Signing of messages.
- Aggregation of Messages
- Delaying of messages.
- …
Basically whenever a new requirement to your message handling arises within your system, you have two possibilities. You either create a bean and start trying to solve the problem by yourself or you start some research and hope that somebody else was facing the same challenge and most likely Camel is already providing a general solution to your problem.
Conclusion
We see that we can easily publish and call web services over Camel. The involved messages can easily be routed with Camel within our application. Also more complex requirements such as redelivery policies can be configured with Camel.
Further Reading
Camel:
http://camel.apache.org/getting-started.html
http://camel.apache.org/cxf.html
http://camel.apache.org/dead-letter-channel.html
SoapUI:
http://www.soapui.org/getting-started/your-first-soapui-project.html
http://www.soapui.org/getting-started/mock-services.html