By definition Web Services (WS) have been designed to support interoperable Machine to Machine interaction over a network. In order to promote interoperability of Web Services, in 2002 the Web Services Interoperability Organization (WS-I) was founded which basically is an industry consortium that publishes profiles. A profile is a set of core specifications (SOAP, WSDL, etc.) in a specific version (SOAP 1.1, UDDI 2, etc.) with some additional requirements to restrict the use of the core specifications.
One of the three profiles you can actually find in their deliverables is the Basic Profile (official abbreviation is BP), which uses Web Services Description Language (WSDL) to enable the description of services as sets of endpoints operating on messages. Almost everybody is BP compliant: Glassfish Metro (JAX-WS RI), IBM WebSphere, Apache Axis 1.2+, JBossWS, ASP.NET 2.0.
The keyword here is “almost”…
My Project in a Nutshell
There is a team at my workplace that administers the Greek School Network Directory Service which currently contains more than 170,000 entries including school accounts for email and dialup access, teacher accounts and several student accounts. For various reasons that you can find at my colleague’s blog there was a need to perform LDAP operations over WSs – in fact we had to “Move LDAP writes to Web Services”.
The server-point would actually perform the LDAP “writes” to the Directory Server and have a PHP WSs interface. For the PHP implementation, NuSOAP was chosen. NuSOAP is probably the most popular PHP SOAP implementation and is widely used, eg. in Amazon Web Services.
It is a group of PHP classes that allow developers to create and consume SOAP web services. It does not require any special PHP extensions. The current release version of NuSOAP at the time supports much of the SOAP specification. It can generate WSDL and also consume it for use in serialization. Both rpc/encoded and document/literal services are supported.
Here follows the skeleton of the PHP configuration for our deployment:
<?php // The library require_once('lib/nusoap.php'); // The services exposed require_once('myinterface.functions.php'); $server = new soap_server(); $server->configureWSDL('Interface',"http://server/interface.php"); $server->wsdl->schemaTargetNamespace="http://server/interface.php?wsdl"; $server->register('ExistsUser', array('SubsystemID' => 'xsd:integer', 'UserNumber' => 'xsd:string'), array('Status' => 'xsd:integer', 'ErrorMessage' => 'xsd:string', 'UserName' => 'xsd:string'), "http://server/interface.php",false,false,'literal', 'ExistsUser will return Status=0 if user does not exist, Status=1 if user exists and set UserName accordingly, Status=-1 and set ErrorMeessage if an error occurs. It should be used to check if user exists before creating username. UserNumber is the number the user has in the subsystem. SubsystemID is the numerical ID of the subsystem'); $server->service($HTTP_RAW_POST_DATA); ?>
NuSOAP was not BP compliant until a few days ago when CVS revision 109 of nusoap.php was introduced. The motivation for this article was the hard way we had to find out…
The system my team was responsible for was the client which would have to wrap the WS related code into Java POJOs that would either be used as library code or through portlets. Implementing the functionality we needed in Jetspeed portlets was another issue but it goes beyond the scope of this post.
For the Java WS framework there were several candidates like Apache Axis and JAX-RPC but we choose JAX-WS 2.1, both because of its elegant programming model and the fact that in the newly published Java EE 6 proposal JAX-RPC will be proposed for future deprecation.
Our development platform was NetBeans 5.5 which provided a powerful wizard that starting from the WSDL that NuSOAP published, created the necessary Java stub code for our operations.
This is a small portion of the WSDL for the operation ExistsUser:
… <message name="ExistsUserRequest"> <part name="SubsystemID" type="xsd:integer" /> <part name="UserNumber" type="xsd:string" /> </message> <message name="ExistsUserResponse"> <part name="Status" type="xsd:integer" /> <part name="ErrorMessage" type="xsd:string" /> <part name="UserName" type="xsd:string" /> </message> … <operation name="ExistsUser"> <soap:operation soapAction="http://server/interface.php/ExistsUser" style="rpc"/> <input> <soap:body use="literal" namespace="http://server/interface.php"/> </input> <output> <soap:body use="literal" namespace="http://server/interface.php"/> </output> </operation> …
Of course to debug Web Services you need tools like:
- Wireshark which is a network protocol analyzer and can give you a print out of the complete TCP stream for a given set of WS calls and
- XMLSpy which for me is the easiest tool to create WS clients and debug your WS without writing code.
Of course the fact that the above development stack was found appropriate for our project doesn’t mean that it is suitable for every WS project. BTW a nice introduction on making similar technical decisions for Java projects can be found in the book Expert One-on-One J2EE Design and Development (AKA “The J2EE Bible”) by Rod Johnson in chapter 2 entitled “J2EE Projects: Choices and Risks”.
Holder <T> and Java’s heavy burden from C
Java Web Services make use of the Holder concept that was introduced since WSDL can specify interfaces that return multiple parameters (rather than just one single return value). In Java you can only return directly a single new object through a return value. To return multiple objects you have to return a collection object (which is still only a single object) or some other "container" object. To return a new object through a parameter, you need a holder object. Java’s object reference parameters only allow the modification of existing object instances.
Holders are necessary because the Java language designers stuck with the "pass-by-value" semantics of the C-family of languages. If a parameter is a simple type its value is copied, if it is an object instance the reference (value) to it is copied. As a result you are unable to modify the value in the original simple type or the reference (value) to the object from the called method (while you can change the passed instance, you cannot replace the passed instance). The holder mimics "pass-by-reference" semantics which in C/C++ is is done with a pointer-to-a-pointer and in C# directly through the ref keyword.
Some consider attempts to imitate call by reference in OO languages a sign of poor object-oriented design. Dale King points out that attempts to fake call by reference are usually a sign of poor object-oriented design: “a function should not be trying to return more than one thing”. He uses the term thing because it is proper to return more than one value (e.g. returning a Point that contains two values). If you are trying to return two values, the test he like to apply is whether you can come up with a logical name for the values as a group. If you can’t, you had better look to see if maybe you what you have is really two functions lumped together. WSDL never strived towards OO so that is where this mismatch comes from. It may have been a better idea for Java to wrap the WSDL method return value and all of the OUT parameters into a single wrapper object which becomes the return value of the Java interface method.
For our implementation we had the WS method:
@WebMethod(operationName = "ExistsUser", action = "http://server/interface.php/ExistsUser") public void existsUser( @WebParam(name = "SubsystemID", partName = "SubsystemID") BigInteger subsystemID, @WebParam(name = "UserNumber", partName = "UserNumber") String userNumber, @WebParam(name = "Status", mode = Mode.INOUT, partName = "Status") Holder status, @WebParam(name = "ErrorMessage", mode = Mode.OUT, partName = "ErrorMessage") Holder errorMessage, @WebParam(name = "UserName", mode = Mode.OUT, partName = "UserName") Holder userName);
This method would return the status in the client:
BigInteger status = new BigInteger("0", 10); Holder statusHolder = new Holder(status); … // Actual call of the WS port.existsUser(subsystemID, userNumber, statusHolder, errorMessage, userName); … status = statusHolder.value System.out.println(status);
If you fire up the JAX-WS wizard in NetBeans and feed it with the WSDL you get 100% of the necessary code for an “almost” working demo.
Again the keyword is “almost”…
Bumps on the Road
If you have Java SE 6 installed and start of with NetBeans and JAX-WS there is a big chance you’ll run into problems. Java 6 ships with JAX-WS 2.0 API in rt.jar (so as we had to find out) you need to download the latest stable version of JAX-WS (2.1.1) and copy jaxws-api.jar and jaxb-api.jar into JRE endorsed directory. Obviously you still need other JAX-WS jars in your classpath.
The above problem was relatively simple to solve compared to the fact that statusHolder.value would not alter after the WS invocation no matter what!
Although the WS would get invoked properly on the server and we would receive a SOAP response:
<?xml version="1.0" encoding="UTF-8"?> <SOAP-ENV:Envelope …> <SOAP-ENV:Body> <ExistsUserResponse xmlns="http://server/interface.php"> <Status>1</Status> <ErrorMessage></ErrorMessage> <UserName>john</UserName> </ExistsUserResponse> </SOAP-ENV:Body> </SOAP-ENV:Envelope>
the @$%$# statusHolder.value would remain the same!
Weeks of debugging passed during which we had to dig into specs, JAX-WS RI code and forums to find out that:
- The SOAP response we got from NuSOAP was not compliant with BP since according to WS-I “An ENVELOPE described with an rpc-literal binding MUST place the part accessor elements for parameters and return value in no namespace.” In our case "Status" is in http://server/interface.php" namespace instead of no namespace, and
- Since JAX-WS RI 2.1 would encounter a response not valid according to BP, would simply ignore it without even spawning an error message.
Number one (1) has been reported to NuSOAP mailing list and we can confirm that it been fixed in CVS revision 109 of nusoap.php that should now conform to the WS-I Basic Profile (thanks Scott).
Number two (2) has been filled as an “enhancement” of JAX-WS RI for the version 2.1.3: “Ignore rpc/lit part accessors’ namespace, there are quite a few implementations that do not obey BP”.
The Morale of the Story
At this point I cannot help but to quote Dave Podnar’s Five Stages of Dealing with Web Services:
- Denial – It’s Simple Object Access Protocol, right?
- Over Involvement – OK, I’ll read the SOAP, WSDL, WS-I BP, JAX-RPC, SAAJ, JAX-P,… specs. next, I’ll check the Wiki and finally follow an example showing service and client sides.
- Anger – I can’t believe those #$%&*@s made it so difficult!
- Guilt – Everyone is using Web Services, it must be me, I must be missing something.
- Acceptance – It is what it is, Web Services aren’t simple or easy.