Pages

Thursday, August 1, 2013

JAX-WS SOAP over JMS

With WebLogic 12.1.2 Oracle now also supports JAX-WS SOAP over JMS. Before 12.1.2 we had to use JAX-RPC and without any JDeveloper support. We need to use ANT to generate all the web service code. See this blogpost for all the details.

In this blogpost I will show you all the necessary JDeveloper steps to create a SOAP over JMS JAX-WS Web Service  ( Bottom up approach) and generate a Web Service Proxy client to invoke this service, plus let you know what works and what not.

We start with a simple HelloService class with a sayHello method.


After this we can start the Create Java Web Service wizard.


Select our HelloService class


Select our sayHello method


This will generated the following code.  Besides @WebService , @WebMethod and @WebParam annotation we can change the name attribute of the WebParam annotation and optional add the WebResult annotation.


Next step is to add the @JMSTransportService annotations on this HelloService class.
This will import com.oracle.webservices.api.jms.JMSTransportService class.


When we click on this annotation we modify all the JMS transport properties in the JDeveloper property window


We can also change this Web Service activation properties.


With this as result.

I will use localhost as jndiURL, when you change this to the server url ( you can't deploy it to an other server)  but then you can invoke this JMS service by generating a JAX-WS proxy client without changing anything.


Before we can deploy this Web Service to a WebLogic Domain we need to extend this domain with the WebLogic JAX-WS SOAP/JMS Extension ( or add $WebLogicHome/common/templates/wls/wls_webservice_soapjms.jar template with WLST )


This will create the following Queues and Connection Factory in its own JMS module and JMS Server


We can deploy the Web Service to the JAX-WS WebLogic Domain. 


Also retrieve the WSDL with HTTP.


The WSDL show all our JMS properties in the SOAP Binding and as one string in the soap address location attribute.


Next step is generating a JAX-WS Client and invoke this JMS service. 
Select our Web Service,  from there we can generate a new Web Service client.



Do this in a new project called JmsClient


Enable Copy WSDL into Project.


Leave the default url as it is, even when it is not valid.


Next we can change the PortClient class so it won't use HTTP to invoke this service. 


The documentation says we can do this in 3 ways ( for me only one way really works )

We can JMSTransportClient annotation to our Service class

@WebServiceClient(name = "HelloService",
                  targetNamespace = "http://jms.ws.amis.nl/",
                  wsdlLocation = "http://localhost:7101/...")

@JMSTransportClient (
 targetService = "HelloService",
 destinationName ="com.oracle.webservices.api.jms.RequestQueue",
 replyToName = "com.oracle.webservices.api.jms.ResponseQueue",
 jndiURL ="t3://devagent30:7001",
 jndiInitialContextFactory="com.oracle.webservices.api.jms.ConnectionFactory" ,
 jndiConnectionFactoryName="weblogic.jms.ConnectionFactory" ,
 timeToLive=1000,
 priority=4,
 messageType=JMSMessageType.TEXT)

public class HelloService_Service extends Service {

or as JMSTransportClientFeature in our PortClient class.

JMSTransportClientFeature feature =
  JMSTransportClientFeature.builder()
    .targetService("HelloService")
    .jndiURL("t3://devagent30:7001")
    .destinationName("com.oracle.webservices.api.jms.RequestQueue")
    .replyToName("com.oracle.webservices.api.jms.ResponseQueue")
    .destinationType(JMSDestinationType.QUEUE)
    .messageType(JMSMessageType.TEXT)
    .jndiInitialContextFactory("com.oracle.webservices.api.jms.ConnectionFactory")
    .priority(4)
    .build();

HelloService_Service helloService_Service =
  new HelloService_Service(url,new WebServiceFeature[]{feature});

But the only thing what works for me is changing the SOAP service address location url

like this

HelloService helloService = helloService_Service.getHelloServicePort();

BindingProvider bp = (BindingProvider) helloService;
bp.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY,
"jms:jndi:com.oracle.webservices.api.jms.RequestQueue"+
"?targetService=HelloService&"+
"replyToName=com.oracle.webservices.api.jms.ResponseQueue&" +
"jndiURL=t3://devagent30:7001&messageType=TEXT&"+
"deliveryMode=PERSISTENT&priority=4");

System.out.println(helloService.sayHello("hi"));

You can now invoke the JMS Service with proxy client and check the message count on the request and response queue in the WebLogic console.

Here you can look and download my test project.


4 comments:

  1. Hi Edwin,
    I see that we are not invoking the webservice with any callback. Whereas in HTTPAsync WS we used to do that by passing an Async Handler. How to do the same or similar stuff over here ? I mean I want to invoke the WS and continue other activities, and when the response comes, ( which in the http async version used to be indicated with the isDone() == true ), I will act on it.
    Thanks in advance
    Ashish/-

    ReplyDelete
    Replies
    1. Hi,

      What is your application server , cause you can use async JAX-WS , EJB and Servlet which will give you a future to lookup the results . Maybe you can check this
      https://jax-ws.java.net/2.2.6/docs/ch03.html#section-346120708971664

      Thanks

      Delete
    2. Hi,
      My App server is WLS 12.1.2. And I am developing it with Eclipse, and I had managed exactly what you have pointed out. But still the request was not able to look up the Webservice from the JMS layer. Then did a small change to the Provider end by adding targetService=. And the SOAP over JMS worked. :)
      Just one more doubt. When I had only Async version, i mean not with JMS, just async alone, my synchronous request as well as my asynchronous request was working simultaneously. But with the SOAP over JMS changes on the same webservice, Sync version stopped working. Is that expected ?

      Delete
  2. Hi,
    Is there anyway to retrieve the JMSCorrelationID or JMSMessageID in this scenario of SOAP over JMS?

    ReplyDelete