javaserviceloader

ServiceConfigurationError: not a subtype


I have been working on a quite complex project for a while. It involves 2 Modules, net.lightbluefoxlabs.dev.multirealm.core.multirealmcore and net.lbflabs.dev.realms.survivalrealm, where the first has an abstract class which in itself is derived from another class (Maybe this is the issue?). When trying to create a service for the class, I get this error: java.util.ServiceConfigurationError: net.lightbluefoxlabs.dev.multirealm.core.multirealmcore.Realms.LoadedRealmPlugin: net.lbflabs.dev.realms.survivalrealm.SurvivalRealm not a subtype even though it does actually extends the correct class. Here is the code loading the service:

File file = new File("Dir/plugin_jar.jar");
URLClassLoader c = new URLClassLoader(new URL[]{file.getAbsoluteFile().toURI().toURL()});
ServiceLoader<LoadedRealmPlugin> loader = ServiceLoader.load(LoadedRealmPlugin.class, c);
LoadedRealmPlugin p = loader.iterator().next(); // Throws the exception
return p;

I have simplified the class and packet/module names since my project is already quite big and I do not think it is necessary. If you need any additional info, please ask.

Edit: Stacktrace

[13:08:24] [Thread-9/WARN]: Exception in thread "Thread-9" java.util.ServiceConfigurationError: net.lightbluefoxlabs.dev.multirealm.core.multirealmcore.Realms.LoadedRealmPlugin: net.lbflabs.dev.realms.survivalrealm.SurvivalRealm not a subtype
[13:08:24] [Thread-9/WARN]:     at java.base/java.util.ServiceLoader.fail(Unknown Source)
[13:08:24] [Thread-9/WARN]:     at java.base/java.util.ServiceLoader$LazyClassPathLookupIterator.hasNextService(Unknown Source)
[13:08:24] [Thread-9/WARN]:     at java.base/java.util.ServiceLoader$LazyClassPathLookupIterator.hasNext(Unknown Source)
[13:08:24] [Thread-9/WARN]:     at java.base/java.util.ServiceLoader$2.next(Unknown Source)
[13:08:24] [Thread-9/WARN]:     at java.base/java.util.ServiceLoader$2.next(Unknown Source)
[13:08:24] [Thread-9/WARN]:     at java.base/java.util.ServiceLoader$3.next(Unknown Source)
[13:08:24] [Thread-9/WARN]:     at net.lightbluefoxlabs.dev.multirealm.core.multirealmcore.Realms.LoadedRealmPlugin.RealmFactory(LoadedRealmPlugin.java:22)
[13:08:24] [Thread-9/WARN]:     at net.lightbluefoxlabs.dev.multirealm.core.multirealmcore.Realms.LoadedRealmPlugin.RealmFactory(LoadedRealmPlugin.java:32)
[13:08:24] [Thread-9/WARN]:     at net.lightbluefoxlabs.dev.multirealm.core.multirealmcore.Realms.UnloadedRealm.GetLoadedRealm(UnloadedRealm.java:25)
[13:08:24] [Thread-9/WARN]:     at net.lightbluefoxlabs.dev.multirealm.core.multirealmcore.Realms.UnloadedRealm.Load(UnloadedRealm.java:39)
[13:08:24] [Thread-9/WARN]:     at net.lightbluefoxlabs.dev.multirealm.core.multirealmcore.Realms.RealmBase.Initialize(RealmBase.java:33)
[13:08:24] [Thread-9/WARN]:     at net.lightbluefoxlabs.dev.multirealm.core.multirealmcore.Realms.RealmBase.<init>(RealmBase.java:25)
[13:08:24] [Thread-9/WARN]:     at net.lightbluefoxlabs.dev.multirealm.core.multirealmcore.Realms.UnloadedRealm.<init>(UnloadedRealm.java:18)
[13:08:24] [Thread-9/WARN]:     at net.lightbluefoxlabs.dev.multirealm.core.multirealmcore.Realms.RealmManager.Initialize(RealmManager.java:25)
[13:08:24] [Thread-9/WARN]:     at net.lightbluefoxlabs.dev.multirealm.core.multirealmcore.Realms.RealmManager.lambda$InitializeAsync$0(RealmManager.java:42)
[13:08:24] [Thread-9/WARN]:     at java.base/java.lang.Thread.run(Unknown Source)

Note that A.ServiceTemplate is net.lightbluefoxlabs.dev.multirealm.core.multirealmcore.Realms.LoadedRealmPlugin and B.ServiceProvider is net.lbflabs.dev.realms.survivalrealm.SurvivalRealm


Solution

  • Class identity is based on the FQN of the class and the ClassLoader it is being loaded in.

    So the class net.lightbluefoxlabs.dev.multirealm.core.multirealmcore.Realms.LoadedRealmPlugin loaded in classloader 1 is a different class then net.lightbluefoxlabs.dev.multirealm.core.multirealmcore.Realms.LoadedRealmPlugin loaded in classloader 2. You will get classcast exceptions or not a subtype exception simply because they aren't the same (from the pov of the JVM).

    As mentioned in the other question regarding the construction of your own ClassLoader you should pass a parent ClassLoader. This ClassLoader should have the shared classes as to allow proper hierarchies of classes to be constructed.