Issues with ServiceLoader used by internal JDK classes
See original GitHub issueFirst 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
ServiceLoadermechanism (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.Providerfile 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 aMETA-INF/services/javax.xml.ws.spi.Providerfile 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:
- Created 4 years ago
- Comments:8 (8 by maintainers)

Top Related StackOverflow Question
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
PluginClassLoaderwith the same approach as #337, and it has worked well for more than 6 months already.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.