mercredi 18 mars 2009

Dynamic web service invocation

The main goal of this section is to show you how to invoke a web service using the dynamic web service invocation api (DII).

There is two ways to invoke the web service dynamically :
  • Using the Jax-Rpc's dynamic call api (jax.xml.rpc.Call)
  • Using the Jax-Ws dynamic Dispatch api

First of all, you will find here under the code of a simple web service we will use in the sample :

package test;

import javax.ejb.Stateless;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;

@Stateless
@WebService(name="DynamicWs",
serviceName="DynamicWsService",
portName="DynamicWsPort",
targetNamespace="http://test"
)
public class DynamicWs {
@WebMethod(operationName="hello")
@WebResult(name="result",
targetNamespace="http://test")
public String hello( @WebParam(name="name", targetNamespace="http://test") String name)
{
return "hello " + name;
}
}

This sample exposes the hello method which receives a string in parameter and returns a string in response to the client.


Dynamic invocation using jax.xml.rpc.Call api

The client code for dynamic method invocation using the rpc Call api :

package client;

import java.rmi.RemoteException;

import javax.xml.namespace.QName;
import javax.xml.rpc.Call;
import javax.xml.rpc.ParameterMode;
import javax.xml.rpc.Service;
import javax.xml.rpc.ServiceException;
import javax.xml.rpc.ServiceFactory;
import javax.xml.rpc.encoding.XMLType;

import org.junit.Test;


public class TestDynamicWs {
private static final String END_POINT = "http://localhost:8080/DynamicWsService/DynamicWs";
private static final String TNS = "http://test";

@Test
public void testDynamicInvocation() throws ServiceException, RemoteException
{
Service service = ServiceFactory.newInstance().createService (
new QName( TNS, "DynamicWsService")
);
Call call = service.createCall ();
call.setTargetEndpointAddress ( END_POINT);
// Set call properties
call.setProperty(Call.SOAPACTION_USE_PROPERTY, new Boolean(true));
call.setProperty(Call.SOAPACTION_URI_PROPERTY, "");
call.setProperty(Call.ENCODINGSTYLE_URI_PROPERTY , "");
call.setProperty(Call.OPERATION_STYLE_PROPERTY, "rpc");

// Set the name of the operation to call
call.setOperationName( new QName( TNS, "hello"));
// Define and add the parameter value
call.addParameter(
"name", // parameter name
XMLType.XSD_STRING, // parameter XML type QName
String.class, // parameter Java type class
ParameterMode.IN); // parameter mode

// Define the type of the result returned by the method
call.setReturnType(XMLType.XSD_STRING);

// Call the method with parameters
String response = (String) call.invoke(new String[] {"franck"});

System.out.println ("Response is '" + response + "'");
}
}


Fist of all, we have to create the service using the javax.xml.rpc.ServiceFactory and instanciate the javax.xml.rpc.Call object.
This Call object will be configured using some properties :
  • Call.SOAPACTION_USE_PROPERTY : this property indicates whether or not SOAPAction is to be used. We set it to TRUE.
  • Call.OPERATION_STYLE_PROPERTY : this property is set to "rpc" to indicates that the operation will be accessed by rpc.
Once configured, we defined the operation to invoke
call.setOperationName( new QName( TNS, "hello"));


and the type of input and output parameters
        // Define and add the parameter value
call.addParameter(
"name", // parameter name
XMLType.XSD_STRING, // parameter XML type QName
String.class, // parameter Java type class
ParameterMode.IN); // parameter mode

// Define the type of the result returned by the method
call.setReturnType(XMLType.XSD_STRING);

The XMLType class contains all QName that defines the type of parameters we could use.

Now, it's time to invoke the remote method using rpc call and encoding
        // Call the method with parameters
String response = (String) call.invoke(new String[] {"franck"});


Remark : Just note that the client never recovered the wsdl file on the server !

The message that has been sent by the client code :

<env:envelope enc="http://schemas.xmlsoap.org/soap/encoding/" env="http://schemas.xmlsoap.org/soap/envelope/" ns0="http://test" xsd="http://www.w3.org/2001/XMLSchema" xsi="http://www.w3.org/2001/XMLSchema-instance">
<env:body>
<ns0:hello>
<name>franck</name>
</ns0:hello>
</env:body>
</env:envelope>


The response received by the client :

<s:envelope s="http://schemas.xmlsoap.org/soap/envelope/">
<s:body>
<helloresponse xmlns="http://test">
<result>hello franck</result>
</helloresponse>
</s:body>
</s:envelope>


Dynamic call using the jax-ws dynamic Dispatch api
In this level we will the Dispatch api of the jax-ws api to create a SOAP envelop and invoke the web service.
You will find here under the source code to invoke the hello method through the Dispatch api :

package client;

import java.net.URL;
import java.util.Iterator;

import javax.xml.namespace.QName;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPConstants;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPPart;
import javax.xml.soap.Text;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.ws.Dispatch;
import javax.xml.ws.Service;

import org.junit.Test;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class TestDynamicWs2 {
private static final String TNS = "http://test";
private static final String WSDL = "http://localhost:8080/DynamicWsService/DynamicWs?wsdl";

@Test
public void testDynamicInvocation() throws Exception
{
/* wsdl url you can get from link provided by glassfish server.*/
URL url = new URL( WSDL);

/* service name & port name as mentioned in wsdl, which can also be viewed in glassfish */
QName serviceName = new QName( TNS, "DynamicWsService");
QName portName = new QName( TNS, "DynamicWsPort");
Service service = Service.create(url, serviceName);

/** Create a Dispatch instance from a service.**/
Dispatch dispatch = service.createDispatch( portName,
SOAPMessage.class,
Service.Mode.MESSAGE
);

MessageFactory mf;
try {
mf = MessageFactory.newInstance(SOAPConstants.DEFAULT_SOAP_PROTOCOL);

// Create a message. This example works with the SOAPPART.
SOAPMessage req = mf.createMessage();
SOAPPart part = req.getSOAPPart();

// Obtain the SOAPEnvelope and header and body elements.
SOAPEnvelope env = part.getEnvelope();
SOAPHeader header = env.getHeader();
SOAPBody body = env.getBody();

// Construct the message payload.
SOAPElement operation = body.addChildElement( "hello", "ns", TNS);
SOAPElement value = operation.addChildElement( "name", "ns", TNS);
value.addTextNode( "franck");
req.saveChanges();

// Invoke the service using the soap request
SOAPMessage res = (SOAPMessage)dispatch.invoke(req);

SOAPPart soapPart = res.getSOAPPart();
NodeList list = soapPart.getElementsByTagNameNS (TNS, "result");
// Read the first node
Node node = list.item ( 0);

// Display the result
System.out.println ( "Server say : " + node.getTextContent ());

} catch (Exception ex) {
ex.printStackTrace ();
throw ex;
}

}


First of all, we have to connect to server to retrieve the wsdl file and the port associated to the service :

/* wsdl url you can get from link provided by glassfish server.*/
URL url = new URL( WSDL);

/* service name & port name as mentioned in wsdl, which can also be viewed in glassfish */
QName serviceName = new QName( TNS, "DynamicWsService");
QName portName = new QName( TNS, "DynamicWsPort");
Service service = Service.create(url, serviceName);

The wsdl file will contains the end point definition to find the right service deployed on the right server.

After we create a Dispatch object on the port and indicates that we want to deal with soap message :

/** Create a Dispatch instance from a service.**/
Dispatch dispatch = service.createDispatch( portName,
SOAPMessage.class,
Service.Mode.MESSAGE
);


It is now the time to create a soap message and fill it with invocation parameters :

MessageFactory mf = MessageFactory.newInstance(SOAPConstants.DEFAULT_SOAP_PROTOCOL);

// Create a message. This example works with the SOAPPART.
SOAPMessage req = mf.createMessage();


The api used to set attributs in the soap envelop are very similar to the DOM api used in xml parsing.

SOAPElement operation = body.addChildElement( "hello", "ns", TNS);
SOAPElement value = operation.addChildElement( "name", "ns", TNS);
value.addTextNode( "franck");
req.saveChanges(); // Save the changes made on the soap envelop


Now invoke you service :
            // Invoke the service using the soap request
SOAPMessage res = (SOAPMessage)dispatch.invoke(req);


And retrieve your result :

SOAPPart soapPart = res.getSOAPPart();
NodeList list = soapPart.getElementsByTagNameNS (TNS, "result");
// Parse the node list here...