question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Issues with ServiceLoader used by internal JDK classes

See original GitHub issue

First of all, thanks for an awesome library! We’ve been successfully using it in production for close to 4 years now already.

We’ve recently stumbled upon an issue related to ServiceLoader used by a plugin. A little bit of context:

  • the plugin uses the JAX-WS APIs available in OpenJDK 8
  • the JAX-WS implementation to use is automagically discovered using Java’s built-in ServiceLoader mechanism (we have no control over this apart from the next point)
  • the implementation to use is configurable by using a META-INF/services/javax.xml.ws.spi.Provider file that contains the name of the class that implements the interface
  • we’re currently using OpenJDK 8, and OpenJDK 8 bundles a reference implementation of javax.xml.ws.spi.Provider (com.sun.xml.internal.ws.spi.ProviderImpl)
  • a critical point: CXF is available on the class path outside the plugin, and CXF also provides an implementation of javax.xml.ws.spi.Provider (org.apache.cxf.jaxws.spi.ProviderImpl), and has a META-INF/services/javax.xml.ws.spi.Provider file in its JAR that points to that implementation

Our problem is that in some cases, the plugin doesn’t work correctly when the CXF implementation is used, so we would like to force the plugin to use the OpenJDK bundled one instead (or the one from the com.sun.xml.ws:jaxws-rt Maven package when running Java 9+). This doesn’t currently seem to be possible though.

After spending some time stepping through pf4j and JDK code, I think that the problem is that the META-INF/services/javax.xml.ws.spi.Provider file in the plugin is never found/used by java.util.ServiceLoader. This is caused by ServiceLoader using java.lang.ClassLoader::getResources(String), which is a method that isn’t overriden by pf4j’s PluginClassLoader. The java.lang.ClassLoader::getResources(String) method will find both the file from CXF and the one in the plugin, but the one from CXF will be earlier in the returned Enumeration<URL>, causing it to be used.

package java.lang;

public class ClassLoader {

  public Enumeration<URL> getResources(String name) throws IOException {
        @SuppressWarnings("unchecked")
        Enumeration<URL>[] tmp = (Enumeration<URL>[]) new Enumeration<?>[2];
        if (parent != null) {
            tmp[0] = parent.getResources(name);
        } else {
            tmp[0] = getBootstrapResources(name);
        }
        tmp[1] = findResources(name);

        return new CompoundEnumeration<>(tmp);
    }
}

I have been thinking about how to fix this, and one workaround I came up with was to extend PluginClassLoader and override the getResources(String) method, and change it to reverse the order of the returned resources like this:

class ResourcesFromPluginClassLoader extends PluginClassLoader {

    ResourcesFromPluginClassLoader( PluginManager pluginManager, PluginDescriptor pluginDescriptor ) {
        super( pluginManager, pluginDescriptor, pluginManager.getClass().getClassLoader() );
    }

    @Override
    public Enumeration<URL> getResources( String name ) throws IOException {
        @SuppressWarnings( "unchecked" )
        Enumeration<URL>[] tmp = (Enumeration<URL>[])new Enumeration<?>[2];

        tmp[0] = findResources( name );

        if ( getParent() != null ) {
            tmp[1] = getParent().getResources( name );
        }

        return new CompoundEnumeration<>( tmp );
    }
}

Another approach I’ve been thinking of (but not tested) is to override PluginClassLoader and add support for blacklisting certain classes/resources from being seen by a plugin. But in the end I came to the conclusion that I think this is a limitation in pf4j itself. I’d be interesting to hear your thoughts about this issue, and if you have any other ideas of how to solve/workaround it.

We’re currently using the ancient pf4j 0.12.0, but I’ve looked through release notes and the source code and haven’t seen any changes that would fix this issue.

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:8 (8 by maintainers)

github_iconTop GitHub Comments

1reaction
slovdahlcommented, Apr 14, 2020

It’s fine to close it. We haven’t still had the time to update to a version with this change, but we’re using a subclassed PluginClassLoader with the same approach as #337, and it has worked well for more than 6 months already.

1reaction
slovdahlcommented, Sep 4, 2019

I’ll do that, but don’t hold your breath, it’ll likely take some time before I get it done. I’ve been experimenting with updating from 0.12.0 to the latest release, but came across some breaking changes that made it a bit challenging.

Read more comments on GitHub >

github_iconTop Results From Across the Web

ServiceLoader (Java Platform SE 8 ) - Oracle Help Center
A simple service-provider loading facility. A service is a well-known set of interfaces and (usually abstract) classes. A service provider is a specific ......
Read more >
java.util.ServiceLoader does not work inside of an ...
I am using Java 17. I have created a library with a bunch of utility stuff, including a "Service", doing something.
Read more >
Super Charge the Module Aware Service Loader in Java 11
Service Provider: A service provider is a concrete implementation of a service; ServiceLoader: The ServiceLoader class is a facility to locate ...
Read more >
ServiceLoaderRewriter not working for multiple services ...
lang.Class.classForName(Native Method) at java.lang.Class.forName(Class.java:454) at java.util.ServiceLoader$LazyIterator ...
Read more >
[JDK-8230843] Race condition in java.util.ServiceLoader
From the ServiceLoader spec: "Instances of this class are not safe for use by multiple concurrent threads". So pilot error, not an issue....
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found