103928aee4356845252ac6b662d5c72c29903813eJake Slack//
203928aee4356845252ac6b662d5c72c29903813eJake Slack//  ========================================================================
303928aee4356845252ac6b662d5c72c29903813eJake Slack//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
403928aee4356845252ac6b662d5c72c29903813eJake Slack//  ------------------------------------------------------------------------
503928aee4356845252ac6b662d5c72c29903813eJake Slack//  All rights reserved. This program and the accompanying materials
603928aee4356845252ac6b662d5c72c29903813eJake Slack//  are made available under the terms of the Eclipse Public License v1.0
703928aee4356845252ac6b662d5c72c29903813eJake Slack//  and Apache License v2.0 which accompanies this distribution.
803928aee4356845252ac6b662d5c72c29903813eJake Slack//
903928aee4356845252ac6b662d5c72c29903813eJake Slack//      The Eclipse Public License is available at
1003928aee4356845252ac6b662d5c72c29903813eJake Slack//      http://www.eclipse.org/legal/epl-v10.html
1103928aee4356845252ac6b662d5c72c29903813eJake Slack//
1203928aee4356845252ac6b662d5c72c29903813eJake Slack//      The Apache License v2.0 is available at
1303928aee4356845252ac6b662d5c72c29903813eJake Slack//      http://www.opensource.org/licenses/apache2.0.php
1403928aee4356845252ac6b662d5c72c29903813eJake Slack//
1503928aee4356845252ac6b662d5c72c29903813eJake Slack//  You may elect to redistribute this code under either of these licenses.
1603928aee4356845252ac6b662d5c72c29903813eJake Slack//  ========================================================================
1703928aee4356845252ac6b662d5c72c29903813eJake Slack//
1803928aee4356845252ac6b662d5c72c29903813eJake Slack
1903928aee4356845252ac6b662d5c72c29903813eJake Slackpackage org.eclipse.jetty.webapp;
2003928aee4356845252ac6b662d5c72c29903813eJake Slack
2103928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.io.File;
2203928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.io.IOException;
2303928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.net.URL;
2403928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.net.URLClassLoader;
2503928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.security.CodeSource;
2603928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.security.PermissionCollection;
2703928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.ArrayList;
2803928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.Collections;
2903928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.Enumeration;
3003928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.HashSet;
3103928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.List;
3203928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.Locale;
3303928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.Set;
3403928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.StringTokenizer;
3503928aee4356845252ac6b662d5c72c29903813eJake Slack
3603928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.util.StringUtil;
3703928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.util.log.Log;
3803928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.util.log.Logger;
3903928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.util.resource.Resource;
4003928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.util.resource.ResourceCollection;
4103928aee4356845252ac6b662d5c72c29903813eJake Slack
4203928aee4356845252ac6b662d5c72c29903813eJake Slack
4303928aee4356845252ac6b662d5c72c29903813eJake Slack/* ------------------------------------------------------------ */
4403928aee4356845252ac6b662d5c72c29903813eJake Slack/** ClassLoader for HttpContext.
4503928aee4356845252ac6b662d5c72c29903813eJake Slack * Specializes URLClassLoader with some utility and file mapping
4603928aee4356845252ac6b662d5c72c29903813eJake Slack * methods.
4703928aee4356845252ac6b662d5c72c29903813eJake Slack *
4803928aee4356845252ac6b662d5c72c29903813eJake Slack * This loader defaults to the 2.3 servlet spec behavior where non
4903928aee4356845252ac6b662d5c72c29903813eJake Slack * system classes are loaded from the classpath in preference to the
5003928aee4356845252ac6b662d5c72c29903813eJake Slack * parent loader.  Java2 compliant loading, where the parent loader
5103928aee4356845252ac6b662d5c72c29903813eJake Slack * always has priority, can be selected with the
5203928aee4356845252ac6b662d5c72c29903813eJake Slack * {@link org.eclipse.jetty.webapp.WebAppContext#setParentLoaderPriority(boolean)}
5303928aee4356845252ac6b662d5c72c29903813eJake Slack * method and influenced with {@link WebAppContext#isServerClass(String)} and
5403928aee4356845252ac6b662d5c72c29903813eJake Slack * {@link WebAppContext#isSystemClass(String)}.
5503928aee4356845252ac6b662d5c72c29903813eJake Slack *
5603928aee4356845252ac6b662d5c72c29903813eJake Slack * If no parent class loader is provided, then the current thread
5703928aee4356845252ac6b662d5c72c29903813eJake Slack * context classloader will be used.  If that is null then the
5803928aee4356845252ac6b662d5c72c29903813eJake Slack * classloader that loaded this class is used as the parent.
5903928aee4356845252ac6b662d5c72c29903813eJake Slack *
6003928aee4356845252ac6b662d5c72c29903813eJake Slack */
6103928aee4356845252ac6b662d5c72c29903813eJake Slackpublic class WebAppClassLoader extends URLClassLoader
6203928aee4356845252ac6b662d5c72c29903813eJake Slack{
6303928aee4356845252ac6b662d5c72c29903813eJake Slack    private static final Logger LOG = Log.getLogger(WebAppClassLoader.class);
6403928aee4356845252ac6b662d5c72c29903813eJake Slack
6503928aee4356845252ac6b662d5c72c29903813eJake Slack    private final Context _context;
6603928aee4356845252ac6b662d5c72c29903813eJake Slack    private final ClassLoader _parent;
6703928aee4356845252ac6b662d5c72c29903813eJake Slack    private final Set<String> _extensions=new HashSet<String>();
6803928aee4356845252ac6b662d5c72c29903813eJake Slack    private String _name=String.valueOf(hashCode());
6903928aee4356845252ac6b662d5c72c29903813eJake Slack
7003928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
7103928aee4356845252ac6b662d5c72c29903813eJake Slack    /** The Context in which the classloader operates.
7203928aee4356845252ac6b662d5c72c29903813eJake Slack     */
7303928aee4356845252ac6b662d5c72c29903813eJake Slack    public interface Context
7403928aee4356845252ac6b662d5c72c29903813eJake Slack    {
7503928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
7603928aee4356845252ac6b662d5c72c29903813eJake Slack        /** Convert a URL or path to a Resource.
7703928aee4356845252ac6b662d5c72c29903813eJake Slack         * The default implementation
7803928aee4356845252ac6b662d5c72c29903813eJake Slack         * is a wrapper for {@link Resource#newResource(String)}.
7903928aee4356845252ac6b662d5c72c29903813eJake Slack         * @param urlOrPath The URL or path to convert
8003928aee4356845252ac6b662d5c72c29903813eJake Slack         * @return The Resource for the URL/path
8103928aee4356845252ac6b662d5c72c29903813eJake Slack         * @throws IOException The Resource could not be created.
8203928aee4356845252ac6b662d5c72c29903813eJake Slack         */
8303928aee4356845252ac6b662d5c72c29903813eJake Slack        Resource newResource(String urlOrPath) throws IOException;
8403928aee4356845252ac6b662d5c72c29903813eJake Slack
8503928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
8603928aee4356845252ac6b662d5c72c29903813eJake Slack        /**
8703928aee4356845252ac6b662d5c72c29903813eJake Slack         * @return Returns the permissions.
8803928aee4356845252ac6b662d5c72c29903813eJake Slack         */
8903928aee4356845252ac6b662d5c72c29903813eJake Slack        PermissionCollection getPermissions();
9003928aee4356845252ac6b662d5c72c29903813eJake Slack
9103928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
9203928aee4356845252ac6b662d5c72c29903813eJake Slack        /** Is the class a System Class.
9303928aee4356845252ac6b662d5c72c29903813eJake Slack         * A System class is a class that is visible to a webapplication,
9403928aee4356845252ac6b662d5c72c29903813eJake Slack         * but that cannot be overridden by the contents of WEB-INF/lib or
9503928aee4356845252ac6b662d5c72c29903813eJake Slack         * WEB-INF/classes
9603928aee4356845252ac6b662d5c72c29903813eJake Slack         * @param clazz The fully qualified name of the class.
9703928aee4356845252ac6b662d5c72c29903813eJake Slack         * @return True if the class is a system class.
9803928aee4356845252ac6b662d5c72c29903813eJake Slack         */
9903928aee4356845252ac6b662d5c72c29903813eJake Slack        boolean isSystemClass(String clazz);
10003928aee4356845252ac6b662d5c72c29903813eJake Slack
10103928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
10203928aee4356845252ac6b662d5c72c29903813eJake Slack        /** Is the class a Server Class.
10303928aee4356845252ac6b662d5c72c29903813eJake Slack         * A Server class is a class that is part of the implementation of
10403928aee4356845252ac6b662d5c72c29903813eJake Slack         * the server and is NIT visible to a webapplication. The web
10503928aee4356845252ac6b662d5c72c29903813eJake Slack         * application may provide it's own implementation of the class,
10603928aee4356845252ac6b662d5c72c29903813eJake Slack         * to be loaded from WEB-INF/lib or WEB-INF/classes
10703928aee4356845252ac6b662d5c72c29903813eJake Slack         * @param clazz The fully qualified name of the class.
10803928aee4356845252ac6b662d5c72c29903813eJake Slack         * @return True if the class is a server class.
10903928aee4356845252ac6b662d5c72c29903813eJake Slack         */
11003928aee4356845252ac6b662d5c72c29903813eJake Slack        boolean isServerClass(String clazz);
11103928aee4356845252ac6b662d5c72c29903813eJake Slack
11203928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
11303928aee4356845252ac6b662d5c72c29903813eJake Slack        /**
11403928aee4356845252ac6b662d5c72c29903813eJake Slack         * @return True if the classloader should delegate first to the parent
11503928aee4356845252ac6b662d5c72c29903813eJake Slack         * classloader (standard java behaviour) or false if the classloader
11603928aee4356845252ac6b662d5c72c29903813eJake Slack         * should first try to load from WEB-INF/lib or WEB-INF/classes (servlet
11703928aee4356845252ac6b662d5c72c29903813eJake Slack         * spec recommendation).
11803928aee4356845252ac6b662d5c72c29903813eJake Slack         */
11903928aee4356845252ac6b662d5c72c29903813eJake Slack        boolean isParentLoaderPriority();
12003928aee4356845252ac6b662d5c72c29903813eJake Slack
12103928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
12203928aee4356845252ac6b662d5c72c29903813eJake Slack        String getExtraClasspath();
12303928aee4356845252ac6b662d5c72c29903813eJake Slack
12403928aee4356845252ac6b662d5c72c29903813eJake Slack    }
12503928aee4356845252ac6b662d5c72c29903813eJake Slack
12603928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
12703928aee4356845252ac6b662d5c72c29903813eJake Slack    /** Constructor.
12803928aee4356845252ac6b662d5c72c29903813eJake Slack     */
12903928aee4356845252ac6b662d5c72c29903813eJake Slack    public WebAppClassLoader(Context context)
13003928aee4356845252ac6b662d5c72c29903813eJake Slack        throws IOException
13103928aee4356845252ac6b662d5c72c29903813eJake Slack    {
13203928aee4356845252ac6b662d5c72c29903813eJake Slack        this(null,context);
13303928aee4356845252ac6b662d5c72c29903813eJake Slack    }
13403928aee4356845252ac6b662d5c72c29903813eJake Slack
13503928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
13603928aee4356845252ac6b662d5c72c29903813eJake Slack    /** Constructor.
13703928aee4356845252ac6b662d5c72c29903813eJake Slack     */
13803928aee4356845252ac6b662d5c72c29903813eJake Slack    public WebAppClassLoader(ClassLoader parent, Context context)
13903928aee4356845252ac6b662d5c72c29903813eJake Slack        throws IOException
14003928aee4356845252ac6b662d5c72c29903813eJake Slack    {
14103928aee4356845252ac6b662d5c72c29903813eJake Slack        super(new URL[]{},parent!=null?parent
14203928aee4356845252ac6b662d5c72c29903813eJake Slack                :(Thread.currentThread().getContextClassLoader()!=null?Thread.currentThread().getContextClassLoader()
14303928aee4356845252ac6b662d5c72c29903813eJake Slack                        :(WebAppClassLoader.class.getClassLoader()!=null?WebAppClassLoader.class.getClassLoader()
14403928aee4356845252ac6b662d5c72c29903813eJake Slack                                :ClassLoader.getSystemClassLoader())));
14503928aee4356845252ac6b662d5c72c29903813eJake Slack        _parent=getParent();
14603928aee4356845252ac6b662d5c72c29903813eJake Slack        _context=context;
14703928aee4356845252ac6b662d5c72c29903813eJake Slack        if (_parent==null)
14803928aee4356845252ac6b662d5c72c29903813eJake Slack            throw new IllegalArgumentException("no parent classloader!");
14903928aee4356845252ac6b662d5c72c29903813eJake Slack
15003928aee4356845252ac6b662d5c72c29903813eJake Slack        _extensions.add(".jar");
15103928aee4356845252ac6b662d5c72c29903813eJake Slack        _extensions.add(".zip");
15203928aee4356845252ac6b662d5c72c29903813eJake Slack
15303928aee4356845252ac6b662d5c72c29903813eJake Slack        // TODO remove this system property
15403928aee4356845252ac6b662d5c72c29903813eJake Slack        String extensions = System.getProperty(WebAppClassLoader.class.getName() + ".extensions");
15503928aee4356845252ac6b662d5c72c29903813eJake Slack        if(extensions!=null)
15603928aee4356845252ac6b662d5c72c29903813eJake Slack        {
15703928aee4356845252ac6b662d5c72c29903813eJake Slack            StringTokenizer tokenizer = new StringTokenizer(extensions, ",;");
15803928aee4356845252ac6b662d5c72c29903813eJake Slack            while(tokenizer.hasMoreTokens())
15903928aee4356845252ac6b662d5c72c29903813eJake Slack                _extensions.add(tokenizer.nextToken().trim());
16003928aee4356845252ac6b662d5c72c29903813eJake Slack        }
16103928aee4356845252ac6b662d5c72c29903813eJake Slack
16203928aee4356845252ac6b662d5c72c29903813eJake Slack        if (context.getExtraClasspath()!=null)
16303928aee4356845252ac6b662d5c72c29903813eJake Slack            addClassPath(context.getExtraClasspath());
16403928aee4356845252ac6b662d5c72c29903813eJake Slack    }
16503928aee4356845252ac6b662d5c72c29903813eJake Slack
16603928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
16703928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
16803928aee4356845252ac6b662d5c72c29903813eJake Slack     * @return the name of the classloader
16903928aee4356845252ac6b662d5c72c29903813eJake Slack     */
17003928aee4356845252ac6b662d5c72c29903813eJake Slack    public String getName()
17103928aee4356845252ac6b662d5c72c29903813eJake Slack    {
17203928aee4356845252ac6b662d5c72c29903813eJake Slack        return _name;
17303928aee4356845252ac6b662d5c72c29903813eJake Slack    }
17403928aee4356845252ac6b662d5c72c29903813eJake Slack
17503928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
17603928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
17703928aee4356845252ac6b662d5c72c29903813eJake Slack     * @param name the name of the classloader
17803928aee4356845252ac6b662d5c72c29903813eJake Slack     */
17903928aee4356845252ac6b662d5c72c29903813eJake Slack    public void setName(String name)
18003928aee4356845252ac6b662d5c72c29903813eJake Slack    {
18103928aee4356845252ac6b662d5c72c29903813eJake Slack        _name=name;
18203928aee4356845252ac6b662d5c72c29903813eJake Slack    }
18303928aee4356845252ac6b662d5c72c29903813eJake Slack
18403928aee4356845252ac6b662d5c72c29903813eJake Slack
18503928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
18603928aee4356845252ac6b662d5c72c29903813eJake Slack    public Context getContext()
18703928aee4356845252ac6b662d5c72c29903813eJake Slack    {
18803928aee4356845252ac6b662d5c72c29903813eJake Slack        return _context;
18903928aee4356845252ac6b662d5c72c29903813eJake Slack    }
19003928aee4356845252ac6b662d5c72c29903813eJake Slack
19103928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
19203928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
19303928aee4356845252ac6b662d5c72c29903813eJake Slack     * @param resource Comma or semicolon separated path of filenames or URLs
19403928aee4356845252ac6b662d5c72c29903813eJake Slack     * pointing to directories or jar files. Directories should end
19503928aee4356845252ac6b662d5c72c29903813eJake Slack     * with '/'.
19603928aee4356845252ac6b662d5c72c29903813eJake Slack     */
19703928aee4356845252ac6b662d5c72c29903813eJake Slack    public void addClassPath(Resource resource)
19803928aee4356845252ac6b662d5c72c29903813eJake Slack        throws IOException
19903928aee4356845252ac6b662d5c72c29903813eJake Slack    {
20003928aee4356845252ac6b662d5c72c29903813eJake Slack        if (resource instanceof ResourceCollection)
20103928aee4356845252ac6b662d5c72c29903813eJake Slack        {
20203928aee4356845252ac6b662d5c72c29903813eJake Slack            for (Resource r : ((ResourceCollection)resource).getResources())
20303928aee4356845252ac6b662d5c72c29903813eJake Slack                addClassPath(r);
20403928aee4356845252ac6b662d5c72c29903813eJake Slack        }
20503928aee4356845252ac6b662d5c72c29903813eJake Slack        else
20603928aee4356845252ac6b662d5c72c29903813eJake Slack        {
20703928aee4356845252ac6b662d5c72c29903813eJake Slack            addClassPath(resource.toString());
20803928aee4356845252ac6b662d5c72c29903813eJake Slack        }
20903928aee4356845252ac6b662d5c72c29903813eJake Slack    }
21003928aee4356845252ac6b662d5c72c29903813eJake Slack
21103928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
21203928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
21303928aee4356845252ac6b662d5c72c29903813eJake Slack     * @param classPath Comma or semicolon separated path of filenames or URLs
21403928aee4356845252ac6b662d5c72c29903813eJake Slack     * pointing to directories or jar files. Directories should end
21503928aee4356845252ac6b662d5c72c29903813eJake Slack     * with '/'.
21603928aee4356845252ac6b662d5c72c29903813eJake Slack     */
21703928aee4356845252ac6b662d5c72c29903813eJake Slack    public void addClassPath(String classPath)
21803928aee4356845252ac6b662d5c72c29903813eJake Slack    	throws IOException
21903928aee4356845252ac6b662d5c72c29903813eJake Slack    {
22003928aee4356845252ac6b662d5c72c29903813eJake Slack        if (classPath == null)
22103928aee4356845252ac6b662d5c72c29903813eJake Slack            return;
22203928aee4356845252ac6b662d5c72c29903813eJake Slack
22303928aee4356845252ac6b662d5c72c29903813eJake Slack        StringTokenizer tokenizer= new StringTokenizer(classPath, ",;");
22403928aee4356845252ac6b662d5c72c29903813eJake Slack        while (tokenizer.hasMoreTokens())
22503928aee4356845252ac6b662d5c72c29903813eJake Slack        {
22603928aee4356845252ac6b662d5c72c29903813eJake Slack            Resource resource= _context.newResource(tokenizer.nextToken().trim());
22703928aee4356845252ac6b662d5c72c29903813eJake Slack            if (LOG.isDebugEnabled())
22803928aee4356845252ac6b662d5c72c29903813eJake Slack                LOG.debug("Path resource=" + resource);
22903928aee4356845252ac6b662d5c72c29903813eJake Slack
23003928aee4356845252ac6b662d5c72c29903813eJake Slack            // Add the resource
23103928aee4356845252ac6b662d5c72c29903813eJake Slack            if (resource.isDirectory() && resource instanceof ResourceCollection)
23203928aee4356845252ac6b662d5c72c29903813eJake Slack                addClassPath(resource);
23303928aee4356845252ac6b662d5c72c29903813eJake Slack            else
23403928aee4356845252ac6b662d5c72c29903813eJake Slack            {
23503928aee4356845252ac6b662d5c72c29903813eJake Slack                // Resolve file path if possible
23603928aee4356845252ac6b662d5c72c29903813eJake Slack                File file= resource.getFile();
23703928aee4356845252ac6b662d5c72c29903813eJake Slack                if (file != null)
23803928aee4356845252ac6b662d5c72c29903813eJake Slack                {
23903928aee4356845252ac6b662d5c72c29903813eJake Slack                    URL url= resource.getURL();
24003928aee4356845252ac6b662d5c72c29903813eJake Slack                    addURL(url);
24103928aee4356845252ac6b662d5c72c29903813eJake Slack                }
24203928aee4356845252ac6b662d5c72c29903813eJake Slack                else if (resource.isDirectory())
24303928aee4356845252ac6b662d5c72c29903813eJake Slack                    addURL(resource.getURL());
24403928aee4356845252ac6b662d5c72c29903813eJake Slack                else
24503928aee4356845252ac6b662d5c72c29903813eJake Slack                    throw new IllegalArgumentException("!file: "+resource);
24603928aee4356845252ac6b662d5c72c29903813eJake Slack            }
24703928aee4356845252ac6b662d5c72c29903813eJake Slack        }
24803928aee4356845252ac6b662d5c72c29903813eJake Slack    }
24903928aee4356845252ac6b662d5c72c29903813eJake Slack
25003928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
25103928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
25203928aee4356845252ac6b662d5c72c29903813eJake Slack     * @param file Checks if this file type can be added to the classpath.
25303928aee4356845252ac6b662d5c72c29903813eJake Slack     */
25403928aee4356845252ac6b662d5c72c29903813eJake Slack    private boolean isFileSupported(String file)
25503928aee4356845252ac6b662d5c72c29903813eJake Slack    {
25603928aee4356845252ac6b662d5c72c29903813eJake Slack        int dot = file.lastIndexOf('.');
25703928aee4356845252ac6b662d5c72c29903813eJake Slack        return dot!=-1 && _extensions.contains(file.substring(dot));
25803928aee4356845252ac6b662d5c72c29903813eJake Slack    }
25903928aee4356845252ac6b662d5c72c29903813eJake Slack
26003928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
26103928aee4356845252ac6b662d5c72c29903813eJake Slack    /** Add elements to the class path for the context from the jar and zip files found
26203928aee4356845252ac6b662d5c72c29903813eJake Slack     *  in the specified resource.
26303928aee4356845252ac6b662d5c72c29903813eJake Slack     * @param lib the resource that contains the jar and/or zip files.
26403928aee4356845252ac6b662d5c72c29903813eJake Slack     */
26503928aee4356845252ac6b662d5c72c29903813eJake Slack    public void addJars(Resource lib)
26603928aee4356845252ac6b662d5c72c29903813eJake Slack    {
26703928aee4356845252ac6b662d5c72c29903813eJake Slack        if (lib.exists() && lib.isDirectory())
26803928aee4356845252ac6b662d5c72c29903813eJake Slack        {
26903928aee4356845252ac6b662d5c72c29903813eJake Slack            String[] files=lib.list();
27003928aee4356845252ac6b662d5c72c29903813eJake Slack            for (int f=0;files!=null && f<files.length;f++)
27103928aee4356845252ac6b662d5c72c29903813eJake Slack            {
27203928aee4356845252ac6b662d5c72c29903813eJake Slack                try
27303928aee4356845252ac6b662d5c72c29903813eJake Slack                {
27403928aee4356845252ac6b662d5c72c29903813eJake Slack                    Resource fn=lib.addPath(files[f]);
27503928aee4356845252ac6b662d5c72c29903813eJake Slack                    String fnlc=fn.getName().toLowerCase(Locale.ENGLISH);
27603928aee4356845252ac6b662d5c72c29903813eJake Slack                    // don't check if this is a directory, see Bug 353165
27703928aee4356845252ac6b662d5c72c29903813eJake Slack                    if (isFileSupported(fnlc))
27803928aee4356845252ac6b662d5c72c29903813eJake Slack                    {
27903928aee4356845252ac6b662d5c72c29903813eJake Slack                        String jar=fn.toString();
28003928aee4356845252ac6b662d5c72c29903813eJake Slack                        jar=StringUtil.replace(jar, ",", "%2C");
28103928aee4356845252ac6b662d5c72c29903813eJake Slack                        jar=StringUtil.replace(jar, ";", "%3B");
28203928aee4356845252ac6b662d5c72c29903813eJake Slack                        addClassPath(jar);
28303928aee4356845252ac6b662d5c72c29903813eJake Slack                    }
28403928aee4356845252ac6b662d5c72c29903813eJake Slack                }
28503928aee4356845252ac6b662d5c72c29903813eJake Slack                catch (Exception ex)
28603928aee4356845252ac6b662d5c72c29903813eJake Slack                {
28703928aee4356845252ac6b662d5c72c29903813eJake Slack                    LOG.warn(Log.EXCEPTION,ex);
28803928aee4356845252ac6b662d5c72c29903813eJake Slack                }
28903928aee4356845252ac6b662d5c72c29903813eJake Slack            }
29003928aee4356845252ac6b662d5c72c29903813eJake Slack        }
29103928aee4356845252ac6b662d5c72c29903813eJake Slack    }
29203928aee4356845252ac6b662d5c72c29903813eJake Slack
29303928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
29403928aee4356845252ac6b662d5c72c29903813eJake Slack    public PermissionCollection getPermissions(CodeSource cs)
29503928aee4356845252ac6b662d5c72c29903813eJake Slack    {
29603928aee4356845252ac6b662d5c72c29903813eJake Slack        // TODO check CodeSource
29703928aee4356845252ac6b662d5c72c29903813eJake Slack        PermissionCollection permissions=_context.getPermissions();
29803928aee4356845252ac6b662d5c72c29903813eJake Slack        PermissionCollection pc= (permissions == null) ? super.getPermissions(cs) : permissions;
29903928aee4356845252ac6b662d5c72c29903813eJake Slack        return pc;
30003928aee4356845252ac6b662d5c72c29903813eJake Slack    }
30103928aee4356845252ac6b662d5c72c29903813eJake Slack
30203928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
30303928aee4356845252ac6b662d5c72c29903813eJake Slack    public Enumeration<URL> getResources(String name) throws IOException
30403928aee4356845252ac6b662d5c72c29903813eJake Slack    {
30503928aee4356845252ac6b662d5c72c29903813eJake Slack        boolean system_class=_context.isSystemClass(name);
30603928aee4356845252ac6b662d5c72c29903813eJake Slack        boolean server_class=_context.isServerClass(name);
30703928aee4356845252ac6b662d5c72c29903813eJake Slack
30803928aee4356845252ac6b662d5c72c29903813eJake Slack        List<URL> from_parent = toList(server_class?null:_parent.getResources(name));
30903928aee4356845252ac6b662d5c72c29903813eJake Slack        List<URL> from_webapp = toList((system_class&&!from_parent.isEmpty())?null:this.findResources(name));
31003928aee4356845252ac6b662d5c72c29903813eJake Slack
31103928aee4356845252ac6b662d5c72c29903813eJake Slack        if (_context.isParentLoaderPriority())
31203928aee4356845252ac6b662d5c72c29903813eJake Slack        {
31303928aee4356845252ac6b662d5c72c29903813eJake Slack            from_parent.addAll(from_webapp);
31403928aee4356845252ac6b662d5c72c29903813eJake Slack            return Collections.enumeration(from_parent);
31503928aee4356845252ac6b662d5c72c29903813eJake Slack        }
31603928aee4356845252ac6b662d5c72c29903813eJake Slack        from_webapp.addAll(from_parent);
31703928aee4356845252ac6b662d5c72c29903813eJake Slack        return Collections.enumeration(from_webapp);
31803928aee4356845252ac6b662d5c72c29903813eJake Slack    }
31903928aee4356845252ac6b662d5c72c29903813eJake Slack
32003928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
32103928aee4356845252ac6b662d5c72c29903813eJake Slack    private List<URL> toList(Enumeration<URL> e)
32203928aee4356845252ac6b662d5c72c29903813eJake Slack    {
32303928aee4356845252ac6b662d5c72c29903813eJake Slack        if (e==null)
32403928aee4356845252ac6b662d5c72c29903813eJake Slack            return new ArrayList<URL>();
32503928aee4356845252ac6b662d5c72c29903813eJake Slack        return Collections.list(e);
32603928aee4356845252ac6b662d5c72c29903813eJake Slack    }
32703928aee4356845252ac6b662d5c72c29903813eJake Slack
32803928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
32903928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
33003928aee4356845252ac6b662d5c72c29903813eJake Slack     * Get a resource from the classloader
33103928aee4356845252ac6b662d5c72c29903813eJake Slack     *
33203928aee4356845252ac6b662d5c72c29903813eJake Slack     * NOTE: this method provides a convenience of hacking off a leading /
33303928aee4356845252ac6b662d5c72c29903813eJake Slack     * should one be present. This is non-standard and it is recommended
33403928aee4356845252ac6b662d5c72c29903813eJake Slack     * to not rely on this behavior
33503928aee4356845252ac6b662d5c72c29903813eJake Slack     */
33603928aee4356845252ac6b662d5c72c29903813eJake Slack    public URL getResource(String name)
33703928aee4356845252ac6b662d5c72c29903813eJake Slack    {
33803928aee4356845252ac6b662d5c72c29903813eJake Slack        URL url= null;
33903928aee4356845252ac6b662d5c72c29903813eJake Slack        boolean tried_parent= false;
34003928aee4356845252ac6b662d5c72c29903813eJake Slack        boolean system_class=_context.isSystemClass(name);
34103928aee4356845252ac6b662d5c72c29903813eJake Slack        boolean server_class=_context.isServerClass(name);
34203928aee4356845252ac6b662d5c72c29903813eJake Slack
34303928aee4356845252ac6b662d5c72c29903813eJake Slack        if (system_class && server_class)
34403928aee4356845252ac6b662d5c72c29903813eJake Slack            return null;
34503928aee4356845252ac6b662d5c72c29903813eJake Slack
34603928aee4356845252ac6b662d5c72c29903813eJake Slack        if (_parent!=null &&(_context.isParentLoaderPriority() || system_class ) && !server_class)
34703928aee4356845252ac6b662d5c72c29903813eJake Slack        {
34803928aee4356845252ac6b662d5c72c29903813eJake Slack            tried_parent= true;
34903928aee4356845252ac6b662d5c72c29903813eJake Slack
35003928aee4356845252ac6b662d5c72c29903813eJake Slack            if (_parent!=null)
35103928aee4356845252ac6b662d5c72c29903813eJake Slack                url= _parent.getResource(name);
35203928aee4356845252ac6b662d5c72c29903813eJake Slack        }
35303928aee4356845252ac6b662d5c72c29903813eJake Slack
35403928aee4356845252ac6b662d5c72c29903813eJake Slack        if (url == null)
35503928aee4356845252ac6b662d5c72c29903813eJake Slack        {
35603928aee4356845252ac6b662d5c72c29903813eJake Slack            url= this.findResource(name);
35703928aee4356845252ac6b662d5c72c29903813eJake Slack
35803928aee4356845252ac6b662d5c72c29903813eJake Slack            if (url == null && name.startsWith("/"))
35903928aee4356845252ac6b662d5c72c29903813eJake Slack            {
36003928aee4356845252ac6b662d5c72c29903813eJake Slack                if (LOG.isDebugEnabled())
36103928aee4356845252ac6b662d5c72c29903813eJake Slack                    LOG.debug("HACK leading / off " + name);
36203928aee4356845252ac6b662d5c72c29903813eJake Slack                url= this.findResource(name.substring(1));
36303928aee4356845252ac6b662d5c72c29903813eJake Slack            }
36403928aee4356845252ac6b662d5c72c29903813eJake Slack        }
36503928aee4356845252ac6b662d5c72c29903813eJake Slack
36603928aee4356845252ac6b662d5c72c29903813eJake Slack        if (url == null && !tried_parent && !server_class )
36703928aee4356845252ac6b662d5c72c29903813eJake Slack        {
36803928aee4356845252ac6b662d5c72c29903813eJake Slack            if (_parent!=null)
36903928aee4356845252ac6b662d5c72c29903813eJake Slack                url= _parent.getResource(name);
37003928aee4356845252ac6b662d5c72c29903813eJake Slack        }
37103928aee4356845252ac6b662d5c72c29903813eJake Slack
37203928aee4356845252ac6b662d5c72c29903813eJake Slack        if (url != null)
37303928aee4356845252ac6b662d5c72c29903813eJake Slack            if (LOG.isDebugEnabled())
37403928aee4356845252ac6b662d5c72c29903813eJake Slack                LOG.debug("getResource("+name+")=" + url);
37503928aee4356845252ac6b662d5c72c29903813eJake Slack
37603928aee4356845252ac6b662d5c72c29903813eJake Slack        return url;
37703928aee4356845252ac6b662d5c72c29903813eJake Slack    }
37803928aee4356845252ac6b662d5c72c29903813eJake Slack
37903928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
38003928aee4356845252ac6b662d5c72c29903813eJake Slack    @Override
38103928aee4356845252ac6b662d5c72c29903813eJake Slack    public Class<?> loadClass(String name) throws ClassNotFoundException
38203928aee4356845252ac6b662d5c72c29903813eJake Slack    {
38303928aee4356845252ac6b662d5c72c29903813eJake Slack        return loadClass(name, false);
38403928aee4356845252ac6b662d5c72c29903813eJake Slack    }
38503928aee4356845252ac6b662d5c72c29903813eJake Slack
38603928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
38703928aee4356845252ac6b662d5c72c29903813eJake Slack    @Override
38803928aee4356845252ac6b662d5c72c29903813eJake Slack    protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
38903928aee4356845252ac6b662d5c72c29903813eJake Slack    {
39003928aee4356845252ac6b662d5c72c29903813eJake Slack        Class<?> c= findLoadedClass(name);
39103928aee4356845252ac6b662d5c72c29903813eJake Slack        ClassNotFoundException ex= null;
39203928aee4356845252ac6b662d5c72c29903813eJake Slack        boolean tried_parent= false;
39303928aee4356845252ac6b662d5c72c29903813eJake Slack
39403928aee4356845252ac6b662d5c72c29903813eJake Slack        boolean system_class=_context.isSystemClass(name);
39503928aee4356845252ac6b662d5c72c29903813eJake Slack        boolean server_class=_context.isServerClass(name);
39603928aee4356845252ac6b662d5c72c29903813eJake Slack
39703928aee4356845252ac6b662d5c72c29903813eJake Slack        if (system_class && server_class)
39803928aee4356845252ac6b662d5c72c29903813eJake Slack        {
39903928aee4356845252ac6b662d5c72c29903813eJake Slack            return null;
40003928aee4356845252ac6b662d5c72c29903813eJake Slack        }
40103928aee4356845252ac6b662d5c72c29903813eJake Slack
40203928aee4356845252ac6b662d5c72c29903813eJake Slack        if (c == null && _parent!=null && (_context.isParentLoaderPriority() || system_class) && !server_class)
40303928aee4356845252ac6b662d5c72c29903813eJake Slack        {
40403928aee4356845252ac6b662d5c72c29903813eJake Slack            tried_parent= true;
40503928aee4356845252ac6b662d5c72c29903813eJake Slack            try
40603928aee4356845252ac6b662d5c72c29903813eJake Slack            {
40703928aee4356845252ac6b662d5c72c29903813eJake Slack                c= _parent.loadClass(name);
40803928aee4356845252ac6b662d5c72c29903813eJake Slack                if (LOG.isDebugEnabled())
40903928aee4356845252ac6b662d5c72c29903813eJake Slack                    LOG.debug("loaded " + c);
41003928aee4356845252ac6b662d5c72c29903813eJake Slack            }
41103928aee4356845252ac6b662d5c72c29903813eJake Slack            catch (ClassNotFoundException e)
41203928aee4356845252ac6b662d5c72c29903813eJake Slack            {
41303928aee4356845252ac6b662d5c72c29903813eJake Slack                ex= e;
41403928aee4356845252ac6b662d5c72c29903813eJake Slack            }
41503928aee4356845252ac6b662d5c72c29903813eJake Slack        }
41603928aee4356845252ac6b662d5c72c29903813eJake Slack
41703928aee4356845252ac6b662d5c72c29903813eJake Slack        if (c == null)
41803928aee4356845252ac6b662d5c72c29903813eJake Slack        {
41903928aee4356845252ac6b662d5c72c29903813eJake Slack            try
42003928aee4356845252ac6b662d5c72c29903813eJake Slack            {
42103928aee4356845252ac6b662d5c72c29903813eJake Slack                c= this.findClass(name);
42203928aee4356845252ac6b662d5c72c29903813eJake Slack            }
42303928aee4356845252ac6b662d5c72c29903813eJake Slack            catch (ClassNotFoundException e)
42403928aee4356845252ac6b662d5c72c29903813eJake Slack            {
42503928aee4356845252ac6b662d5c72c29903813eJake Slack                ex= e;
42603928aee4356845252ac6b662d5c72c29903813eJake Slack            }
42703928aee4356845252ac6b662d5c72c29903813eJake Slack        }
42803928aee4356845252ac6b662d5c72c29903813eJake Slack
42903928aee4356845252ac6b662d5c72c29903813eJake Slack        if (c == null && _parent!=null && !tried_parent && !server_class )
43003928aee4356845252ac6b662d5c72c29903813eJake Slack            c= _parent.loadClass(name);
43103928aee4356845252ac6b662d5c72c29903813eJake Slack
43203928aee4356845252ac6b662d5c72c29903813eJake Slack        if (c == null)
43303928aee4356845252ac6b662d5c72c29903813eJake Slack            throw ex;
43403928aee4356845252ac6b662d5c72c29903813eJake Slack
43503928aee4356845252ac6b662d5c72c29903813eJake Slack        if (resolve)
43603928aee4356845252ac6b662d5c72c29903813eJake Slack            resolveClass(c);
43703928aee4356845252ac6b662d5c72c29903813eJake Slack
43803928aee4356845252ac6b662d5c72c29903813eJake Slack        if (LOG.isDebugEnabled())
43903928aee4356845252ac6b662d5c72c29903813eJake Slack            LOG.debug("loaded " + c+ " from "+c.getClassLoader());
44003928aee4356845252ac6b662d5c72c29903813eJake Slack
44103928aee4356845252ac6b662d5c72c29903813eJake Slack        return c;
44203928aee4356845252ac6b662d5c72c29903813eJake Slack    }
44303928aee4356845252ac6b662d5c72c29903813eJake Slack
44403928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
44503928aee4356845252ac6b662d5c72c29903813eJake Slack    public String toString()
44603928aee4356845252ac6b662d5c72c29903813eJake Slack    {
44703928aee4356845252ac6b662d5c72c29903813eJake Slack        return "WebAppClassLoader=" + _name+"@"+Long.toHexString(hashCode());
44803928aee4356845252ac6b662d5c72c29903813eJake Slack    }
44903928aee4356845252ac6b662d5c72c29903813eJake Slack}
450