Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3

Better JMS Transport for CXF Webservice using Apache Camel

Excerpt

Configuring JMS in Apache CXF before Version 2.1.3 is possible but not really easy or nice. This article shows how to use Apache Camel to provide a better JMS Transport for CXF.

The original post is from Christian Schneider´s blog and can be found here

Why not simply use JMS Features of Apache CXF

JMS configuration in Apache CXF is possible but the configuration is not very flexible and quite error prone. In CXF you have to configure a JMSConduit or a JMSDestination for each webservice. The connection between Conduit and the Service proxy is the endpoint name which looks like "{http://service.test\Image Removed}HelloWorldPort.jms-conduit". As this name is never explicitly configured elsewhere it is quite probable that you misspell the name. If this happens then the JMS Transport just does not work. There is no good error reporting to show you what you did wrong. The next thing is that you have to use JNDI for the connectionFactory and the queue name. This is something that beginners with JMS have many problems with.

Why is using Apache Camel better in the JMS Transport layer

Update: Since CXF 2.1.3 there is a new way of configuring JMS (Using the JMSConfigFeature). It makes JMS config for CXF as easy as with Camel. Using Camel for JMS is still a good idea if you want to use the rich feature of Camel for routing and other Integration Scenarios that CXF does not support.

You can find the original announcement for this Tutorial and some additional info on Christian Schneider´s BlogIn apache camel you can simply reference the ConnectionFactory as a spring bean. This means you can either define it directly in a spring bean what is the ideal way to start or you can use spring´s JNDI binding to retrieve it from your application server for production use.
The next nice thing is that you can configure all JMS options like Receive Timeout or username / password in a central location, the JMSComponent and then share this config for several services. On the other hand you can easily configure different JMS providers.
The last thing that I do not need right now but is nice to have is that you have the full power of Camel´s routing config. So if you want to do additional things from simply calling the service it is easy.

So how to connect Apache Camel and CXF

The best way to connect Camel and CXF is using the Camel transport for CXF. This is a camel module that registers with camel cxf as a new transport. It is quite easy to configure.

Code Block
java
java
<bean class="org.apache.camel.component.cxf.transport.CamelTransportFactory">
  <property name="bus" ref="cxf" />
  <property name="camelContext" ref="camelContext" />
  <property name="transportIds">
    <list>
      <value>http://cxf.apache.org/transports/camel</value>
    </list>
  </property>
</bean>

That is all. This bean registers with cxf CXF and provides a new transport prefix camel:// that can be used in CXF address configurations. The bean references a bean cxf . You do not have to explicitly configure this bean as cxf does this on it´s own. The other thing the CamelTransportFactory needs is a reference to a which will be already present in your config. The other refrenceis a camel context. We will later define this bean to provide the routing config.

How is JMS configured in Camel

In camel you need two things to configure JMS. A ConnectionFactory and a JMSComponent. The connectionFactory is just As ConnectionFactory you can simply set up the normal Factory your JMS provider offers or it can bind a JNDI connectionFactoryConnectionFactory. In this example we use the ConnectionFactory provided by ActiveMQ. The connectionFactory is the place to set username and password.

Code Block
java
java
<bean id="jmsConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
  <property name="brokerURL" value="tcp://localhost:61616" />
</bean>

Then we set up the JMSComponent. It offers a new tranport transport prefix to camel that we simply call jms. If we need several JMSComponents we can differentiate them by their name.

...

You can find more details about the JMSComponent at http://activemq.apache.org/camel/jms.htmlthe Camel Wiki. For example you find the complete configuration options and a JNDI sample there.

Setting up the CXF client

We will configure a simple CXF webservice client. It will use stub code generated from a wsdl. I will show how to do the stub generation later. The webservice client will not be configured to use JMS directly instead we let it communicate on a camel direct endpoint that will later be routed to a JMS queue. You can also use a direct: Endpoint and do the routing to JMS in the Camel Context.

Code Block
java
java
<client id="CustomerService" xmlns="http://cxf.apache.org/jaxws" xmlns:customer="http://customerservice.example.com/"
  serviceName="customer:CustomerServiceService"
  endpointName="customer:CustomerServiceEndpoint"
  address="camel:jms://directqueue:CustomerService"
  serviceClass="com.example.customerservice.CustomerService">
</client>

We explicitly configure serviceName and endpointName so they are not read from the wsdl. The names we use are arbitrary and have no further function but we set them to look nice. The serviceclass points to the service interface that was generated from the wsdl. Now the important thing is address. Here we tell cxf to use the camel transport and register a direct endpoint named CustomerService.

Routing the client to the jms queue

, use the JmsComponent who registered the prefix "jms" and use the queue "CustomerService".

Setting up the CamelContext

As we do not need additional routing an empty CamelContext bean will sufficeThe last step we have to do is routing Exchanges the cxf client sends to the direct endpoint from there to a JMS queue or topic. This is done in the camelContext bean.

Code Block
java
java
<camelContext id="camelContext" xmlns="http://activemq.apache.org/camel/schema/spring">
  <route>
    <from uri="direct:CustomerService" />
    <to uri="jms:queue:CustomerService" />
  </route>
</camelContext>

You can make it even easier

If you only want to attach your cxf service to a jms queue and do not need the routing capabilities of Apache Camel you can use a Camel jms endpoint instead of a camel direct endpoint:

Code Block
xmlxml
address="camel:jms:queue:CustomerService"

Running the Example

  • Follow the readme.txt

Conclusion

As you have seen in this example you can use Camel to dramatically ease JMS configuration compared with CXFs native JMS Transport. For me this is of course only a workaround. We should work on the native JMS config for CXF to make it at least as good as Camel´s. As CXF and Camel are two projects that have very good connections between them it is perhaps even possible to have a common transport layer for CXF and camel.

When I announced this Howto on the camel mailing list James Strachan told me that it is also possible to make your webservices transactional by using Spring declarative transactions. I will write a spearate article that focuses on how to do this.

Thanks

Many thanks to Eduard Hildebrandt http://www.family-hildebrandt.com who helped a lot in making this example work by debugging all the problems we initially had and providing patches for the issuesconnect services to JMS easily while being able to also use the rich integration features of Apache Camel.