/*
* File name: PluginFactory.java
* Java version: 6.0
* Author(s): Lukas König
* File created: 20.05.2009
*
* (c) This file and the EAS (Easy Agent Simulation) framework containing it
* is protected by Creative Commons by-nc-sa license. Any altered or
* further developed versions of this file have to meet the agreements
* stated by the license conditions.
*
* In a nutshell
* -------------
* You are free:
* - to Share -- to copy, distribute and transmit the work
*
* Under the following conditions:
* - Attribution -- You must attribute the work in the manner specified by the
* author or licensor (but not in any way that suggests that they endorse
* you or your use of the work).
* - Noncommercial -- You may not use this work for commercial purposes.
* - Share Alike -- If you alter, transform, or build upon this work, you may
* distribute the resulting work only under the same or a similar license to
* this one.
*
* + Detailed license conditions (Germany):
* http://creativecommons.org/licenses/by-nc-sa/3.0/de/
* + Detailed license conditions (unported):
* http://creativecommons.org/licenses/by-nc-sa/3.0/deed.en
*
* This header must be placed in the beginning of any version of this file.
*/
package eas.plugins;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Modifier;
import java.util.LinkedList;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import eas.miscellaneous.StaticMethods;
import eas.miscellaneous.inspectJAR_RAM.Finder;
import eas.miscellaneous.system.Reflections;
import eas.miscellaneous.system.ZipIt;
import eas.plugins.standard.other.NullPlugin;
import eas.simulation.EASRunnable;
import eas.startSetup.GlobalVariables;
import eas.startSetup.ParCollection;
import eas.startSetup.Starter;
/**
* Implements a factory for plugin objects. All plugins contained in the
* package hierarchy get scanned automatically.
*
* @author Lukas König
*/
public class PluginFactory {
/**
* Das Standardplugin.
*/
public static final String STD_PLUGIN = new NullPlugin().id();
/**
* Lists all classes of the framework.
*
* @return All classes of the framework.
* Note that classes from .jar files are ignored.
*/
private static LinkedList<Class<?>> getAllClasses() {
return Reflections.getAllClassesRecursively(GlobalVariables.ROOT_PACKAGE_NAME);
}
@SuppressWarnings("rawtypes")
class SuperFinder extends Finder.DefaultSpecification {
@Override
public boolean isClassFinder() {
return true;
}
@Override
public java.lang.String entryPath() {
return "eas.plugins.Plugin";
}
public LinkedList<Class> classes = new LinkedList<Class>();
@Override
public boolean accepts(final java.lang.Class class_) {
if (Plugin.class.isAssignableFrom(class_)) {
classes.add(class_);
return true;
} else {
return false;
}
}
}
public static LinkedList<Class<Plugin<?>>> STORED_PLUGINS
= loadPluginsFromFile(false);
public static final String PLUGIN_STORAGE_FILE_NAME = "plugins.dat";
@SuppressWarnings("unchecked")
public static LinkedList<Class<Plugin<?>>> loadPluginsFromFile(boolean includeHidden) {
LinkedList<Class<Plugin<?>>> plugins;
try {
plugins = deserializePlugins(PLUGIN_STORAGE_FILE_NAME);
} catch (Exception e) {
GlobalVariables.getPrematureParameters().logInfo("<PluginFactory> Plugins could not be loaded from file: " + e.toString());
GlobalVariables.getPrematureParameters().logInfo("<PluginFactory> Searching plugins in local class tree...");
if (new File(".").getAbsolutePath().contains(" ")) {
GlobalVariables.getPrematureParameters().logWarning(
"<PluginFactory> EAS should not be located in a directory path which contains white spaces. "
+ "Otherwise automatic plugin detection may be delayed.\n"
+ "Path: " + new File(".").getAbsolutePath());
}
plugins = findAllNonAbstractPluginClasses(includeHidden);
GlobalVariables.getPrematureParameters().logInfo("<PluginFactory> " + plugins.size() + " Plugins extracted from local class tree.");
try {
serializePlugins(plugins, PLUGIN_STORAGE_FILE_NAME);
} catch (IOException e1) {
GlobalVariables.getPrematureParameters().log(StaticMethods.LOG_ERROR, "<PluginFactory> Plugins not serialized.");
throw new RuntimeException(e1.toString());
}
}
// Try extracting plugins from JAR file.
if (plugins == null || plugins.size() == 0) {
GlobalVariables.getPrematureParameters().logInfo(
"<PluginFactory> No plugins found in local class tree. Try generating JAR and extracting from there.");
try {
PluginFactory.createJAR("./TEMP-JAR---123.jar");
SuperFinder f = new PluginFactory().new SuperFinder();
Finder ff = new Finder(f);
ff.inspectJar(new JarFile(new File("./TEMP-JAR---123.jar")));
for (Class<?> c : f.classes) {
if (!c.isInterface() && !c.isAnonymousClass()
&& !Modifier.toString(c.getModifiers()).contains("abstract")) {
PluginProperties p = c.getAnnotation(PluginProperties.class);
boolean hidden = false;
if (p != null) {
hidden = p.pluginIsHidden();
}
if (includeHidden || !hidden) {
plugins.add((Class<Plugin<?>>) c);
}
}
}
String succ = "Successful -- ";
if (plugins == null || plugins.size() == 0) {
succ = "Sorry, did not work (try removing white spaces!) -- ";
}
GlobalVariables.getPrematureParameters().logInfo(
"<PluginFactory> " + succ + plugins.size() + " plugins extracted from JAR.");
// Store plugins.
try {
serializePlugins(plugins, PLUGIN_STORAGE_FILE_NAME);
} catch (IOException e1) {
GlobalVariables.getPrematureParameters().log(StaticMethods.LOG_ERROR, "<PluginFactory> Plugins not serialized.");
throw new RuntimeException(e1.toString());
}
} catch (IOException e) {
GlobalVariables.getPrematureParameters().logError(
"<PluginFactory> Plugins could not be extracted from JAR (" + e.toString() + ")");
} finally {
StaticMethods.deleteDAT("TEMP-JAR---123.jar");
}
}
if (plugins == null || plugins.size() == 0) {
GlobalVariables.getPrematureParameters().logError("<PluginFactory> No plugins loaded -- you will not be able to start a simulation.");
plugins = new LinkedList<Class<Plugin<?>>>();
}
return plugins;
}
public static void serializePlugins(LinkedList<Class<Plugin<?>>> o, String filename) throws IOException {
FileOutputStream f_out = new FileOutputStream(filename);
ObjectOutputStream obj_out = new ObjectOutputStream (f_out);
obj_out.writeObject (o);
obj_out.close();
f_out.close();
}
@SuppressWarnings("unchecked")
private static LinkedList<Class<Plugin<?>>> deserializePlugins(String filename)
throws IOException, ClassNotFoundException {
GlobalVariables.getPrematureParameters().logInfo("<PluginFactory> Try loading plugins from file: " + filename + "...");
FileInputStream f_in = new FileInputStream(filename);
ObjectInputStream obj_in = new ObjectInputStream (f_in);
LinkedList<Class<Plugin<?>>> obj = (LinkedList<Class<Plugin<?>>>) obj_in.readObject();
GlobalVariables.getPrematureParameters().logInfo("<PluginFactory> " + obj.size() + " Plugins loaded from file: " + filename + ".");
obj_in.close();
f_in.close();
return obj;
}
/**
* Search all plugins from package tree and jars.
*
* @return All classes from the framework that implement the plugin
* interface and which do NOT contain the plugin property
* "pluginIsHidden = true" within the PluginProperties
* annotation.
*/
@SuppressWarnings({ "unchecked" })
public static LinkedList<Class<Plugin<?>>> findAllNonAbstractPluginClasses(boolean includeHidden) {
LinkedList<Class<Plugin<?>>> classes = new LinkedList<Class<Plugin<?>>>();
// Find plugins in local tree.
try {
for (Class<?> c : getAllClasses()) {
if (Plugin.class.isAssignableFrom(c)) {
if (!c.isInterface() && !c.isAnonymousClass()
&& !Modifier.toString(c.getModifiers()).contains("abstract")) {
PluginProperties p = c.getAnnotation(PluginProperties.class);
boolean hidden = false;
if (p != null) {
hidden = p.pluginIsHidden();
}
if (includeHidden || !hidden) {
classes.add((Class<Plugin<?>>) c);
}
}
}
}
} catch (Exception e) {
}
// Find plugins in jar file.
try {
SuperFinder f = new PluginFactory().new SuperFinder();
Finder ff = new Finder(f);
ff.inspectJar();
for (Class<?> c : f.classes) {
if (!c.isInterface() && !c.isAnonymousClass()
&& !Modifier.toString(c.getModifiers()).contains("abstract")) {
PluginProperties p = c.getAnnotation(PluginProperties.class);
boolean hidden = false;
if (p != null) {
hidden = p.pluginIsHidden();
}
if (includeHidden || !hidden) {
classes.add((Class<Plugin<?>>) c);
}
}
}
} catch (Exception e) {
}
return classes;
}
private static boolean outputWritten = false;
/**
* Gibt ein konstantes Plugin mit dem übergebenen Namen zurück.
*
* @param plugName Der Name des Plugins.
* @param params Die Parameter.
*
* @return Das Plugin, falls ein Matching mit dem Namen gefunden
* wurde, <code>null</code> sonst.
*/
public static Plugin<?> getKonstPlug(
final String plugName,
final ParCollection params) {
Plugin<?> plugin = null;
LinkedList<Class<Plugin<?>>> pluginClasses = PluginFactory.STORED_PLUGINS;
LinkedList<Plugin<?>> plugins = new LinkedList<Plugin<?>>();
Plugin<?> testObject;
for (Class<Plugin<?>> c : pluginClasses) {
try {
testObject = c.newInstance();
plugins.add(testObject);
if (testObject.id().equalsIgnoreCase(plugName)) {
if (plugin != null) {
StaticMethods.log(
StaticMethods.LOG_ERROR,
"<PluginFactory> Plugin id "
+ plugName
+ " exists more than once. "
+ "Chosing randomly.",
params);
}
plugin = testObject;
}
} catch (Exception e) {
StaticMethods.logError("<PluginFactory> There was a problem with plugin " + c + ":\n", params);
e.printStackTrace();
}
}
if (!outputWritten) {
for (Plugin<?> p : plugins) {
try {
StaticMethods.log(
StaticMethods.LOG_INFO,
"<PluginFactory> Plugin found \"" + p.id() + "\".",
params);
} catch (Exception e) {
}
}
outputWritten = true;
}
/*
* Plugins from jar-files have to be included in the code here.
* Use the pattern given once as an example in the comment below.
* Note that possibly existing plugins with the same id will be
* overwritten by the plugin from the jar file (being logged as an
* error).
*/
/* if (plugName.equalsIgnoreCase(new TestPlug1().id())) {
if (plugin != null) {
SonstMeth.log(
SonstMeth.LOG_ERROR,
"Plugin id "
+ plugName
+ " exists more than once. "
+ "Chosing randomly.",
params);
}
plugin = new TestPlug1();
}
*/
if (plugin == null) {
StaticMethods.log(
StaticMethods.LOG_ERROR,
"<PluginFactory> Plugin not found: " + plugName,
params);
// GeneralDialog dia = new GeneralDialog(
// null,
// "WARNING: Plugin " + plugName + " not found.",
// "PluginFactory: " + "\"" + plugName + "\" not found.",
// GeneralDialog.OK_BUTT,
// null);
// dia.setVisible(true);
return null;
}
StaticMethods.log(
StaticMethods.LOG_STAGE1,
"<PluginFactory> Plugin generated: " + plugName,
params);
return plugin;
}
/**
* @param plugStr Der zu überprüfende String.
*
* @return Ob der String zu einem registrierten Plugin gehört.
*/
public static boolean existsPlugin(final String plugStr) {
return PluginFactory.getKonstPlug(plugStr, null) != null;
}
private static void createJAR(String jarFileName) throws IOException {
File jarFile = new File(jarFileName);
// JAR file name + (relative) path.
final JarOutputStream jos = ZipIt.getJARstream(
jarFile,
Starter.class.getName());
ZipIt.addToJARDirectoryRecursively(
jos,
Starter.class.getPackage().getName().split("[.]")[0], // "eas"
jarFile);
jos.close();
}
private static LinkedList<Class<AbstractDefaultPlugin<?>>> getAllADPluginClasses() {
LinkedList<Class<AbstractDefaultPlugin<?>>> list = new LinkedList<>();
for (Class<Plugin<?>> pClass : STORED_PLUGINS) {
try {
@SuppressWarnings("unchecked")
Class<AbstractDefaultPlugin<?>> adpClass = (Class<AbstractDefaultPlugin<?>>)
((AbstractDefaultPlugin<?>) pClass.newInstance()).getClass();
list.add(adpClass);
} catch (Exception e) {}
}
return list;
}
/**
* Returns all plugins that can be used in the context of a given EASRunnable.
*
* @param rbl The runnable to check againts.
*
* @return The list of matching plugins.
*/
public static LinkedList<Class<AbstractDefaultPlugin<?>>> getMatchingPlugins(EASRunnable rbl) {
LinkedList<Class<AbstractDefaultPlugin<?>>> matchingPlugins = new LinkedList<>();
for (Class<AbstractDefaultPlugin<?>> pClass : getAllADPluginClasses()) {
Class<?> desiredClass = StaticMethods.getTypeArguments(AbstractDefaultPlugin.class, pClass).get(0);
if (rbl != null && desiredClass != null && desiredClass.isAssignableFrom(rbl.getClass())) {
matchingPlugins.add(pClass);
}
}
return matchingPlugins;
}
}