Basic authentication in Camel

I need to call a webservice that uses basic authentication. All the solutions and suggestions I could find via google where ether complicated or a lot of work. Until I looked at the CXF manual (http://camel.apache.org/cxf.html). As of version 2.12.3, you can do it by simply adding the username and password to your endpoint in your camel route.

.to("cxf:bean:myCxfEndpoint?username=<username>&password=<password>")
Advertisements

Calling a Soap Service with camel

Invoking a SOAP service in Fuse (camel) is similar to exposing a SOAP service. The first steps are the same, you have to create a CXF endpoint. This is explained in full in this blogpost.

You first generate the source classes based on the WSDL. After that you register it as a CXF endpoint in (in my case) the blueprint file of your project. The main difference is that you need to specify the address / endpoint of service.

<cxf:cxfEndpoint id="newOrderEndpoint"  
address="http://localhost:8088/mockNewOrder" 
serviceClass="nl.rubix.service.neworder.NewOrderServiceOperationsPortType"/>

Calling the service from your route can be done by simply setting the CXF endpoint in your “to” statement.

.to("cxf:bean:theRefToYourCXFBean")

If you would now call the service, CXF would choose the first operation in the WDSL. So we have to tell CXF which operation it needs to call. This is done by setting the header “OperationName”. This header will tell CXF to call the operation you want.

.setHeader("OperationName", simple("getTheSpecialOrder"))

The last part is creating the body for the request message. One way to do this is in a processor. In my case the code looks like this:

ObjectFactory objFac = new ObjectFactory();
GetTheSpecialOrderRequest requestMsg = objFac.createGetTheSpecialOrderRequest();
requestMsg.setOrderNumber(BigInteger.valueOf(123));
exchange.getOut().setBody(requestMsg);		

If everything is set correctly you should be able to call the service. In order to test it properly I created a mockserver in SoapUI. Which is included in the sources of this project (project sources).

Fuse contract first webservice

In this blog post I will explain how you can create a SOAP web service in Fuse. You can implement a SOAP interface with the help of CXF. Creating a soap web service with CXF can be done in two ways, contract first or code based. Code based means that the contract is based on the java code. Contract first means that you first specify the contract (WSDL, XSD) and base the implementation on that contract. Because contract first gives you complete control over the interface contract I think it is the best way to go.

In this example I have chosen to use blueprint in combination with the java DSL.

So the first thing, and in my opinion the hardest part, is creating the WSDL. You can find mine and the sources of the project (and the WSDL) here.

The next thing you need to do is generate the code based on the WSDL. This is done with mvn. By adding the following snippets to your pom.xml file mvn will generate the java code that represents the WSDL.

First add the CXF dependency:

<dependency>
    <groupId>org.apache.camel</groupId>
     <artifactId>camel-cxf</artifactId>
     <version><your camel version></version>
</dependency>

Add the plugin that generates the java source code for the contract. Make sure it points to the location of your WSDL and that the WSDL is inside the package:

<plugin>
	<groupId>org.apache.cxf</groupId>
	<artifactId>cxf-codegen-plugin</artifactId>
	<version>3.0.4.redhat-620133</version>
	<executions>
		<execution>
			<id>generate-sources</id>
			<phase>generate-sources</phase>
			<configuration>
				<sourceRoot>target/generated/src/main/java</sourceRoot>
				<wsdlOptions>
					<wsdlOption>
						<wsdl>${project.basedir}/src/main/resources/wsdl/newOrder.wsdl</wsdl>
						<extraargs>
							<extraarg>-impl</extraarg>
						</extraargs>
					</wsdlOption>
				</wsdlOptions>
				<fork>true</fork>
				<additionalJvmArgs>-Djavax.xml.accessExternalSchema=jar:file,file</additionalJvmArgs>
			</configuration>
			<goals>
				<goal>wsdl2java</goal>
			</goals>
		</execution>
	</executions>
</plugin>

After these changes are made you have to run “mvn generate-sources” in order to generate the sources. After this is done, you will probably need to update your project inside your IDE. Otherwise you might not see your generated sources in the project structure.

After the sources have been generated you can add the endpoint to the blueprint of your project. Do not forget to add the CXF namespace. By specifying the endpoint inside your blueprint you can later use it inside your route.

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:cxf="http://camel.apache.org/schema/blueprint/cxf"
xmlns:camel="http://camel.apache.org/schema/blueprint"
xsi:schemaLocation="
                http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd
                http://camel.apache.org/schema/blueprint/cxf http://camel.apache.org/schema/blueprint/cxf/camel-cxf.xsd
                http://camel.apache.org/schema/blueprint http://camel.apache.org/schema/blueprint/camel-blueprint.xsd"
<cxf:cxfEndpoint id="newOrderEndpoint"  address="/newOrder/"  serviceClass="nl.rubix.service.neworder.NewOrderServiceOperationsPortType">

Now you are ready to create your endpoint in the camel route. Add the following statement to your route (in my case the java dsl), it should look like this:

from("cxf:bean:newOrderEndpoint")

If you want to see the results of your work you could deploy the bundle to your server. After this is done you should be able to see the service (and the contract) listed at http://localhost:8181/cxf.

Now that the configuration things are done we can actually start implementing the service. The contract in this example specifies two operations with, for the case of simplicity, the same response (xsd element). So we need to change the route so that it can process the two operations.

One way of doing this is by using the recipientList. This allows you to dynamically call another route. If you base the recipientList on the operationName header camel will send the message to the corresponding route.   All the HTTP / SOAP headers are accessible in the route so it is quite straightforward.

.recipientList(simple("direct:${header.operationName}"));
        
from("direct:getOrder")
.log("operation: getTheOrder")
.processRef("contractFirstProcessor");

from("direct:getTheSpecialOrder")
.log("operation: GetTheSpecialOrder")
.processRef("getTheSpecialOrderProcessor");

Each operation in a WSDL normally has a different implementation behind it. Within Fuse I solved it by creating sub routes and for each operation a separate processor. By adding the subroutes you can create specific orchestration per operation. For example call a different database procedure or underlying web service.

The final piece of the puzzle is of course the transformation of the input message and generating the output message. In this case I made a stub that maps part of the input message to the output message. This is done in the processors.

Accessing the input message in the processor can be done in one statement. The generated code contains the class that specifies the input message of the operation. In this case the GetOrderRequest.class. The only thing that you need to do is casting it to the right class. This can be done with the following statement:

GetOrderRequest input = exchange.getIn().getBody(GetOrderRequest.class);

In order to create the output message you can also use the classes generated by CXF. If everything has been generated successfully you should have an ObjectFactory class with which you can generate the output. I mapped the input to the output (OrderNumber) and made sure the generated XML was conform the contract specifications.

ObjectFactory objFac = new ObjectFactory();
GetOrderResponse outbody = objFac.createGetOrderResponse();
outbody.setOrderNumber(input.getOrderNumber());
CustomerType newCustomer = new CustomerType();
newCustomer.setName("Dirk");
newCustomer.setLastname("Janssen");
outbody.setCustomer(newCustomer);
OrderType order = new OrderType();
order.setPrice(12);
order.setProductName("TV");
OrderListType newOrderList = new OrderListType();
newOrderList.getOrder().add(order);

outbody.setOrderList(newOrderList);

After this is you only have to pass the output message back to CXF:

exchange.getOut().setBody(outbody);

Generate a similar processor for the other operation and you are done. Deploy the service to the server and test it (for example, with soapUI).

Read More »