Package org.apache.tomcat.util.loader

Source Code of org.apache.tomcat.util.loader.Loader

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License.  You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/


package org.apache.tomcat.util.loader;


import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.Vector;


/**
* Boostrap loader for Catalina or other java apps.
*
* This application constructs a class loader
* for use in loading the Catalina internal classes (by accumulating all of the
* JAR files found in the "server" directory under "catalina.home"), and
* starts the regular execution of the container.  The purpose of this
* roundabout approach is to keep the Catalina internal classes (and any
* other classes they depend on, such as an XML parser) out of the system
* class path and therefore not visible to application level classes.
*
*
* Merged with CatalinaProperties:
* Load a properties file describing the modules and startup sequence.
* This is responsible for configuration of the loader.
* TODO: support jmx-style configuration, including persistence.
* TODO: better separate legacy config and the new style
*
* The properties file will be named "loader.properties" or
* "catalina.properties" ( for backwad compatibility ) and
* will be searched in:
*  - TODO
*
* Properties used:
*  - TODO
*
* loader.* and *.loader properties are used internally by the loader (
*  *.loader is for backward compat with catalina ).
* All other properties in the config file are set as System properties.
*
* Based on o.a.catalina.bootstrap.CatalinaProperties - utility class to read
* the bootstrap Catalina configuration.

*
* @author Craig R. McClanahan
* @author Remy Maucherat
* @author Costin Manolache
*/
public final class Loader  {

    private static final boolean DEBUG=true; //LoaderProperties.getProperty("loader.debug.Loader") != null;
    // If flat, only one loader is created. If false - one loader per jar/dir
    private static final boolean FLAT=false;//LoaderProperties.getProperty("loader.Loader.flat") != null;
   
    // -------------------------------------------------------------- Constants


    private static final String CATALINA_HOME_TOKEN = "${catalina.home}";
    private static final String CATALINA_BASE_TOKEN = "${catalina.base}";


    // ------------------------------------------------------- Static Variables


    /**
     * Daemon object used by main.
     */
    private static Loader daemon = null;

    // one should be enough
    ModuleListener listener;


    // -------------------------------------------------------------- Variables

    protected Repository commonRepository = null;
    protected Repository catalinaRepository = null;
    protected Repository sharedRepository = null;

    protected ClassLoader catalinaLoader = null;
    private String[] args;
    private Hashtable repositories=new Hashtable();
    private ClassLoader parentClassLoader;
   
    private static Properties properties = null;


    private static String propFile;

    // -------------------------------------------------------- Private Methods
   
    /** Set the parent class loader - can be used instead of setParent,
     * in case this is the top loader and needs to delagate to embedding app.
     * The common loader will delegate to this loader
     *
     * @param myL
     */
    public void setParentClassLoader(ClassLoader myL) {
        this.parentClassLoader=myL;
    }



    /** Initialize the loader, creating all repositories.
     *  Will create common, server, shared.
     * 
     *  TODO: create additional repos.
     *
     */
    public void init() {
        try {
            commonRepository = initRepository("common", null, parentClassLoader);
            catalinaRepository = initRepository("server", commonRepository,null);
            catalinaLoader = catalinaRepository.getClassLoader();
            sharedRepository = initRepository("shared", commonRepository,null);
        } catch (Throwable t) {
            log("Class loader creation threw exception", t);
            System.exit(1);
        }
    }

    /** Create a new repository.
     *  No Module is added ( currently )
     *  TODO: use props to prepopulate, if any is present.
     *
     * @param name
     * @param parent
     * @return
     */
    public Repository createRepository(String name, Repository parent) {
        Repository lg=new Repository(this);

        lg.setName(name);
      
        lg.setParent( parent );
       
        repositories.put(name, lg);
       
        if( listener != null ) {
            listener.repositoryAdd(lg);
        }
        return lg;
    }

    public Enumeration getRepositoryNames() {
        return repositories.keys();
    }
   
    /** Create a module using the NAME.loader property to construct the
     *  classpath.
     *
     * @param name
     * @param parent
     * @return
     * @throws Exception
     */
    private Repository initRepository(String name, Repository parent, ClassLoader pcl)
        throws Exception
    {
        String value = getProperty(name + ".loader");

        Repository lg=createRepository(name, parent );
        if( pcl != null )
            lg.setParentClassLoader( pcl );
        if( DEBUG ) log( "Creating loading group " + name + " - " + value + " " + pcl);
       
        if ((value == null) || (value.equals("")))
            return lg;

        ArrayList unpackedList = new ArrayList();
        ArrayList packedList = new ArrayList();
        ArrayList urlList = new ArrayList();

        Vector repo=split( value );
        Enumeration elems=repo.elements();
        while (elems.hasMoreElements()) {
            String repository = (String)elems.nextElement();

            // Local repository
            boolean packed = false;
           
            if (repository.startsWith(CATALINA_HOME_TOKEN)) {
                repository = getCatalinaHome()
                    + repository.substring(CATALINA_HOME_TOKEN.length());
            } else if (repository.startsWith(CATALINA_BASE_TOKEN)) {
                repository = getCatalinaBase()
                    + repository.substring(CATALINA_BASE_TOKEN.length());
            }

            // Check for a JAR URL repository
            try {
                urlList.add(new URL(repository));
                continue;
            } catch (MalformedURLException e) {
                // Ignore
            }

            if (repository.endsWith("*.jar")) {
                packed = true;
                repository = repository.substring
                    (0, repository.length() - "*.jar".length());
            }
            if (packed) {
                packedList.add(new File(repository));
            } else {
                unpackedList.add(new File(repository));
            }
        }

        File[] unpacked = (File[]) unpackedList.toArray(new File[0]);
        File[] packed = (File[]) packedList.toArray(new File[0]);
        URL[] urls = (URL[]) urlList.toArray(new URL[0]);

        // previously: ClassLoaderFactory.createClassLoader
        initRepository(lg, unpacked, packed, urls, parent); //new ModuleGroup();
       
       
        // TODO: JMX registration for the group loader
        /*
        // Register the server classloader
        ObjectName objectName =
            new ObjectName("Catalina:type=ServerClassLoader,name=" + name);
        mBeanServer.registerMBean(classLoader, objectName);
        */
       
        return lg; // classLoader;

    }

    /** Small hack to allow a loader.properties file to be passed in CLI, to
     * allow a more convenient method to start with different params from
     * explorer/kde/etc.
     *
     * If the first arg ends with ".loader", it is used as loader.properties
     * file and removed from args[]. 
     */
    private void processCLI() {
        if( args!=null && args.length > 0 &&
                (args[0].toLowerCase().endsWith(".tomcat") ||
                        args[0].toLowerCase().endsWith(".loader") ||
                        args[0].toLowerCase().endsWith("loader.properties") )) {
            String props=args[0];
            String args2[]=new String[args.length-1];
            System.arraycopy(args, 1, args2, 0, args2.length);
            args=args2;
            setPropertiesFile(props);
        } else {
            loadProperties();
        }
    }
   
    /**
     * Initialize:
     *  - detect the home/base directories
     *  - init the loaders / modules
     *  - instantiate the "startup" class(es)
     *
     */
    public void main()
        throws Exception
    {
        processCLI();
       
        // Set Catalina path
        setCatalinaHome();
        setCatalinaBase();

        init();
       
        Thread.currentThread().setContextClassLoader(catalinaLoader);

        securityPreload(catalinaLoader);

        autostart();
    }

    private void autostart() throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        // Load our startup classes and call its process() method
        /* Why multiple classes ?
         * - maybe you want to start more "servers" ( tomcat ,something else )
         * - easy hook for other load on startup modules ( like a jmx hook )
         * - maybe split the loader-specific code from catalina
         */
        String startupClasses=getProperty("loader.auto-startup",
                "org.apache.catalina.startup.CatalinaModuleListener");
        Vector v=split( startupClasses );
       
        for( int i=0; i<v.size(); i++ ) {
            String startupCls=(String)v.elementAt(i);
       
            if (DEBUG)
                log("Loading startup class " + startupCls);
           
            Class startupClass =
                catalinaLoader.loadClass(startupCls);
           
            Object startupInstance = startupClass.newInstance();
           
            if( startupInstance instanceof ModuleListener ) {
                addModuleListener((ModuleListener)startupInstance);

                // it can get args[] and properties from Loader
                listener.setLoader(this);
               
                // all arg processing moved there. Maybe we can make it consistent
                // for all startup schemes
                listener.start();
            } else if ( startupInstance instanceof Runnable ) {
                ((Runnable)startupInstance).run();
            } else {
                Class paramTypes[] = new Class[0];
                Object paramValues[] = new Object[0];
                Method method =
                    startupInstance.getClass().getMethod("execute", paramTypes);
                if( method==null )
                    method = startupInstance.getClass().getMethod("start", paramTypes);
                if( method!=null )
                    method.invoke(startupInstance, paramValues);
            }

        }
    }

    /** Returns one of the repositories.
     *
     *  Typically at startup we create at least: "common", "shared" and "server", with
     *  same meaning as in tomcat.
     *
     * @param name
     * @return
     */
    public Repository getRepository( String name ) {
        return (Repository)repositories.get(name);
    }
   
    private  static void securityPreload(ClassLoader loader)
        throws Exception {

        if( System.getSecurityManager() == null ){
            return;
        }

        String value=getProperty("security.preload");
        Vector repo=split( value );
        Enumeration elems=repo.elements();
        while (elems.hasMoreElements()) {
            String classN = (String)elems.nextElement();
            try {
                loader.loadClass( classN);
            } catch( Throwable t ) {
                // ignore
            }
        }
    }


    // ----------------------------------------------------------- Main Program

    /** Access to the command line arguments, when Loader is used to launc an app.
     */
    public String[] getArgs() {
        return args;
    }

    /**
     * Main method.
     *
     * @param args Command line arguments to be processed
     */
    public static void main(String args[]) {
       
        try {
            if (daemon == null) {
                daemon = new Loader();
                daemon.args=args;

                try {
                    daemon.main();
                } catch (Throwable t) {
                    t.printStackTrace();
                    return;
                }
            }
        } catch (Throwable t) {
            t.printStackTrace();
        }

    }


    /**
     * Initialize the loader properties explicitely.
     *
     * TODO: add setPropertiesRes
     *
     * @param props
     */
    public void setPropertiesFile(String props) {
        propFile=props;
        loadProperties();
    }

    /**
     * Return specified property value.
     */
    static String getProperty(String name) {
        if( properties==null ) loadProperties();
        return properties.getProperty(name);
    }


    /**
     * Return specified property value.
     */
    static String getProperty(String name, String defaultValue) {
        if( properties==null ) loadProperties();
        return properties.getProperty(name, defaultValue);
    }

    /**
     * Load properties.
     * Will try:
     * - "catalina.config" system property ( a URL )
     * - "catalina.base", "catalina.home", "user.dir" system properties +
     *    "/conf/" "../conf" "/" +  "loader.properties" or "catalina.properties"
     * - /org/apache/catalina/startup/catalina.properties
     *
     * Properties will be loaded as system properties.
     *
     * loader.properties was added to allow coexistence with bootstrap.jar ( the
     * current scheme ), since the classpaths are slightly different.
     */
    static void loadProperties() {
        properties = new Properties();
       
        InputStream is = null;
        Throwable error = null;

        // TODO: paste the code to do ${} substitution
        // TODO: add the code to detect where tomcat-properties is loaded from
        if( propFile != null ) {
            try {
                File properties = new File(propFile);
                is = new FileInputStream(properties);
                if( is!=null && DEBUG ) {
                    log("Loaded from loader.properties " + properties );
                }
            } catch( Throwable t) {
                System.err.println("Can't find " + propFile);
                return;
            }
        }
       
        if( is == null ) {
            try {
                // "catalina.config" system property
                String configUrl = System.getProperty("catalina.config");
                if (configUrl != null) {
                    is = (new URL(configUrl)).openStream();
                    if( is!=null && DEBUG ) {
                        log("Loaded from catalina.config " + configUrl );
                    }
                }
            } catch (Throwable t) {
                // Ignore
            }
        }

        if( is == null ) {
            try {
                // "loader.config" system property
                String configUrl = System.getProperty("loader.config");
                if (configUrl != null) {
                    is = (new URL(configUrl)).openStream();
                    if( is!=null && DEBUG ) {
                        log("Loaded from catalina.config " + configUrl );
                    }
                }
            } catch (Throwable t) {
                // Ignore
            }
        }

        if (is == null) {
            try {
                setCatalinaBase(); // use system properties, then user.dir
                File home = new File(getCatalinaBase());
                File conf = new File(home, "conf");
               
                // use conf if exists, or the base directory otherwise
                if( ! conf.exists() ) conf = new File(home, "../conf");
                if( ! conf.exists() ) conf = home;
                File propertiesF=null;
                ifconf.exists() ) 
                     propertiesF= new File(conf, "loader.properties");
                if( ! propertiesF.exists() ) {
                    propertiesF= new File( home, "loader.properties");
                }
                if( propertiesF.exists() )
                    is = new FileInputStream(propertiesF);
                if( is!=null && DEBUG ) {
                    log("Loaded from loader.properties " + properties );
                }
            } catch (Throwable t) {
                // Ignore
            }
        }

        if (is == null) {
            try {
                File home = new File(getCatalinaBase());
                File conf = new File(home, "conf");
                File properties = new File(conf, "catalina.properties");
                is = new FileInputStream(properties);
                if( is!=null && DEBUG ) {
                    log("Loaded from catalina.properties " + properties );
                }
            } catch (Throwable t) {
                // Ignore
            }
        }

        if (is == null) {
            try {
                is = Loader.class.getResourceAsStream
                    ("/org/apache/catalina/startup/catalina.properties");
                if( is!=null && DEBUG ) {
                    log("Loaded from o/a/c/startup/catalina.properties " );
                }

            } catch (Throwable t) {
                // Ignore
            }
        }

        if (is == null) {
            try {
                is = Loader.class.getResourceAsStream
                    ("loader.properties");
                if( is!=null && DEBUG ) {
                    log("Loaded from res loader.properties " );
                }
            } catch (Throwable t) {
                // Ignore
            }
        }
       
        if (is != null) {
            try {
                properties.load(is);
                is.close();
            } catch (Throwable t) {
                error = t;
            }
        }

//        if ((is == null) || (error != null)) {
//            // Do something
//            log("Error: no properties found !!!");
//        }

        // Register the _unused_ properties as system properties
        if( properties != null ) {
            Enumeration enumeration = properties.propertyNames();
            while (enumeration.hasMoreElements()) {
                String name = (String) enumeration.nextElement();
                String value = properties.getProperty(name);
                if( "security.preload".equals( name )) continue;
                if( "package.access".equals( name )) continue;
                if( "package.definition".equals( name )) continue;
                if( name.endsWith(".loader")) continue;
                if( name.startsWith("loader.")) continue;
                if (value != null) {
                    System.setProperty(name, value);
                }
            }
        }

    }

    static void setCatalinaHome(String s) {
        System.setProperty( "catalina.home", s );
    }

    static void setCatalinaBase(String s) {
        System.setProperty( "catalina.base", s );
    }


    /**
     * Get the value of the catalina.home environment variable.
     *
     * @deprecated
     */
    static String getCatalinaHome() {
        if( properties==null ) loadProperties();
        return System.getProperty("catalina.home",
                                  System.getProperty("user.dir"));
    }
   
   
    /**
     * Get the value of the catalina.base environment variable.
     *
     * @deprecated
     */
    static String getCatalinaBase() {
        if( properties==null ) loadProperties();
        return System.getProperty("catalina.base", getCatalinaHome());
    }

   
    /**
     * Set the <code>catalina.base</code> System property to the current
     * working directory if it has not been set.
     */
    static void setCatalinaBase() {
        if( properties==null ) loadProperties();

        if (System.getProperty("catalina.base") != null)
            return;
        if (System.getProperty("catalina.home") != null)
            System.setProperty("catalina.base",
                               System.getProperty("catalina.home"));
        else
            System.setProperty("catalina.base",
                               System.getProperty("user.dir"));

    }


    /**
     * Set the <code>catalina.home</code> System property to the current
     * working directory if it has not been set.
     */
    static void setCatalinaHome() {

        if (System.getProperty("catalina.home") != null)
            return;
        File bootstrapJar =
            new File(System.getProperty("user.dir"), "bootstrap.jar");
        File tloaderJar =
            new File(System.getProperty("user.dir"), "tomcat-loader.jar");
        if (bootstrapJar.exists() || tloaderJar.exists()) {
            try {
                System.setProperty
                    ("catalina.home",
                     (new File(System.getProperty("user.dir"), ".."))
                     .getCanonicalPath());
            } catch (Exception e) {
                // Ignore
                System.setProperty("catalina.home",
                                   System.getProperty("user.dir"));
            }
        } else {
            System.setProperty("catalina.home",
                               System.getProperty("user.dir"));
        }

    }


   
    /**
     * Get the module from the classloader. Works only for classloaders created by
     * this package - or extending ModuleClassLoader.
     *
     * This shold be the only public method that allows this - Loader acts as a
     * guard, only if you have the loader instance you can access the internals.
     *
     *
     * @param cl
     * @return
     */
    public Module getModule(ClassLoader cl ) {
        if( cl instanceof ModuleClassLoader ) {
            return ((ModuleClassLoader)cl).getModule();
        }
        return null;
    }
   
    /**
     * Create and return a new class loader, based on the configuration
     * defaults and the specified directory paths:
     *
     * @param unpacked Array of pathnames to unpacked directories that should
     *  be added to the repositories of the class loader, or <code>null</code>
     * for no unpacked directories to be considered
     * @param packed Array of pathnames to directories containing JAR files
     *  that should be added to the repositories of the class loader,
     * or <code>null</code> for no directories of JAR files to be considered
     * @param urls Array of URLs to remote repositories, designing either JAR
     *  resources or uncompressed directories that should be added to
     *  the repositories of the class loader, or <code>null</code> for no
     *  directories of JAR files to be considered
     * @param parent Parent class loader for the new class loader, or
     *  <code>null</code> for the system class loader.
     *
     * @exception Exception if an error occurs constructing the class loader
     */
    private void initRepository(Repository lg, File unpacked[],
            File packed[],  URL urls[],  Repository parent)
        throws Exception
    {
        StringBuffer sb=new StringBuffer();

        // Construct the "class path" for this class loader
        ArrayList list = new ArrayList();
       
        // Add unpacked directories
        if (unpacked != null) {
            for (int i = 0; i < unpacked.length; i++)  {
                File file = unpacked[i];
                if (!file.exists() || !file.canRead()) {
                    if (DEBUG)
                        log("  Not found:  "+ file.getAbsolutePath());
                    continue;
                }
//                String cPath=file.getCanonicalPath();
//                URL url=null;
//               
//                if( cPath.toLowerCase().endsWith(".jar") ||
//                        cPath.toLowerCase().endsWith(".zip") ) {
//                    url = new URL("file", null, cPath);
//                } else {
//                    url = new URL("file", null, cPath + File.separator);
//                }
                URL url=file.toURL();
                if (DEBUG)
                    sb.append(" : "+ url);
                if( ! FLAT ) {
                    addLoader(lg, parent, new URL[] { url });
                } else {
                    list.add(url);
                }
            }
        }
       
        // Add packed directory JAR files
        if (packed != null) {
            for (int i = 0; i < packed.length; i++) {
                File directory = packed[i];
                if (!directory.isDirectory() || !directory.exists() ||
                        !directory.canRead()) {
                    if (DEBUG)
                        log("  Not found:  "+ directory.getAbsolutePath());
                    continue;
                }
                String filenames[] = directory.list();
                for (int j = 0; j < filenames.length; j++) {
                    String filename = filenames[j].toLowerCase();
                    if (!filename.endsWith(".jar"))
                        continue;
                    File file = new File(directory, filenames[j]);
//                    if (DEBUG)
//                        sb.append(" [pak]="+ file.getCanonicalPath());
//                    URL url = new URL("file", null,
//                            file.getCanonicalPath());
                    URL url=file.toURL();
                    if (DEBUG)
                        sb.append(" pk="+ url);

                    if( ! FLAT ) {
                        addLoader(lg, parent, new URL[] { url });
                    } else {
                        list.add(url);
                    }
                }
            }
        }
       
        // Add URLs
        if (urls != null) {
            for (int i = 0; i < urls.length; i++) {
                if( ! FLAT ) {
                    addLoader(lg, parent, new URL[] { urls[i] });
                } else {
                    list.add(urls[i]);
                }
                if (DEBUG)
                    sb.append(" "+ urls[i]);
            }
        }
       
        // Construct the class loader itself
       
        // TODO: experiment with loading each jar in a separate loader.
        if (DEBUG)
            log("Creating new class loader " + lg.getName() + " " + sb.toString());
       
       
        URL[] array = (URL[]) list.toArray(new URL[list.size()]);
        if( array.length > 0 ) {
            addLoader(lg, parent, array);
        }
    }
   
    /**
     * @param lg
     * @param parent
     * @param list
     */
    private void addLoader(Repository lg, Repository parent, URL array[])
        throws Exception
    {
        Module module=new Module();
       
        module.setParent( parent );
        module.setClasspath( array );
       
        lg.addModule(module);
       
    }

    private static Vector split( String value ) {
        Vector result=new Vector();
        StringTokenizer tokenizer = new StringTokenizer(value, ",");
        while (tokenizer.hasMoreElements()) {
            String repository = tokenizer.nextToken();
            repository.trim();
            if( ! "".equals(repository) )
                result.addElement(repository);
        }
        return result;
    }

    void notifyModuleStart(Module module) {
        if(listener!=null) listener.moduleStart(module);
    }

    void notifyModuleStop(Module module) {
        if( listener!=null ) listener.moduleStop(module);
    }
   
    /** Add a module listener.
     *
     * To keep the dependencies minimal, the loader package only implements the
     * basic class loading mechanism - but any advanced feature ( management,
     * policy, etc ) should be implemented by a module.
     *
     * @param listener
     */
    public void addModuleListener(ModuleListener listener) {
        this.listener=listener;
    }

    private static void log(String s) {
        System.err.println("Main: " + s);
    }
   
    private static void log( String msg, Throwable t ) {
        System.err.println("Main: " + msg);
        t.printStackTrace();
    }

}
TOP

Related Classes of org.apache.tomcat.util.loader.Loader

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.