Friday, March 25, 2011

Old gSOAP web services from Java

I spent a considerable amount of time trying to get an old web service (gSOAP/2.6) consumed from Java. This is the Web Services exposed by an old version of Advent Geneva.

Dynamic Axis2

While Axis2 using axiom seemed to do the trick it was failing to parse some error results.

Static JAX-WS stubs

Someone in the team proposed to go with stubs and as that service does not changes every so often I agreed to give it a try. After some time spent on the task it came back to my plate as JAX-WS stubs were not working as expected. The stubs were generated only after some edition of the WSDL but I could not get the responses from any available object even though TcpMon showed correct request and responses going over the wire. As there were warnings from the stubs generation I suspected there was further changes to be made to the wsdl. For example one of the stubs was returning empty string while from TcpMon a value could be seen.
GenevaService service = new GenevaService( wsdlLocation, new QName(targetNamespace, serviceName));
GenevaServicePortType port = service.getGenevaServicePort();
ObjectFactory objectFactory = new ObjectFactory();            
log.info(port.startCallableRunrep(9000, "genevaServer", "user", "password", ""));

Apache CXF

I tried Apache CXF but I got an error basically saying "Forget it, this is too old for me":
WSDLToJava Error: Rpc/encoded wsdls are not supported with CXF

Dynamic JAX-WS

Advent Geneva uses gSOAP to expose services that are used from clients to pull report data and so there are no complex objects being returned, but more than anything tabular data. That gives the possibility to work without deserializing. While that approach as stated before was failing for some error responses for dynamic Axis2 approach (using Axiom) the problem got corrected when the service client was migrated to use dynamic JAX-WS. Below is the relevant code for the method failing before which just sends credentials and returns a session id:
            Service service = GenevaService.create(wsdlLocation, new QName(targetNamespace, serviceName));
            Dispatch dispatch = service.createDispatch(
                    new QName(targetNamespace, portName), SOAPMessage.class, GenevaService.Mode.MESSAGE);
            
            MessageFactory factory = MessageFactory.newInstance();
       
            SOAPMessage soapRequest = factory.createMessage();
            SOAPEnvelope envelope = soapRequest.getSOAPPart()
                .getEnvelope();
            SOAPElement soapElement = soapRequest.getSOAPBody().addBodyElement(
                envelope.createName("StartCallableRunrep", "gen", targetNamespace));
            
            soapElement.addChildElement(envelope.createName("portNumber"))
                .addTextNode("9000");
            soapElement.addChildElement(envelope.createName("hostname"))
            .addTextNode("genevaServer");
            soapElement.addChildElement(envelope.createName("username"))
            .addTextNode("username");
            soapElement.addChildElement(envelope.createName("password"))
            .addTextNode("password");
       
            SOAPMessage soapResponse = dispatch.invoke(soapRequest);
            Element startCallableRunrepResponse = (Element) soapResponse.getSOAPBody().getFirstChild();
            NodeList nodeList = startCallableRunrepResponse.getChildNodes();
            String runrepSessionId = null;
            for( int i= 0; i < nodeList.getLength(); i++ ) {
                Node node = nodeList.item(i);
                if( "runrepSessionId".equals(node.getLocalName()) ) {
                    runrepSessionId = node.getTextContent();
                }
            }
            log.info(runrepSessionId);

Conclusion

Once again I corroborated that there is no one technology better than other in terms of building web service clients. The specific needs of a project can really make you spend hundreds of hours if you want to stick to Axis2 or any other preferred API. It is wise to try from SOAPUI which internally uses apache http commons directly when consuming web services. If the web service works from there you still can have surprises. You can try stubs, dynamic services and DOM, SAX, XPATH or dynamic services and deserialization with any binding technology.

I personally prefer dynamic clients to be honest, they adapt to changes and while they can demand more low level plumbing they almost 100% of the time will work even when consuming really old web services.

No comments:

Followers