I'd like to test out the modifications I made for PlugIn Dependency
support with my Super Select Tool. I've already got the code for the
modifications written and it will only require some small changes to
make my Super Select Tool a test implementation.

If you don't remember why I suggested a plug-in dependency system, you
can read about it here:


I have attached the two source code files that will allow OpenJUMP to
support plug-in dependency. The system is very simple. It makes some
changes to the PlugInManger.loadPlugInClasses method and adds to
Collections to the same class. It also adds an interface to the core
in the org.openjump.workbench.plugIn package. This is the interface
that needs to be implemented by plug-ins that want to particpate in
the dependency system.

Please remember that my system does not break existing plug-ins or
require new plug-ins to participate in the plug-in dependency system.
I think I've found an elegant solution that will allow support for
plug-in dependency on an optional basis, and with minimal
modifications to the core.

I'd like to ask concerned and/or interested programmers to look at the
attached source code. Please let me know if you have any concerns
about the code. I will proceed with my test implementation using the
Super Select Tool and will of course fix any bugs I find. I can make a
build of OpenJUMP that contains the support for plug-in dependency
available for others to try if they are worried about my breaking the
nightly build.

The Sunburned Surveyor
/*
 * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI for
 * visualizing and manipulating spatial features with geometry and attributes.
 * 
 * Copyright (C) 2003 Vivid Solutions
 * 
 * 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.
 * 
 * For more information, contact:
 * 
 * Vivid Solutions Suite #1A 2328 Government Street Victoria BC V8T 5G5 Canada
 * 
 * (250)385-6040 www.vividsolutions.com
 */
package com.vividsolutions.jump.workbench.plugin;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;

import org.apache.log4j.Logger;

import com.vividsolutions.jts.util.Assert;
import com.vividsolutions.jump.task.TaskMonitor;
import com.vividsolutions.jump.util.LangUtil;
import com.vividsolutions.jump.util.StringUtil;
import com.vividsolutions.jump.workbench.WorkbenchContext;

/* These imports were added to support Plug-In dependencies. [The Sunburned 
Surveyor] */
import org.openjump.workbench.plugIn.IPlugInDependency;

/**
 * Loads plug-ins (or more precisely, Extensions), and any JAR files that they
 * depend on, from the plug-in directory.
 */
public class PlugInManager {
        private static Logger LOG = Logger.getLogger(PlugInManager.class);
    private TaskMonitor monitor;
    private WorkbenchContext context;
    private Collection configurations = new ArrayList();

    private File plugInDirectory;
    
    /* 
     *  Added a LinkedList to contain plug-ins that were sucessfully intialized.
     * [The Sunburned Surveyor - 20070406]
     */
    LinkedList initializedPlugIns = new LinkedList();
    LinkedList initializationFailures = new LinkedList();

    /**
     * @param plugInDirectory
     *            null to leave unspecified
     */
    public PlugInManager(WorkbenchContext context, File plugInDirectory,
            TaskMonitor monitor) throws Exception {
        this.monitor = monitor;
        Assert.isTrue((plugInDirectory == null)
                || plugInDirectory.isDirectory());
        classLoader = plugInDirectory != null ? new URLClassLoader(
                toURLs((File[]) findFilesRecursively(plugInDirectory).toArray(
                        new File[] {}))) : getClass().getClassLoader();
        this.context = context;
        //Find the configurations right away so they get reported to the splash
        //screen ASAP. [Jon Aquino]
        configurations
                .addAll(plugInDirectory != null ? 
findConfigurations(plugInDirectory)
                        : new ArrayList());
        configurations.addAll(findConfigurations(context.getWorkbench()
                .getProperties().getConfigurationClasses()));
        this.plugInDirectory = plugInDirectory;
    }

    public void load() throws Exception {
        loadPlugInClasses(context.getWorkbench().getProperties()
                .getPlugInClasses(getClassLoader()));
        loadConfigurations();
    }

    private void loadConfigurations() throws Exception {
        for (Iterator i = configurations.iterator(); i.hasNext();) {
            Configuration configuration = (Configuration) i.next();
            configuration.configure(new PlugInContext(context, null, null,
                    null, null));
        }
    }

    public static String name(Configuration configuration) {
        if (configuration instanceof Extension) {
            return ((Extension) configuration).getName();
        }
        return StringUtil.toFriendlyName(configuration.getClass().getName(),
                "Configuration")
                + " (" + configuration.getClass().getPackage().getName() + ")";
    }

    public static String version(Configuration configuration) {
        if (configuration instanceof Extension) {
            return ((Extension) configuration).getVersion();
        }
        return "";
    }

    private Collection findConfigurations(List classes) throws Exception {
        ArrayList configurations = new ArrayList();
        for (Iterator i = classes.iterator(); i.hasNext();) {
            Class c = (Class) i.next();
            if (!Configuration.class.isAssignableFrom(c)) {
                continue;
            }
            LOG.debug("Loading " + c.getName());
            System.out.println("Loading " + c.getName());
            Configuration configuration = (Configuration) c.newInstance();
            configurations.add(configuration);
            monitor.report("Loading " + name(configuration) + " "
                    + version(configuration));
        }
        return configurations;
    }

    private void loadPlugInClasses(List plugInClasses) throws Exception {
        for (Iterator i = plugInClasses.iterator(); i.hasNext();) {
            Class plugInClass = (Class) i.next();
            PlugIn plugIn = (PlugIn) plugInClass.newInstance();
            plugIn.initialize(new PlugInContext(context, null, null, null,
                            null));

            if (plugIn instanceof IPlugInDependency)
            {
                /* 
                 * Check for successful initialization of the plug-in. If it 
was successful, add a reference
                 * to the plug-in to the IntializedPlugIns collection stored in 
this class. 
                 */
                
                /* First, cast to the IPlugInInitializationIndicator interface. 
*/
                IPlugInDependency testSuccess = (IPlugInDependency) plugIn;
                
                /* See if the plug-in was correctly initialized. */
                if(testSuccess.plugInInitializationSuccesful() == true)
                    initializedPlugIns.add(plugIn);
                else
                {
                    String[] errorMessages = new String[3];
                    errorMessages[0] = testSuccess.getClass().getName();
                    errorMessages[1] = testSuccess.getUserErrorMessage();
                    errorMessages[2] = testSuccess.getProgrammerErrorMessage();
                    
                    initializationFailures.add(errorMessages);
                }
            }
            
            /* Now send the LinkedList of initialized plug-ins and the 
LinkedList contain information on plug-ins that
             failed to initialize to all of the plug-ins in the list. */
            Iterator goOverEach = initializedPlugIns.iterator();
            while (goOverEach.hasNext() == true)
            {
                Object toCast = goOverEach.next();
                IPlugInDependency sendTo = (IPlugInDependency) toCast;
                sendTo.plugInInitializationComplete(initializedPlugIns);
                
sendTo.receivePlugInInitializationFailureInfo(initializationFailures);
            }
        }
    }

    private ClassLoader classLoader;

    private Collection findFilesRecursively(File directory) {
        Assert.isTrue(directory.isDirectory());
        Collection files = new ArrayList();
        for (Iterator i = Arrays.asList(directory.listFiles()).iterator(); i
                .hasNext();) {
            File file = (File) i.next();
            if (file.isDirectory()) {
                files.addAll(findFilesRecursively(file));
            }
            if (!file.isFile()) {
                continue;
            }
            files.add(file);
        }
        return files;
    }

    private Collection findConfigurations(File plugInDirectory)
            throws Exception {
        ArrayList configurations = new ArrayList();
        for (Iterator i = findFilesRecursively(plugInDirectory).iterator(); i
                .hasNext();) {
            File file = (File) i.next();
            try {
                configurations.addAll(findConfigurations(classes(new ZipFile(
                        file), classLoader)));
            } catch (ZipException e) {
                //Might not be a zipfile. Eat it. [Jon Aquino]
            }
        }
        return configurations;
    }

    private URL[] toURLs(File[] files) {
        URL[] urls = new URL[files.length];
        for (int i = 0; i < files.length; i++) {
            try {
                urls[i] = new URL("jar:file:" + files[i].getPath() + "!/");
            } catch (MalformedURLException e) {
                Assert.shouldNeverReachHere(e.toString());
            }
        }
        return urls;
    }

    private List classes(ZipFile zipFile, ClassLoader classLoader) {
        ArrayList classes = new ArrayList();
        for (Enumeration e = zipFile.entries(); e.hasMoreElements();) {
            ZipEntry entry = (ZipEntry) e.nextElement();
            //Filter by filename; otherwise we'll be loading all the classes,
            // which takes
            //significantly longer [Jon Aquino]
            if (!(entry.getName().endsWith("Extension.class") || entry
                    .getName().endsWith("Configuration.class"))) {
                //Include "Configuration" for backwards compatibility. [Jon
                // Aquino]
                continue;
            }
            Class c = toClass(entry, classLoader);
            if (c != null) {
                classes.add(c);
            }
        }
        return classes;
    }

    private Class toClass(ZipEntry entry, ClassLoader classLoader) {
        if (entry.isDirectory()) {
            return null;
        }
        if (!entry.getName().endsWith(".class")) {
            return null;
        }
        if (entry.getName().indexOf("$") != -1) {
            //I assume it's not necessary to load inner classes explicitly.
            // [Jon Aquino]
            return null;
        }
        String className = entry.getName();
        className = className.substring(0, className.length()
                - ".class".length());
        className = StringUtil.replaceAll(className, "/", ".");
        Class candidate;
        try {
            candidate = classLoader.loadClass(className);
        } catch (ClassNotFoundException e) {
            Assert.shouldNeverReachHere("Class not found: " + className
                    + ". Refine class name algorithm.");
            return null;
        } catch (Throwable t) {
            LOG.error("Throwable encountered loading " + className
                    + ":");
            //e.g. java.lang.VerifyError: class
            // org.apache.xml.serialize.XML11Serializer
            //overrides final method [Jon Aquino]
            t.printStackTrace(System.out);
            return null;
        }
        return candidate;
    }

    public Collection getConfigurations() {
        return Collections.unmodifiableCollection(configurations);
    }

    /**
     * To access extension classes, use this ClassLoader rather than the default
     * ClassLoader. Extension classes will not be present in the latter.
     */
    public ClassLoader getClassLoader() {
        return classLoader;
    }
    /**
     * @return possibly null
     */
    public File getPlugInDirectory() {
        return plugInDirectory;
    }
}
/*
 * IPlugInDependency.java
 *
 * Created on April 6, 2007, 9:18 AM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package org.openjump.workbench.plugIn;

import java.util.Collection;

public interface IPlugInDependency
{
    public abstract void plugInInitializationComplete(Collection 
argInitializedPlugIns);
    
    public boolean plugInInitializationSuccesful();
    
    public String getUserErrorMessage();
    
    public String getProgrammerErrorMessage();
    
    public void receivePlugInInitializationFailureInfo(Collection argFailures);
}
-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2005.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
_______________________________________________
Jump-pilot-devel mailing list
Jump-pilot-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/jump-pilot-devel

Reply via email to