JaxWS webservice client for Java 6 not working in Java 8

I have a JaxWS web service client that has been running Java 6 for many years. Now that Java has been upgraded to version 8, we get a NullPointerException when receiving the port

java.lang.NullPointerException at com.sun.xml.internal.ws.client.ClientContainer$1.getResource(Unknown Source) at com.sun.xml.internal.ws.assembler.MetroConfigLoader.locateResource(Unknown Source) at com.sun.xml.internal.ws.assembler.MetroConfigLoader.locateResource(Unknown Source) at com.sun.xml.internal.ws.assembler.MetroConfigLoader.init(Unknown Source) at com.sun.xml.internal.ws.assembler.MetroConfigLoader.<init>(Unknown Source) at com.sun.xml.internal.ws.assembler.TubelineAssemblyController.getTubeCreators(Unknown Source) at com.sun.xml.internal.ws.assembler.MetroTubelineAssembler.createClient(Unknown Source) at com.sun.xml.internal.ws.client.Stub.createPipeline(Unknown Source) at com.sun.xml.internal.ws.client.Stub.<init>(Unknown Source) at com.sun.xml.internal.ws.client.Stub.<init>(Unknown Source) at com.sun.xml.internal.ws.client.Stub.<init>(Unknown Source) at com.sun.xml.internal.ws.client.sei.SEIStub.<init>(Unknown Source) at com.sun.xml.internal.ws.client.WSServiceDelegate.getStubHandler(Unknown Source) at com.sun.xml.internal.ws.client.WSServiceDelegate.createEndpointIFBaseProxy(Unknown Source) at com.sun.xml.internal.ws.client.WSServiceDelegate.getPort(Unknown Source) at com.sun.xml.internal.ws.client.WSServiceDelegate.getPort(Unknown Source) at com.sun.xml.internal.ws.client.WSServiceDelegate.getPort(Unknown Source) at com.sun.xml.internal.ws.client.WSServiceDelegate.getPort(Unknown Source) at javax.xml.ws.Service.getPort(Unknown Source) at myclient.stub.MyService.<init>(MyService.java:38) 

I tried to launch it using Java 7 version 1.7.0_80, and there it also works, but the very first version of Java 8 throws this exception.

I’ve been banging my head on this for several minutes, so if someone can give me some tips on where to start fixing this, it will be really great.

Here's the WSDL, I edited it a bit, as this is not my service, but hopefully this is enough?

  <?xml version="1.0" encoding="utf-8" standalone="no"?> <wsdl:definitions xmlns:ns1="http://www.dummyservice/sample/interface" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="Sample" targetNamespace="http://www.dummyservice/sample/interface"> <wsdl:types> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" targetNamespace="http://www.dummyservice/sample/interface" xmlns="http://www.dummyservice/sample/interface" elementFormDefault="qualified" jaxb:version="2.0"> <xs:element name="PersonQuery"> <xs:complexType> <xs:sequence> <xs:element name="system" type="xs:string" /> <xs:element name="user" type="xs:string" /> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="PersonReply"> <xs:complexType> <xs:sequence> <xs:element name="Header" type="HeaderType" /> <xs:element name="person" type="PersonType" minOccurs="0" maxOccurs="1" /> <xs:element name="address" type="AddressType" minOccurs="0" maxOccurs="unbounded" /> </xs:sequence> </xs:complexType> </xs:element> <xs:complexType name="HeaderType"> <xs:sequence> <xs:element name="tila" type="StatusType" /> </xs:sequence> </xs:complexType> <xs:simpleType name="StatusType"> <xs:annotation> <xs:appinfo> <jaxb:typesafeEnumClass> <jaxb:typesafeEnumMember name="SUCCESS" value="0001" /> <jaxb:typesafeEnumMember name="FAIL" value="0000" /> </jaxb:typesafeEnumClass> </xs:appinfo> </xs:annotation> <xs:restriction base="xs:string"> <xs:enumeration value="0000" /> <xs:enumeration value="0001" /> </xs:restriction> </xs:simpleType> <xs:complexType name="PersonType"> <xs:sequence> <xs:element name="firstname" type="xs:string" minOccurs="0" /> <xs:element name="lastname" type="xs:string" minOccurs="0" /> </xs:sequence> </xs:complexType> <xs:complexType name="AddressType"> <xs:sequence> <xs:element name="addresstype" type="AddresstypeType" minOccurs="0" /> <xs:element name="streetaddress" type="xs:string" minOccurs="0" /> <xs:element name="city" type="xs:string" minOccurs="0" /> <xs:element name="postalcode" type="xs:string" minOccurs="0" /> </xs:sequence> </xs:complexType> <xs:simpleType name="AddresstypeType"> <xs:annotation> <xs:appinfo> <jaxb:typesafeEnumClass> <jaxb:typesafeEnumMember name="HOME" value="001" /> <jaxb:typesafeEnumMember name="OFFICE" value="002" /> </jaxb:typesafeEnumClass> </xs:appinfo> </xs:annotation> <xs:restriction base="xs:string"> <xs:enumeration value="001" /> <xs:enumeration value="002" /> </xs:restriction> </xs:simpleType> </xs:schema> </wsdl:types> <wsdl:message name="PersonQueryOperationRequest"> <wsdl:part element="ns1:PersonQuery" name="parameters" /> </wsdl:message> <wsdl:message name="PersonQueryOperationResponse"> <wsdl:part element="ns1:PersonReply" name="parameters" /> </wsdl:message> <wsdl:portType name="SamplePort"> <wsdl:operation name="PersonQueryOperation"> <wsdl:input message="ns1:PersonQueryOperationRequest" /> <wsdl:output message="ns1:PersonQueryOperationResponse" /> </wsdl:operation> </wsdl:portType> <wsdl:binding name="SampleSOAP" type="ns1:SamplePort"> <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" /> <wsdl:operation name="PersonQueryOperation"> <soap:operation soapAction="http://www.dummyservice/sample/interface/SampleOperation" /> <wsdl:input> <soap:body use="literal" /> </wsdl:input> <wsdl:output> <soap:body use="literal" /> </wsdl:output> </wsdl:operation> </wsdl:binding> <wsdl:service name="SampleService"> <wsdl:port binding="ns1:SampleSOAP" name="Sample"> <soap:address location="https://127.0.0.1/data/ws" /> </wsdl:port> </wsdl:service> </wsdl:definitions> 

Editing. It seems that the problem with loading classes is a problem, and the class loader of the class classes and the class loader of the classes are null in this ClientContainer.

 private final ResourceLoader loader = new ResourceLoader() { public URL More ...getResource(String resource) throws MalformedURLException { ClassLoader cl = Thread.currentThread().getContextClassLoader(); if (cl == null) { cl = this.getClass().getClassLoader(); } return cl.getResource("META-INF/"+resource); } }; 

When we explicitly install the system class loader in the context class loader before calling ws call, it started working. But is it good for this? I am wondering why this stopped working in Java8, and could this be a problem in their ws implementation?

Hello,

Janne

+6
source share
3 answers

We saw this problem (the same stack trace and all) after upgrading from Java 6 to Java 8. First I will post our solution, and then the following explanation.

For us, the main conditions under which this problem arises is:

  • Java 8 works as an embedded JVM through jvm.dll from a Windows program. I tried to reproduce the problem running in the standalone Java 8 JVM and I could not get it to happen. However, performing battery life was very helpful in diagnosing the problem.
  • Using the proxy method to initialize the connection to the JAX-WS web service, which dynamically creates a stub on the client side using the server-side WSDL.

Workaround:

When you call a call to Service.getPort(Class<T>) , you need to execute it in your thread, separate from the thread in which you made the previous call. This allows you to install ClassLoader in this thread in ClassLoader, which is not a bootstrap class loader, which is the main problem in ClientContainer.java (more on this below). Here is an example of code that works for us. You will have to change it to suit your needs.

 public class YourClass { private YourWebService yourWebService; // You may want to synchronize on this method depending on your use case public YourWebService getYourWebService() { if ( this.yourWebService == null ) { // We create a thread so that we can set the ClassLoader Thread t = new Thread() { public void run() { synchronized(this) { // Get the string of your webservice WSDL from somewhere String url = "http://YOURHOST:YOURPORT/your-web-service/YourWebService?wsdl"; URL srvUrl = null; try { srvUrl = new URL( url ); } catch ( MalformedURLException ex ) { throw new RuntimeException( String.format( "Malformed URL: %s", url ), ex ); } QName qName = new QName( "your-webservice-namespace", "YourWebServiceName" ); Service service = Service.create( srvUrl, qName ); this.yourWebService = service.getPort( YourWebService.class ); notify(); } } }; // Thread.currentThread().getContextClassloader() // returns null in com.sun.xml.internal.ws.client.ClientContainer. // (See http://hg.openjdk.java.net/jdk8/jdk8/jaxws/file/d03dd22762db/src/share/jaxws_classes/com/sun/xml/internal/ws/client/ClientContainer.java, lines 39-47) // To work around that, I force setting of the ContextClassLoader // on this thread (in which the Service.getPort() method will run) so // that when ClientContainer calls Thread.currentThread().getContextClassLoader(), it doesn't get a null // (ie, the bootstrap classloader). // t.setContextClassLoader( YourClass.class.getClassLoader() ); t.start(); // Wait until above thread completes in order to return yourWebService synchronized( t ) { try { t.wait(); } catch ( InterruptedException e ) { e.printStackTrace(); } } } return this.yourWebService; } } 

Additional information and information:

The difficulty in diagnosing this for us was that the problem arose only inside the Windows product running the embedded JVM. Without remote debugging on this JVM, it would take a lot more time to figure out the core of the problem. As soon as I saw that 1) when I ran the same code that calls Service.getPort(Class<T>) inside a stand-alone JVM (outside of a Windows product) and 2) that the ClientContainer class ClientContainer able to get the current ClassLoader stream and 3) that the returned ClassLoader was not a ClassLoader loader (i.e. not null ), it made me realize that I needed to find a way to make sure that the thread that the ClientContainer was ClientContainer did not get the bootstrap ClassLoader. Then the goal was to see if I could find a way to change the ClassLoader allowed by the ClientContainer code.

ClientContainer source: http://hg.openjdk.java.net/jdk8/jdk8/jaxws/file/d03dd22762db/src/share/jaxws_classes/com/sun/xml/internal/ws/client/ClientContainer.java

Note that there are two attempts at the ClientContainer source to allow the class loader. The problem is that if both of these attempts return the bootstrap class loader, a NullPointerException will be thrown at line 45 because cl will be null:

 cl.getResource("META-INF/"+resource); 

This workaround ensures that the class loader allowed by the ClientContainer code will be the class loader installed in your thread.

I sent a ticket for the JAX-WS team to investigate the problem here: https://java.net/jira/browse/JAX_WS-1178

+4
source

Try creating stubs for the webservice client in Java8 again.

Typically, we create a jar of stubs and put them in the classpath. Given that Java is backward compatible, we skip this step of restoring the flag when upgrading to a higher version.

But since it works and only doesn't work in Java 8 for Java 6 and 7, I would recommend creating a Stub again to eliminate any failure in Java 8 backward compatibility.

+1
source

I encountered the same problem after migrating to Java 8. @ Answer 2AGuy helps a lot. However, the problem has occurred for both Mac and Linux for me. And there may be a simpler version to solve the problem. Your code may run below the line before calling getPort() :

Thread.currentThread().setContextClassLoader(XXX.class.getClassLoader());

This will replace the thread context class loader from the Bootstrap class loader to the one you install it, without the overhead, to create a new thread to install the Loader class.

The main reason for this is still a mystery to me. My suspicion is that the thread to create the SOAP service proxy (the service endpoint proxy in the documentation context) is the main thread or created by it whose Loader class is the bootloader class loader, is loaded at the very early stage of the JVM launch. Based on the Java doc for Class#getClassLoader() , NullPointer appears.

Some implementations may use null to represent the loader of the load class. This method returns null in such implementations if this class was loaded by the bootstrap boot loader.

+1
source

All Articles