2

I am currently making a small plugin framework for a program in Java. Basically GRPluginManager.loadPlugins() loops trough all jar files in a directory, then reads the name of the Main-class of this plugin from a specific file inside of the jar and loads it into the classpath. After that it calls the enable() method of this plugin-Main-class (which extends GRPlugin).

However, it doesn't work. It looks like the loading of the plugin-Main-class works fine and only the parent class (GRPlugin) can not be found. But it definitely exists.

GRPlugin.java

package io.github.grengine.core.plugins;

public class GRPlugin {

    public void enable(){

    }

    public void disable(){

    }
}

GRPluginManager.java

package io.github.grengine.core.plugins;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import org.bukkit.plugin.java.JavaPluginLoader;
import org.yaml.snakeyaml.Yaml;

import io.github.grengine.JarUtils;
import io.github.grengine.Log;
import io.github.grengine.Main;

public class GRPluginManager {

    private static final String PLUGIN_PATH = Main.main.getDataFolder()+File.separator+"storage"+File.separator+"plugins";
    private ArrayList<GRPlugin> plugins = new ArrayList<GRPlugin>();

    public ArrayList<GRPlugin> loadPlugins() throws Exception{

    File filePath = new File(PLUGIN_PATH);
    File files [] = filePath.listFiles();

    //Iterate over files in the plugin directory

    for(File file:files){
        if(file.isFile()){

            Log.Info("Reading "+file);

            ZipFile zipFile = new ZipFile(file);


            String fullyQualifiedName = null;
            String plname = null;
            String plversion = null;

            Enumeration<? extends ZipEntry> entries = zipFile.entries();

            while (entries.hasMoreElements()) {
                ZipEntry ze = (ZipEntry) entries.nextElement();
                if(ze.getName().equals("gre"+File.separator+"ext.yml")){
                    Yaml yaml = new Yaml();
                    InputStream is = zipFile.getInputStream(ze);
                    Map<?, ?> map = (Map<?, ?>) yaml.load(is);

                    fullyQualifiedName = (String) map.get("main");
                    plname = (String) map.get("name");
                    plversion = (String) map.get("version");
                }
            }


            zipFile.close();


            Log.Info("Enabling "+plname+" "+plversion+" ...");

            URLClassLoader classLoader = URLClassLoader.newInstance(new URL[] { new URL("file:"+file) });


            Class<?> clazz = classLoader.loadClass(fullyQualifiedName);
            GRPlugin pl = (GRPlugin) clazz.newInstance();
            pl.enable();

Log.Info("Enabling "+plname+" "+plversion+" ...");

        }else {
         //skip folders
         continue;
       }
    }
    return plugins;
    }

}

And the Main class of the plugin (the class extending GRPlugin) / Main.java

package io.github.plutos;

import io.github.grengine.Log;
import io.github.grengine.core.plugins.GRPlugin;

public class Main extends GRPlugin{

    @Override
    public void enable() {
        Log.Info("ENABLED");

    }

    @Override
    public void disable() {
        Log.Info("DISABLED");

    }

}

Here is the stacktrace:

java.lang.NoClassDefFoundError: io/github/grengine/core/plugins/GRPlugin
        at java.lang.ClassLoader.defineClass1(Native Method) ~[?:1.8.0_05]
        at java.lang.ClassLoader.defineClass(ClassLoader.java:760) ~[?:1.8.0_05]
        at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142) ~[?:1.8.0_05]
        at java.net.URLClassLoader.defineClass(URLClassLoader.java:455) ~[?:1.8.0_05]
        at java.net.URLClassLoader.access$100(URLClassLoader.java:73) ~[?:1.8.0_05]
        at java.net.URLClassLoader$1.run(URLClassLoader.java:367) ~[?:1.8.0_05]
        at java.net.URLClassLoader$1.run(URLClassLoader.java:361) ~[?:1.8.0_05]
        at java.security.AccessController.doPrivileged(Native Method) ~[?:1.8.0_05]
        at java.net.URLClassLoader.findClass(URLClassLoader.java:360) ~[?:1.8.0_05]
        at java.lang.ClassLoader.loadClass(ClassLoader.java:424) ~[?:1.8.0_05]
        at java.net.FactoryURLClassLoader.loadClass(URLClassLoader.java:798) ~[?:1.8.0_05]
        at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ~[?:1.8.0_05]
        at io.github.grengine.core.plugins.GRPluginManager.loadPlugins(GRPluginManager.java:82) ~[?:?]
        at io.github.grengine.Main.onEnable(Main.java:56) ~[?:?]
        at org.bukkit.plugin.java.JavaPlugin.setEnabled(JavaPlugin.java:321) ~[spigot-core.jar:git-Spigot-c5146ba-c637b93]
        at org.bukkit.plugin.java.JavaPluginLoader.enablePlugin(JavaPluginLoader.java:340) [spigot-core.jar:git-Spigot-c5146ba-c637b93]
        at org.bukkit.plugin.SimplePluginManager.enablePlugin(SimplePluginManager.java:405) [spigot-core.jar:git-Spigot-c5146ba-c637b93]
        at org.bukkit.craftbukkit.v1_8_R3.CraftServer.loadPlugin(CraftServer.java:356) [spigot-core.jar:git-Spigot-c5146ba-c637b93]
        at org.bukkit.craftbukkit.v1_8_R3.CraftServer.enablePlugins(CraftServer.java:316) [spigot-core.jar:git-Spigot-c5146ba-c637b93]
        at net.minecraft.server.v1_8_R3.MinecraftServer.s(MinecraftServer.java:418) [spigot-core.jar:git-Spigot-c5146ba-c637b93]
        at net.minecraft.server.v1_8_R3.MinecraftServer.k(MinecraftServer.java:382) [spigot-core.jar:git-Spigot-c5146ba-c637b93]
        at net.minecraft.server.v1_8_R3.MinecraftServer.a(MinecraftServer.java:337) [spigot-core.jar:git-Spigot-c5146ba-c637b93]
        at net.minecraft.server.v1_8_R3.DedicatedServer.init(DedicatedServer.java:256) [spigot-core.jar:git-Spigot-c5146ba-c637b93]
        at net.minecraft.server.v1_8_R3.MinecraftServer.run(MinecraftServer.java:528) [spigot-core.jar:git-Spigot-c5146ba-c637b93]
        at java.lang.Thread.run(Thread.java:745) [?:1.8.0_05]
Caused by: java.lang.ClassNotFoundException: io.github.grengine.core.plugins.GRPlugin
        at java.net.URLClassLoader$1.run(URLClassLoader.java:372) ~[?:1.8.0_05]
        at java.net.URLClassLoader$1.run(URLClassLoader.java:361) ~[?:1.8.0_05]
        at java.security.AccessController.doPrivileged(Native Method) ~[?:1.8.0_05]
        at java.net.URLClassLoader.findClass(URLClassLoader.java:360) ~[?:1.8.0_05]
        at java.lang.ClassLoader.loadClass(ClassLoader.java:424) ~[?:1.8.0_05]
        at java.net.FactoryURLClassLoader.loadClass(URLClassLoader.java:798) ~[?:1.8.0_05]
        at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ~[?:1.8.0_05]
        ... 25 more

I just can't find a solution for this problem, I already tried different ClassLoaders (ClassLoader.getSystemClassLoader() and GRPlugin.class.getClassLoader()) ...

Can you tell what i am doing wrong? If you need more information, feel free to ask!

Thanks in advance, fusionlightcat

fusionlightcat
  • 365
  • 1
  • 5
  • 20

2 Answers2

5

If you don't set a parent classloader to your custom classloader, the system classloader will be used as parent by default.

It seems that in your environment GRPlugin is loaded by a descendant of the system classloader and hence the error.

Use this instead:

URLClassLoader classLoader = URLClassLoader.newInstance(new URL[] { new URL("file:"+file) }, GRPlugin.class.getClassLoader());
idelvall
  • 1,536
  • 15
  • 25
1

You can see plugin system implementation in ipscan open source project. Here is start points for exploration:

  • main Plugin interface. Any of plugins in ipscan must implement it.
  • PluginLoader class is your GRPluginManager analog. It loads plugins from different places (in project jar, external jars etc.) Look here implementation of PluginClassLoader which extends URLClassLoader and loads classes from jar files.
  • feeders folder with some inproject plugins which implemented Plugin interface.

UPDATE: also you can reuse for your plugin system standard java.util.ServiceLoader or some alternative solution

Community
  • 1
  • 1
Andriy Kryvtsun
  • 3,220
  • 3
  • 27
  • 41