Under some circumstances, the ServiceLoader.load mechanism will fail when used from an isolated plugin path directory and return an incomplete (often empty) ServiceLoader instance.
- Include a META-INF/services/... file in one of the JARS located in a plugin's directory with one or more implementations of that service listed inside. For the sake of example, let's say the name of this service is com.example.MyService
- Program that plugin to invoke ServiceLoader.load(com.example.MyService.class)
- Start the Connect framework, making sure this plugin is included on the plugin path and that it somehow invokes the ServiceLoader.load(...) method
- Observe that the services loaded by that invocation do not include the ones described in the META-INF/services/... file contained in the JAR in the plugin's directory
This is because the ServiceLoader.load(Class) method uses the current thread's context classloader to locate resources and load services. The current thread's context classloader is, in most cases, an instance of DelegatingClassLoader, which will (unless asked to locate resources corresponding to a provider-configuration file for a REST extension or config provider) simply delegate resource location to the parent and, unless asked to locate a class for a recognized plugin, also delegate class loading to the parent. Thus, none of the plugin's JARs are scanned for either provider-configuration files or for actual service classes.
A viable workaround for some cases is to instead use the ServiceLoader.load(Class, ClassLoader) method, specifying the current class's classloader as the second argument. This causes the plugin's PluginClassLoader, which will scan all JARs in the plugin's directory to be used to locate resources and classes.
However, this may not be feasible in all cases, especially when working with external libraries that may be difficult or impossible to apply this workaround on.