1

Situation

I'm developing an application that uses Java's URLClassLoader and ServiceLoader to load jar files. Inside these jar files is the provider that implements my interface. The folder structure is as described in this post by oracle meaning that:

  • The interface is in the same directory as the class that implements the interface in the plugin (com.x.projectname.plugin.IInterface.java). In the plugin, both the interface and the class that implements it are in the com.x.projectname.plugin folder.
  • In the plugin there's a resources.META-INF.services directory with one file in it: com.x.projectname.plugin.IInterface with the following content: com.x.projectname.plugin.ClassThatImplementsIInterface.

When running on my local machine (tested with Oracle JDK 1.8 162 and OpenJDK 1.8 171) the plugin is loaded in fine and the application can use the plugin as desired.

The problem

When running the main application in a docker container it cannot load the desired plugin. The Docker container has a folder on the machine mounted inside it, where the plugin resides. The docker image of the application uses openjdk:8-jdk-alpine, but the problem persists whether I use the alpine version or not.

Upon trying to load the plugin using Serviceloader.load() (See ServiceLoader.load(InterfaceName.class, ClassLoader loader)

it crashes with the following error: Provider com.x.projectname.plugin.InterfaceImplementingClass not a subtype.

Here's the relevant part of the stacktrace:

Caused by: java.util.ServiceConfigurationError: com.x.projectname.plugin.IInterface: Provider com.x.projectname.plugin.ImplementingClass not a subtype
        at java.util.ServiceLoader.fail(ServiceLoader.java:239) ~[na:1.8.0_151]
        at java.util.ServiceLoader.access$300(ServiceLoader.java:185) ~[na:1.8.0_151]
        at java.util.ServiceLoader$LazyIterator.nextService(ServiceLoader.java:376) ~[na:1.8.0_151]
        at java.util.ServiceLoader$LazyIterator.next(ServiceLoader.java:404) ~[na:1.8.0_151]
        at java.util.ServiceLoader$1.next(ServiceLoader.java:480) ~[na:1.8.0_151]
        at com.x.projectname.loader.PluginLoader.loadPlugins(PluginLoader.java:118) ~[classes!/:na]
        at com.x.projectname.loader.PluginLoader.initializePluginLoading(PluginLoader.java:67) ~[classes!/:na]
        at com.x.projectname.service.PluginService.<init>(PluginService.java:37) ~[classes!/:na]
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:1.8.0_151]
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) ~[na:1.8.0_151]
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:1.8.0_151]

Any ideas why it won't load plugins when ran inside a Docker container?

EDIT 1: Dockerfile and docker run command

FROM openjdk:8-jdk-alpine
ARG JAR_FILE
ADD ${JAR_FILE} /CICD-dashboard.jar

# Remote debugging port for intelliJ == address
EXPOSE 50505
ENTRYPOINT [ "java", "-Xrunjdwp:transport=dt_socket,address=50505,suspend=n,server=y", "-jar", "/X-project.jar"]

The docker run command:

docker run -d -v /home/folder/pluginfolder:/pluginfolder -p=50505:50505 image-name
Alihossein shahabi
  • 4,034
  • 2
  • 33
  • 53
Oromë
  • 199
  • 2
  • 16
  • Can you please upload your Dockerfile which you used to build the image and the `docker run` command or `docker-compose.yml` whichever you used to start the container. – Saqib Ahmed Apr 26 '18 at 10:14
  • @SaqibAhmed Of course, added it! – Oromë Apr 26 '18 at 10:20
  • If you run this jar file on your local system with `java -jar ..`, does it work? – Saqib Ahmed Apr 26 '18 at 10:28
  • Just tested it, and no it does not. Which seems very weird because running it through intelliJ does work. – Oromë Apr 26 '18 at 10:42
  • It is not weird. That's the problem all along. It's not a docker related problem. You'll have to create an [Uberjar](https://dzone.com/articles/creating-executable-uber-jar%E2%80%99s) of your Java code so that it contains all the dependencies inside. – Saqib Ahmed Apr 26 '18 at 10:54
  • What build tool you are using? Maven, Gradle etc – Saqib Ahmed Apr 26 '18 at 10:54
  • Alright I made a fat/Uberjar using shadowJar (http://imperceptiblethoughts.com/shadow/#configuring_shadow), and now it works locally. I still get the same error when running it in a docker container though. – Oromë Apr 26 '18 at 13:16
  • Just to be sure, you created a new image from the given dockerfile and started a container from that image?? Also try to remove images with `docker rmi` and build a fresh image. If it works on your machine with `java -jar`, it should also work in the docker container with the same arguments. – Saqib Ahmed Apr 26 '18 at 13:30
  • Apologies for the late response @SaqibAhmed. As it turns out, I was not building the fat jar correctly; that indeed was the solution! – Oromë May 01 '18 at 12:37

1 Answers1

1

As it turns out, the answer was as Saqib said in a comment: build a fat jar. I used shadowJar for this: http://imperceptiblethoughts.com/shadow/#configuring_shadow.

Oromë
  • 199
  • 2
  • 16