com.aurel.track.util.PluginUtils.java Source code

Java tutorial

Introduction

Here is the source code for com.aurel.track.util.PluginUtils.java

Source

/**
 * Genji Scrum Tool and Issue Tracker
 * Copyright (C) 2015 Steinbeis GmbH & Co. KG Task Management Solutions
    
 * <a href="http://www.trackplus.com">Genji Scrum Tool</a>
 *
 * 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 3 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, see <http://www.gnu.org/licenses/>.
 */

/* $Id:$ */

package com.aurel.track.util;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.jar.JarFile;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import javax.servlet.ServletContext;

import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.util.Version;

import com.aurel.track.StartServlet;
import com.aurel.track.dbase.HandleHome;
import com.aurel.track.lucene.LuceneUtil;

public class PluginUtils {

    private static final Log LOGGER = LogFactory.getLog(PluginUtils.class);

    /****************************Methods which search for classes extending a superclass/interface in a specific package**********************
    /*
     * The following five methods which search for classes extending a superclass/interface in a specific package
     * Warning: this methods doesn't find all subclasses if the package
     * is scattered over more jar files or it is defined in both the /classes folder and jars in classpath.
     * It finds only the first such package unit contained in a jar or the /classes folder,
     * (the first package unit, depends on the classloader of the application server,
     * so it could vary from app. server to app. server)
     * and ignores all other package units which can be found in the classpath.
     * This methods are fast because the search in just one package
     * and it doesn't matter whether the package is in the /classes folder or in a jar file from classpath
     * But you should be sure that this package is not scattered over more places!!!
     */

    /**
     * Get the names of all the classes extending (implementing) a given
     * class (interface) in the currently loaded packages
     * Warning: it does not finds all classes if a package
     * is scattered over more jar files or it is found in both classes and jars!
     * @param superClass the class to inherit from
     */
    /*public static List findSubclassInAllPackages(Class superClass, ServletContext servletContext) {
       List classes = new ArrayList();
       List classesInPackage = new ArrayList();
       Package [] pcks = Package.getPackages();
       for (int i=0;i<pcks.length;i++) {
     classesInPackage = findSubclassInPackage(pcks[i].getName(), superClass, servletContext);
     if (classesInPackage!=null) {
        classes.addAll(classesInPackage);
     }
       }
       return classes;
    }*/

    /**
     * Get the names of all the classes extending (implementing) a given
     * class (interface) in the currently loaded packages (classes and jars).
     * Warning: it does not finds all classes if a package
     * is scattered over more jar files or it is found in both classes and jars!
     * @param superClassName the name of the class to inherit from
     */
    /*public static List findSubclassesInAllPackages(String superClassName, ServletContext servletContext)
    {
       List classes = new ArrayList();
       Class superClass = null;
       try {
     superClass = Class.forName(superClassName);
       } catch (ClassNotFoundException e) {
     LOGGER.error("Superclass not found " + e.getMessage());
     return classes;
       }
       if (superClass!=null)
       {
     classes = findSubclassInAllPackages(superClass, servletContext);
       }
       return classes;
    }*/

    /**
     * Get the names of all the classes extending (implementing) a given
     * class (interface) in a given package (classes and jars).
     * Warning: it does not finds all classes if a package
     * is scattered over more jar files or it is found in both classes and jars!
     * The first package URL wins!
     * @param pckgname the fully qualified name of the package
     * @param tosubclass the fully qualified name of the class to inherit from
     */
    /*public static List findSubclassInPackage(Package thePackage, Class theSuperClass, ServletContext servletContext) {
       return findSubclassInPackage(thePackage.getName(), theSuperClass, servletContext);
    }*/

    /**
     * Get the names of all the classes extending (implementing) a given
     * class (interface) in a given package.
     * Warning: it does not finds all classes if a package
     * is scattered over more jar files or it is found in both classes and jars!
     * The first package URL wins!
     * @param pckgname the fully qualified name of the package
     * @param tosubclass the name of the class to inherit from
     */
    /*public static List findSubclassInPackage(String pckname, String tosubclassname, ServletContext servletContext) {
    List classes = new ArrayList();
    try {
       Class tosubclass = Class.forName(tosubclassname);
       classes = findSubclassInPackage(pckname, tosubclass, servletContext);
    } catch (ClassNotFoundException e) {
       LOGGER.error("Superclass not found " + e.getMessage());
    }
       return classes;
    }*/

    /**
     * Get the names of all the classes inheriting or implementing a given
     * class in a given package.
     * Warning: it does not finds all classes if a package
     * is scattered over more jar files or it is found in both classes and jars!
     * The first package URL wins!
     * @param pckgname the fully qualified name of the package
     * @param tosubclass the Class object to inherit from
     */
    /*public static List findSubclassInPackage(String pckgname, Class tosubclass, ServletContext servletContext) {
       List classes = new ArrayList();
        
       //get the package directory
       //try through class.getResource (should work for most app servers)
       File directory = getPackageFileThroughClassFromClasses(pckgname);
       if (directory==null)
       {
     //try through servletContext (for not expanded .wars, for example by weblogic)
     directory = getPackageFileThroughServletContextFromClasses(servletContext, pckgname);
       }
       if (directory!=null)
       {
     // Get the list of the files contained in the package
     String [] files = directory.list();
     for (int i=0;i<files.length;i++) {
        // we are only interested in .class files
        if (files[i].endsWith(".class")) {
           // removes the .class extension
           String classname = files[i].substring(0,files[i].length()-6);
           try {
              // Try to create an instance of the object
              Class foundClass = Class.forName(pckgname + "." + classname);
              Object o = foundClass.newInstance();
              if (tosubclass.isInstance(o)) {
                 classes.add(foundClass.getName());
              }
           }
           catch (ClassNotFoundException cnfex)
           {}
           catch (InstantiationException iex) {
           // We try to instanciate an interface
           // or an object that does not have a
           // default constructor
           } catch (IllegalAccessException iaex) {
              // The class is not public
           }
        }
     }
       } else {
     try {
        // It does not work with the filesystem: we must
        // be in the case of a package contained in a jar file.
        //it works for expanded .wars
        URL url = getPackageURLFromJars(pckgname);
        if (url!=null) {
           URLConnection urlConn = url.openConnection();
           if (urlConn instanceof JarURLConnection) {
              JarURLConnection conn = (JarURLConnection)urlConn;
              String starts = conn.getEntryName();
              JarFile jfile = conn.getJarFile();
              Enumeration e = jfile.entries();
              while (e.hasMoreElements()) {
                 ZipEntry entry = (ZipEntry)e.nextElement();
                 String entryname = entry.getName();
                 if (entryname.startsWith(starts)
                 &&(entryname.lastIndexOf('/')<=starts.length())
                 &&entryname.endsWith(".class")) {
                 String classname = entryname.substring(0,entryname.length()-6);
                 if (classname.startsWith("/"))
                    classname = classname.substring(1);
                 classname = classname.replace('/','.');
                 try {
                    // Try to create an instance of the object
                    Object o = Class.forName(classname).newInstance();
                    if (tosubclass.isInstance(o)) {
                       classes.add(classname);
                    }
                 } catch (ClassNotFoundException cnfex) {
                    LOGGER.error(cnfex);
                 } catch (InstantiationException iex) {
                    // We try to instanciate an interface
                    // or an object that does not have a
                    // default constructor
                 } catch (IllegalAccessException iaex) {
                    // The class is not public
                 }
                 }
              }
           } else {
              //By BEA Weblogic urlConn will be of type wblogic.utils.zip.ZipURLConnection,
              // but we do not have this class in the classpath, so we try to get the jar file explicit and search the jar file
              //
              String jarURLString = url.toString();
              if (jarURLString!=null)
              {
                 jarURLString = jarURLString.substring(0, jarURLString.lastIndexOf(".jar") + 4);
                 File urlFile = getFileFromURL(new URL(jarURLString));
                 classes = getSubclassesFromJarInLib(urlFile, tosubclass, null, null);
              }
           }
        }
     }
     catch (Exception ex) {
        LOGGER.error("Finding a class in a package in a jar failed with " + ex.getMessage());
     }
       }
       return classes;
    }*/

    /******************************Helper classes************************************************/

    /**
     * Gets the file containing a package
     * class in a given package.
     * Warning: it does not finds all classes if a package containing the class
     * is scattered over more jar files or it is found in both classes and jars!
     * The first package URL wins!
     * @param pckgname the fully qualified name of the package
     * @param tosubclass the Class object to inherit from
     */
    /*private static File getPackageFileThroughClassFromClasses(String pckgname) {
       // Translate the package name into an absolute path
       String name = new String(pckgname);
       if (!name.startsWith("/")) {
     name = "/" + name;
       }
       name = name.replace('.','/');
       URL url = PluginUtils.class.getResource(name);
       return getFileFromURL(url);
    }*/

    /**
     * Gets the file containing a package
     * class in a given package.
     * Warning: it does not finds all classes if a package containing the class
     * is scattered over more jar files or it is found in both classes and jars!
     * The first package URL wins!
     * @param pckgname the fully qualified name of the package
     * @param tosubclass the Class object to inherit from
     */
    /*private static File getPackageFileThroughServletContextFromClasses(ServletContext servletContext, String pckgname) {
       // Translate the package name into an absolute path
       String name = new String(pckgname);
       if (!name.startsWith("/")) {
     name = "/" + name;
       }
       name = name.replace('.','/');
       URL url = null;
       try {
     url = servletContext.getResource("/WEB-INF/classes" + name);
       } catch (MalformedURLException e) {
       }
       return getFileFromURL(url);
    }*/

    /**
     * Gets the file containing a package
     * class in a given package.
     * Warning: it does not finds all classes if a package containing the class
     * is scattered over more jar files or it is found in both classes and jars!
     * The first package URL wins!
     * @param pckgname the fully qualified name of the package
     * @param tosubclass the Class object to inherit from
     */
    /*private static URL getPackageURLFromJars(String pckgname) {
       // Translate the package name into an absolute path
       String name = new String(pckgname);
       if (!name.startsWith("/")) {
     name = "/" + name;
       }
       name = name.replace('.','/');
       return PluginUtils.class.getResource(name);
    }*/

    /**
     * Gets a file with a given path relativ to classes directory from the WEB-INF
     * @param url
     * @return
     */
    public static File getFileFromURL(URL url) {
        File file;
        if (url == null) {
            return null;
        }
        if (url.getPath() != null) {
            file = new File(url.getPath());
            if (file.exists()) {
                //valid url
                return file;
            }
        }

        //valid file through URI?
        //see Bug ID:  4466485 (http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4466485)
        URI uri = null;
        try {
            //get rid of spaces
            uri = new URI(url.toString());
        } catch (URISyntaxException e1) {
        }
        if (uri == null) {
            return null;
        }
        if (uri.getPath() != null) {
            file = new File(uri.getPath());
            if (file.exists()) {
                return file;
            }
        }
        //back to URL from URI
        try {
            url = uri.toURL();
        } catch (MalformedURLException e) {
        }
        if (url != null && url.getPath() != null) {
            file = new File(url.getPath());
            if (file.exists()) {
                //valid url
                return file;
            }
        }
        return null;
    }

    /**
     * Transforms the given URL which may contain escaping characters (like %20 for space)
     * to an URL which may conatin illegal URL characters (like space) but is valid for creating a file based on the resulting URL
     * The escape characters will be changed back to characters that are illegal in URLs (like space) because the file could be
     * constructed just in such a way in some servlet containers like Tomcat5.5
     * @param url
     * @return
     */
    public static URL createValidFileURL(URL url) {
        File file;
        URL fileUrl;
        if (url == null) {
            return null;
        }
        if (url.getPath() != null) {
            file = new File(url.getPath());
            //we have no escape characters or the servlet container can deal with escape characters
            if (file.exists()) {
                //valid url
                return url;
            }
        }
        //valid file through URI?
        //see Bug ID:  4466485 (http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4466485)
        URI uri = null;
        try {
            //get rid of spaces
            uri = new URI(url.toString());
        } catch (URISyntaxException e1) {
        }
        if (uri == null) {
            return null;
        }
        if (uri.getPath() != null) {
            //the decoded path component of this URI
            file = new File(uri.getPath());
            if (file.exists()) {
                try {
                    //file.toURL() does not automatically escape characters that are illegal in URLs
                    fileUrl = file.toURL(); // revert, problems with Windows?
                    if (fileUrl != null) {
                        return fileUrl;
                    }
                } catch (MalformedURLException e) {
                }
            }
        }
        //back to URL from URI
        try {
            url = uri.toURL();
        } catch (MalformedURLException e) {
        }
        if (url != null && url.getPath() != null) {
            file = new File(url.getPath());
            if (file.exists()) {
                //valid url
                return url;
            }
        }
        return null;
    }

    /**
     * Methods which search for classes extending a superclass/interface in a specific jar file
     * It does not deals with packages, is searches the entire content of the jar
     */

    /**
     * Gets the names of all the subclasses from a jar which extend (implement) a superclass (interface).
     * It does not deal with packages, it tries to find all such classes within the jar
     * Sometimes it is not enough to find the classes in a package because:
     *    1.   The subclasses are not necessary in the same package as the superclass
     *    2.   When the same package exists in two or more jar files then only the first jar is found (by URL)
     * @param file the jar file
     * @param superclass
      * @param constructorClasses
      * @param constructorParameters
     * @return
     */
    private static List<String> getSubclassesFromJarInLib(File file, Class superclass, Class[] constructorClasses,
            Object[] constructorParameters) {
        List<String> classes = new ArrayList<String>();
        Object o;
        if (file == null || !file.exists() || superclass == null) {
            return classes;
        }
        JarFile jfile = null;
        try {
            jfile = new JarFile(file);
        } catch (IOException e1) {
        }
        if (jfile != null) {
            LOGGER.debug("Searching in " + file.getName());
            try {
                Enumeration e = jfile.entries();
                while (e.hasMoreElements()) {
                    ZipEntry entry = (ZipEntry) e.nextElement();
                    String entryname = entry.getName();
                    if (//entryname.startsWith(starts) &&
                        //(entryname.lastIndexOf('/')<=starts.length()) &&
                    entryname.endsWith(".class")) {
                        String classname = entryname.substring(0, entryname.length() - 6);
                        if (classname.startsWith("/")) {
                            classname = classname.substring(1);
                        }
                        classname = classname.replace('/', '.');
                        try {
                            // Try to create an instance of the object
                            Class c = null;
                            try {
                                c = Class.forName(classname);
                            } catch (Exception classByName) {
                                LOGGER.debug(
                                        "Finding a class by name " + classname + " failed with " + classByName);
                            }
                            if (c != null) {
                                o = null;
                                if (constructorClasses == null || constructorClasses.length == 0) {
                                    //default constructor
                                    o = c.newInstance();
                                } else {
                                    //probably lucene analyzers with Version
                                    Constructor ct = null;
                                    try {
                                        ct = c.getConstructor(constructorClasses);
                                    } catch (Exception getConst) {
                                        LOGGER.debug(getConst);
                                    }
                                    if (ct == null) {
                                        //older analyzers (lucene<3)
                                        try {
                                            //default constructor. Some analyzers use default constructor even in lucene 3.0
                                            //(although the corresponding javadoc states it with Version parameter)
                                            o = c.newInstance();
                                        } catch (Exception exception) {
                                        }
                                    } else {
                                        try {
                                            if (ct != null) {
                                                o = ct.newInstance(constructorParameters);
                                            }
                                        } catch (Exception callConst) {
                                        }
                                    }
                                }
                                if (o != null && superclass.isInstance(o)) {
                                    classes.add(classname);
                                    LOGGER.debug("Found analizer: " + classname);
                                }
                            }
                        } catch (InstantiationException iex) {
                            // We try to instanciate an interface
                            // or an object that does not have a
                            // default constructor, ignore
                        } catch (IllegalAccessException iaex) {
                            // The class is not public, ignore
                        } catch (Exception ex) {
                            LOGGER.warn("Finding a class in a jar failed with exception " + ex.getMessage());
                        }
                    }
                }
            } catch (Exception t) {
                LOGGER.warn("Finding a class in a jar failed with throwable " + t.getMessage());
            }
        }
        return classes;
    }

    /**
     * Gets the files from a directory which satisfy a FileFilter
     * @param directory
     * @param filter
     * @return
     */
    public static File[] getFilesFromDir(File directory, FilenameFilter filter) {
        File[] arrFiles = new File[0];
        if (directory != null && directory.exists() && directory.isDirectory()) {
            arrFiles = directory.listFiles(filter);
        }
        return arrFiles;
    }

    /**
     * Helper class for filtering the files
     * @author Tamas Ruff
     *
     */
    static class FileFilterByStartAndEnd implements FilenameFilter {
        private String startsWith;
        //default is ".jar"
        private String endsWith = ".jar";

        /**
         * @return Returns the endsWith.
         */
        public String getEndsWith() {
            return endsWith;
        }

        /**
         * @param endsWith The endsWith to set.
         */
        public void setEndsWith(String endsWith) {
            this.endsWith = endsWith;
        }

        /**
         * @return Returns the startsWith.
         */
        public String getStartsWith() {
            return startsWith;
        }

        /**
         * @param startsWith The startsWith to set.
         */
        public void setStartsWith(String startsWith) {
            this.startsWith = startsWith;
        }

        /**
         * accept a file when starts and ends accordingly
         */
        @Override
        public boolean accept(File file, String fileName) {
            /*String absolutPath = file.getAbsolutePath();
            int index = absolutPath.lastIndexOf(File.separator);
            if (index!=-1 && index<absolutPath.length())
            {
               String fileName = file.getAbsolutePath().substring(index+1).toLowerCase();*/
            if (startsWith != null && endsWith != null) {
                return fileName.startsWith(startsWith) && fileName.endsWith(endsWith);
            } else {
                //return all jars
                return true;
            }
            /*}
            return false;*/
        }
    }

    /**
     * Gets the classes from the specified classResourcePath and the specified servletContextResourcePath
     * which are in jars beginning with and ending with  which extend/implement a class
     * @param servletContext
     * @param superClass
     * @param jarStartsWith
     * @param jarEndsWith
     * @param classResourcePath
     * @param servletContextResourcePath
     * @return
     */
    private static Set<String> getClassesFromLibJars(ServletContext servletContext, Class superClass,
            String jarStartsWith, String jarEndsWith, String classResourcePath, String servletContextResourcePath,
            Class[] constructorClasses, Object[] constructorParameters) {
        Set<String> foundAnalyzersSet = new TreeSet<String>();
        try {
            File lib = PluginUtils.getFileFromURL(PluginUtils.class.getResource(classResourcePath));
            //define the file filter
            FileFilterByStartAndEnd filter = new FileFilterByStartAndEnd();
            filter.setStartsWith(jarStartsWith);
            filter.setEndsWith(jarEndsWith);

            //get the analyzers from the jars
            File[] foundJars = PluginUtils.getFilesFromDir(lib, filter);
            if (foundJars != null) {
                for (int i = 0; i < foundJars.length; i++) {
                    foundAnalyzersSet.addAll(PluginUtils.getSubclassesFromJarInLib(foundJars[i], superClass,
                            constructorClasses, constructorParameters));
                }
            }
            //if .jars not yet found - probably because we use unexpaned .war (for ex. by weblogic) - try to get the resources through the servlet context
            if (foundJars == null || foundJars.length == 0) {
                URL urlLib = null;
                try {
                    urlLib = servletContext.getResource(servletContextResourcePath);
                } catch (MalformedURLException e) {
                    LOGGER.error("Getting the URL through getServletContext().getResource(path) failed with "
                            + e.getMessage());
                }
                lib = PluginUtils.getFileFromURL(urlLib);
                foundJars = PluginUtils.getFilesFromDir(lib, filter);
                if (foundJars != null) {
                    for (int i = 0; i < foundJars.length; i++) {
                        foundAnalyzersSet.addAll(PluginUtils.getSubclassesFromJarInLib(foundJars[i], superClass,
                                constructorClasses, constructorParameters));
                    }
                }
            }
        } catch (Exception e) {
            LOGGER.warn("Getting the other lucene analysers failed with " + e.getMessage());
            LOGGER.debug(ExceptionUtils.getStackTrace(e));
        }

        return foundAnalyzersSet;
    }

    /**
     * Search the lucene analyzer implementations from the /WEB-INF/lib
     * The search will take place only in jars with the following naming pattern  "lucene<xxxx>.jar"
     * @param servletContext
     * @return
     */
    public static Set<String> getAnalyzersFromJars(ServletContext servletContext) {
        return getClassesFromLibJars(servletContext, Analyzer.class, "lucene", ".jar", "/../lib/", "/WEB-INF/lib",
                /*new Class[] {Version.class}, new Object[]{LuceneUtil.VERSION}*/null, null);
    }

    /**
     * Search the event subscriber implementations from the jars from the /WEB-INF/lib
     * The search will take place in jars with the following naming pattern  "eventSubscriber<xxxx>.jar" and
     * as an extra in the jar named _wl_cls_gen.jar because Weblogic 10 packs all the classes
     * directory in this jar in the temporary location of the expanded war file
     * @param servletContext
     * @return
     */
    /*public static Set getEventSubscribersFromJars(ServletContext servletContext) {
       Set eventSubscribers = new TreeSet();
       eventSubscribers.addAll(getClassesFromLibJars(servletContext, IEventSubscriber.class, "_wl_cls_gen", ".jar", "/../lib/", "/WEB-INF/lib", null, null));
       eventSubscribers.addAll(getClassesFromLibJars(servletContext, IEventSubscriber.class, "eventSubscriber", ".jar", "/../lib/", "/WEB-INF/lib", null, null));
       return eventSubscribers;
    }*/

    /**
     * Gets a file (or directory) with a specific name from the root of the web application
     * @param servletContext
     * @param fileName
     * @return
     */
    public static File getResourceFileFromWebAppRoot(ServletContext servletContext, String fileName) {
        URL urlDest;
        File file = null;
        //first try to get the template dir through class.getResource(path)
        urlDest = PluginUtils.class.getResource("/../../" + fileName);
        urlDest = createValidFileURL(urlDest);
        if (urlDest != null) {
            file = new File(urlDest.getPath());
        }
        //second try to get the template dir through servletContext.getResource(path)
        if ((file == null || !file.exists()) && servletContext != null) {
            try {
                urlDest = servletContext.getResource("/" + fileName);
                urlDest = createValidFileURL(urlDest);
                if (urlDest != null) {
                    file = new File(urlDest.getPath());
                }
            } catch (MalformedURLException e) {
                LOGGER.error("Getting the URL through getServletContext().getResource(path) failed with "
                        + e.getMessage());
            }
        }
        //third try to get the template dir through servletContext.getRealPath(path)
        if ((file == null || !file.exists()) && servletContext != null) {
            String path;
            //does not work for unexpanded .war
            path = servletContext.getRealPath(File.separator) + fileName;
            file = new File(path);
        }
        return file;
    }

    private static final Class[] parameters = new Class[] { URL.class };

    /**
     * Add class folders and jar files in the lib directories for all plugins.
     * @param tpHome the Genji home directory which includes the plugin directory
     * @return
     */
    public static void addPluginLocationsToClassPath(String tpHome) {
        File[] pluginDirs = new File[0];
        File directory = new File(tpHome + File.separator + HandleHome.PLUGINS_DIR);
        File resources = new File(tpHome + File.separator + HandleHome.XRESOURCES_DIR);
        File logos = new File(tpHome + File.separator + HandleHome.LOGOS_DIR);
        if (directory != null && directory.exists() && directory.isDirectory()) {
            pluginDirs = directory.listFiles();
        } else {
            return;
        }
        // Expand Genji plugins first
        for (File f : pluginDirs) {
            if (!f.isDirectory() && f.getName().endsWith(".tpx")) {
                String targetDir = f.getAbsolutePath().substring(0, f.getAbsolutePath().length() - 4);
                File td = new File(targetDir);
                if (!td.exists() || (td.lastModified() < f.lastModified()) || !td.isDirectory()) {
                    try {
                        unzipFileIntoDirectory(f, directory);
                    } catch (Exception e) {
                        LOGGER.error("Problem unzipping archive: " + e.getMessage());
                    }
                }
            }
        }
        pluginDirs = directory.listFiles();
        // Now process the directories in <TRACKPLUS_HOME>/plugins
        ArrayList<File> files = new ArrayList<File>();
        ArrayList<File> jsdirs = new ArrayList<File>();
        ArrayList<String> bundles = new ArrayList<String>();

        for (File f : pluginDirs) {
            if (f != null && f.exists() && f.isDirectory()) {
                files.add(f);
                File classes = new File(f.getAbsolutePath() + File.separator + "classes");
                if (classes != null && classes.exists() && classes.isDirectory()) {
                    files.add(classes);
                }
                File libs = new File(f.getAbsolutePath() + File.separator + "lib");
                File[] jars = new File[0];
                if (libs != null && libs.exists() && libs.isDirectory()) {
                    jars = libs.listFiles();
                }

                for (File fj : jars) {
                    if (fj.exists() && !fj.isDirectory() && fj.getAbsolutePath().endsWith(".jar")) {
                        files.add(fj);
                    }
                }

                File conf = new File(f.getAbsolutePath() + File.separator + "conf");
                if (conf != null && conf.exists() && conf.isDirectory()) {
                    files.add(conf);
                }

                File js = new File(f.getAbsolutePath() + File.separator + "js");
                if (js != null && js.exists() && js.isDirectory()) {
                    jsdirs.add(f);
                }

                bundles.add("resources." + f.getName());
            }
        }
        URLClassLoader sysloader = null;
        try {
            sysloader = (URLClassLoader) StartServlet.class.getClassLoader();

            Class sysclass = URLClassLoader.class;
            for (File file : files) {
                try {
                    LOGGER.info("Adding " + file.getAbsolutePath() + " to classpath.");
                    Method method = sysclass.getDeclaredMethod("addURL", parameters);
                    method.setAccessible(true);
                    method.invoke(sysloader, new Object[] { file.toURI().toURL() });
                } catch (Exception t) {
                    LOGGER.error(ExceptionUtils.getStackTrace(t));
                }
            }

            try {
                LOGGER.info("Trying to add " + resources.getAbsolutePath() + " to classpath.");
                Method method = sysclass.getDeclaredMethod("addURL", parameters);
                method.setAccessible(true);
                method.invoke(sysloader, new Object[] { resources.toURI().toURL() });
            } catch (Exception t) {
                LOGGER.info("No custom resources found, okay.");
            }

            try {
                LOGGER.info("Trying to add " + logos.getAbsolutePath() + " to classpath.");
                Method method = sysclass.getDeclaredMethod("addURL", parameters);
                method.setAccessible(true);
                method.invoke(sysloader, new Object[] { logos.toURI().toURL() });
            } catch (Exception t) {
                LOGGER.info("No custom logos found, okay.");
            }
        } catch (Exception e) {
            LOGGER.warn(
                    "URLClassloader not supported. You have to add your plugins to the Java classpath manually");
        }
        setJavaScriptExtensionDirs(jsdirs);
        setBundles(bundles);

        return;
    }

    private static ArrayList<File> extJavaScriptDirs = null;

    private static synchronized void setJavaScriptExtensionDirs(ArrayList<File> jsdirs) {
        extJavaScriptDirs = jsdirs;
    }

    private static ArrayList<String> bundles = null;

    private static void setBundles(ArrayList<String> bund) {
        bundles = bund;
    }

    public static ArrayList<String> getBundles() {
        return bundles;
    }

    /**
     * Returns a list with directories from <TRACKPLUS_HOME>/plugins/<pluginXY>/js
     * containing JavaScript extensions for plugins. The classes there are added
     * to the ExtJS namespace path as "pluginXY":path for dynamic loading.
     * @return
     */
    public static List<File> getJavaScriptExtensionDirs() {
        return extJavaScriptDirs;
    }

    /**
     * Unzip this file into the given directory. If the zipFile is called "zipFile.zip",
     * the files will be placed into "targetDir/zipFile".
     * @param zipFile
     * @param targetDir
     */
    public static void unzipFileIntoDirectory(File zipFile, File targetDir) {
        try {
            LOGGER.debug("Expanding Genji extension file " + zipFile.getName());
            int BUFFER = 2048;
            File file = zipFile;
            ZipFile zip = new ZipFile(file);
            String newPath = targetDir + File.separator
                    + zipFile.getName().substring(0, zipFile.getName().length() - 4);
            new File(newPath).mkdir();
            Enumeration zipFileEntries = zip.entries();
            // Process each entry
            while (zipFileEntries.hasMoreElements()) {
                // grab a zip file entry
                ZipEntry entry = (ZipEntry) zipFileEntries.nextElement();
                String currentEntry = entry.getName();
                File destFile = new File(newPath, currentEntry);
                //destFile = new File(newPath, destFile.getName());
                File destinationParent = destFile.getParentFile();
                // create the parent directory structure if needed
                destinationParent.mkdirs();
                if (!entry.isDirectory()) {
                    BufferedInputStream is = new BufferedInputStream(zip.getInputStream(entry));
                    int currentByte;
                    // establish buffer for writing file
                    byte data[] = new byte[BUFFER];
                    // write the current file to disk
                    LOGGER.debug("Unzipping " + destFile.getAbsolutePath());
                    FileOutputStream fos = new FileOutputStream(destFile);
                    BufferedOutputStream dest = new BufferedOutputStream(fos, BUFFER);
                    // read and write until last byte is encountered
                    while ((currentByte = is.read(data, 0, BUFFER)) != -1) {
                        dest.write(data, 0, currentByte);
                    }
                    dest.flush();
                    dest.close();
                    is.close();
                }
            }
        } catch (Exception e) {
            LOGGER.error(ExceptionUtils.getStackTrace(e));
        }
    }

    public static void extractArchiveFromClasspath(String dirInClasspath, String filter, File toDir) {
        URL urlDest;
        File directory = null;
        //first try to get the template dir through class.getResource(path)
        urlDest = PluginUtils.class.getResource(dirInClasspath); // example: "/plugins"
        urlDest = PluginUtils.createValidFileURL(urlDest);
        if (urlDest != null) {
            LOGGER.info("Retrieving archive from " + urlDest.toString());
            directory = new File(urlDest.getPath());
            Long uuid = new Date().getTime();
            File tmpDir = new File(
                    System.getProperty("java.io.tmpdir") + File.separator + "TrackTmp" + uuid.toString());
            if (!tmpDir.isDirectory()) {
                tmpDir.mkdir();
            }
            tmpDir.deleteOnExit();

            File[] files = null;

            if (filter != null && !"".equals(filter)) {
                files = directory.listFiles(new PluginUtils.Filter(filter));
            } else {
                files = directory.listFiles();
            }

            if (files == null || files.length == 0) {
                LOGGER.info("Problem extracting archive: No files.");
                return;
            }
            for (int index = 0; index < files.length; index++) {
                File zipFile = null;
                try {
                    zipFile = files[index];
                    LOGGER.info("Extracting archive from " + files[index].getName());
                    unzipFileIntoDirectory(zipFile, toDir);
                } catch (Exception e) {
                    LOGGER.error("Problem unzipping archive " + files[index].getName());
                    LOGGER.debug(ExceptionUtils.getStackTrace(e));
                }
            }
        }
    }

    static class Filter implements FileFilter {

        // String patternString = ".*http://.*";

        private Pattern pattern = null;

        public Filter(String filter) {
            this.pattern = Pattern.compile(filter);
        }

        @Override
        public boolean accept(File file) {
            Matcher matcher = pattern.matcher(file.getName());
            return matcher.matches();
        }
    }
}