Enterprise Integration Zone is brought to you in partnership with:

Mitch Pronschinske is the Lead Research Analyst at DZone. Researching and compiling content for DZone's research guides is his primary job. He likes to make his own ringtones, watches cartoons/anime, enjoys card and board games, and plays the accordion. Mitch is a DZone Zone Leader and has posted 2578 posts at DZone. You can read more from them at their website. View Full User Profile

Apache CXF 2.3: The Implementation of SOAP Over JMS Specification

02.05.2011
| 18824 views |
  • submit to reddit

If you are working on building mission critical Web services and cannot afford any lost messages , leveraging the reliable messaging transport feature of the Java Message Services(JMS) may be a viable option.  This approach allows you to deploy critical Web services without requiring an additional, often complex reliable message interaction mechanism be developed.

This article was authored by Willem Jiang, a FuseSource developer and Apache CXF contributor.

 

In fact, the recent released SOAP/JMS Specification is designed expressly to allow different Web services vendors’ SOAP/JMS applications to easily communicate and the recent released CXF 2.3 provides a full implementation of this important SOAP/JMS specification.

 

This article will give an overview of how developers can use Apache CXF 2.3, or Fuse Services Framework, to build and deploy reliable Web services using SOAP over JMS. Fuse Services Framework is a productized and fully supported distribution of Apache CXF.

 

SOAP over JMS specification

 

The SOAP over JMS specification defines how SOAP binds to a messaging system that supports JMS. Binding is specified for both SOAP 1.1 and SOAP 1.2 using the same SOAP 1.2 Protocol Binding Framework used for SOAP/HTTP. It’s important to remember that this specification doesn’t define any wire format for the SOAP/JMS message. It only specifies how to define the connection to a destination, for example the JMS message headers and properties which will be used for the SOAP binding based on the JMS API.

 

Also, it is important to remember in this process is that if you want to connect to a JMS Destination, you need to use JNDI for JMS administered objects (such as the ConnectionFactory, Destinations and timeToLive properties for a MessageProducer).. In the SOAP/JMS specification, there are three kinds of properties which will be used in the SOAP over JMS binding.

 

Connecting a destination:

 

Name

Description

lookupVarant

Specifies the technique to use for looking up the given Destination name.

destinationName

Specifies the name of a Destination, for lookup as per the lookupVariant. If the variant is “jndi”, this is the Java Naming and Directory Interface (JNDI) name of the destination.

jndiConnectionFactoryName

Specifies the JNDI name of the connection factory.

jndiInitialContextFactory

Specifies the fully qualified Java class name of the InitialContextFactory to use.

jndiURL

Specifies the JNDI provider URL.

jndiContextParameter

Provides mechanism to set additional, arbitrary JNDI environment properties, other than jndiURL and jndiInitialContextFactory.

 

SOAP/JMS also defines several properties for the JMS message header.  For example:

 

Name

Description

deliveryMode

Indicates whether the requested message is PERSISTENT or NON_PERSISTENT

timeToLive

The lifetime, in milliseconds, of the request message.

priority

The JMS priority associated with the request message.

replyToName

Specifies the name of the destination to which a response message will be sent.

topicReplyToName

Specifies the name of the topic destination to which a response message will be sent.

 

And JMS message properties:

 

Name

Description

targetService

Used by the service implementation to dispatch the service request.

bindingVersion

Specifies the version of SOAP/JMS binding that is being used.

contentType

Describes the content of the SOAP message.

soapAction

As with SOAP/HTTP

isFault

This property indicates whether a SOAP/JMS message corresponds a SOAP fault.

requestURI

Specifies the JMS URI of the service.

 

The last five JMS message properties (bindingVersion, contentType, soapAction, isFault, requestURI) are used at the runtime of SOAP/JMS, allowing you to specify the other properties in the WSDL, as expressed in the below code example:

<wsdl:binding name="JMSGreeterPortBinding" type="tns:JMSGreeterPortType">
	<soap:binding style="document" transport="http://www.w3.org/2010/soapjms/" />
	<!-- You can specify the soap/jms binding properties in binding -->
	<soapjms:deliveryMode>NON_PERSISTENT</soapjms:deliveryMode>		
	<wsdl:operation name="greetMe">
		<soap:operation soapAction="test" style="document" />
		<wsdl:input name="greetMeRequest">
			<soap:body use="literal" />
		</wsdl:input>
		<wsdl:output name="greetMeResponse">
			<soap:body use="literal" />
		</wsdl:output>
	</wsdl:operation>
           ...
</wsdl:binding>

<wsdl:service name="JMSGreeterService">
<!-- You can specify the soap/jms binding properties in the service -->
	<soapjms:jndiConnectionFactoryName>ConnectionFactory</soapjms:jndiConnectionFactoryName>
	<soapjms:jndiInitialContextFactory>
		org.apache.activemq.jndi.ActiveMQInitialContextFactory
	</soapjms:jndiInitialContextFactory>
	<soapjms:jndiURL>tcp://localhost:61616</soapjms:jndiURL>
	<soapjms:priority>6</soapjms:priority>
	<soapjms:timeToLive>2000</soapjms:timeToLive>
		
	<wsdl:port binding="tns:JMSGreeterPortBinding" name="GreeterPort">
	<!-- You can specify the soap/jms binding properties in port -->
		<soapjms:replyToName>dynamicQueues/soap.jms.example.replyQueue</soapjms:replyToName-->
		<soapjms:deliveryMode>PERSISTENT</soapjms:deliveryMode>
		<soapjms:priority>4</soapjms:priority>
		<soapjms:timeToLive>30000</soapjms:timeToLive>
		<soap:address
			location="jms:jndi:dynamicQueues/soap.jms.example"/>
	</wsdl:port>
</wsdl:service>

The SOAP/JMS specification allows you to specify the various JMS properties used in  the WSDL binding , service and port. You can also specify these properties by using the JMS Uniform Resource Identifier(URI) for the port. Values specified at the service will propagate to all ports while all values specified at the binding will propagate to all ports using that binding.  For example, the jndiInitialContextFactory can be indicated for WSDL service, and in turn, it is then implied for all of the contained WSDL port elements.

If a property is specified at multiple levels within the WSDL document, the most specific setting must take precedence.  For example, URI specified in the address element’s location attribute first, and then other properties set on the port, then service, then binding. 

Before the introduction of Apache CXF 2.3, if you wanted to define a JMS endpoint address information for a service you could only specify the address in WSDL port as the Apache CXF JMS transport extension as indicated below:

<wsdl:service name="JMSGreeterService">
    <wsdl:port binding="tns:JMSGreeterPortBinding" name="GreeterPort">
         <jms:address
         	 destinationStyle="queue"
             jndiConnectionFactoryName="ConnectionFactory" 
             jndiDestinationName="dynamicQueues/test.cxf.jmstransport.queue">
             <jms:JMSNamingProperty name="java.naming.factory.initial" value="org.apache.activemq.jndi.ActiveMQInitialContextFactory"/>
             <jms:JMSNamingProperty name="java.naming.provider.url" value="tcp://localhost:61616"/>
         </jms:address>
         <jms:clientConfig useConduitIdSelector="false"/>
     </wsdl:port>
</wsdl:service>

 

JMS URI


When compared to the old JMS transport implementation of Apache CXF, the SOAP/JMS specification provides a significantly more flexible way to configure the JMS endpoint address, and the JMS URI for the port makes both publishing and consuming the SOAP/JMS service easier.

As with the previously described URI, the JMS URI start with “jms:” and  is followed by the jms-variant. and jms-dest. You can specify other properties as query parameter.

jms-uri = "jms:" jms-variant ":" jms-dest [ "?" param *( "&" param ) ]

Specification Properties and URI Representations: 

Specification Property

URI Representation

deliveryMode

as deliveryMode query parameter

destinationName

as jms-dest portion of URI syntax

lookupVariant

as jms-variant portion of the syntax

jndiConnectionFactoryName

as jndiConnectionFactoryName query parameter

jndiInitialContextFactory

as jndiInitialContextFactory query parameter

jndiURL

as jndiURL querey parameter

jndiContextParameter

as a query parameter combining the string “jndi” with the jndiContextParameter’s name attribute

replyToName

as replyToName query parameter

topicReplyToName

as topicReplyToName query parameter

priority

as priority query parameter

targetSerice

as targetService query parameter

timeToLive

as timeToLive query parameter

 

Prior to the release of Apache CXF 2.3, it was impossible for you to start the Apache CXF server by calling the JAXWS API with a single line of address without WSDL such as the http transport does.

    Object implementor = new GreeterJMSImpl();
    String address = "http://localhost:9000/cxfservice";
    Endpoint.publish(address, implementor);

With the help of JMS URI, using Apache CXF 2.3 you can set the JMS endpoint address with a single line of JMS URI:

    Object implementor = new GreeterJMSImpl();
    String address = "jms:jndi:dynamicalQueue/example.soap.jms.queue?"
+    "jndiConnectionFactoryName=ConnectionFactory"
+    "&jndiInitialContextFactory=org.apache.activemq.jndi.ActiveMQInitialContextFactory";
    Endpoint.publish(address, implementor);

Example


Now that we’ve covered some of the basics for reliable service delivery using SOAP/JMS in Apache CXF 2.3,  the following example illustrates a different way to provide or consume the Web service with or without WSDL by using the Apache CXF API or JAXWS API.

You can find the WSDL in the $EXAMPLE/wsdl directory, and we used the Apache CXF wsdl to java to generate the Service Endpoint Interface (SEI), you can find the generated file in the $EXAMPLE/target/generated directory after running the “mvn clean install” from the root.
 
If you want to run the example, you need to start the JMS broker first. Here we use ActiveMQ broker service to create the broker as so:

          BrokerService broker = new BrokerService();
     broker.setDataDirectory("target/activemq-data");
     broker.addConnector("tcp://localhost:61616");
     broker.start();
     System.out.println("JMS broker ready ...");
     Thread.sleep(125 * 60 * 1000);
     System.out.println("JMS broker exiting");
     broker.stop();
     System.exit(0);

You can use mvn -Pjms.broker to start the JMS broker.

Start the server by using “mvn -Pserver” in a new console, and start the client by using “mvn -Pclient” in another console. The server and client are created by using JAXWS API with WSDL.

Server:
   // There is a wsdlLocation annotation attribute in the GreeterJMSImplWithWSDL.class
    Object implementor = new GreeterJMSImplWithWSDL();
    String address = "jms:jndi:dynamicQueues/soap.jms.example";
    Endpoint.publish(address, implementor);

Client:
        File wsdl = new File("./wsdl/jms_greeter.wsdl");
    JMSGreeterService service = new JMSGreeterService(wsdl.toURI().toURL(), SERVICE_NAME);
    JMSGreeterPortType client = (JMSGreeterPortType)service.getPort(PORT_NAME, JMSGreeterPortType.class);
    return client;

Apache CXF 2.3 will build ServiceModel from the WSDL or the Java code. If you are using WSDL it should be relatively easy to determine if you are using the SOAP/JMS binding or not.

But what if you are using the code first developing model without WSDL?

In that situation, you can specify the transportID with “http://www.w3.org/2010/soapjms/” to make sure Apache CXF 2.3 uses the SOAP/JMS implementation if there is no WSDL information in your SEI when you use the Apache CXF API.

Server:

JaxWsServerFactoryBean factory = new JaxWsServerFactoryBean();
Object implementor = new GreeterJMSImplWithWSDL();
factory.setServiceBean(implementor);
factory.setTransportId(JMSSpecConstants.SOAP_JMS_SPECIFICATION_TRANSPORTID);
factory.setAddress(JMS_ENDPOINT_URI);
factory.create();

Client:

JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
factory.setTransportId(JMSSpecConstants.SOAP_JMS_SPECIFICATION_TRANSPORTID);
factory.setAddress(JMS_ENDPOINT_URI);
JMSGreeterPortType client = factory.create(JMSGreeterPortType.class);

If you are using the JAXWS API, CXF will chose the SOAP/JMS implementation if the address is using the JMS URI,  but be sure  to specify the transport id when you creating the port.

Server

Object implementor = new GreeterJMSImplWithoutWSDL();
String address = "jms:jndi:dynamicQueues/soap.jms.example";
Endpoint.publish(address, implementor);

Client

Service service = Service.create(SERVICE_NAME);
// Add a port to the Service with the SOAP JMS transport ID
service.addPort(PORT_NAME, JMSSpecConstants.SOAP_JMS_SPECIFICATION_TRANSPORTID, JMS_ENDPOINT_URI);
JMSGreeterPortType client = service.getPort(PORT_NAME, JMSGreeterPortType.class);

If you want to try out this out,  you need to use the “mvn -Pserver ” or “mvn -Pclient” with the profile “-Pcxf” (using CXF API without WSDL) or “-Pjaxws” (using JAXWS API without WSDL), eg “mvn -Pserver -Pcxf” or “mvn -Pclient -Pjaxws”.

I work for FuseSource, a company that offers subscriptions and professional services for Apache CXF.
For the examples in this article I am using Fuse Services Framework, a productized and supported distribution of Apache CXF.

There are some great CXF training videos
Free download of CXF
If you have any questions about this article, feel free to post them in the CXF forum
My twitter: willemjiang
My blog

Comments

Willem Jiang replied on Thu, 2011/02/10 - 7:11am

Hi Mitchell,

Could you help me update the article reference link as this

  • There are some great Fuse Service Framework training videos
  • Free download of Fuse Service Framework
  • If you have any questions about this article, feel free to post them in the Fuse Service Framework forum
  • The example can be download from here.
  • BTW, some java codes in the example section should be formatted.

    Thanks.

    Willem

    Bojan Tomic replied on Sun, 2014/07/06 - 1:55pm

    I've got to ask... Why would one  make a WS and then communicate with it through JMS? I mean, why not go for message driven EJBs instead, or something similar? Why the extra SOAP encoding/decoding? At first I thought it's because SOAP can be understood by non-Java clients, but then again, so can JMS... Or am I wrong here?

    Kai Wähner replied on Sun, 2011/03/06 - 4:45am

    You can use non-Java clients and call a JMS application. For instance, here you can find information for the Oracle WebLogic JMS .NET Client:

    http://download.oracle.com/docs/cd/E12840_01/wls/docs103/jms_dotnet/overview.html#wp1077321

    But there may also be political reasons to create a WSDL using SOAP over JMS. For instance, we had a project where all interfaces to another external company had to be definied within a WSDL. Technically this was not necessary, but it was required for governance reasons...

     

    Best regards,

    Kai Wähner (Twitter: @KaiWaehner)

    Jessie Mear replied on Wed, 2011/09/07 - 7:44am

    Among other things, it describes unambiguously JMS message headers and JMS message properties to be set for SOAP payloads, no longer leaving this for the implementation to figure out. ruby on rails

    Comment viewing options

    Select your preferred way to display the comments and click "Save settings" to activate your changes.