/* * Copyright (C) 2004 Anthony Smith * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * ---------------------------------------------------------------------------- * TITLE $Id$ * --------------------------------------------------------------------------- * * --------------------------------------------------------------------------*/ package opendbcopy.plugin; import opendbcopy.config.XMLTags; import opendbcopy.controller.ClasspathLoader; import opendbcopy.controller.MainController; import opendbcopy.io.FileHandling; import opendbcopy.io.ImportFromXML; import opendbcopy.plugin.model.Model; import opendbcopy.plugin.model.exception.MissingAttributeException; import opendbcopy.plugin.model.exception.MissingElementException; import opendbcopy.plugin.model.exception.PluginException; import opendbcopy.plugin.model.exception.UnsupportedAttributeValueException; import opendbcopy.resource.ResourceManager; import org.apache.log4j.Logger; import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.Observable; import java.util.Observer; import java.util.TimeZone; /** * class description * * @author Anthony Smith * @version $Revision$ */ public class PluginManager extends Observable { private static final String TIME_FORMAT = "HH:mm:ss:SSS"; private static Logger logger = Logger.getLogger(PluginManager.class.getName()); private static PluginScheduler pluginScheduler; private MainController controller; private JobManager jm; private ResourceManager rm; private Model currentModel; private SimpleDateFormat df; private HashMap observers; private LinkedList modelsToExecute; private LinkedList modelsLoaded; private Element plugins; private boolean done = false; private boolean started = false; private boolean suspended = false; private boolean interrupted = false; private boolean exceptionOccured = false; private long executionStarted = 0; private int currentExecuteIndex = 0; /** * Creates a new PluginManager object. * * @param controller DOCUMENT ME! * @param pm DOCUMENT ME! * @param plugins DOCUMENT ME! * @param pluginsLocation DOCUMENT ME! * @param pluginFilename DOCUMENT ME! * @param workingModeFilename DOCUMENT ME! * * @throws FileNotFoundException DOCUMENT ME! * @throws JDOMException DOCUMENT ME! * @throws MissingAttributeException DOCUMENT ME! * @throws MissingElementException DOCUMENT ME! * @throws JDOMException DOCUMENT ME! * @throws IOException DOCUMENT ME! * @throws ClassNotFoundException DOCUMENT ME! * @throws InstantiationException DOCUMENT ME! * @throws InvocationTargetException DOCUMENT ME! * @throws IllegalAccessException DOCUMENT ME! * @throws PluginException DOCUMENT ME! * @throws IllegalArgumentException DOCUMENT ME! */ public PluginManager(MainController controller, JobManager pm, Element plugins, String pluginsLocation, String pluginFilename, String workingModeFilename) throws FileNotFoundException, UnsupportedAttributeValueException, MissingAttributeException, MissingElementException, JDOMException, IOException, ClassNotFoundException, InstantiationException, InvocationTargetException, IllegalAccessException, PluginException { if ((pluginsLocation == null) || (pluginFilename == null) || (workingModeFilename == null)) { throw new IllegalArgumentException("Missing arguments values: pluginsLocation=" + pluginsLocation + " pluginFilename=" + pluginFilename + " workingModeFilename=" + workingModeFilename); } this.controller = controller; this.jm = pm; this.rm = controller.getResourceManager(); this.plugins = plugins; modelsLoaded = new LinkedList(); modelsToExecute = new LinkedList(); loadPlugins(pluginsLocation, pluginFilename, workingModeFilename); loadPluginsFromProject(plugins); // set time formatting df = new SimpleDateFormat(TIME_FORMAT); df.setTimeZone(TimeZone.getTimeZone("GMT")); } /** * DOCUMENT ME! */ public final void broadcast() { setChanged(); notifyObservers(); } /** * DOCUMENT ME! * * @param observer DOCUMENT ME! * * @throws IllegalArgumentException DOCUMENT ME! */ public final void registerObserver(Observer observer) { if (observer == null) { throw new IllegalArgumentException("Missing observer"); } this.addObserver(observer); } /** * DOCUMENT ME! * * @param modelElement DOCUMENT ME! * @param title DOCUMENT ME! * * @return DOCUMENT ME! * * @throws MissingAttributeException DOCUMENT ME! * @throws MissingElementException DOCUMENT ME! * @throws ClassNotFoundException DOCUMENT ME! * @throws InstantiationException DOCUMENT ME! * @throws InvocationTargetException DOCUMENT ME! * @throws IllegalAccessException DOCUMENT ME! * @throws PluginException DOCUMENT ME! */ public final Model loadModel(Element modelElement, String title) throws MissingAttributeException, MissingElementException, ClassNotFoundException, InstantiationException, InvocationTargetException, IllegalAccessException, PluginException { Model model = (Model) dynamicallyLoadPluginModel(modelElement); model.setTitle(title); modelsLoaded.add(model); currentModel = model; broadcast(); return model; } /** * DOCUMENT ME! * * @param index DOCUMENT ME! * * @return DOCUMENT ME! */ public final Model getModelLoaded(int index) { return (Model) modelsLoaded.get(index); } /** * DOCUMENT ME! * * @param index DOCUMENT ME! * * @return DOCUMENT ME! */ public final Model getModelToExecute(int index) { return (Model) modelsToExecute.get(index); } /** * DOCUMENT ME! * * @param model DOCUMENT ME! * * @throws IllegalArgumentException DOCUMENT ME! */ public final void setCurrentModel(Model model) { if (model == null) { throw new IllegalArgumentException("Missing model"); } currentModel = model; } /** * inserts given model at index given and shifts former model to the right of the list (index + 1) * * @param sourceIndex DOCUMENT ME! * @param destinationIndex DOCUMENT ME! */ public final void changeOrderPluginToExecute(int sourceIndex, int destinationIndex) { Model sourceModel = (Model) modelsToExecute.remove(sourceIndex); modelsToExecute.add(destinationIndex, sourceModel); broadcast(); } /** * DOCUMENT ME! * * @param model DOCUMENT ME! * @param index DOCUMENT ME! * * @throws IllegalArgumentException DOCUMENT ME! */ public final void addPluginToExecute(Model model, int index) { if (model == null) { throw new IllegalArgumentException("Missing model"); } modelsToExecute.add(index, model); modelsLoaded.remove(model); broadcast(); } /** * DOCUMENT ME! * * @param model DOCUMENT ME! * * @throws IllegalArgumentException DOCUMENT ME! */ public final void addPluginToExecuteLast(Model model) { if (model == null) { throw new IllegalArgumentException("Missing model"); } modelsToExecute.add(model); modelsLoaded.remove(model); broadcast(); } /** * DOCUMENT ME! * * @param model DOCUMENT ME! * * @throws IllegalArgumentException DOCUMENT ME! */ public final void addPluginLoadedLast(Model model) { if (model == null) { throw new IllegalArgumentException("Missing model"); } modelsToExecute.remove(model); modelsLoaded.add(model); broadcast(); } /** * DOCUMENT ME! * * @param index DOCUMENT ME! */ public final void removePluginToExecute(int index) { modelsToExecute.remove(index); broadcast(); } /** * DOCUMENT ME! * * @throws PluginException DOCUMENT ME! */ public final void executePlugins() throws PluginException { if ((modelsToExecute == null) || (modelsToExecute.size() == 0)) { throw new PluginException("No models to execute"); } done = false; interrupted = false; exceptionOccured = false; suspended = false; currentExecuteIndex = 0; pluginScheduler = PluginScheduler.getInstance(this, controller); try { // ensure that plugins are within project ... just in case someone wants to use it. addPluginsToProject(); executionStarted = System.currentTimeMillis(); LinkedList tempPluginsExecuteMetadata = (LinkedList) modelsToExecute.clone(); pluginScheduler.executePlugins(tempPluginsExecuteMetadata); } catch (MissingAttributeException e) { throw new PluginException(e); } catch (ClassNotFoundException e) { throw new PluginException(e); } catch (IllegalAccessException e) { throw new PluginException(e); } catch (InstantiationException e) { throw new PluginException(e); } catch (InvocationTargetException e) { throw new PluginException(e); } } /** * Use this method to test or run a single model -> does not conflict with model chain * * @param model DOCUMENT ME! * * @throws PluginException DOCUMENT ME! */ public final void executePlugin(Model model) throws PluginException { if (model == null) { throw new PluginException("No model to execute"); } done = false; interrupted = false; exceptionOccured = false; suspended = false; currentExecuteIndex = 0; pluginScheduler = PluginScheduler.getInstance(this, controller); try { executionStarted = System.currentTimeMillis(); pluginScheduler.executeSinglePlugin(model); } catch (MissingAttributeException e) { throw new PluginException(e); } catch (ClassNotFoundException e) { throw new PluginException(e); } catch (IllegalAccessException e) { throw new PluginException(e); } catch (InstantiationException e) { throw new PluginException(e); } catch (InvocationTargetException e) { throw new PluginException(e); } } /** * DOCUMENT ME! */ public final void interruptPlugins() { if (pluginScheduler != null) { pluginScheduler.interruptCurrentPlugin(); } } /** * DOCUMENT ME! * * @param dynamicPluginModel DOCUMENT ME! * * @return DOCUMENT ME! * * @throws MissingAttributeException DOCUMENT ME! * @throws ClassNotFoundException DOCUMENT ME! * @throws InstantiationException DOCUMENT ME! * @throws InvocationTargetException DOCUMENT ME! * @throws IllegalAccessException DOCUMENT ME! */ private Object dynamicallyLoadPluginThread(Model dynamicPluginModel) throws MissingAttributeException, ClassNotFoundException, InstantiationException, InvocationTargetException, IllegalAccessException { Class dynClass = Class.forName(dynamicPluginModel.getThreadClassName()); Constructor[] constructors = dynClass.getConstructors(); Object[] params = new Object[2]; params[0] = controller; params[1] = dynamicPluginModel; // works as long there is only one constructor return constructors[0].newInstance(params); } /** * DOCUMENT ME! * * @param model DOCUMENT ME! * * @return DOCUMENT ME! * * @throws MissingAttributeException DOCUMENT ME! * @throws ClassNotFoundException DOCUMENT ME! * @throws InstantiationException DOCUMENT ME! * @throws InvocationTargetException DOCUMENT ME! * @throws IllegalAccessException DOCUMENT ME! * @throws IllegalArgumentException DOCUMENT ME! */ private Object dynamicallyLoadPluginModel(Element model) throws MissingAttributeException, ClassNotFoundException, InstantiationException, InvocationTargetException, IllegalAccessException { if (model == null) { throw new IllegalArgumentException("Missing model"); } if (model.getAttributeValue(XMLTags.MODEL_CLASS) == null) { throw new MissingAttributeException(model, XMLTags.MODEL_CLASS); } else { Class dynClass = Class.forName(model.getAttributeValue(XMLTags.MODEL_CLASS)); Constructor[] constructors = dynClass.getConstructors(); Object[] params = new Object[2]; params[0] = controller; params[1] = model; // works as long there is only one constructor return constructors[0].newInstance(params); } } /** * loads all available plugins, adds libraries to classpath, adds plugin to working mode creates a clone of plugin root to avoid overwriting of * original configuration! * * @param pluginsLocation DOCUMENT ME! * @param pluginFilename DOCUMENT ME! * @param workingModeFilename DOCUMENT ME! * * @throws UnsupportedAttributeValueException DOCUMENT ME! * @throws MissingAttributeException DOCUMENT ME! * @throws MissingElementException DOCUMENT ME! * @throws JDOMException DOCUMENT ME! * @throws FileNotFoundException DOCUMENT ME! * @throws IOException DOCUMENT ME! * @throws IllegalAccessException DOCUMENT ME! * @throws ClassNotFoundException DOCUMENT ME! * @throws InstantiationException DOCUMENT ME! * @throws InvocationTargetException DOCUMENT ME! * @throws RuntimeException DOCUMENT ME! */ private void loadPlugins(String pluginsLocation, String pluginFilename, String workingModeFilename) throws UnsupportedAttributeValueException, MissingAttributeException, MissingElementException, JDOMException, FileNotFoundException, IOException, IllegalAccessException, ClassNotFoundException, InstantiationException, InvocationTargetException { File pluginsDirectory = FileHandling.getFile(pluginsLocation); File[] pluginDirectories = pluginsDirectory.listFiles(); if (pluginDirectories.length > 0) { for (int i = 0; i < pluginDirectories.length; i++) { File pluginDir = pluginDirectories[i]; // read files of plugin ... ignore file(s) if not within a directory if (pluginDir.isDirectory() && (pluginDir.getName().compareToIgnoreCase("CVS") != 0)) { // load working mode File workingModeFile = FileHandling.getFileInDirectory(pluginDir, workingModeFilename); // load plugin file File pluginFile = FileHandling.getFileInDirectory(pluginDir, pluginFilename); Document pluginDoc = ImportFromXML.importFile(pluginFile); // clone plugin root here so that it does overwrite default plugin settings Element pluginRoot = (Element) pluginDoc.getRootElement().clone(); if (pluginRoot.getAttributeValue(XMLTags.IDENTIFIER) == null) { throw new MissingAttributeException(pluginRoot, XMLTags.IDENTIFIER); } File pluginLibDir = FileHandling.getFileInDirectory(pluginDir, "lib"); File[] pluginJars = FileHandling.getFilesInDirectory(pluginLibDir, "jar", "opendbcopy.jar"); File[] pluginZips = FileHandling.getFilesInDirectory(pluginLibDir, "zip", null); File[] pluginResources = FileHandling.getFilesInDirectory(pluginLibDir, "properties", null); if ((pluginJars.length == 0) && (pluginZips.length == 0)) { throw new RuntimeException("Missing libraries (jar or zip) for plugin class " + pluginRoot.getAttributeValue(XMLTags.IDENTIFIER)); } for (int j = 0; j < pluginJars.length; j++) { ClasspathLoader.addResourceToClasspath(pluginJars[j]); } for (int j = 0; j < pluginZips.length; j++) { ClasspathLoader.addResourceToClasspath(pluginZips[j]); } for (int j = 0; j < pluginResources.length; j++) { ClasspathLoader.addResourceToClasspath(pluginResources[j]); } // add working mode controller.addPluginGuiForPlugin(ImportFromXML.importFile(workingModeFile).getRootElement(), pluginRoot); } } } } /** * DOCUMENT ME! */ protected final void addPluginsToProject() { if (modelsToExecute.size() > 0) { // if project contains already plugin elements, remove those first so that content is ordered like process_order plugins.removeChildren(XMLTags.PLUGIN); Iterator itPlugins = modelsToExecute.iterator(); int processOrder = 0; while (itPlugins.hasNext()) { Model model = (Model) itPlugins.next(); model.setProcessOrder(processOrder); Element pluginClone = (Element) model.getPlugin().clone(); plugins.addContent(pluginClone.detach()); processOrder++; } } } /** * DOCUMENT ME! * * @param plugins DOCUMENT ME! * * @throws MissingAttributeException DOCUMENT ME! * @throws MissingElementException DOCUMENT ME! * @throws ClassNotFoundException DOCUMENT ME! * @throws InstantiationException DOCUMENT ME! * @throws InvocationTargetException DOCUMENT ME! * @throws IllegalAccessException DOCUMENT ME! * @throws PluginException DOCUMENT ME! */ protected final void loadPluginsFromProject(Element plugins) throws MissingAttributeException, MissingElementException, ClassNotFoundException, InstantiationException, InvocationTargetException, IllegalAccessException, PluginException { this.plugins = plugins; if (plugins.getChildren(XMLTags.PLUGIN).size() > 0) { Iterator itPlugin = plugins.getChildren(XMLTags.PLUGIN).iterator(); while (itPlugin.hasNext()) { Model model = (Model) dynamicallyLoadPluginModel((Element) itPlugin.next()); if (model.getProcessOrder() >= 0) { addPluginToExecute(model, model.getProcessOrder()); controller.loadPluginGuiFromModel(model, true); } else { throw new MissingAttributeException(model.getPlugin(), XMLTags.PROCESS_ORDER); } } } } /** * DOCUMENT ME! */ protected final void resetModels() { if (plugins.getChildren(XMLTags.PLUGIN).size() > 0) { plugins.removeChildren(XMLTags.PLUGIN); } modelsToExecute.clear(); currentModel = null; broadcast(); } /** * DOCUMENT ME! */ protected final void resetCurrentModel() { currentModel = null; } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ public final int getNbrPluginsToExecute() { if (modelsToExecute == null) { return 0; } else { return modelsToExecute.size(); } } /** * Is set and only set if all plugins were executed without having thrown exceptions. Exceptions which are caught by plugins do not necessarily * stop the whole plugin chain * * @return Returns the done. */ public final boolean isDone() { return done; } /** * DOCUMENT ME! * * @param done The done to set. */ protected final void setDone(boolean done) { this.done = done; String[] param = { df.format(new Date(System.currentTimeMillis() - executionStarted)) }; logger.info(rm.getString("text.execute.done") + " (" + rm.getString("text.execute.time", param) + ")"); broadcast(); // checks first if opendbcopy must be shutdown itself or not controller.shutdownOpendbcopy(); } /** * DOCUMENT ME! * * @return Returns the exceptionOccured. */ public final boolean isExceptionOccured() { return exceptionOccured; } /** * DOCUMENT ME! * * @param exceptionOccured The exceptionOccured to set. */ protected final void setExceptionOccured(boolean exceptionOccured) { this.exceptionOccured = exceptionOccured; broadcast(); } /** * DOCUMENT ME! * * @return Returns the interrupted. */ public final boolean isInterrupted() { return interrupted; } /** * DOCUMENT ME! * * @param interrupted The interrupted to set. */ protected final void setInterrupted(boolean interrupted) { this.interrupted = interrupted; logger.info(rm.getString("text.execute.interruptRequested")); broadcast(); } /** * DOCUMENT ME! * * @return Returns the suspended. */ public final boolean isSuspended() { return suspended; } /** * DOCUMENT ME! * * @param suspended The suspended to set. */ protected final void setSuspended(boolean suspended) { this.suspended = suspended; broadcast(); } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ public final int getCurrentExecuteIndex() { return currentExecuteIndex; } /** * DOCUMENT ME! */ protected final void incrementCurrentExecuteIndex() { currentExecuteIndex++; broadcast(); } /** * DOCUMENT ME! * * @return Returns the currentPluginModel. */ public final Model getCurrentModel() { return currentModel; } /** * DOCUMENT ME! * * @return Returns the modelsToExecute. */ public final LinkedList getModelsToExecute() { return modelsToExecute; } /** * DOCUMENT ME! * * @return Returns the modelsLoaded. */ public final LinkedList getModelsLoaded() { return modelsLoaded; } }