/** * Copyright (c) 2012, Hadyn Richard * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is furnished to do * so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package net.scapeemulator.game.plugin; import java.io.File; import java.io.IOException; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import javax.script.ScriptException; import org.codehaus.jackson.JsonFactory; import org.codehaus.jackson.JsonParser; import org.codehaus.jackson.JsonToken; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Created by Hadyn Richard */ public final class PluginLoader { /** * The logger for this class. */ private static final Logger logger = LoggerFactory.getLogger(PluginLoader.class); /** * The JSON factory to create new JSON parsers with. */ private JsonFactory factory = new JsonFactory(); /** * The ruby script environment for the plugin loader. */ private RubyScriptEnvironment scriptEnvironment = new RubyScriptEnvironment(); /** * The map that contains all the parsed plugin data. */ private Map<String, PluginData> parsedPluginData = new HashMap<>(); /** * The set of plugins that have had their scripts loaded. */ private Set<String> loadedPlugins = new HashSet<>(); /** * Constructs a new {@link PluginLoader}; */ public PluginLoader() {} /** * Sets the context for the script environment. * @param context The context for the environment. */ public void setContext(ScriptContext context) { scriptEnvironment.setContext(context); } /** * Loads all the plugins from the specified directory. */ public void load(String dir) throws IOException, ScriptException { load(new File(dir)); } /** * Loads all the plugins from the specified file directory. */ public void load(File dir) throws IOException, ScriptException { scriptEnvironment.eval(new Script(new File(dir, "bootstrap.rb"))); for(File file : dir.listFiles()) { /* Skip over non-directory files */ if(!file.isDirectory()) { continue; } File dataFile = new File(file, "plugin.json"); /* Check to see if the json data file exists */ if(!dataFile.exists()) { logger.warn("missing plugin.json file from '" + file.getName() + "' plugin"); continue; } JsonParser parser = factory.createJsonParser(dataFile); /* Check to see if the JSON structure start is correct */ if(parser.nextToken() != JsonToken.START_OBJECT) { throw new IOException(); } /* Load the plugin data from the JSON file */ PluginData pluginData = new PluginData(); while(parser.nextToken() != JsonToken.END_OBJECT) { String currentName = parser.getCurrentName(); if ( currentName == null ) continue; switch(currentName.toLowerCase()) { case "scripts": /* Check to see if the next token is valid */ if(parser.nextToken() != JsonToken.START_ARRAY) { throw new IOException(); } while(parser.nextToken() != JsonToken.END_ARRAY) { pluginData.addScript(parser.getText()); } break; case "dependencies": /* Check to see if the next token is valid */ if(parser.nextToken() != JsonToken.START_ARRAY) { throw new IOException(); } while(parser.nextToken() != JsonToken.END_ARRAY) { pluginData.addDependency(parser.getText()); } break; case "authors": /* Check to see if the next token is valid */ if(parser.nextToken() != JsonToken.START_ARRAY) { throw new IOException(); } while(parser.nextToken() != JsonToken.END_ARRAY) { /* La de la */ } break; } } parsedPluginData.put(file.getName(), pluginData); } /* Load each of the plugins from its data */ for(Entry<String, PluginData> entry : parsedPluginData.entrySet()) { /* Check if the plugin has already been loaded */ if(loadedPlugins.contains(entry.getKey())) { continue; } loadPlugin(dir, entry.getKey(), entry.getValue()); } logger.info("PluginLoader loaded " + loadedPlugins.size() + " plugins..."); } /** * Loads a plugin from the specified root directory, name, and plugin data. */ private void loadPlugin(File dir, String name, PluginData data) throws IOException, ScriptException { /* Check if the plugin has already been loaded */ if(loadedPlugins.contains(name)) { return; } /* Load all of the dependencies first */ for(String pluginName : data.getDependencies()) { /* Check if the dependency is valid */ if(!parsedPluginData.containsKey(pluginName)) { continue; } loadPlugin(dir, pluginName, parsedPluginData.get(pluginName)); } File scriptDir = new File(dir, name); /* Evaluate each of the scripts */ for(String scriptName : data.getScripts()) { logger.info("Loading: " + scriptName); scriptEnvironment.eval(new Script(new File(scriptDir, scriptName))); } /* Note that the plugin has been loaded */ loadedPlugins.add(name); } /** * Purges all the internal data. */ public void purge() { scriptEnvironment.purge(); parsedPluginData.clear(); loadedPlugins.clear(); } }