SOA, WebService and web service, WSDL, UDDI, BPEL and WSIF, Axis, SOAP, JAVA-RPC etc. etc. If Google has not found this blog by now… Seriously, these are all hot topics. Integration, service publication, orchestration – we are bombarded with all of these. For a workshop we will shortly organize at AMIS, UI have attempted to create some simple introductions to some of these concepts. One of them is WSIF and using it for decoupling Service providers and their implementation from the service consumers. Not by implementing all Services as SOAP WebServices, as you might expect, but by describing the Services using WSDL – even if they are not WebServices in the strict SOAP sense of the word – and having WSIF figure out how to make the call.
In this article I will describe the first steps you could make with Apache WISF to call Java based services – a fancy expression for some public static Java Methods – as if they were Web Services. The Web Service Invocation Framework (WSIF) allows us to call semi-WebServices from our Java application. These services are described through a WSDL but not asssociated with a SOAP binding.
WSIF is an open-ended framework for invoking WSDL described services. As long as a WSDL description of the endpoint exists, it should be possible to use this framework to invoke that piece of software, whatever it may be. WSIF, an Apache technology originally developed by IBM alphaWorks as a part of its Web Services Toolkit, extends the Web services model by allowing you to describe services in WSDL, even if it’s not a Web service that communicates through SOAP. WSIF also allows you to map such a service to the actual implementation and protocol. In other words, you can bind the abstract description of any WebService-like service to a corresponding resource, which can communicate using one of the supported WSIF providers. WSIF supports WSDL descriptions for services offered by Java classes, EJB, JCA, HTTP GET and POST, and sockets. Oracle BPEL for example has extended the WSIF framework with a provider for PL/SQL procedure calls.
Why use WSIF
Enterprise information systems usually consist of many different software pieces, such as legacy applications accessible though JCA, EJBs, Web services developed on different platforms, and so on. To integrate all these pieces you have to deal with different protocols. For example, if software migrates to a different server or has been upgraded to use a new technology, you have to upgrade the integration code. Or you expose all these services – the legacy APIs – as SOAP based WebServices. Then the Service Consumers only need to know how to invoke a SOAP WebService and you are in business. Writing a new application requires only SOAP knowledge and of course the implementation of the WebServices for all legacy services —unless you use WSIF.
WSIF allows many of these legacy services to be invoked by service consumers as if they were WebServices without them actually having been implemented as such. If you tell the WSIF framework through a somewhat extended WSDL document what the service looks like in functional terms and how it can be invoked in terms of protocol and physical location, the WSIF framework can translate a simple service call from the service consumer into the required native protocol call; it also transforms the service response back into a generic response format. So the service consumer needs to knowledge of the actual implementation of the service; the consumer does not need to know how to communicate with a SOAP WebService, nor to a JMS, an EJB or a JCA service. That is all taken care of by the WSIF framework and its protocol specific providers.
If now the service is moved to another physical location, only the WSDL needs to be changed, not the service consumer itself. If the service is implemented in a SOAP WebService instead of the former EJB implementation, only the WSDL needs to be changed and again not the service consumer. And we do not have to do anything on the service side either: we do not have to implemement a SOAP WebService frontend for each service.
WSIF offers other important benefits:
- Invoking services through WSIF maintains the performance of native protocols. Thus, when invoking Java resources, native Java classes, EJBs, or any other resources, you do not have to pay the performance penalty of (SOAP) Web services.
- WSIF enables automatic propagation of transactional contexts between invoked transaction-aware Java resources using Java Transaction API (JTA). That way Java resources can participate in distributed transactions.
WSIF is also used by WebService oriented frameworks like BPEL containers for calling local Java services: the BPEL process calls a partnerLink whose WSDL is in fact an extended WSIF WSDL with special bindings, for Java for example. The Oracle BPEL Process Manager knows how to invoke such a WebService (-like service), using the WSIF instead of a normal SOAP communication.
WSIF is also used for easy testing of the WSDL and implementation of a service that is to be deployed as a “real” SOAP web service (without the actual overhead of the SOAP container). If you have a piece of Java that you are going to publish as SOAP WebService, you can test the WSDL and the invocation of that Java functionality through WSIF without having to set up a SOAP container and actually deploying the WebService.
In theory, the WSIF framework can easily be extended with additional Providers that could take care of calls to services implemented in other technology/protocol combinations than the ones currently supported in the Apache WSIF distribution – SOAP, EJB, Java, JMS and JCA. I have not been able to locate (m)any additional providers and at first glance the instruction for creating your own provider did not strike as easy. However, it seems an entertaining challenge for a long winter night sometime.
Creating a WSIF client consuming a SOAP WebService
We will embark on our WSIF tour in the most obvious way: calling regular SOAP WebServices. For this example I will make use of the OTN GUIDService (see OTN’s Hosted Services). This WebService Generates an identifier that is guaranteed to be unique, universally. It is the simplest WebService one can imagine: no input parameters and a single string as output.
- I start by downloading the Apache WSIF release 2.0 . I extract the zip onto my local hard drive.
- Retrieve the WSDL document for this WebService and store it on my file system as OTNGUIDGenerator.wsdl. The WSDL is at: http://webservices.oracle.com/ws/guid/oracle.ws.OTNGUIDGenerator?WSDL
- Next, I take the StockQuote example from the Apache Samples directory (WSIF_HOME\samples\clients\stockquote\main.java) to get inspired by and nick some code from
- Now it’s time to fire up my IDE (well, you do not really need it, but I am a lazy type of guy). I have used both Eclipse 3.1 and JDeveloper 10.1.3 for this exercise. I create a new project, add the OTNGUIDGenerator.wsdl file. I also add all libraries in WSIF_HOME\lib and WSIF_HOME\build\lib to the project’s Classpath – just to be sure.
- I create a new Java class – nl.amis.identity.IdentityManager. It provides a public static method getIdentity() that takes no input and returns a unique String. It will make use of the Oracle GUIDWebService – if that one is available.
- The getIdentity() method calls a private method getGUIDResult() that in turn uses the WSIF framework to make the call to the SOAP based WebService. It is very important to note that this method does not refer in any way to SOAP specific aspects, neither the URL nor aspects of the SOAP:Binding element. The method looks like this:
private static String getGUIDResult(String portName) { String result = ""; try { // create the ServiceFactory WSIFServiceFactory factory = WSIFServiceFactory.newInstance(); // create a Service based on the Service and PortType combination // in the specified WSDL WSIFService service = factory.getService("OTNGUIDGenerator.wsdl" , null, null , "http://webservices.oracle.com/wstns/oracle/ws/OTNGUIDGenerator" // portType namespace , "OTNGUIDGeneratorPortType" // portType ); WSIFPort port = service.getPort(); // Create the operation object for the getGUID operation in the WSDL WSIFOperation operation = port.createOperation("getGUID", "getGUID0Request","getGUID0Response"); WSIFMessage input = operation.createInputMessage(); WSIFMessage output = operation.createOutputMessage(); WSIFMessage fault = operation.createFaultMessage(); // invoke the Service boolean success = operation.executeRequestResponseOperation(input, output, fault); String guidResult = (String)output.getObjectPart("return"); if (success == true) { result = guidResult; } else { System.out.println("No Result..Unable to execute the Operation!!"); } } catch (WSIFException we) { System.out.println("Got exception from WSIF, details:"); // we.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } return result; }
This method asks WSIF to call a Service – which happens to be a SOAP WebService. It refers to the WSDL document that defines a single service that contains a PortType called OTNGUIDGeneratorPortType. One of the operations on this PortType (in fact the only one) is called getGUID in the WSDL. We tell WSIF to create the inputMessage (which is empty) and initialize the output and fault (exception) parts. Next the service request is made – that is: this method calls upon WSIF to call the specified operation on the indicated port type on the current service. WSIF interprets that information in the context of the only port it can find in the WSDL: a SOAP Port linked to the URL http://webservices.oracle.com/ws/guid/oracle.ws.OTNGUIDGenerator.
The WSDL document looks as follows:
<definitions name="OTNGUIDGenerator" targetNamespace="http://webservices.oracle.com/wstns/oracle/ws/OTNGUIDGenerator" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http://webservices.oracle.com/wstns/oracle/ws/OTNGUIDGenerator" xmlns:ns1="http://webservices.oracle.com/wstns/oracle/ws/OTNGUIDGenerator/schema" > <types> <schema targetNamespace="http://webservices.oracle.com/wstns/oracle/ws/OTNGUIDGenerator/schema" xmlns="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"/> </types> <message name="getGUID0Request"/> <message name="getGUID0Response"> <part name="return" type="xsd:string"/> </message> <portType name="OTNGUIDGeneratorPortType"> <operation name="getGUID"> <input name="getGUID0Request" message="tns:getGUID0Request"/> <output name="getGUID0Response" message="tns:getGUID0Response"/> </operation> </portType> <binding name="OTNGUIDGeneratorBinding" type="tns:OTNGUIDGeneratorPortType"> <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/> <operation name="getGUID"> <soap:operation style="rpc" soapAction=""/> <input name="getGUID0Request"> <soap:body use="encoded" namespace="oracle.otn.ws.emarket.OTNGUIDGenerator" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/> </input> <output name="getGUID0Response"> <soap:body use="encoded" namespace="oracle.otn.ws.emarket.OTNGUIDGenerator" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/> </output> </operation> </binding> <service name="OTNGUIDGenerator"> <port name="OTNGUIDGeneratorSoapPort" binding="tns:OTNGUIDGeneratorBinding"> <soap:address location="http://webservices.oracle.com/ws/guid/oracle.ws.OTNGUIDGenerator"/> </port> </service> </definitions>
The best way to read this WSDL is probably starting from the bottom: a single service called OTNGUIDGenerator that contains a single port called OTNGUIDGeneratorSoapPort that is associated with the SOAP binding tns:OTNGUIDGeneratorBinding. This binding can be seen above the service element. It defines the binding’s protocol (SOAP over HTTP) and specifies the single getGUID operation. This operation can be seen as the implementation of the operation getGUID that is defined on the portType element. The portType is the functional, generic, protocol-free description of the functionality of the Web Service. It defines the operations and specifies the input message and output message for each operation. These messages are defined – their structure that is – in message elements.
We can now write a simple Java application to use our getGUIDResult() method and get hold of unique identities, without any knowledge of the fact that an external service was used to provide that identity. And what’s even more surprising: the getGUIDResult() method itself knows that it used WSIF to call an external service, but it does not know that it was in fact a SOAP Web Service. The Service consumer only knows functional details about the service.
To prove this point a little further, read on.
Extending the WSIF client for also consuming a Java WebService
We would like our getGUIDResult() to also return sensible output if we are offline. So even if the WebService it not available, should the getGUIDResult() method return a value. To facilitate this, we create our own Identity Service, a simple Java Class that returns a String (that happens to be not quite so unique…)
package nl.amis.soa; public class IdentityGenerator { public IdentityGenerator() { } /** * This method will return a virtually unique string. It cvan be used as a * unique identifier within the current JVM Session. * * @return Unique String */ public String getFreshIdentity() { return "X"; } }
And now for the climax in this story: we add little extra information to the WSDL document, a WSIF specific extension, that defines an additional port for an extra binding of the portType OTNGUIDGeneratorPortType. We already have a binding of the portType to the soap implementation that Oracle provides on OTN. We add a binding to the Java based implementation in our own class IdentityGenerator. We define this Java binding in the WSDL file like this:
<!-- Java binding --> <binding name='IndentityGeneratorJavaBinding' type='tns:OTNGUIDGeneratorPortType'> <java:binding/> <format:typeMapping encoding="Java" style="Java"> <format:typeMap typeName="xsd:string" formatType="java.lang.String" /> </format:typeMapping> <operation name="getGUID"> <java:operation methodName="getFreshIdentity" methodType="instance" /> <input/> </operation> </binding>
In this binding, we indicate the type: java:binding (WSIF also supports EJB, JMS and JCA in addition to SOAP). We specify that the getGUID operation that is defined in the portType is mapped to the Java method getFreshIdentity.
We also add two namespaces:
xmlns:format="http://schemas.xmlsoap.org/wsdl/formatbinding/" xmlns:java="http://schemas.xmlsoap.org/wsdl/java/"
and of course the port-definition:
<service name="OTNGUIDGenerator"> <port name="OTNGUIDGeneratorSoapPort" binding="tns:OTNGUIDGeneratorBinding"> <soap:address location="http://webservices.oracle.com/ws/guid/oracle.ws.OTNGUIDGenerator"/> </port> <port name="IdentityJavaPort" binding="tns:IndentityGeneratorJavaBinding"> <java:address className="nl.amis.soa.IdentityGenerator"/> </port> </service>
With all this in place, we can make a tiny change to the service consumer code. We check the result of our service call and if the result suggests that the peferred service-port was not available, we use the fallback-port; that results in a call to the Java implementation when the SOAP WebService is not available:
package nl.amis.identity; import org.apache.wsif.WSIFException; import org.apache.wsif.WSIFMessage; import org.apache.wsif.WSIFOperation; import org.apache.wsif.WSIFPort; import org.apache.wsif.WSIFService; import org.apache.wsif.WSIFServiceFactory; public class IdentityManager { public IdentityManager() { } private static String SOAP_PORT = "OTNGUIDGeneratorSoapPort"; private static String JAVA_PORT = "IdentityJavaPort"; private static String getGUIDResult(String portName) { String result = ""; try { // create the ServiceFactory WSIFServiceFactory factory = WSIFServiceFactory.newInstance(); // create a Service based on the Service and PortType combination // in the specified WSDL WSIFService service = factory.getService("OTNGUIDGenerator.wsdl" , null, null , "http://webservices.oracle.com/wstns/oracle/ws/OTNGUIDGenerator" // portType namespace , "OTNGUIDGeneratorPortType" // portType ); WSIFPort port; if (portName == null) port = service.getPort(); else port = service.getPort(portName); // Create the operation object for the getGUID operation in the WSDL WSIFOperation operation = port.createOperation("getGUID", "getGUID0Request","getGUID0Response"); WSIFMessage input = operation.createInputMessage(); WSIFMessage output = operation.createOutputMessage(); WSIFMessage fault = operation.createFaultMessage(); // invoke the Service boolean success = operation.executeRequestResponseOperation(input, output, fault); String guidResult = (String)output.getObjectPart("return"); if (success == true) { result = guidResult; } else { System.out.println("No Result..Unable to execute the Operation!!"); } } catch (WSIFException we) { System.out.println("Got exception from WSIF, details:"); // we.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } return result; } /** * This method will return a virtually unique string. It cvan be used as a * unique identifier within the current JVM Session. * * @return Unique String */ public static String getIdentity() { // note: this method will make use of either the OTNGUIDGenerator based // service - when available - and else the local java based // IdentityGenerator service String identity; identity = getGUIDResult(SOAP_PORT); if ((identity == null) || (identity.length() < 2)) { // alternative approach identity = getGUIDResult(JAVA_PORT); } return identity; } public static void main(String[] args) { System.out.println("Your new unique Identity is " + IdentityManager.getIdentity()); } }
The result of running class IdentityManager is something like:
Resources
The sources for this introduction, WSIFIntro.zip , packaged as JDeveloper 10.1.3 Project (note: you will have tp download Apache WSIF 2.0 yourself)
Web Service Invocation Framework – Adding wheels to Web services By: Anshuk Pal Chaudhari; Sandeep Gaikwad; Ambar Verma; V. Niranjan, SOA WebServices Journal (Sep. 23, 2005)
Using Java from BPEL with WSIF, Anthony Reynolds, December 15, 2004
WSIF, webservice-a-lize uw applicaties – WhiteHorses, September 2004
Using WSIF for Integration by Matjaz B. Juric, part 4 of SOA Best Practices: The BPEL Cookbook on OTN (Oracle Technology Network)
How to write a WSIF provider – Apache WSIF Project Site
Here is a tutorial I wrote for anyone who is struggling to get their head round using wsif on Oracle:
http://soa-mobile.blogspot.com/2009/01/using-wsif-with-oracle-bpel-process.html
Great entry, nice clear example of using client side access.
Latest JDeveloper release now supports generation of WSIF binding for Java as well as SOAP 1.1 and 1.2.
As Sandor says, in Oracle BPEL the use-case is usually the other way round to this, as we use WSIF to invoke all interfaces, SOAP, Java or JCA – hence good to see another use for a very powerful level of indirection.
One remark about the use of WSIF in Oracle BPEL Process Manager: all Service calls
are done using WSIF, normally using the SOAP binding, and as soon as you use one
of the adapters (e.g. DB for PL/SQL, File, etc), we will use the JCA binding.
There are also two interesting blog entries from my colleague in the UK about WSIF
and BPEL:
http://www.orablogs.com/reynolds/archives/2005_09.html
and
http://www.orablogs.com/reynolds/archives/000747.html
There is also an interesting SOA Cookbook on this subject on OTN:
http://www.oracle.com/technology/pub/articles/bpel_cookbook/index.html
(see #4)
The above example requires a lot of libraries. Organizing them can be a tedious job, that can be made a lot easier with the use of Maven. This archive contains a Maven2 and JUnit enabled update of the example.
Unfortunately, some licences do not allow libraries to be part of the central Maven repositories. That’s, for example, the case for many javax libraries. So you’ll have to manually download and add activation.jar (v1.0.2), mail.jar (v1.3.2) and j2ee.jar (any version?!) to your local repository:
mvn install:install-file -Dfile=mail.jar -DgroupId=javax.mail -DartifactId=mail -Dversion=1.3.2 -Dpackaging=jar
mvn install:install-file -Dfile=activation.jar -DgroupId=javax.activation -DartifactId=activation -Dversion=1.0.2 -Dpackaging=jar
mvn install:install-file -Dfile=j2ee.jar -DgroupId=javax.j2ee -DartifactId=j2ee -Dversion=1.4 -Dpackaging=jar
You can now run the untitests on the commandline with ‘mvn test’ or generate a project site with ‘mvn site’.
It is also very easy to generate Eclipse project files with ‘mvn eclipse:eclipse’, but make sure that you define in Eclipse the M2_REPOS variable that points to the maven repository root. Unfortunately maven cannot (yet?) generate the JDeveloper .jpr file, so I’ve included mine in the archive. Beware, it uses relative references to the files in my own maven repository, so you’d probably have to change these.