Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NullPointerException in javax.xml.ws.Service.<init> #161

Closed
dsgrieve opened this issue Feb 19, 2021 · 1 comment · Fixed by #162
Closed

NullPointerException in javax.xml.ws.Service.<init> #161

dsgrieve opened this issue Feb 19, 2021 · 1 comment · Fixed by #162

Comments

@dsgrieve
Copy link

Call to Provider.provider() from javax.xml.ws.Service constructors returns null in Java 11 if osgi-resource-locator is on the classpath. This jar can be on the classpath as a result of a transitive dependency. The cause of the NullPointerException is because the call to isOSGi() from javax.xml.ws.spi.FactoryFinder#find can return true in Java 11 even if the application is not OSGi; whereas in Java 1.8, the call would return false under the same circumstance.

The NullPointerException actually comes as a result of org.glassfish.hk2.osgiresourcelocator.ServiceLoader#lookupProviderInstance returning null to javax.xml.ws.spi.FactoryFinder#lookupUsingOSGiServiceLoader. The reason ServiceLoader#lookupProviderInstance returns null is because the ServiceLoader#initialize method has never been called. The reason the initialize method has never been called is because this is not an OSGi application which would normally load and initialize the bundle.

The lookupUsingOSGiServiceLoader method is invoked because the call to javax.xml.ws.spi.FactoryFinder#isOSGi() returns true. On JDK1.8, isOSGi() returns false. So why does isOSGi() return true? All the method does is check to see if Class.forName("org.glassfish.hk2.osgiresourcelocator.ServiceLoader") throws a ClassNotFoundException. On JDK 11, the exception is not thrown, but it is thrown on JDK 1.8. To understand why, we need to look at what happens in Class.forName.

The first thing Class.forName does is find the class for the caller using Reflection#getCallerClass(). Both 1.8 and 11 return the FactoryFinder class, as expected. Next, it finds the ClassLoader for the caller class using (package-private) ClassLoader#getClassLoader. And here lies the difference. On 1.8, this returns null, which is the bootstrap class loader. On 11, this returns the application class loader.

The reason we get the bootstrap class loader on 1.8 is that the javax.xml.ws package is part of the JDK in 1.8. In Java 9, it was removed (along with other Java EE classes - see JEP 320: Remove the Java EE and CORBA Modules). So, on 11, these classes have to be added to the classpath. This is why when Class.forName gets the class loader for javax.xml.ws.spi.FactoryFinder.class on Java 11, it gets the application class loader. And the lookup of the o.g.h.o.ServiceLoader class succeeds on 11 because osgi-resource-locator-1.0.1.jar is also on the classpath (in this case, due to a transitive dependency).

It is worth noting that the FactoryFinder#find method is coded differently in jaxws-api than it was in JDK 1.8. In Java 11, the method goes through a sequence of checks to find the service provider. First it looks to see if there is a jaxws.properties file in the JRE and uses the properties there. If jaxws.properties is not found, it looks for the system property javax.xml.ws.spi.Provider. Failing that, it checks isOSGi(). If the OSGi check fails it assumes the default of com.sun.xml.internal.ws.spi.ProviderImpl.

The workaround is to set the system property javax.xml.ws.spi.Provider=com.sun.xml.internal.ws.spi.ProviderImpl and add a dependency to jaxws-ri to the project.

Recommended diff is:
`
diff --git a/api/src/main/java/jakarta/xml/ws/spi/FactoryFinder.java b/api/src/main/java/jakarta/xml/ws/spi/FactoryFinder.java
index 4e259d5..7375203 100644
--- a/api/src/main/java/jakarta/xml/ws/spi/FactoryFinder.java
+++ b/api/src/main/java/jakarta/xml/ws/spi/FactoryFinder.java
@@ -71,7 +71,8 @@ class FactoryFinder {

     // handling Glassfish (platform specific default)
     if (isOsgi()) {
  •        return (T) lookupUsingOSGiServiceLoader(factoryId);
    
  •        provider = (T) lookupUsingOSGiServiceLoader(factoryId);
    
  •        if (provider != null) return provider;
       }
    
       if (fallbackClassName == null) {
    

`

@lukasj
Copy link
Contributor

lukasj commented Feb 19, 2021

Thank you for detailed analysis! It came in just in time :-)

It is worth noting that the FactoryFinder#find method is coded differently in jaxws-api than it was in JDK 1.8.

see https://bugs.openjdk.java.net/browse/JDK-8131667 on when and why...

note that the lookup sequence is defined by the JAX-WS spec or now a days by Jakarta XML Web Services spec

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants