[[SOAP_over_JMS]] = SOAP over JMS JBoss Web Services allows communication over the _JMS_ transport. The functionality comes from Apache CXF support for the http://www.w3.org/TR/soapjms/[SOAP over Java Message Service 1.0] specification, which is aimed at a set of standards for interoperable transport of _SOAP_ messages over _JMS_. On top of Apache CXF functionalities, the JBossWS integration allows users to deploy WS archives containing both _JMS_ and _HTTP_ endpoints the same way as they do for basic _HTTP_ WS endpoints (in _war_ archives). The webservices layer of WildFly takes care of looking for _JMS_ enpdoints in the deployed archive and starts them delegating to the Apache CXF core similarly as with _HTTP_ endpoints. [[configuring-soap-over-jms]] == Configuring SOAP over JMS As per specification, the _SOAP over JMS_ transport configuration is controlled by proper elements and attributes in the `binding` and `service` elements of the WSDL contract. So a _JMS_ endpoint is usually developed using a contract-first approach. The http://cxf.apache.org/docs/soap-over-jms-10-support.html[Apache CXF documentation] covers all the details of the supported configurations. The minimum configuration implies: * setting a proper JMS URI in the `soap:address` `location` [1] * providing a JNDI connection factory name to be used for connecting to the queues [2] * setting the transport binding [3] [source,xml] ---- ...   java:/ConnectionFactory ---- Apache CXF takes care of setting up the JMS transport for endpoint implementations whose `@WebService` annotation points to a port declared for JMS transport as explained above. [NOTE] JBossWS currently supports POJO endpoints only for JMS transport use. The endpoint classes can be deployed as part of _jar_ or _war_ archives. The _web.xml_ descriptor in _war_ archives doesn't need any entry for JMS endpoints. [[examples]] == Examples [[jms-endpoint-only-deployment]] === JMS endpoint only deployment In this example we create a simple endpoint relying on _SOAP over JMS_ and deploy it as part of a jar archive. The endpoint is created using wsconsume tool from a WSDL contract such as: [source,xml] ---- java:jms/RemoteConnectionFactory org.jboss.naming.remote.client.InitialContextFactory http-remoting://myhost:8080 java:/ConnectionFactory ---- [IMPORTANT] The _HelloWorldImplPort_ here is meant for using the _testQueue_ that has to be created before deploying the endpoint. At the time of writing, _java:/ConnectionFactory_ is the default connection factory JNDI location on WildFly For allowing remote JNDI lookup of the connection factory, a specific service ( `HelloWorldService`) for remote clients is added to the WSDL. The _java:jms/RemoteConnectionFactory_ is the JNDI location of the same connection factory mentioned above, except it's exposed for remote lookup. The `soapjms:jndiInitialContextFactory` and `soap:jmsjndiURL` complete the remote connection configuration, specifying the initial context factory class to use and the JNDI registry address. [IMPORTANT] Have a look at the application server domain for finding out the configured connection factory JNDI locations. The endpoint implementation is a basic JAX-WS POJO using @WebService annotation to refer to the consumed contract: [source, java] ---- package org.jboss.test.ws.jaxws.cxf.jms;   import javax.jws.WebService;   @WebService ( portName = "HelloWorldImplPort", serviceName = "HelloWorldServiceLocal", wsdlLocation = "META-INF/wsdl/HelloWorldService.wsdl", endpointInterface = "org.jboss.test.ws.jaxws.cxf.jms.HelloWorld", targetNamespace = "http://org.jboss.ws/jaxws/cxf/jms" ) public class HelloWorldImpl implements HelloWorld { public String echo(String input) { return input; } } ---- [NOTE] The endpoint implementation references the `HelloWorldServiceLocal` wsdl service, so that the local JNDI connection factory location is used for starting the endpoint on server side. That's pretty much all. We just need to package the generated service endpoint interface, the endpoint implementation and the WSDL file in a _jar_ archive and deploy it: .... alessio@inuyasha /dati/jbossws/stack/cxf/trunk $ jar -tvf ./modules/testsuite/cxf-tests/target/test-libs/jaxws-cxf-jms-only-deployment.jar 0 Thu Jun 23 15:18:44 CEST 2011 META-INF/ 129 Thu Jun 23 15:18:42 CEST 2011 META-INF/MANIFEST.MF 0 Thu Jun 23 15:18:42 CEST 2011 org/ 0 Thu Jun 23 15:18:42 CEST 2011 org/jboss/ 0 Thu Jun 23 15:18:42 CEST 2011 org/jboss/test/ 0 Thu Jun 23 15:18:42 CEST 2011 org/jboss/test/ws/ 0 Thu Jun 23 15:18:42 CEST 2011 org/jboss/test/ws/jaxws/ 0 Thu Jun 23 15:18:42 CEST 2011 org/jboss/test/ws/jaxws/cxf/ 0 Thu Jun 23 15:18:42 CEST 2011 org/jboss/test/ws/jaxws/cxf/jms/ 313 Thu Jun 23 15:18:42 CEST 2011 org/jboss/test/ws/jaxws/cxf/jms/HelloWorld.class 1173 Thu Jun 23 15:18:42 CEST 2011 org/jboss/test/ws/jaxws/cxf/jms/HelloWorldImpl.class 0 Thu Jun 23 15:18:40 CEST 2011 META-INF/wsdl/ 3074 Thu Jun 23 15:18:40 CEST 2011 META-INF/wsdl/HelloWorldService.wsdl .... [NOTE] A dependency on `org.hornetq` module needs to be added in MANIFEST.MF when deploying to WildFly. .... Manifest-Version: 1.0 Ant-Version: Apache Ant 1.7.1 Created-By: 17.0-b16 (Sun Microsystems Inc.) Dependencies: org.hornetq .... A JAX-WS client can interact with the JMS endpoint the usual way: [source, java] ---- URL wsdlUrl = ... //start another bus to avoid affecting the one that could already be assigned to the current thread - optional but highly suggested Bus bus = BusFactory.newInstance().createBus(); BusFactory.setThreadDefaultBus(bus); try { QName serviceName = new QName("http://org.jboss.ws/jaxws/cxf/jms", "HelloWorldService"); Service service = Service.create(wsdlUrl, serviceName); HelloWorld proxy = (HelloWorld) service.getPort(new QName("http://org.jboss.ws/jaxws/cxf/jms", "HelloWorldImplPort"), HelloWorld.class); setupProxy(proxy); proxy.echo("Hi"); } finally { bus.shutdown(true); } ---- [IMPORTANT] The WSDL location URL needs to be retrieved in a custom way, depending on the client application. Given the endpoint is JMS only, there's no automatically published WSDL contract. in order for performing the remote invocation (which internally goes through remote JNDI lookup of the connection factory), the calling user credentials need to be set into the Apache CXF JMSConduit: [source, java] ---- private void setupProxy(HelloWorld proxy) { JMSConduit conduit = (JMSConduit)ClientProxy.getClient(proxy).getConduit(); JNDIConfiguration jndiConfig = conduit.getJmsConfig().getJndiConfig(); jndiConfig.setConnectionUserName("user"); jndiConfig.setConnectionPassword("password"); Properties props = conduit.getJmsConfig().getJndiTemplate().getEnvironment(); props.put(Context.SECURITY_PRINCIPAL, "user"); props.put(Context.SECURITY_CREDENTIALS, "password"); } ---- [IMPORTANT] Have a look at the WildFly domain and messaging configuration for finding out the actual security requirements. At the time of writing, a user with `guest` role is required and that's internally checked using the `other` security domain. Of course once the endpoint is exposed over JMS transport, any plain JMS client can also be used to send messages to the webservice endpoint. You can have a look at the SOAP over JMS spec details and code the client similarly to [source, java] ---- Properties env = new Properties(); env.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.remote.client.InitialContextFactory"); env.put(Context.PROVIDER_URL, "http-remoting://myhost:8080"); env.put(Context.SECURITY_PRINCIPAL, "user"); env.put(Context.SECURITY_CREDENTIALS, "password"); InitialContext context = new InitialContext(env); QueueConnectionFactory connectionFactory = (QueueConnectionFactory)context.lookup("jms/RemoteConnectionFactory"); Queue reqQueue = (Queue)context.lookup("jms/queue/test"); Queue resQueue = (Queue)context.lookup("jms/queue/test"); QueueConnection con = connectionFactory.createQueueConnection("user", "password"); QueueSession session = con.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); QueueReceiver receiver = session.createReceiver(resQueue); ResponseListener responseListener = new ResponseListener(); //a custom response listener... receiver.setMessageListener(responseListener); con.start(); TextMessage message = session.createTextMessage(reqMessage); message.setJMSReplyTo(resQueue);   //setup SOAP-over-JMS properties... message.setStringProperty("SOAPJMS_contentType", "text/xml"); message.setStringProperty("SOAPJMS_requestURI", "jms:queue:testQueue");   QueueSender sender = session.createSender(reqQueue); sender.send(message); sender.close();   ... ---- [[jms-and-http-endpoints-deployment]] === JMS and HTTP endpoints deployment In this example we create a deployment containing an endpoint that serves over both HTTP and JMS transports. We from a WSDL contract such as below (please note we've two `binding` / `portType` for the same `service`): [source,xml] ---- java:jms/RemoteConnectionFactory org.jboss.naming.remote.client.InitialContextFactory http-remoting://localhost:8080 java:/ConnectionFactory ---- The same considerations of the previous example regarding the JMS queue and JNDI connection factory still apply. + Here we can implement the endpoint in multiple ways, either with a common implementation class that's extended by the JMS and HTTP ones, or keep the two implementation classes independent and just have them implement the same service endpoint interface: [source, java] ---- package org.jboss.test.ws.jaxws.cxf.jms_http;   import javax.jws.WebService;   @WebService ( portName = "HelloWorldImplPort", serviceName = "HelloWorldServiceLocal", wsdlLocation = "WEB-INF/wsdl/HelloWorldService.wsdl", endpointInterface = "org.jboss.test.ws.jaxws.cxf.jms_http.HelloWorld", targetNamespace = "http://org.jboss.ws/jaxws/cxf/jms" ) public class HelloWorldImpl implements HelloWorld { public String echo(String input) { System.out.println("input: " + input); return input; } } ---- [source, java] ---- package org.jboss.test.ws.jaxws.cxf.jms_http;   import javax.jws.WebService;   @WebService ( portName = "HttpHelloWorldImplPort", serviceName = "HelloWorldService", wsdlLocation = "WEB-INF/wsdl/HelloWorldService.wsdl", endpointInterface = "org.jboss.test.ws.jaxws.cxf.jms_http.HelloWorld", targetNamespace = "http://org.jboss.ws/jaxws/cxf/jms" ) public class HttpHelloWorldImpl implements HelloWorld { public String echo(String input) { System.out.println("input (http): " + input); return "(http) " + input; } } ---- Both classes are packaged together the service endpoint interface and the WSDL file in a _war_ archive: .... alessio@inuyasha /dati/jbossws/stack/cxf/trunk $ jar -tvf ./modules/testsuite/cxf-spring-tests/target/test-libs/jaxws-cxf-jms-http-deployment.war 0 Thu Jun 23 15:18:44 CEST 2011 META-INF/ 129 Thu Jun 23 15:18:42 CEST 2011 META-INF/MANIFEST.MF 0 Thu Jun 23 15:18:44 CEST 2011 WEB-INF/ 569 Thu Jun 23 15:18:40 CEST 2011 WEB-INF/web.xml 0 Thu Jun 23 15:18:44 CEST 2011 WEB-INF/classes/ 0 Thu Jun 23 15:18:42 CEST 2011 WEB-INF/classes/org/ 0 Thu Jun 23 15:18:42 CEST 2011 WEB-INF/classes/org/jboss/ 0 Thu Jun 23 15:18:42 CEST 2011 WEB-INF/classes/org/jboss/test/ 0 Thu Jun 23 15:18:42 CEST 2011 WEB-INF/classes/org/jboss/test/ws/ 0 Thu Jun 23 15:18:42 CEST 2011 WEB-INF/classes/org/jboss/test/ws/jaxws/ 0 Thu Jun 23 15:18:42 CEST 2011 WEB-INF/classes/org/jboss/test/ws/jaxws/cxf/ 0 Thu Jun 23 15:18:42 CEST 2011 WEB-INF/classes/org/jboss/test/ws/jaxws/cxf/jms_http/ 318 Thu Jun 23 15:18:42 CEST 2011 WEB-INF/classes/org/jboss/test/ws/jaxws/cxf/jms_http/HelloWorld.class 1192 Thu Jun 23 15:18:42 CEST 2011 WEB-INF/classes/org/jboss/test/ws/jaxws/cxf/jms_http/HelloWorldImpl.class 1246 Thu Jun 23 15:18:42 CEST 2011 WEB-INF/classes/org/jboss/test/ws/jaxws/cxf/jms_http/HttpHelloWorldImpl.class 0 Thu Jun 23 15:18:40 CEST 2011 WEB-INF/wsdl/ 3068 Thu Jun 23 15:18:40 CEST 2011 WEB-INF/wsdl/HelloWorldService.wsdl .... A trivial web.xml descriptor is also included to trigger the HTTP endpoint publish: [source,xml] ---- EndpointServlet org.jboss.test.ws.jaxws.cxf.jms_http.HttpHelloWorldImpl EndpointServlet /* ---- [IMPORTANT] Here too the MANIFEST.MF needs to declare a dependency on _org.hornetq_ module when deploying to WildFly. Finally, the JAX-WS client can ineract with both JMS and HTTP endpoints as usual: [source, java] ---- //start another bus to avoid affecting the one that could already be assigned to current thread - optional but highly suggested Bus bus = BusFactory.newInstance().createBus(); BusFactory.setThreadDefaultBus(bus); try { QName serviceName = new QName("http://org.jboss.ws/jaxws/cxf/jms", "HelloWorldService"); Service service = Service.create(wsdlUrl, serviceName);   //JMS test HelloWorld proxy = (HelloWorld) service.getPort(new QName("http://org.jboss.ws/jaxws/cxf/jms", "HelloWorldImplPort"), HelloWorld.class); setupProxy(proxy); proxy.echo("Hi"); //HTTP test HelloWorld httpProxy = (HelloWorld) service.getPort(new QName("http://org.jboss.ws/jaxws/cxf/jms", "HttpHelloWorldImplPort"), HelloWorld.class); httpProxy.echo("Hi"); } finally { bus.shutdown(true); } ---- [[use-of-endpoint.publish-api]] === Use of Endpoint.publish() API An alternative to deploying an archive containing JMS endpoints is in starting them directly using the JAX-WS `Endpoint.publish(..)` API. That's as easy as doing: [source, java] ---- Object implementor = new HelloWorldImpl(); Endpoint ep = Endpoint.publish("jms:queue:testQueue", implementor); try { //use or let others use the endpoint } finally { ep.stop(); } ---- where `HelloWorldImpl` is a POJO endpoint implementation referencing a JMS _port_ in a given WSDL contract, as explained in the previous examples. The main difference among the deployment approach is in the direct control and responsibility over the endpoint lifecycle ( _start/publish_ and _stop_).