Friday, October 22, 2010

Calling Web Services Dynamically

In normal SOA architecture projects, we have static web services which get consumed by clients but in some cases ( especially for Orchestration) we may need to call web services dynamically through client.

There are several ways available to consume services dynamically and you may get lots of material on Google for these methods. I am collecting some of the ways I have tried out, for reference purpose and will let you know pros and cons of each of them.

Following are the ways to call web services dynamically
1) Call through a Java client
2) Call through a PL/SQL procedure
3) Call through a BPEL process.

1) Call through a Java Client: As I am from Java back ground, I used it as my first choice.
With use of javax.xml and javax.jws package, one can invoke web services.
Advantage of calling web services through Java client is, it is very easy to maintain code. Exception handling is also very rich. Formatting and altering xml input and output data is also quite easy.
Limitations/issues with this approach are, though it supports all types of protocols but it require to write code separately for each format. Second issue with this approach is, it require XML to object and object to XML conversion, which make it a bit slow.
Following is a code snippet for your help. I am calling a service support SOAP1.2 RPC.

public String callService(String namespace, String Servicename,
String porttype, URL WSDLURL, String dataload) {
String targetNS = namespace;
QName serviceName = new QName(targetNS, Servicename);
QName portName = new QName(targetNS, porttype);
URL wsdlURL = WSDLURL;

Service service = Service.create(wsdlURL, serviceName);
service.addPort(portName, SOAPBinding.SOAP11HTTP_BINDING,
wsdlURL.toExternalForm());
Dispatch

dispatch = null;
StreamSource xmlSource = null;
ByteArrayOutputStream xmlByteArray = new ByteArrayOutputStream();
TransformerFactory fac = TransformerFactory.newInstance();
dataload = addSoapEnvelope(dataload);

try {
Transformer x = fac.newTransformer();
x.transform(new StreamSource(new StringReader(dataload)),
new StreamResult(xmlByteArray));
xmlSource =
new StreamSource(new StringReader(xmlByteArray.toString()));

dispatch =
service.createDispatch(portName, Source.class, Mode.MESSAGE);
} catch (TransformerConfigurationException e) {
e.printStackTrace();
} catch (TransformerException e) {
e.printStackTrace();
}
//calling WS with help of Dispatcher and getting Result as Source
Source result = dispatch.invoke(xmlSource);

StringWriter outputxml = new StringWriter();
Transformer outx;
try {
outx = fac.newTransformer();
outx.transform(result, new StreamResult(outputxml));
} catch (TransformerConfigurationException e) {
e.printStackTrace();
} catch (TransformerException e) {
e.printStackTrace();
}
String resultstr = outputxml.toString();
if (resultstr.indexOf("?>") > 0) {
resultstr = resultstr.substring(resultstr.indexOf("?>") + 2);
}
MessageFactory factory;
StringWriter output = null;
//Converting Source result to SOAP Message
try {
factory = MessageFactory.newInstance();
SOAPMessage message = factory.createMessage();
message.getSOAPPart().setContent(new StreamSource(new StringReader(resultstr)));
message.saveChanges();
NodeList list = message.getSOAPBody().getChildNodes();
Node node = list.item(0);
output = new StringWriter();
Transformer out;
out = fac.newTransformer();
out.transform(new DOMSource(node), new StreamResult(output));

} catch (TransformerConfigurationException e) {
e.printStackTrace();
} catch (TransformerException e) {
e.printStackTrace();
} catch (SOAPException e) {
e.printStackTrace();
}
//Converting SOAP Return to String for processing
String outputstr = output.toString();
//removing from returned output
if (outputstr.indexOf("?>") > 0) {
outputstr = outputstr.substring(outputstr.indexOf("?>") + 2);
}
return (outputstr);
}
Please note that it is not the best way to do it. You may find better way to do the same like using WS-I APIs


2) Call through a PL/SQL procedure: If you are using event based program where events are getting stored in DB, you may need to call services from DB side. I am aware of Oracle Database, so I am using it only but I think similar option must be available with other databases as well.
Advantage is, you need not have additional program call to invoke WS and you can secure some n/w bandwidth and processing time. It is hard to believe but database too have some very good API's to handle XML data.
Limitation is, it doesn't support security and oracle standards/recommendations do not suggest for this approach. But in some places it may work as life saver.
FUNCTION new_request(method IN VARCHAR2,
namespace IN VARCHAR2)
RETURN request AS
req request;
BEGIN
req.method := method;
req.namespace := namespace;
RETURN req;
END;
Following is the code snippet from soap_rpc package for raising new request:

PROCEDURE add_parameter(req IN OUT NOCOPY request,
name IN VARCHAR2,
type IN VARCHAR2,
value IN VARCHAR2) AS
BEGIN
req.body := req.body ||
'<' xsi:type="'||type||'">'||value||'';
END;

PROCEDURE generate_envelope(req IN OUT NOCOPY request,
env IN OUT NOCOPY VARCHAR2) AS
BEGIN

env := '

<' '||req.namespace|| ' SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">'||
req.body||'';
END;

For more details you may refer to http://www.oracle.com/technology/tech/webservices/htdocs/samples/dbwebservice/DBWebServices_PLSQL.html page.


3) Call through a BPEL process This is the most declarative approach where the services of similar input and output type can be called dynamically. Here I have used Oracle SOA Suite 11g and again I am pretty much sure that other SOA Suite must have similar feature.
Here I have created a dummy WSDL with request and response and associated with BPEL Process. Now at Run time, we can assign endpoint reference to dummy partner link to invoke the service.
Advantage is, it is purely declarative in nature; it is very useful in case of load balancing where we can use multiple end points for similar feature and redirect them to other endpoint if one service end point is down.
Limitation is, it will work only in case of similar messages as the message types are bind with BPEL.

Following is the code of Dynamic BPEL. bpel file snippet for your reference:

No comments:

Post a Comment