diff --git a/src/cz/marwland/mc/core/MarwCore.java b/src/cz/marwland/mc/core/MarwCore.java index 927f570..54d5286 100644 --- a/src/cz/marwland/mc/core/MarwCore.java +++ b/src/cz/marwland/mc/core/MarwCore.java @@ -2,6 +2,11 @@ package cz.marwland.mc.core; import java.io.File; import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; import java.nio.file.Path; import java.util.HashMap; import java.util.Map; @@ -18,6 +23,7 @@ import cz.marwland.mc.core.storage.StorageCredentials; import cz.marwland.mc.core.storage.impl.MariaDbConnectionFactory; import cz.marwland.mc.core.storage.impl.MySqlConnectionFactory; import cz.marwland.mc.core.util.ConfigUtil; +import cz.marwland.mc.core.util.FileUtil; public class MarwCore extends JavaPlugin { @@ -25,29 +31,33 @@ public class MarwCore extends JavaPlugin { private HashMap features = new HashMap<>(); private static MarwCore INSTANCE = null; private File modulesFolder = null; + private File libsFolder = null; private ModuleClassLoader moduleClassLoader; private SQLStorage sqlStorage; @Override public void onEnable() { INSTANCE = this; - this.getCommand("marw").setExecutor(new MarwCommandExecutor()); - + modulesFolder = this.getModulesFolderPath().toFile(); + modulesFolder.mkdirs(); + libsFolder = this.getLibsFolderPath().toFile(); + libsFolder.mkdir(); configManager = new ConfigManager(this); configManager.registerConfig("config.yml"); configManager.loadConfig("config.yml"); + this.loadModulesAndLibs(); this.loadDatabase(); - - modulesFolder = this.getModulesFolderPath().toFile(); - modulesFolder.mkdirs(); - this.loadAndEnableModules(); + + this.getCommand("marw").setExecutor(new MarwCommandExecutor()); + this.enableModules(); } @Override public void onDisable() { this.features.forEach((k, v) -> v.onDisable()); this.configManager.save(); - this.sqlStorage.shutdown(); + if (sqlStorage != null) + this.sqlStorage.shutdown(); } @Override @@ -56,13 +66,14 @@ public class MarwCore extends JavaPlugin { } public void reload() { - this.reloadConfig(); this.features.forEach((k, v) -> v.onDisable()); + this.reloadConfig(); + this.loadModulesAndLibs(); this.loadDatabase(); - this.loadAndEnableModules(); + this.enableModules(); } - public void loadModules() { + public void loadModulesAndLibs() { if (moduleClassLoader != null) { try { moduleClassLoader.close(); @@ -71,10 +82,17 @@ public class MarwCore extends JavaPlugin { } } - moduleClassLoader = new ModuleClassLoader(); + URLClassLoader pluginClassLoader = (URLClassLoader) this.getClassLoader(); + FileUtil.addFromDirectory(pluginClassLoader, libsFolder); + //FileUtil.addFromDirectory(pluginClassLoader, modulesFolder); + moduleClassLoader = new ModuleClassLoader(pluginClassLoader); + //moduleClassLoader.addFromDirectory(libsFolder); moduleClassLoader.addFromDirectory(modulesFolder); - + } + + public void enableModules() { for (String modClass : configManager.getConfig("config.yml").getStringList("modules")) { + //Feature f = FileUtil.doConstructor((URLClassLoader) this.getClassLoader(), modClass, this); Feature f = moduleClassLoader.doConstructor(modClass, this); if (f == null) continue; @@ -82,10 +100,6 @@ public class MarwCore extends JavaPlugin { this.addFeature(f); this.getLogger().log(Level.INFO, "Loaded " + f.getName() + " @ " + modClass); } - } - - public void loadAndEnableModules() { - this.loadModules(); configManager.load(); this.features.forEach((k, v) -> v.onEnable()); } @@ -114,6 +128,10 @@ public class MarwCore extends JavaPlugin { return this.getDataFolder().toPath().resolve("modules"); } + public Path getLibsFolderPath() { + return this.getDataFolder().toPath().resolve("libs"); + } + public ModuleClassLoader getModuleClassLoader() { return this.moduleClassLoader; } @@ -141,7 +159,7 @@ public class MarwCore extends JavaPlugin { props ); - String method = cfg.getString("method"); + String method = cfg.getString("dataSource.method"); if (method.equalsIgnoreCase("mariadb")) { this.sqlStorage = new SQLStorage(new MariaDbConnectionFactory(creds)); } else if (method.equalsIgnoreCase("mysql")) { diff --git a/src/cz/marwland/mc/core/features/ModuleClassLoader.java b/src/cz/marwland/mc/core/features/ModuleClassLoader.java index 33c0e0d..9eb3ba0 100644 --- a/src/cz/marwland/mc/core/features/ModuleClassLoader.java +++ b/src/cz/marwland/mc/core/features/ModuleClassLoader.java @@ -15,8 +15,8 @@ public class ModuleClassLoader extends URLClassLoader { private ArrayList jars = new ArrayList<>(); - public ModuleClassLoader() { - super(new URL[] { }, ModuleClassLoader.class.getClassLoader()); + public ModuleClassLoader(ClassLoader parent) { + super(new URL[] { }, parent); } public void addFile(File file) throws IOException { diff --git a/src/cz/marwland/mc/core/util/FileUtil.java b/src/cz/marwland/mc/core/util/FileUtil.java new file mode 100644 index 0000000..b1f0553 --- /dev/null +++ b/src/cz/marwland/mc/core/util/FileUtil.java @@ -0,0 +1,99 @@ +package cz.marwland.mc.core.util; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.charset.StandardCharsets; +import java.util.logging.Level; + +import org.bukkit.plugin.java.JavaPlugin; + +import cz.marwland.mc.core.features.Feature; + +public class FileUtil { + + public static String readFileFromJAR(Class parent, String path) throws IOException { + InputStream in = parent.getResourceAsStream(path); + if (in == null) { + System.err.println("Invalid path: " + path); + return null; + } + ByteArrayOutputStream result = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int length; + while ((length = in.read(buffer)) != -1) { + result.write(buffer, 0, length); + } + String ret = result.toString(StandardCharsets.UTF_8.name() ); + in.close(); + return ret; + } + + public static void addFromDirectory(URLClassLoader cl, File modDir) { + if (!modDir.isDirectory()) + throw new IllegalArgumentException("Provided 'modDir' argument is not a directory: " + modDir.getPath()); + + for (File mod : modDir.listFiles()) { + if (mod.isDirectory()) + continue; + int lastdot = mod.getName().lastIndexOf('.'); + if (lastdot < 0) + continue; + + if (!mod.getName().substring(lastdot + 1).equalsIgnoreCase("jar")) + continue; + + try { + Method method = URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{ URL.class }); + method.setAccessible(true); + method.invoke(cl, new Object[]{ mod.toURI().toURL() }); + } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | MalformedURLException e) { + e.printStackTrace(); + } + System.out.println(">>> Loaded: " + mod.getAbsolutePath()); + } + } + + public static Feature doConstructor(ClassLoader cl, String classPath) + throws ClassNotFoundException, ClassCastException, IllegalAccessException, InstantiationException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException { + Class jarClass; + jarClass = Class.forName(classPath, true, cl); + + Class pluginClass; + pluginClass = jarClass.asSubclass(Feature.class); + + return pluginClass.getDeclaredConstructor().newInstance(); + } + + public static Feature doConstructor(ClassLoader cl, String classPath, JavaPlugin plugin) { + try { + return doConstructor(cl, classPath); + } catch (ClassNotFoundException e) { + plugin.getLogger().log(Level.SEVERE, "Cannot find class '" + classPath + "'!"); + } catch (ClassCastException e) { + plugin.getLogger().log(Level.SEVERE, "Class '" + classPath + "' does not extend Feature!"); + } catch (IllegalAccessException e) { + plugin.getLogger().log(Level.SEVERE, "No public constructor in '" + classPath + "'!"); + } catch (InstantiationException e) { + plugin.getLogger().log(Level.SEVERE, "Cannot instantiate '" + classPath + "'!"); + } catch (IllegalArgumentException e) { + plugin.getLogger().log(Level.SEVERE, "Illegal arguments to the constructor of '" + classPath + "'!"); + } catch (InvocationTargetException e) { + plugin.getLogger().log(Level.SEVERE, "Error occured whilst initializing '" + classPath + "'!"); + e.printStackTrace(); + } catch (NoSuchMethodException e) { + plugin.getLogger().log(Level.SEVERE, "Class '" + classPath + "' does not have a public constructor!"); + } catch (SecurityException e) { + plugin.getLogger().log(Level.SEVERE, "SecurityException occured whilst initializing '" + classPath + "'!"); + e.printStackTrace(); + } + return null; + } + +}