Access external web service from within FUSE ESB with soap over jms
Acessing external web service using SOAP/JMS
This use case describes how to create an external Web service with soap/jms and access it from FUSE ESB. It demonstrates how to
- Create the external service
- Package the required artifacts into a WAR and deploy it to either Tomcat/Jetty
- Create a service unit for accessing the service in FUSE ESB
The WarehouseSoapJMS service is used to determine if Items are in Stock or awaiting the arrival of Stock.
This use case uses SOAP/JMS.

In the Logisticx demo the Warehouse Soap/JMS Service Source code will be located in "warehouse/service-warehouse-soapjms " and its deployable artifact will be called "warehouse-soapjms.war". The CXF BC Provider for accessing the Warehouse SoapJMS Service from FUSE ESB is defined in the order-route/warehouse-provider-soapjms-su service unit and it will be deployed in the "order-sa" Service assembly. The order-route project is described as part of the Content Based Routing use case.
Walkthrough
To create and access an external Web service from Fuse ESB using soap/jms you need to do the following:
- Create the external web service
- Create the servlet deployment descriptor (web.xml) file.
- Create the web service configuration file (ws-beans.xml).
- Build and deploy the war file on Tomcat.
- Create a CXF-BC provider service unit to setup access to the external service.
1. Create the external web service
We are not going to go into detail here about creating the actual web service as this has been described earlier.
The full implementation of the external warehouse soap/jms is in the Logisticx warehouse module in the directory service-warehouse-soapjms.
This External Web service implements the same SEI and inherits its base implementation from the Service-Warehouse Web service SOAP/HTTP. The only difference is that all messages will be sent SOAP/JMS.
The SEI defined in service-warehouse module and the sub implementation class defined in service-warehouse-soapjms is setup as follows:
Example 1.0: Warehouse SEI
@WebService( targetNamespace = "http://logisticx.demo.fuse.iona.com/warehouseService/") public interface Warehouse { @WebMethod public abstract OrderStatus inStock(Order order); @WebMethod public abstract void updateStock(Order order); }
The implementation of the Soap/JMS service will be
Example 1.1: WarehouseSoapJMSImpl class
@WebService( serviceName = "WarehouseServiceSoapJMS", targetNamespace = "http://logisticx.demo.fuse.iona.com/warehouseService/", portName = "WarehouseEndpointSoapJMS", endpointInterface="com.iona.fuse.demo.logisticx.service.warehouse.Warehouse" ) public class WarehouseSoapJMSImpl extends WarehouseBaseImpl { public void WarehouseSoapJMSImpl() { } }
The CXF and ActiveMQ jars need to be added into the container classpath. Add these dependencies to your pom.xml file.
To use JMS as a transport, your endpoints need the information needed to connect to the JMS broker and the JMS destinations. This information can be provided by either adding a WSDL port element to the endpoint's WSDL document or by adding the JMS configuration into the endpoint's configuration document.
JMS endpoints need to know certain basic information about how to establish a connection to the proper destination.
There is a complete description of these options described in the FUSE Services Framework documentation - Using the Jms Transport.
Example 1.2: warehouse-soapJjms.wsdl file
.... <wsdl:service name="WarehouseServiceSoapJMS"> <wsdl:port name="WarehouseEndpointSoapJMS" binding="tns:WarehouseServiceSoapBinding"> <jms:address destinationStyle="queue" jndiConnectionFactoryName="ConnectionFactory" jndiDestinationName="dynamicQueues/warehouseDestination" jndiReplyDestinationName="dynamicQueues/warehouseReplyDestination"> <jms:JMSNamingProperty name="java.naming.factory.initial" value="org.apache.activemq.jndi.ActiveMQInitialContextFactory" /> <jms:JMSNamingProperty name="java.naming.provider.url" value="tcp://localhost:61677" /> </jms:address> </wsdl:port> </wsdl:service> ....
The JMS destination information is provided using the jms:address element and its child, the jms:JMSNamingProperties element. The jms:address element's attributes specify the information needed to identify the JMSbroker and the destination. The jms:JMSNamingProperties element specifies the Java properties used to connect to the JNDIservice.
The address element
The basic configuration for a JMS endpoint is done by using a jms:address element as the child of your service's port element. The jms:address element uses the attributes described in Table 2.1 to configure the connection to the JMS broker.
Table 2.1. JMS Endpoint Attributes
| destinationStyle jndiConnectionFactoryName |
Specifies if the JMS destination is a JMS queue or a JMS topic. Specifies the JNDI name bound to the JMS connection factory to use when connecting to the JMS destination. |
| jndiDestinationName | Specifies the JNDI name bound to the JMS destination to which requests are sent. |
| jndiReplyDestinationName | Specifies the JNDI name bound to the JMS destinations where replies are sent. This attribute allows you to use a user defined destination for replies. For more details see Using a named reply destination. |
| connectionUserName | Specifies the user name to use when connecting to a JMS broker. |
| connectionPassword | Specifies the password to use when connecting to a JMS broker. |
The JMS Naming Properties element
To increase interoperability with JMS and JNDI providers, the jms:address element has a child element,jms:JMSNamingProperties, that allows you to specify the values used to populate the properties used when connecting to the JNDI provider. The jms:JMSNamingProperties element has two attributes:name and value.name specifies the name of the property to set. value attribute specifies the value for the specified property. jms:JMSNamingProperties element can also be used for specification of provider specific properties.
By default, FUSE Services Framework endpoints using JMS create a temporary queue for sending replies back and forth. You can change this behavior by setting the jndiReplyDestinationName attribute in the endpoint's contract. A client endpoint will listen for replies on the specified destination and it will specify the value of the attribute in the ReplyTo field of all outgoing requests. A service endpoint will use the value of the jndiReplyDestinationName attribute as the location for placing replies if there is no destination specified in the request's ReplyTo field.
2. Create the servlet deployment descriptor (web.xml) file.
The deployment descriptor below will need modification depending on the web service stack that you will be using. We are using CXF, which offers two options for service provider configuration: a standard configuration file named "cxf-servlet.xml" that loads all internal CXF modules and another option for explicitly listing the CXF modules you wish to load. Note with the latter option you can name the file whatever you wish but to keep things simple we will keep the file named web.xml. Each stack uses its own servlet to handle the web service requests.
As we are using Maven save the web.xml file to service-warehouse-soapjms/src/main/webapp/WEB-INF(Maven) directory of your project.
Example 1.3 web.xml for the warehouse service (soap/jms)
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.4" mlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <display-name>Spring Warehouse</display-name> <description>Spring Warehouse sample application</description> <!-- - Loads the root application context of this web app at startup, - by default from "/WEB-INF/applicationContext.xml". - Note that you need to fall back to Spring's ContextLoaderServlet for - J2EE servers that do not follow the Servlet 2.4 initialization order. - - Use WebApplicationContextUtils.getWebApplicationContext(servletContext) - to access it anywhere in the web application, outside of the framework. - - The root context is the parent of all servlet-specific contexts. - This means that its beans are automatically available in these child contexts, - both for getBean(name) calls and (external) bean references. --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>WEB-INF/ws-beans.xml</param-value> </context-param> <!-- - A web app can contain any number of such servlets. - Note that this web app has a shared root application context, serving as parent - of all DispatcherServlet contexts. --> <servlet> <servlet-name>CXFServlet</servlet-name> <display-name>CXFServlet</display-name> <description>Apache CXF Endpoint</description> <servlet-class> org.apache.cxf.transport.servlet.CXFServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>CXFServlet</servlet-name> <url-pattern>/services/*</url-pattern> </servlet-mapping> </web-app>
3. Create the web service configuration file (ws-beans.xml).
The configuration file will be different depending on what framework you will use. Here we are using CXF. The configuration files work in conjunction with (and take precedence over) the JSR-181 annotations on the SIB for configuration of the web service provider.
CXF uses a Spring-based configuration file. Copy the following ws-beans.xml file to your service-warehouse-soapjms/src/main/webapp/WEB-INF directory for Maven.
Example 1.4 wsbeans.xml for the warehouse service (soap/jms)
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxws="http://cxf.apache.org/jaxws" xmlns:jms="http://cxf.apache.org/transports/jms" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd http://cxf.apache.org/transports/jms http://cxf.apache.org/schemas/configuration/jms.xsd"> <!-- List of individual modules of CXF runtime being loaded address="http://localhost:8199/warehouse/services/warehouse" --> <import resource="classpath:META-INF/cxf/cxf.xml" /> <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" /> <import resource="classpath:META-INF/cxf/cxf-extension-jms.xml" /> <import resource="classpath:META-INF/cxf/cxf-servlet.xml" /> <jaxws:endpoint id="WarehouseEndpointPointSoapJMS" implementor="#WarehouseSoapJMSImpl" address="jms://dummy" wsdlLocation="classpath:warehouse-soapjms.wsdl" endpointName="ware:WarehouseEndpointSoapJMS" serviceName="ware:WarehouseServiceSoapJMS" xmlns:ware="http://logisticx.demo.fuse.iona.com/warehouseService/"/> <bean id="WarehouseSoapJMSImpl" class="com.iona.fuse.demo.logisticx.service.warehouse.WarehouseSoapJMSImpl"/> </beans>
For the Service creation to find the WSDL for the WarehouseSoapJMS service it needs to be copied to service-warehouse-soapjms/src/main/webapp/WEB-INF/classes so that its on the classpath.
4. Build and deploy the war file on Tomcat.
There are a lot of articles and tutorials about setting up tomcat so we are not going to describe that here.
Example : Glen Mazza's blog has a very good description of the steps you need to take to get Tomcat up and running.
You have two options - you can do "mvn install" and copy the war file to the Webapps directory of tomcat or setup the tomcat-maven-plugin in your pom.xml and run "mvn tomcat:deploy". This command will first compile the service classes and package them into a WAR file before deploying.
Ensure that the JMS Broker is running before starting Tomcat (There is an embedded JMS Broker started as part of deploying and starting the Order Processing Assembly to FUSE ESB).
If you are getting any error messages, check Tomcat's server logs in the $CATALINA_HOME/logs directory for more detailed information--the timestamps of the files will let you know which log files are being updated.
5. Create a CXF-BC provider service unit to setup access to the external service.
Take a look at the Content Based Router use case to setup the order-route project that this service unit will be part of.
After you have the order-route project setup - run the following maven archetype command from LOGISTICX/order-route directory.
Example 1.5 Maven Archetype command
DEMO_LOCATION\> mvn archetype:create -DarchetypeGroupId=org.apache.servicemix.tooling -DarchetypeArtifactId=servicemix-cxf-bc-service-unit -DarchetypeVersion=3.3.1.5-fuse -DgroupId=com.iona.fuse.demo.logisticx.order.warehouse -DartifactId=warehouse-provider-soapjms-su
This creates a directory called warehouse-provider-soapjms-su that is taken from the artifactId parameter.
It generates
- xbean.xml configuration file
- Same wsdl file
- POM File
The first three parameters to the mvn command identify which Maven archetype to use for the archetype:create goal, while the groupId and artifactId parameters identify the Maven project that is being generated.
For the Order Processing service to send a request to the external Soap/Jms warehouse service it needs to create a CXF-BC service unit and set the cxf-bc provider in the xbean.xml file. You also need to provide the generated wsdl file in the resources directory with the xbean.xml file so that it will be on the classpath.
Renaming the Project
You need to give the project a unique name as follows:
In the warehouse-provider-soaphttp-su directory.
1. Open the pom.xml in a text editor.
2. Change the <name> attribute to <name>Soap/JMS Warehouse Processor Service Unit</name>.
Because we ran Maven's archetype:create from the directory containing the top level pom.xml, Maven has used the groupId from the parent pom.xml for our new service unit and added a module to the parent pom.xml.
Example 1.6: Module definition
<modules> <module>order-camel-cbr-su</module> <module>warehouse-provider-soapjms-su</module> </modules>
Updating the configuration
The configuration for the warehouse-provider-soapjms-su will be added to the generated xbean.xml file.
LOGISTICX_DIR/order-route/warehouse-provider-soapjms-su/src/main/resources/xbean.xml
Change the xbean.xml file to the following
Example 1.7: xbean.xml file
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:cxfbc="http://servicemix.apache.org/cxfbc/1.0" xmlns:ware="http://logisticx.demo.fuse.iona.com/warehouseService/"> <!-- Accesses the external soap/jms warehouse service --> <cxfbc:provider service="ware:WarehouseServiceSoapJMS" endpoint="WarehouseEndpointSoapJMS" useJBIWrapper="false" wsdl="classpath:warehouse-soapjms.wsdl" interfaceName="ware:Warehouse" busCfg="./spring-jms.xml"> <cxfbc:inInterceptors> <bean class="org.apache.cxf.interceptor.LoggingInInterceptor"/> </cxfbc:inInterceptors> <cxfbc:outInterceptors> <bean class="org.apache.cxf.interceptor.LoggingOutInterceptor"/> </cxfbc:outInterceptors> <cxfbc:inFaultInterceptors> <bean class="org.apache.cxf.interceptor.LoggingInInterceptor"/> </cxfbc:inFaultInterceptors> <cxfbc:outFaultInterceptors> <bean class="org.apache.cxf.interceptor.LoggingOutInterceptor"/> </cxfbc:outFaultInterceptors> </cxfbc:provider> </beans>
You will note here that we have an attribute "busCfg" - this allows us to specify extra configuration.
Place this file in the resources directory along with the wsdl file.
Example 1.8: spring-jms.xml file
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jms="http://cxf.apache.org/transports/jms" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd http://cxf.apache.org/transports/jms http://cxf.apache.org/schemas/configuration/jms.xsd"> <!-- Adds a timeout value --> <jms:conduit name="{http://logisticx.demo.fuse.iona.com/warehouseService/}WarehouseEndpointSoapJMS.jms-conduit"> <jms:clientConfig clientReceiveTimeout="30000" messageTimeToLive="30000" /> </jms:conduit> </beans>
The clientReceiveTimeout specifies the amount of time, in milliseconds, that the endpoint will wait for a response before it times out and issues an exception. The default value is 2000.
Update Order-SA Service Assembly
This service unit will be part of the order-route-sa assembly.
You will need to add the following dependency to the order-sa/POM File
Example 1.7 Order-SA POM File
.... <dependency> <groupId>com.iona.fuse.demo.logisticx.order.warehouse</groupId> <artifactId>warehouse-provider-soapjms-su</artifactId> <version>${logisticx-version}</version> </dependency> ....
This service will get invoked from the "cbrService" of the Content Based Router if a GPS Product is ordered. It is part of the order-sa Service Assembly.
