Forum Stats

  • 3,853,745 Users
  • 2,264,263 Discussions
  • 7,905,440 Comments

Discussions

jax-ws 2.2.8 and ws-addressing: Client throwing java.lang.NullPointerException on receipt of HTTP 20

Server: JBoss EAP 6.2.0

Client: JDK 1.7.0_51 x64

JAX-WS: RI 2.2.8 ( via -Djava.endorsed.dirs )

I am getting a java.lang.NullPointerException when calling the operation on the WS endpoint from the client when using non-anonymous replyTo address.

I have simplified the scenario into a small test case that hopefully others can replicate. Since the exception is happening on the client instead of the server, I would think that the container used is irrelevant, but I have specified it nonetheless.

1) WebService:

package test.webservice;


import java.util.Random;


import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.xml.ws.soap.Addressing;


@WebService(targetNamespace="http://services.nowhere.org/")
@Addressing(required=true)
@SOAPBinding(style = SOAPBinding.Style.DOCUMENT)
public class RandomTest {
  
    @WebMethod
    public long nextRandom(@WebParam boolean forceException) throws Exception {
        if( forceException ) {
            throw new Exception("Some exception");
        }
        Random rand = new Random();
        return rand.nextLong();
    }


}


2) Generated WSDL by JBossEAP 6.2.2:

<?xml version='1.0' encoding='UTF-8'?><wsdl:definitions xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsp="http://www.w3.org/ns/ws-policy" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" xmlns:tns="http://webservice.test/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:ns1="http://schemas.xmlsoap.org/soap/http" name="RandomTestService" targetNamespace="http://webservice.test/">
  <wsdl:types>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://webservice.test/" elementFormDefault="unqualified" targetNamespace="http://webservice.test/" version="1.0">


  <xs:element name="nextRandom" type="tns:nextRandom"/>


  <xs:element name="nextRandomResponse" type="tns:nextRandomResponse"/>


  <xs:complexType name="nextRandom">
    <xs:sequence/>
  </xs:complexType>


  <xs:complexType name="nextRandomResponse">
    <xs:sequence>
      <xs:element name="return" type="xs:long"/>
    </xs:sequence>
  </xs:complexType>


</xs:schema>
  </wsdl:types>
  <wsdl:message name="nextRandom">
    <wsdl:part element="tns:nextRandom" name="parameters">
    </wsdl:part>
  </wsdl:message>
  <wsdl:message name="nextRandomResponse">
    <wsdl:part element="tns:nextRandomResponse" name="parameters">
    </wsdl:part>
  </wsdl:message>
  <wsdl:portType name="RandomTest">
    <wsdl:operation name="nextRandom">
      <wsdl:input message="tns:nextRandom" name="nextRandom" wsam:Action="http://webservice.test/RandomTest/nextRandomRequest" wsaw:Action="http://webservice.test/RandomTest/nextRandomRequest">
    </wsdl:input>
      <wsdl:output message="tns:nextRandomResponse" name="nextRandomResponse" wsam:Action="http://webservice.test/RandomTest/nextRandomResponse" wsaw:Action="http://webservice.test/RandomTest/nextRandomResponse">
    </wsdl:output>
    </wsdl:operation>
  </wsdl:portType>
  <wsdl:binding name="RandomTestServiceSoapBinding" type="tns:RandomTest">
    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
    <wsaw:UsingAddressing wsdl:required="true"/>
    <wsp:PolicyReference URI="#RandomTestServiceSoapBinding_WSAM_Addressing_Policy"/>
    <wsdl:operation name="nextRandom">
      <soap:operation soapAction="" style="document"/>
      <wsdl:input name="nextRandom">
        <soap:body use="literal"/>
      </wsdl:input>
      <wsdl:output name="nextRandomResponse">
        <soap:body use="literal"/>
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>
  <wsdl:service name="RandomTestService">
    <wsdl:port binding="tns:RandomTestServiceSoapBinding" name="RandomTestPort">
      <soap:address location="http://localhost:8080/servertest/RandomTest"/>
    </wsdl:port>
  </wsdl:service>
    <wsp:Policy xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="RandomTestServiceSoapBinding_WSAM_Addressing_Policy"><wsam:Addressing><wsp:Policy/></wsam:Addressing></wsp:Policy>
</wsdl:definitions>



3) ant build.xml to generate the client code from WSDL

<?xml version="1.0" encoding="UTF-8"?>
<project default="build" basedir="..">
    <property name="jaxws.classpath" location="C://jaxws-2.2.8/jaxws-ri/lib/*.jar"/>

    <taskdef name="wsimport" classname="com.sun.tools.ws.ant.WsImport">
           <classpath path="${jaxws.classpath}"/>
    </taskdef>

    <target name="build" >
        <!-- For these to work, the JAR files in tools/jaxws-ri must be included in Ant's classpath -->
        <wsimport wsdl="http://localhost:8080/servertest/RandomTest?wsdl"
               verbose="true"
               sourcedestdir="src"
               destdir="bin"
               keep="true">
               <xjcarg value="-enableIntrospection"/>
        </wsimport>
    </target>
</project>



4) Client code

4a) ClientTest.java - Actual client run from client

package test.wsclient;


import java.util.ArrayList;
import java.util.List;


import javax.xml.ws.BindingProvider;
import javax.xml.ws.Endpoint;
import javax.xml.ws.handler.Handler;
import javax.xml.ws.soap.AddressingFeature;


import org.nowhere.services.RandomTest;
import org.nowhere.services.RandomTestService;


public class ClientTest {
   
    public static void main(String args[]) throws Exception {
        ClientTest app = new ClientTest();
        app.testAddressing();
    }
   
    public void testAddressing() throws Exception {
        String REPLY_TO_ADDRESS = "http://localhost:8082/servertest/RandomCallback";
        String FAULT_TO_ADDRESS = "http://localhost:8082/servertest/RandomCallbackFault";
       
        RandomTestService service = new RandomTestService();
        RandomTest port = service.getRandomTestPort(new AddressingFeature());
       
        BindingProvider provider = (BindingProvider) port;
        
        // pass the replyTo address to the handler
        provider.getRequestContext().put("ReplyTo", REPLY_TO_ADDRESS);
        provider.getRequestContext().put("FaultTo", FAULT_TO_ADDRESS);
       
        // Register handlers to set the ReplyTo and FaultTo on the SOAP request sent to the WS endpoint
        List<Handler> handlerChain = new ArrayList<Handler>();
        handlerChain.add(new ClientHandler());
        provider.getBinding().setHandlerChain(handlerChain);


        // Start endpoint to receive callbacks from WS
        Endpoint endpoint = Endpoint.publish(REPLY_TO_ADDRESS, new CallbackSEI());
       
        try {
            port.nextRandom(false);
        } catch( Exception ex ) {
            ex.printStackTrace();
        } finally {
            Thread.sleep(10000);
        }
       
        endpoint.stop();
        System.exit(0);
    }




}


4b) ClientHandler.java - Used to set the wsa ReplyTo address and FaultTo address when sending SOAP request from client to server

package test.wsclient;


import java.util.Set;


import javax.xml.namespace.QName;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPHeader;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.MessageContext.Scope;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;


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


public class ClientHandler implements SOAPHandler<SOAPMessageContext> {
   
    public ClientHandler() {};
   
    @Override
    public Set<QName> getHeaders() {
        return null;
    }
    
    @Override
    public void close(MessageContext arg0) {
       
    }
   
    @Override
    public boolean handleFault(SOAPMessageContext context) {
        return true;
    }
   
    protected void setAnAddress(SOAPHeader header, String tagName, String address) {
        NodeList nodeListReplyTo = header.getElementsByTagName(tagName);
        NodeList nodeListAddress = nodeListReplyTo.item(0).getChildNodes();
        for (int i = 0; i < nodeListAddress.getLength(); i++) {
            Node node = nodeListAddress.item(i);
            if ("Address".equals(node.getLocalName())) {
                node.setTextContent(address);
                break;
            }
        }
    }
   
    protected String getMessageID(SOAPHeader header) {
        NodeList nodeListMessageId = header.getElementsByTagName("MessageID");
        return nodeListMessageId.item(0).getTextContent();
    }       
   
    @Override
    public boolean handleMessage(SOAPMessageContext context) {
        Boolean isOutbound = (Boolean) context.get(SOAPMessageContext.MESSAGE_OUTBOUND_PROPERTY);
        if (isOutbound) {
            try {
                SOAPEnvelope envelope = context.getMessage().getSOAPPart().getEnvelope();
                SOAPHeader header = envelope.getHeader();
                
                /* extract the generated MessageID */
                String messageID = getMessageID(header);
                context.put("MessageID", messageID);
                context.setScope("MessageID", Scope.APPLICATION);
                
                /* change ReplyTo address */
                setAnAddress(header, "ReplyTo", (String) context.get("ReplyTo"));
                setAnAddress(header, "FaultTo", (String) context.get("FaultTo"));
            } catch (Exception ex) {
                throw new RuntimeException(ex);
            }
        }
        
        return true;
    }
}   

4c) CallbackSEI.java - endpoint on the client for server to send the SOAP response back to the client

package test.wsclient;


import javax.annotation.Resource;
import javax.jws.Oneway;
import javax.jws.WebParam;
import javax.jws.WebService;
import javax.xml.ws.Action;
import javax.xml.ws.RequestWrapper;
import javax.xml.ws.WebServiceContext;
import javax.xml.ws.soap.Addressing;


@WebService
@Addressing
//@HandlerChain(file = "/handler-chain.xml")
public class CallbackSEI {
   
    @Resource
    private WebServiceContext context;
   
    /**
     * If there is no namespace specified in the method below, then the CallbackSEI needs to be in the same package as the
     * WS endpoint.
     */
    @Oneway
    @Action(input="http://services.nowhere.org/RandomTest/nextRandomResponse")
    @RequestWrapper(localName="nextRandomResponse", targetNamespace="http://services.nowhere.org/")
    public void handleNotification(@WebParam(name="return")long random) {
        System.out.println("Asynch response received");
        System.out.println( random );
        //System.out.println("This response relates to the message ID: "+ getMessageID());
    }
   
}

In summary:

Server is listening on port 8080

Client will listen in port 8082 for the callback from the server for the SOAP response

Now when I run the client, I see that the proper behaviour as far as ws-addressing is concerned. That is:

client  -- SOAP request ( on port 8080 ) --> server
client <-- HTTP 202 ( empty HTTP body )  --- server
client <-- SOAP response ( on port 8082 )  --- server



All well and good, except that I am getting a NullPointerException on the client side when I call the operation.

With debugging of the SOAP request and responses, I get the following output:

---[HTTP request - http://localhost:8080/servertest/RandomTest]---
Accept: text/xml, multipart/related
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://services.nowhere.org/RandomTest/nextRandomRequest"
User-Agent: JAX-WS RI 2.2.8 svn-revision#13980
<?xml version='1.0' encoding='UTF-8'?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><S:Header><To xmlns="http://www.w3.org/2005/08/addressing">http://localhost:8080/servertest/RandomTest</To><Action xmlns="http://www.w3.org/2005/08/addressing">http://services.nowhere.org/RandomTest/nextRandomRequest</Action><ReplyTo xmlns="http://www.w3.org/2005/08/addressing">
    <Address>http://localhost:8082/servertest/RandomCallback</Address>
</ReplyTo><FaultTo xmlns="http://www.w3.org/2005/08/addressing">
    <Address>http://localhost:8082/servertest/RandomCallbackFault</Address>
</FaultTo><MessageID xmlns="http://www.w3.org/2005/08/addressing">uuid:bcd2f6ef-3034-49e8-b837-dbd6a772fb93</MessageID></S:Header><S:Body><ns2:nextRandom xmlns:ns2="http://services.nowhere.org/"><arg0>false</arg0></ns2:nextRandom></S:Body></S:Envelope>--------------------


---[HTTP response - http://localhost:8080/servertest/RandomTest - 202]---
null: HTTP/1.1 202 Accepted
Content-Length: 0
Content-Type: text/xml;charset=UTF-8
Date: Fri, 18 Jul 2014 08:34:36 GMT
Server: Apache-Coyote/1.1
--------------------


java.lang.NullPointerException
    at com.sun.proxy.$Proxy38.nextRandom(Unknown Source)
    at test.wsclient.ClientTest.testAddressing(ClientTest.java:43)
    at test.wsclient.ClientTest.main(ClientTest.java:18)
---[HTTP request]---
Cache-control: no-cache
Host: localhost:8082
Content-type: text/xml; charset=UTF-8
Content-length: 704
Connection: keep-alive
Pragma: no-cache
User-agent: Apache CXF 2.7.7.redhat-1
Accept: */*
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Header><Action xmlns="http://www.w3.org/2005/08/addressing">http://services.nowhere.org/RandomTest/nextRandomResponse</Action><MessageID xmlns="http://www.w3.org/2005/08/addressing">urn:uuid:65d8d7fc-09e4-494a-a9c5-0a01faf4d7e6</MessageID><To xmlns="http://www.w3.org/2005/08/addressing">http://localhost:8082/servertest/RandomCallback</To><RelatesTo xmlns="http://www.w3.org/2005/08/addressing">uuid:bcd2f6ef-3034-49e8-b837-dbd6a772fb93</RelatesTo></soap:Header><soap:Body><ns2:nextRandomResponse xmlns:ns2="http://services.nowhere.org/"><return>2870062781194370669</return></ns2:nextRandomResponse></soap:Body></soap:Envelope>--------------------


Asynch response received
2870062781194370669

As you can see from the output above, the proxy is throwing an Exception when it receives the HTTP 202 response.

Any ideas ?

Tagged:
This discussion has been closed.