1

Like spigot/bukkit plugins id like to be able to load jars in a file and load their classes. Ive managed to get this working with java class loader but the class has to extend a runnable for it to work. Id like to have my own custom interface to implement for each plugin(jar). So I could have functions that get ran on plugin load and so on. If anyone knows how to do this please let me know.

1 Answer 1

2

Plugin structure

A plugin is a .jar file. The plugin has a plugin.properties file with the properties of the plugin.

It looks like this:

plugin.main=com.example.plugins.ExamplePlugin
plugin.name=Example Plugin
plugin.description=Test 123
plugin.version=1.0

The file contains the main class, the plugin name, a description and the version.

A plugin must have a class that inherits from the abstract plugin class. This counts as the main class.

Code structure

Let's begin with the Plugin-class:

package com.example.plugins;

public abstract class Plugin {
    
    protected PluginProperty property;
    
    public abstract void onEnable();
    public abstract void onDisable();

    public PluginProperty getProperty() {
        return property;
    }
    public void setProperty(PluginProperty property) {
        this.property = property;
    }
}

As you may see, I have chosen an abstract class here.

The class consists of two abstract methods (onEnable and onDisable). The plugin also has a PluginProperty object. The equivalent of this class in Spigot would be JavaPlugin.

Let's take a look at the PluginProperty class.

package com.example.plugins;

public class PluginProperty {
    
    private String main;
    
    private String name;
    private String description;
    private double version;
    
    public PluginProperty(String main, String name, String description, double version) {
        this.main = main;
        
        this.name = name;
        this.description = description;
        this.version = version;
    }

    public String getMain() {
        return main;
    }

    public void setMain(String main) {
        this.main = main;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public double getVersion() {
        return version;
    }

    public void setVersion(double version) {
        this.version = version;
    }
}

This class has all the necessary properties of a plugin in it. Most of the things here are self-explanatory, but I would still like to discuss main.

The string holds the name of the main plugin class of the plugin. This is basically the same as the main in the plugin.yml in Spigot.

The loader

Here is the PluginLoader-class:

package com.example.plugins;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Properties;
import java.util.zip.ZipException;

public class PluginLoader {
    
    private static PluginProperty loadPluginProperties(File file) throws ZipException, IOException {
        URL url = file.toURI().toURL();
        String jarURL = "jar:" + url +"!/plugin.properties";
        
        InputStream input;
        URL inputURL = new URL(jarURL);
        JarURLConnection conn = (JarURLConnection)inputURL.openConnection();
        input = conn.getInputStream();
        
        Properties property = new Properties();
        
        property.load(input);
        
        String main = property.getProperty("plugin.main");
        
        String name = property.getProperty("plugin.name");
        String description = property.getProperty("description");
        double version = Double.parseDouble(property.getProperty("plugin.version"));
        
        
        return new PluginProperty(main, name, description, version);
    }
    
    public static Plugin loadPlugin(File file) throws IOException, InstantiationException, IllegalAccessException, ClassNotFoundException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
        if(!file.exists()) {
            return null;
        }
        
        PluginProperty property = loadPluginProperties(file);
        
        URL url = file.toURI().toURL();
        String jarURL = "jar:" + url + "!/";
        URL urls[] = {new URL(jarURL)};
        URLClassLoader ucl = new URLClassLoader(urls);
        Plugin plugin = (Plugin) Class.forName(property.getMain(), true, ucl).getDeclaredConstructor().newInstance();
        plugin.setProperty(property);
        
        return plugin;
    }
}

The private loadPluginProperties method loads the plugin properties and returns the required object. The loadPlugin method loads the main class specified in the properties into an object and returns it.

Examples

I just gave you the basic framework for the plugin system. But how should you use it? Let's start with an example loader.

Here is the Main-class:

package com.example.plugins;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;

public class Main {
    
    public static List<Plugin> plugins = new ArrayList<Plugin>();

    public static void main(String[] args) {
        File[] pluginFiles = new File("plugins").listFiles();
        
        //Load plugins
        for(File f : pluginFiles) {
            if(f.isDirectory()) {
                continue;
            }
            
            if(!f.getName().endsWith(".jar")) {
                continue;
            }
            
            Plugin p = null;
            
            try {
                p = PluginLoader.loadPlugin(f);
            } catch (InstantiationException | IllegalAccessException | ClassNotFoundException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException | IOException e) {
                System.err.println("Failed to load plugin!");
                
                e.printStackTrace();
            }
            
            Main.plugins.add(p);
        }
        
        //Enable plugins
        for(Plugin p : plugins) {
            p.onEnable();
        }
        
        //Disable plugins
        for(Plugin p : plugins) {
            p.onDisable();
        }
    }

}

I won't go into much detail here as I think it's pretty self-explanatory. If you have a question, just ask me through the comments.


After exporting the previously written as a JAR, add it to the classpath in a new project. Don't forget to create a plugin.properties file.

This is an example plugin that is compatible with the .properties file specified above:

package com.example.plugins;

public class ExamplePlugin extends Plugin {

    @Override
    public void onEnable() {
        System.out.println("Hello world!");
    }
    
    @Override
    public void onDisable() {
        
    }
}

When I export this plugin and put it in the plugins folder I get the following output: Hello world!

The End

It would be recommended to use JSON or YAML, XML etc. instead of the built-in Java property files. This is the basic structure for plugins. Have fun!

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.