001    /**
002     * Launcher.java
003     * jCOLIBRI2 framework. 
004     * @author Juan A. Recio-García.
005     * GAIA - Group for Artificial Intelligence Applications
006     * http://gaia.fdi.ucm.es
007     * 10/01/2007
008     */
009    package jcolibri.util;
010    
011    import java.io.File;
012    import java.io.IOException;
013    import java.lang.reflect.Method;
014    import java.net.URL;
015    import java.net.URLClassLoader;
016    
017    import javax.xml.parsers.DocumentBuilder;
018    import javax.xml.parsers.DocumentBuilderFactory;
019    
020    import org.w3c.dom.Document;
021    import org.w3c.dom.NamedNodeMap;
022    import org.w3c.dom.Node;
023    import org.w3c.dom.NodeList;
024    
025    
026    /**
027     * This class launches a jCOLIBRI application loading dinamically its libraries. 
028     * It has a method that parses the ".classpath" file of the eclipse project to find the libraries that a project needs.
029     * Then it loads the libraries and executes an application.
030     * This eases the process of invoking a jCOLIBRI application.
031     * 
032     * @author Juan A. Recio Garcia
033     * @version 2.0
034     */
035    public class Launcher
036    {
037    
038        /**
039         * Main method used to launch any application loading the libraries from a eclipse classpath file.
040         * The first argument must be the class to launch and the second (optional) is the name of the eclipse classpath file.
041         * If this second argument is not specified it tries with ".classpath". 
042         */
043        public static void main(String[] args)
044        {
045            if(args.length == 0)
046            {
047                    System.out.println("Usage: jcolibri.util.Launcher MainClass [eclipse_classpath_file]");
048                    System.exit(0);
049            }
050    
051            try {
052                    String classpathfile = ".classpath";
053                    if(args.length==2)
054                            classpathfile = args[1];
055                    
056                    System.out.println("Loading class path from file: "+classpathfile);
057                                    
058                    URL[] libraries = getClassPath(classpathfile);
059                    for(int i=0; i<libraries.length; i++)
060                    {
061                            addURLtoClassLoader(libraries[i]);
062                            System.out.println("Adding library: "+libraries[i]);
063                    }
064                    
065                    Class<?> mainClass = Class.forName(args[0]);
066                    org.apache.commons.logging.LogFactory.getLog(Launcher.class).info("Executing class: "+args[0]);
067                    Method mainMethod = mainClass.getMethod("main", args.getClass());
068                    
069                    String[] newargs = new String[args.length-1];
070                    for(int i=1; i<args.length; i++)
071                            newargs[i-1] = args[1];
072                    
073                    Object[] methodparams = new Object[1];
074                    methodparams[0] = newargs;
075                    mainMethod.invoke(null, methodparams);  
076            } catch (Exception e) {
077                    org.apache.commons.logging.LogFactory.getLog(Launcher.class).error("Launching class "+ e.getMessage());
078                    e.printStackTrace();
079                    }
080            org.apache.commons.logging.LogFactory.getLog(Launcher.class).info("Launch finished");
081            //System.exit(0);
082        }
083    
084        /**
085         * Loads a resource pointed by a URL into the Class Loader.
086         */
087        public static void addURLtoClassLoader(URL u) throws IOException {  
088            URLClassLoader sysloader = (URLClassLoader)ClassLoader.getSystemClassLoader();
089            Class<URLClassLoader> sysclass = URLClassLoader.class;
090         
091            try {
092                    //Please don't read this code. It is a dark way to invoke a private method using reflection.
093                    //The clear way consists on creating your own subclass of URLClassLoader.
094                    Class[] parameters = new Class[]{URL.class};
095                    Method method = sysclass.getDeclaredMethod("addURL",parameters);
096                    method.setAccessible(true);
097                    method.invoke(sysloader,new Object[]{ u });
098            } catch (Throwable t) {
099                    t.printStackTrace();
100                    throw new IOException("Error, could not add URL to system classloader");
101            }//end try catch        
102        }//end method
103        
104        /**
105         * Parses a eclipse classpath file returning a list of URLs to the libraries in that file.
106         */
107        protected static URL[] getClassPath(String classpathfile) throws Exception
108        {
109            
110            java.util.ArrayList<URL> CLASSPATH = new java.util.ArrayList<URL>();
111            try
112            {
113                DocumentBuilder db = DocumentBuilderFactory.newInstance()
114                .newDocumentBuilder();
115                Document doc = db.parse(classpathfile);
116                      
117                NodeList nl = doc.getElementsByTagName("classpathentry");
118                for(int i=0; i<nl.getLength(); i++)
119                {
120                    Node n = nl.item(i);
121                    NamedNodeMap nnm = n.getAttributes();
122                    if(nnm.getNamedItem("kind").getNodeValue().equals("lib"))
123                    {
124                        String lib = nnm.getNamedItem("path").getNodeValue();
125                        File f = new File(lib);
126                        CLASSPATH.add(f.toURI().toURL());
127                    }
128                }
129            } catch (Exception e)
130            {
131                    org.apache.commons.logging.LogFactory.getLog(Launcher.class).error("Error obtaining classpath. Revise your classpath file \n"+e.getMessage());
132                    throw e;
133            }
134    
135            return (URL[])CLASSPATH.toArray(new URL[]{});
136        }
137    
138    }