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.URI;
2403928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.net.URISyntaxException;
2503928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.net.URL;
2603928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.net.URLClassLoader;
2703928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.ArrayList;
2803928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.List;
2903928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.Locale;
3003928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.regex.Pattern;
3103928aee4356845252ac6b662d5c72c29903813eJake Slack
3203928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.server.Connector;
3303928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.server.Server;
3403928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.util.IO;
3503928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.util.PatternMatcher;
3603928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.util.URIUtil;
3703928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.util.log.Log;
3803928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.util.log.Logger;
3903928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.util.resource.JarResource;
4003928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.util.resource.Resource;
4103928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.util.resource.ResourceCollection;
4203928aee4356845252ac6b662d5c72c29903813eJake Slack
4303928aee4356845252ac6b662d5c72c29903813eJake Slackpublic class WebInfConfiguration extends AbstractConfiguration
4403928aee4356845252ac6b662d5c72c29903813eJake Slack{
4503928aee4356845252ac6b662d5c72c29903813eJake Slack    private static final Logger LOG = Log.getLogger(WebInfConfiguration.class);
4603928aee4356845252ac6b662d5c72c29903813eJake Slack
4703928aee4356845252ac6b662d5c72c29903813eJake Slack    public static final String TEMPDIR_CONFIGURED = "org.eclipse.jetty.tmpdirConfigured";
4803928aee4356845252ac6b662d5c72c29903813eJake Slack    public static final String CONTAINER_JAR_PATTERN = "org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern";
4903928aee4356845252ac6b662d5c72c29903813eJake Slack    public static final String WEBINF_JAR_PATTERN = "org.eclipse.jetty.server.webapp.WebInfIncludeJarPattern";
5003928aee4356845252ac6b662d5c72c29903813eJake Slack
5103928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
5203928aee4356845252ac6b662d5c72c29903813eJake Slack     * If set, to a list of URLs, these resources are added to the context
5303928aee4356845252ac6b662d5c72c29903813eJake Slack     * resource base as a resource collection.
5403928aee4356845252ac6b662d5c72c29903813eJake Slack     */
5503928aee4356845252ac6b662d5c72c29903813eJake Slack    public static final String RESOURCE_URLS = "org.eclipse.jetty.resources";
5603928aee4356845252ac6b662d5c72c29903813eJake Slack
5703928aee4356845252ac6b662d5c72c29903813eJake Slack    protected Resource _preUnpackBaseResource;
5803928aee4356845252ac6b662d5c72c29903813eJake Slack
5903928aee4356845252ac6b662d5c72c29903813eJake Slack    @Override
6003928aee4356845252ac6b662d5c72c29903813eJake Slack    public void preConfigure(final WebAppContext context) throws Exception
6103928aee4356845252ac6b662d5c72c29903813eJake Slack    {
6203928aee4356845252ac6b662d5c72c29903813eJake Slack        // Look for a work directory
6303928aee4356845252ac6b662d5c72c29903813eJake Slack        File work = findWorkDirectory(context);
6403928aee4356845252ac6b662d5c72c29903813eJake Slack        if (work != null)
6503928aee4356845252ac6b662d5c72c29903813eJake Slack            makeTempDirectory(work, context, false);
6603928aee4356845252ac6b662d5c72c29903813eJake Slack
6703928aee4356845252ac6b662d5c72c29903813eJake Slack        //Make a temp directory for the webapp if one is not already set
6803928aee4356845252ac6b662d5c72c29903813eJake Slack        resolveTempDirectory(context);
6903928aee4356845252ac6b662d5c72c29903813eJake Slack
7003928aee4356845252ac6b662d5c72c29903813eJake Slack        //Extract webapp if necessary
7103928aee4356845252ac6b662d5c72c29903813eJake Slack        unpack (context);
7203928aee4356845252ac6b662d5c72c29903813eJake Slack
7303928aee4356845252ac6b662d5c72c29903813eJake Slack
7403928aee4356845252ac6b662d5c72c29903813eJake Slack        //Apply an initial ordering to the jars which governs which will be scanned for META-INF
7503928aee4356845252ac6b662d5c72c29903813eJake Slack        //info and annotations. The ordering is based on inclusion patterns.
7603928aee4356845252ac6b662d5c72c29903813eJake Slack        String tmp = (String)context.getAttribute(WEBINF_JAR_PATTERN);
7703928aee4356845252ac6b662d5c72c29903813eJake Slack        Pattern webInfPattern = (tmp==null?null:Pattern.compile(tmp));
7803928aee4356845252ac6b662d5c72c29903813eJake Slack        tmp = (String)context.getAttribute(CONTAINER_JAR_PATTERN);
7903928aee4356845252ac6b662d5c72c29903813eJake Slack        Pattern containerPattern = (tmp==null?null:Pattern.compile(tmp));
8003928aee4356845252ac6b662d5c72c29903813eJake Slack
8103928aee4356845252ac6b662d5c72c29903813eJake Slack        //Apply ordering to container jars - if no pattern is specified, we won't
8203928aee4356845252ac6b662d5c72c29903813eJake Slack        //match any of the container jars
8303928aee4356845252ac6b662d5c72c29903813eJake Slack        PatternMatcher containerJarNameMatcher = new PatternMatcher ()
8403928aee4356845252ac6b662d5c72c29903813eJake Slack        {
8503928aee4356845252ac6b662d5c72c29903813eJake Slack            public void matched(URI uri) throws Exception
8603928aee4356845252ac6b662d5c72c29903813eJake Slack            {
8703928aee4356845252ac6b662d5c72c29903813eJake Slack                context.getMetaData().addContainerJar(Resource.newResource(uri));
8803928aee4356845252ac6b662d5c72c29903813eJake Slack            }
8903928aee4356845252ac6b662d5c72c29903813eJake Slack        };
9003928aee4356845252ac6b662d5c72c29903813eJake Slack        ClassLoader loader = null;
9103928aee4356845252ac6b662d5c72c29903813eJake Slack        if (context.getClassLoader() != null)
9203928aee4356845252ac6b662d5c72c29903813eJake Slack            loader = context.getClassLoader().getParent();
9303928aee4356845252ac6b662d5c72c29903813eJake Slack
9403928aee4356845252ac6b662d5c72c29903813eJake Slack        while (loader != null && (loader instanceof URLClassLoader))
9503928aee4356845252ac6b662d5c72c29903813eJake Slack        {
9603928aee4356845252ac6b662d5c72c29903813eJake Slack            URL[] urls = ((URLClassLoader)loader).getURLs();
9703928aee4356845252ac6b662d5c72c29903813eJake Slack            if (urls != null)
9803928aee4356845252ac6b662d5c72c29903813eJake Slack            {
9903928aee4356845252ac6b662d5c72c29903813eJake Slack                URI[] containerUris = new URI[urls.length];
10003928aee4356845252ac6b662d5c72c29903813eJake Slack                int i=0;
10103928aee4356845252ac6b662d5c72c29903813eJake Slack                for (URL u : urls)
10203928aee4356845252ac6b662d5c72c29903813eJake Slack                {
10303928aee4356845252ac6b662d5c72c29903813eJake Slack                    try
10403928aee4356845252ac6b662d5c72c29903813eJake Slack                    {
10503928aee4356845252ac6b662d5c72c29903813eJake Slack                        containerUris[i] = u.toURI();
10603928aee4356845252ac6b662d5c72c29903813eJake Slack                    }
10703928aee4356845252ac6b662d5c72c29903813eJake Slack                    catch (URISyntaxException e)
10803928aee4356845252ac6b662d5c72c29903813eJake Slack                    {
10903928aee4356845252ac6b662d5c72c29903813eJake Slack                        containerUris[i] = new URI(u.toString().replaceAll(" ", "%20"));
11003928aee4356845252ac6b662d5c72c29903813eJake Slack                    }
11103928aee4356845252ac6b662d5c72c29903813eJake Slack                    i++;
11203928aee4356845252ac6b662d5c72c29903813eJake Slack                }
11303928aee4356845252ac6b662d5c72c29903813eJake Slack                containerJarNameMatcher.match(containerPattern, containerUris, false);
11403928aee4356845252ac6b662d5c72c29903813eJake Slack            }
11503928aee4356845252ac6b662d5c72c29903813eJake Slack            loader = loader.getParent();
11603928aee4356845252ac6b662d5c72c29903813eJake Slack        }
11703928aee4356845252ac6b662d5c72c29903813eJake Slack
11803928aee4356845252ac6b662d5c72c29903813eJake Slack        //Apply ordering to WEB-INF/lib jars
11903928aee4356845252ac6b662d5c72c29903813eJake Slack        PatternMatcher webInfJarNameMatcher = new PatternMatcher ()
12003928aee4356845252ac6b662d5c72c29903813eJake Slack        {
12103928aee4356845252ac6b662d5c72c29903813eJake Slack            @Override
12203928aee4356845252ac6b662d5c72c29903813eJake Slack            public void matched(URI uri) throws Exception
12303928aee4356845252ac6b662d5c72c29903813eJake Slack            {
12403928aee4356845252ac6b662d5c72c29903813eJake Slack                context.getMetaData().addWebInfJar(Resource.newResource(uri));
12503928aee4356845252ac6b662d5c72c29903813eJake Slack            }
12603928aee4356845252ac6b662d5c72c29903813eJake Slack        };
12703928aee4356845252ac6b662d5c72c29903813eJake Slack        List<Resource> jars = findJars(context);
12803928aee4356845252ac6b662d5c72c29903813eJake Slack
12903928aee4356845252ac6b662d5c72c29903813eJake Slack        //Convert to uris for matching
13003928aee4356845252ac6b662d5c72c29903813eJake Slack        URI[] uris = null;
13103928aee4356845252ac6b662d5c72c29903813eJake Slack        if (jars != null)
13203928aee4356845252ac6b662d5c72c29903813eJake Slack        {
13303928aee4356845252ac6b662d5c72c29903813eJake Slack            uris = new URI[jars.size()];
13403928aee4356845252ac6b662d5c72c29903813eJake Slack            int i=0;
13503928aee4356845252ac6b662d5c72c29903813eJake Slack            for (Resource r: jars)
13603928aee4356845252ac6b662d5c72c29903813eJake Slack            {
13703928aee4356845252ac6b662d5c72c29903813eJake Slack                uris[i++] = r.getURI();
13803928aee4356845252ac6b662d5c72c29903813eJake Slack            }
13903928aee4356845252ac6b662d5c72c29903813eJake Slack        }
14003928aee4356845252ac6b662d5c72c29903813eJake Slack        webInfJarNameMatcher.match(webInfPattern, uris, true); //null is inclusive, no pattern == all jars match
14103928aee4356845252ac6b662d5c72c29903813eJake Slack    }
14203928aee4356845252ac6b662d5c72c29903813eJake Slack
14303928aee4356845252ac6b662d5c72c29903813eJake Slack
14403928aee4356845252ac6b662d5c72c29903813eJake Slack    @Override
14503928aee4356845252ac6b662d5c72c29903813eJake Slack    public void configure(WebAppContext context) throws Exception
14603928aee4356845252ac6b662d5c72c29903813eJake Slack    {
14703928aee4356845252ac6b662d5c72c29903813eJake Slack        //cannot configure if the context is already started
14803928aee4356845252ac6b662d5c72c29903813eJake Slack        if (context.isStarted())
14903928aee4356845252ac6b662d5c72c29903813eJake Slack        {
15003928aee4356845252ac6b662d5c72c29903813eJake Slack            if (LOG.isDebugEnabled())
15103928aee4356845252ac6b662d5c72c29903813eJake Slack                LOG.debug("Cannot configure webapp "+context+" after it is started");
15203928aee4356845252ac6b662d5c72c29903813eJake Slack            return;
15303928aee4356845252ac6b662d5c72c29903813eJake Slack        }
15403928aee4356845252ac6b662d5c72c29903813eJake Slack
15503928aee4356845252ac6b662d5c72c29903813eJake Slack        Resource web_inf = context.getWebInf();
15603928aee4356845252ac6b662d5c72c29903813eJake Slack
15703928aee4356845252ac6b662d5c72c29903813eJake Slack        // Add WEB-INF classes and lib classpaths
15803928aee4356845252ac6b662d5c72c29903813eJake Slack        if (web_inf != null && web_inf.isDirectory() && context.getClassLoader() instanceof WebAppClassLoader)
15903928aee4356845252ac6b662d5c72c29903813eJake Slack        {
16003928aee4356845252ac6b662d5c72c29903813eJake Slack            // Look for classes directory
16103928aee4356845252ac6b662d5c72c29903813eJake Slack            Resource classes= web_inf.addPath("classes/");
16203928aee4356845252ac6b662d5c72c29903813eJake Slack            if (classes.exists())
16303928aee4356845252ac6b662d5c72c29903813eJake Slack                ((WebAppClassLoader)context.getClassLoader()).addClassPath(classes);
16403928aee4356845252ac6b662d5c72c29903813eJake Slack
16503928aee4356845252ac6b662d5c72c29903813eJake Slack            // Look for jars
16603928aee4356845252ac6b662d5c72c29903813eJake Slack            Resource lib= web_inf.addPath("lib/");
16703928aee4356845252ac6b662d5c72c29903813eJake Slack            if (lib.exists() || lib.isDirectory())
16803928aee4356845252ac6b662d5c72c29903813eJake Slack                ((WebAppClassLoader)context.getClassLoader()).addJars(lib);
16903928aee4356845252ac6b662d5c72c29903813eJake Slack        }
17003928aee4356845252ac6b662d5c72c29903813eJake Slack
17103928aee4356845252ac6b662d5c72c29903813eJake Slack        // Look for extra resource
17203928aee4356845252ac6b662d5c72c29903813eJake Slack        @SuppressWarnings("unchecked")
17303928aee4356845252ac6b662d5c72c29903813eJake Slack        List<Resource> resources = (List<Resource>)context.getAttribute(RESOURCE_URLS);
17403928aee4356845252ac6b662d5c72c29903813eJake Slack        if (resources!=null)
17503928aee4356845252ac6b662d5c72c29903813eJake Slack        {
17603928aee4356845252ac6b662d5c72c29903813eJake Slack            Resource[] collection=new Resource[resources.size()+1];
17703928aee4356845252ac6b662d5c72c29903813eJake Slack            int i=0;
17803928aee4356845252ac6b662d5c72c29903813eJake Slack            collection[i++]=context.getBaseResource();
17903928aee4356845252ac6b662d5c72c29903813eJake Slack            for (Resource resource : resources)
18003928aee4356845252ac6b662d5c72c29903813eJake Slack                collection[i++]=resource;
18103928aee4356845252ac6b662d5c72c29903813eJake Slack            context.setBaseResource(new ResourceCollection(collection));
18203928aee4356845252ac6b662d5c72c29903813eJake Slack        }
18303928aee4356845252ac6b662d5c72c29903813eJake Slack    }
18403928aee4356845252ac6b662d5c72c29903813eJake Slack
18503928aee4356845252ac6b662d5c72c29903813eJake Slack    @Override
18603928aee4356845252ac6b662d5c72c29903813eJake Slack    public void deconfigure(WebAppContext context) throws Exception
18703928aee4356845252ac6b662d5c72c29903813eJake Slack    {
18803928aee4356845252ac6b662d5c72c29903813eJake Slack        // delete temp directory if we had to create it or if it isn't called work
18903928aee4356845252ac6b662d5c72c29903813eJake Slack        Boolean tmpdirConfigured = (Boolean)context.getAttribute(TEMPDIR_CONFIGURED);
19003928aee4356845252ac6b662d5c72c29903813eJake Slack
19103928aee4356845252ac6b662d5c72c29903813eJake Slack        if (context.getTempDirectory()!=null && (tmpdirConfigured == null || !tmpdirConfigured.booleanValue()) && !isTempWorkDirectory(context.getTempDirectory()))
19203928aee4356845252ac6b662d5c72c29903813eJake Slack        {
19303928aee4356845252ac6b662d5c72c29903813eJake Slack            IO.delete(context.getTempDirectory());
19403928aee4356845252ac6b662d5c72c29903813eJake Slack            context.setTempDirectory(null);
19503928aee4356845252ac6b662d5c72c29903813eJake Slack
19603928aee4356845252ac6b662d5c72c29903813eJake Slack            //clear out the context attributes for the tmp dir only if we had to
19703928aee4356845252ac6b662d5c72c29903813eJake Slack            //create the tmp dir
19803928aee4356845252ac6b662d5c72c29903813eJake Slack            context.setAttribute(TEMPDIR_CONFIGURED, null);
19903928aee4356845252ac6b662d5c72c29903813eJake Slack            context.setAttribute(WebAppContext.TEMPDIR, null);
20003928aee4356845252ac6b662d5c72c29903813eJake Slack        }
20103928aee4356845252ac6b662d5c72c29903813eJake Slack
20203928aee4356845252ac6b662d5c72c29903813eJake Slack
20303928aee4356845252ac6b662d5c72c29903813eJake Slack        //reset the base resource back to what it was before we did any unpacking of resources
20403928aee4356845252ac6b662d5c72c29903813eJake Slack        context.setBaseResource(_preUnpackBaseResource);
20503928aee4356845252ac6b662d5c72c29903813eJake Slack    }
20603928aee4356845252ac6b662d5c72c29903813eJake Slack
20703928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
20803928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
20903928aee4356845252ac6b662d5c72c29903813eJake Slack     * @see org.eclipse.jetty.webapp.AbstractConfiguration#cloneConfigure(org.eclipse.jetty.webapp.WebAppContext, org.eclipse.jetty.webapp.WebAppContext)
21003928aee4356845252ac6b662d5c72c29903813eJake Slack     */
21103928aee4356845252ac6b662d5c72c29903813eJake Slack    @Override
21203928aee4356845252ac6b662d5c72c29903813eJake Slack    public void cloneConfigure(WebAppContext template, WebAppContext context) throws Exception
21303928aee4356845252ac6b662d5c72c29903813eJake Slack    {
21403928aee4356845252ac6b662d5c72c29903813eJake Slack        File tmpDir=File.createTempFile(WebInfConfiguration.getCanonicalNameForWebAppTmpDir(context),"",template.getTempDirectory().getParentFile());
21503928aee4356845252ac6b662d5c72c29903813eJake Slack        if (tmpDir.exists())
21603928aee4356845252ac6b662d5c72c29903813eJake Slack        {
21703928aee4356845252ac6b662d5c72c29903813eJake Slack            IO.delete(tmpDir);
21803928aee4356845252ac6b662d5c72c29903813eJake Slack        }
21903928aee4356845252ac6b662d5c72c29903813eJake Slack        tmpDir.mkdir();
22003928aee4356845252ac6b662d5c72c29903813eJake Slack        tmpDir.deleteOnExit();
22103928aee4356845252ac6b662d5c72c29903813eJake Slack        context.setTempDirectory(tmpDir);
22203928aee4356845252ac6b662d5c72c29903813eJake Slack    }
22303928aee4356845252ac6b662d5c72c29903813eJake Slack
22403928aee4356845252ac6b662d5c72c29903813eJake Slack
22503928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
22603928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
22703928aee4356845252ac6b662d5c72c29903813eJake Slack     * Get a temporary directory in which to unpack the war etc etc.
22803928aee4356845252ac6b662d5c72c29903813eJake Slack     * The algorithm for determining this is to check these alternatives
22903928aee4356845252ac6b662d5c72c29903813eJake Slack     * in the order shown:
23003928aee4356845252ac6b662d5c72c29903813eJake Slack     *
23103928aee4356845252ac6b662d5c72c29903813eJake Slack     * <p>A. Try to use an explicit directory specifically for this webapp:</p>
23203928aee4356845252ac6b662d5c72c29903813eJake Slack     * <ol>
23303928aee4356845252ac6b662d5c72c29903813eJake Slack     * <li>
23403928aee4356845252ac6b662d5c72c29903813eJake Slack     * Iff an explicit directory is set for this webapp, use it. Do NOT set
23503928aee4356845252ac6b662d5c72c29903813eJake Slack     * delete on exit.
23603928aee4356845252ac6b662d5c72c29903813eJake Slack     * </li>
23703928aee4356845252ac6b662d5c72c29903813eJake Slack     * <li>
23803928aee4356845252ac6b662d5c72c29903813eJake Slack     * Iff javax.servlet.context.tempdir context attribute is set for
23903928aee4356845252ac6b662d5c72c29903813eJake Slack     * this webapp && exists && writeable, then use it. Do NOT set delete on exit.
24003928aee4356845252ac6b662d5c72c29903813eJake Slack     * </li>
24103928aee4356845252ac6b662d5c72c29903813eJake Slack     * </ol>
24203928aee4356845252ac6b662d5c72c29903813eJake Slack     *
24303928aee4356845252ac6b662d5c72c29903813eJake Slack     * <p>B. Create a directory based on global settings. The new directory
24403928aee4356845252ac6b662d5c72c29903813eJake Slack     * will be called "Jetty_"+host+"_"+port+"__"+context+"_"+virtualhost
24503928aee4356845252ac6b662d5c72c29903813eJake Slack     * Work out where to create this directory:
24603928aee4356845252ac6b662d5c72c29903813eJake Slack     * <ol>
24703928aee4356845252ac6b662d5c72c29903813eJake Slack     * <li>
24803928aee4356845252ac6b662d5c72c29903813eJake Slack     * Iff $(jetty.home)/work exists create the directory there. Do NOT
24903928aee4356845252ac6b662d5c72c29903813eJake Slack     * set delete on exit. Do NOT delete contents if dir already exists.
25003928aee4356845252ac6b662d5c72c29903813eJake Slack     * </li>
25103928aee4356845252ac6b662d5c72c29903813eJake Slack     * <li>
25203928aee4356845252ac6b662d5c72c29903813eJake Slack     * Iff WEB-INF/work exists create the directory there. Do NOT set
25303928aee4356845252ac6b662d5c72c29903813eJake Slack     * delete on exit. Do NOT delete contents if dir already exists.
25403928aee4356845252ac6b662d5c72c29903813eJake Slack     * </li>
25503928aee4356845252ac6b662d5c72c29903813eJake Slack     * <li>
25603928aee4356845252ac6b662d5c72c29903813eJake Slack     * Else create dir in $(java.io.tmpdir). Set delete on exit. Delete
25703928aee4356845252ac6b662d5c72c29903813eJake Slack     * contents if dir already exists.
25803928aee4356845252ac6b662d5c72c29903813eJake Slack     * </li>
25903928aee4356845252ac6b662d5c72c29903813eJake Slack     * </ol>
26003928aee4356845252ac6b662d5c72c29903813eJake Slack     */
26103928aee4356845252ac6b662d5c72c29903813eJake Slack    public void resolveTempDirectory (WebAppContext context)
26203928aee4356845252ac6b662d5c72c29903813eJake Slack    {
26303928aee4356845252ac6b662d5c72c29903813eJake Slack        //If a tmp directory is already set, we're done
26403928aee4356845252ac6b662d5c72c29903813eJake Slack        File tmpDir = context.getTempDirectory();
26503928aee4356845252ac6b662d5c72c29903813eJake Slack        if (tmpDir != null && tmpDir.isDirectory() && tmpDir.canWrite())
26603928aee4356845252ac6b662d5c72c29903813eJake Slack        {
26703928aee4356845252ac6b662d5c72c29903813eJake Slack            context.setAttribute(TEMPDIR_CONFIGURED, Boolean.TRUE);
26803928aee4356845252ac6b662d5c72c29903813eJake Slack            return; // Already have a suitable tmp dir configured
26903928aee4356845252ac6b662d5c72c29903813eJake Slack        }
27003928aee4356845252ac6b662d5c72c29903813eJake Slack
27103928aee4356845252ac6b662d5c72c29903813eJake Slack
27203928aee4356845252ac6b662d5c72c29903813eJake Slack        // No temp directory configured, try to establish one.
27303928aee4356845252ac6b662d5c72c29903813eJake Slack        // First we check the context specific, javax.servlet specified, temp directory attribute
27403928aee4356845252ac6b662d5c72c29903813eJake Slack        File servletTmpDir = asFile(context.getAttribute(WebAppContext.TEMPDIR));
27503928aee4356845252ac6b662d5c72c29903813eJake Slack        if (servletTmpDir != null && servletTmpDir.isDirectory() && servletTmpDir.canWrite())
27603928aee4356845252ac6b662d5c72c29903813eJake Slack        {
27703928aee4356845252ac6b662d5c72c29903813eJake Slack            // Use as tmpDir
27803928aee4356845252ac6b662d5c72c29903813eJake Slack            tmpDir = servletTmpDir;
27903928aee4356845252ac6b662d5c72c29903813eJake Slack            // Ensure Attribute has File object
28003928aee4356845252ac6b662d5c72c29903813eJake Slack            context.setAttribute(WebAppContext.TEMPDIR,tmpDir);
28103928aee4356845252ac6b662d5c72c29903813eJake Slack            // Set as TempDir in context.
28203928aee4356845252ac6b662d5c72c29903813eJake Slack            context.setTempDirectory(tmpDir);
28303928aee4356845252ac6b662d5c72c29903813eJake Slack            return;
28403928aee4356845252ac6b662d5c72c29903813eJake Slack        }
28503928aee4356845252ac6b662d5c72c29903813eJake Slack
28603928aee4356845252ac6b662d5c72c29903813eJake Slack        try
28703928aee4356845252ac6b662d5c72c29903813eJake Slack        {
28803928aee4356845252ac6b662d5c72c29903813eJake Slack            // Put the tmp dir in the work directory if we had one
28903928aee4356845252ac6b662d5c72c29903813eJake Slack            File work =  new File(System.getProperty("jetty.home"),"work");
29003928aee4356845252ac6b662d5c72c29903813eJake Slack            if (work.exists() && work.canWrite() && work.isDirectory())
29103928aee4356845252ac6b662d5c72c29903813eJake Slack            {
29203928aee4356845252ac6b662d5c72c29903813eJake Slack                makeTempDirectory(work, context, false); //make a tmp dir inside work, don't delete if it exists
29303928aee4356845252ac6b662d5c72c29903813eJake Slack            }
29403928aee4356845252ac6b662d5c72c29903813eJake Slack            else
29503928aee4356845252ac6b662d5c72c29903813eJake Slack            {
29603928aee4356845252ac6b662d5c72c29903813eJake Slack                File baseTemp = asFile(context.getAttribute(WebAppContext.BASETEMPDIR));
29703928aee4356845252ac6b662d5c72c29903813eJake Slack                if (baseTemp != null && baseTemp.isDirectory() && baseTemp.canWrite())
29803928aee4356845252ac6b662d5c72c29903813eJake Slack                {
29903928aee4356845252ac6b662d5c72c29903813eJake Slack                    // Use baseTemp directory (allow the funky Jetty_0_0_0_0.. subdirectory logic to kick in
30003928aee4356845252ac6b662d5c72c29903813eJake Slack                    makeTempDirectory(baseTemp,context,false);
30103928aee4356845252ac6b662d5c72c29903813eJake Slack                }
30203928aee4356845252ac6b662d5c72c29903813eJake Slack                else
30303928aee4356845252ac6b662d5c72c29903813eJake Slack                {
30403928aee4356845252ac6b662d5c72c29903813eJake Slack                    makeTempDirectory(new File(System.getProperty("java.io.tmpdir")),context,true); //make a tmpdir, delete if it already exists
30503928aee4356845252ac6b662d5c72c29903813eJake Slack                }
30603928aee4356845252ac6b662d5c72c29903813eJake Slack            }
30703928aee4356845252ac6b662d5c72c29903813eJake Slack        }
30803928aee4356845252ac6b662d5c72c29903813eJake Slack        catch(Exception e)
30903928aee4356845252ac6b662d5c72c29903813eJake Slack        {
31003928aee4356845252ac6b662d5c72c29903813eJake Slack            tmpDir=null;
31103928aee4356845252ac6b662d5c72c29903813eJake Slack            LOG.ignore(e);
31203928aee4356845252ac6b662d5c72c29903813eJake Slack        }
31303928aee4356845252ac6b662d5c72c29903813eJake Slack
31403928aee4356845252ac6b662d5c72c29903813eJake Slack        //Third ... Something went wrong trying to make the tmp directory, just make
31503928aee4356845252ac6b662d5c72c29903813eJake Slack        //a jvm managed tmp directory
31603928aee4356845252ac6b662d5c72c29903813eJake Slack        if (context.getTempDirectory() == null)
31703928aee4356845252ac6b662d5c72c29903813eJake Slack        {
31803928aee4356845252ac6b662d5c72c29903813eJake Slack            try
31903928aee4356845252ac6b662d5c72c29903813eJake Slack            {
32003928aee4356845252ac6b662d5c72c29903813eJake Slack                // Last resort
32103928aee4356845252ac6b662d5c72c29903813eJake Slack                tmpDir=File.createTempFile("JettyContext","");
32203928aee4356845252ac6b662d5c72c29903813eJake Slack                if (tmpDir.exists())
32303928aee4356845252ac6b662d5c72c29903813eJake Slack                    IO.delete(tmpDir);
32403928aee4356845252ac6b662d5c72c29903813eJake Slack                tmpDir.mkdir();
32503928aee4356845252ac6b662d5c72c29903813eJake Slack                tmpDir.deleteOnExit();
32603928aee4356845252ac6b662d5c72c29903813eJake Slack                context.setTempDirectory(tmpDir);
32703928aee4356845252ac6b662d5c72c29903813eJake Slack            }
32803928aee4356845252ac6b662d5c72c29903813eJake Slack            catch(IOException e)
32903928aee4356845252ac6b662d5c72c29903813eJake Slack            {
33003928aee4356845252ac6b662d5c72c29903813eJake Slack                tmpDir = null;
33103928aee4356845252ac6b662d5c72c29903813eJake Slack                throw new IllegalStateException("Cannot create tmp dir in "+System.getProperty("java.io.tmpdir")+ " for context "+context,e);
33203928aee4356845252ac6b662d5c72c29903813eJake Slack            }
33303928aee4356845252ac6b662d5c72c29903813eJake Slack        }
33403928aee4356845252ac6b662d5c72c29903813eJake Slack    }
33503928aee4356845252ac6b662d5c72c29903813eJake Slack
33603928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
33703928aee4356845252ac6b662d5c72c29903813eJake Slack     * Given an Object, return File reference for object.
33803928aee4356845252ac6b662d5c72c29903813eJake Slack     * Typically used to convert anonymous Object from getAttribute() calls to a File object.
33903928aee4356845252ac6b662d5c72c29903813eJake Slack     * @param fileattr the file attribute to analyze and return from (supports type File and type String, all others return null)
34003928aee4356845252ac6b662d5c72c29903813eJake Slack     * @return the File object, null if null, or null if not a File or String
34103928aee4356845252ac6b662d5c72c29903813eJake Slack     */
34203928aee4356845252ac6b662d5c72c29903813eJake Slack    private File asFile(Object fileattr)
34303928aee4356845252ac6b662d5c72c29903813eJake Slack    {
34403928aee4356845252ac6b662d5c72c29903813eJake Slack        if (fileattr == null)
34503928aee4356845252ac6b662d5c72c29903813eJake Slack        {
34603928aee4356845252ac6b662d5c72c29903813eJake Slack            return null;
34703928aee4356845252ac6b662d5c72c29903813eJake Slack        }
34803928aee4356845252ac6b662d5c72c29903813eJake Slack        if (fileattr instanceof File)
34903928aee4356845252ac6b662d5c72c29903813eJake Slack        {
35003928aee4356845252ac6b662d5c72c29903813eJake Slack            return (File)fileattr;
35103928aee4356845252ac6b662d5c72c29903813eJake Slack        }
35203928aee4356845252ac6b662d5c72c29903813eJake Slack        if (fileattr instanceof String)
35303928aee4356845252ac6b662d5c72c29903813eJake Slack        {
35403928aee4356845252ac6b662d5c72c29903813eJake Slack            return new File((String)fileattr);
35503928aee4356845252ac6b662d5c72c29903813eJake Slack        }
35603928aee4356845252ac6b662d5c72c29903813eJake Slack        return null;
35703928aee4356845252ac6b662d5c72c29903813eJake Slack    }
35803928aee4356845252ac6b662d5c72c29903813eJake Slack
35903928aee4356845252ac6b662d5c72c29903813eJake Slack
36003928aee4356845252ac6b662d5c72c29903813eJake Slack
36103928aee4356845252ac6b662d5c72c29903813eJake Slack    public void makeTempDirectory (File parent, WebAppContext context, boolean deleteExisting)
36203928aee4356845252ac6b662d5c72c29903813eJake Slack    throws IOException
36303928aee4356845252ac6b662d5c72c29903813eJake Slack    {
36403928aee4356845252ac6b662d5c72c29903813eJake Slack        if (parent != null && parent.exists() && parent.canWrite() && parent.isDirectory())
36503928aee4356845252ac6b662d5c72c29903813eJake Slack        {
36603928aee4356845252ac6b662d5c72c29903813eJake Slack            String temp = getCanonicalNameForWebAppTmpDir(context);
36703928aee4356845252ac6b662d5c72c29903813eJake Slack            File tmpDir = new File(parent,temp);
36803928aee4356845252ac6b662d5c72c29903813eJake Slack
36903928aee4356845252ac6b662d5c72c29903813eJake Slack            if (deleteExisting && tmpDir.exists())
37003928aee4356845252ac6b662d5c72c29903813eJake Slack            {
37103928aee4356845252ac6b662d5c72c29903813eJake Slack                if (!IO.delete(tmpDir))
37203928aee4356845252ac6b662d5c72c29903813eJake Slack                {
37303928aee4356845252ac6b662d5c72c29903813eJake Slack                    if(LOG.isDebugEnabled())LOG.debug("Failed to delete temp dir "+tmpDir);
37403928aee4356845252ac6b662d5c72c29903813eJake Slack                }
37503928aee4356845252ac6b662d5c72c29903813eJake Slack
37603928aee4356845252ac6b662d5c72c29903813eJake Slack                //If we can't delete the existing tmp dir, create a new one
37703928aee4356845252ac6b662d5c72c29903813eJake Slack                if (tmpDir.exists())
37803928aee4356845252ac6b662d5c72c29903813eJake Slack                {
37903928aee4356845252ac6b662d5c72c29903813eJake Slack                    String old=tmpDir.toString();
38003928aee4356845252ac6b662d5c72c29903813eJake Slack                    tmpDir=File.createTempFile(temp+"_","");
38103928aee4356845252ac6b662d5c72c29903813eJake Slack                    if (tmpDir.exists())
38203928aee4356845252ac6b662d5c72c29903813eJake Slack                        IO.delete(tmpDir);
38303928aee4356845252ac6b662d5c72c29903813eJake Slack                    LOG.warn("Can't reuse "+old+", using "+tmpDir);
38403928aee4356845252ac6b662d5c72c29903813eJake Slack                }
38503928aee4356845252ac6b662d5c72c29903813eJake Slack            }
38603928aee4356845252ac6b662d5c72c29903813eJake Slack
38703928aee4356845252ac6b662d5c72c29903813eJake Slack            if (!tmpDir.exists())
38803928aee4356845252ac6b662d5c72c29903813eJake Slack                tmpDir.mkdir();
38903928aee4356845252ac6b662d5c72c29903813eJake Slack
39003928aee4356845252ac6b662d5c72c29903813eJake Slack            //If the parent is not a work directory
39103928aee4356845252ac6b662d5c72c29903813eJake Slack            if (!isTempWorkDirectory(tmpDir))
39203928aee4356845252ac6b662d5c72c29903813eJake Slack            {
39303928aee4356845252ac6b662d5c72c29903813eJake Slack                tmpDir.deleteOnExit();
39403928aee4356845252ac6b662d5c72c29903813eJake Slack            }
39503928aee4356845252ac6b662d5c72c29903813eJake Slack
39603928aee4356845252ac6b662d5c72c29903813eJake Slack            if(LOG.isDebugEnabled())
39703928aee4356845252ac6b662d5c72c29903813eJake Slack                LOG.debug("Set temp dir "+tmpDir);
39803928aee4356845252ac6b662d5c72c29903813eJake Slack            context.setTempDirectory(tmpDir);
39903928aee4356845252ac6b662d5c72c29903813eJake Slack        }
40003928aee4356845252ac6b662d5c72c29903813eJake Slack    }
40103928aee4356845252ac6b662d5c72c29903813eJake Slack
40203928aee4356845252ac6b662d5c72c29903813eJake Slack
40303928aee4356845252ac6b662d5c72c29903813eJake Slack    public void unpack (WebAppContext context) throws IOException
40403928aee4356845252ac6b662d5c72c29903813eJake Slack    {
40503928aee4356845252ac6b662d5c72c29903813eJake Slack        Resource web_app = context.getBaseResource();
40603928aee4356845252ac6b662d5c72c29903813eJake Slack        _preUnpackBaseResource = context.getBaseResource();
40703928aee4356845252ac6b662d5c72c29903813eJake Slack
40803928aee4356845252ac6b662d5c72c29903813eJake Slack        if (web_app == null)
40903928aee4356845252ac6b662d5c72c29903813eJake Slack        {
41003928aee4356845252ac6b662d5c72c29903813eJake Slack            String war = context.getWar();
41103928aee4356845252ac6b662d5c72c29903813eJake Slack            if (war!=null && war.length()>0)
41203928aee4356845252ac6b662d5c72c29903813eJake Slack                web_app = context.newResource(war);
41303928aee4356845252ac6b662d5c72c29903813eJake Slack            else
41403928aee4356845252ac6b662d5c72c29903813eJake Slack                web_app=context.getBaseResource();
41503928aee4356845252ac6b662d5c72c29903813eJake Slack
41603928aee4356845252ac6b662d5c72c29903813eJake Slack            // Accept aliases for WAR files
41703928aee4356845252ac6b662d5c72c29903813eJake Slack            if (web_app.getAlias() != null)
41803928aee4356845252ac6b662d5c72c29903813eJake Slack            {
41903928aee4356845252ac6b662d5c72c29903813eJake Slack                LOG.debug(web_app + " anti-aliased to " + web_app.getAlias());
42003928aee4356845252ac6b662d5c72c29903813eJake Slack                web_app = context.newResource(web_app.getAlias());
42103928aee4356845252ac6b662d5c72c29903813eJake Slack            }
42203928aee4356845252ac6b662d5c72c29903813eJake Slack
42303928aee4356845252ac6b662d5c72c29903813eJake Slack            if (LOG.isDebugEnabled())
42403928aee4356845252ac6b662d5c72c29903813eJake Slack                LOG.debug("Try webapp=" + web_app + ", exists=" + web_app.exists() + ", directory=" + web_app.isDirectory()+" file="+(web_app.getFile()));
42503928aee4356845252ac6b662d5c72c29903813eJake Slack            // Is the WAR usable directly?
42603928aee4356845252ac6b662d5c72c29903813eJake Slack            if (web_app.exists() && !web_app.isDirectory() && !web_app.toString().startsWith("jar:"))
42703928aee4356845252ac6b662d5c72c29903813eJake Slack            {
42803928aee4356845252ac6b662d5c72c29903813eJake Slack                // No - then lets see if it can be turned into a jar URL.
42903928aee4356845252ac6b662d5c72c29903813eJake Slack                Resource jarWebApp = JarResource.newJarResource(web_app);
43003928aee4356845252ac6b662d5c72c29903813eJake Slack                if (jarWebApp.exists() && jarWebApp.isDirectory())
43103928aee4356845252ac6b662d5c72c29903813eJake Slack                    web_app= jarWebApp;
43203928aee4356845252ac6b662d5c72c29903813eJake Slack            }
43303928aee4356845252ac6b662d5c72c29903813eJake Slack
43403928aee4356845252ac6b662d5c72c29903813eJake Slack            // If we should extract or the URL is still not usable
43503928aee4356845252ac6b662d5c72c29903813eJake Slack            if (web_app.exists()  && (
43603928aee4356845252ac6b662d5c72c29903813eJake Slack                    (context.isCopyWebDir() && web_app.getFile() != null && web_app.getFile().isDirectory()) ||
43703928aee4356845252ac6b662d5c72c29903813eJake Slack                    (context.isExtractWAR() && web_app.getFile() != null && !web_app.getFile().isDirectory()) ||
43803928aee4356845252ac6b662d5c72c29903813eJake Slack                    (context.isExtractWAR() && web_app.getFile() == null) ||
43903928aee4356845252ac6b662d5c72c29903813eJake Slack                    !web_app.isDirectory())
44003928aee4356845252ac6b662d5c72c29903813eJake Slack                            )
44103928aee4356845252ac6b662d5c72c29903813eJake Slack            {
44203928aee4356845252ac6b662d5c72c29903813eJake Slack                // Look for sibling directory.
44303928aee4356845252ac6b662d5c72c29903813eJake Slack                File extractedWebAppDir = null;
44403928aee4356845252ac6b662d5c72c29903813eJake Slack
44503928aee4356845252ac6b662d5c72c29903813eJake Slack                if (war!=null)
44603928aee4356845252ac6b662d5c72c29903813eJake Slack                {
44703928aee4356845252ac6b662d5c72c29903813eJake Slack                    // look for a sibling like "foo/" to a "foo.war"
44803928aee4356845252ac6b662d5c72c29903813eJake Slack                    File warfile=Resource.newResource(war).getFile();
44903928aee4356845252ac6b662d5c72c29903813eJake Slack                    if (warfile!=null && warfile.getName().toLowerCase(Locale.ENGLISH).endsWith(".war"))
45003928aee4356845252ac6b662d5c72c29903813eJake Slack                    {
45103928aee4356845252ac6b662d5c72c29903813eJake Slack                        File sibling = new File(warfile.getParent(),warfile.getName().substring(0,warfile.getName().length()-4));
45203928aee4356845252ac6b662d5c72c29903813eJake Slack                        if (sibling.exists() && sibling.isDirectory() && sibling.canWrite())
45303928aee4356845252ac6b662d5c72c29903813eJake Slack                            extractedWebAppDir=sibling;
45403928aee4356845252ac6b662d5c72c29903813eJake Slack                    }
45503928aee4356845252ac6b662d5c72c29903813eJake Slack                }
45603928aee4356845252ac6b662d5c72c29903813eJake Slack
45703928aee4356845252ac6b662d5c72c29903813eJake Slack                if (extractedWebAppDir==null)
45803928aee4356845252ac6b662d5c72c29903813eJake Slack                    // Then extract it if necessary to the temporary location
45903928aee4356845252ac6b662d5c72c29903813eJake Slack                    extractedWebAppDir= new File(context.getTempDirectory(), "webapp");
46003928aee4356845252ac6b662d5c72c29903813eJake Slack
46103928aee4356845252ac6b662d5c72c29903813eJake Slack                if (web_app.getFile()!=null && web_app.getFile().isDirectory())
46203928aee4356845252ac6b662d5c72c29903813eJake Slack                {
46303928aee4356845252ac6b662d5c72c29903813eJake Slack                    // Copy directory
46403928aee4356845252ac6b662d5c72c29903813eJake Slack                    LOG.info("Copy " + web_app + " to " + extractedWebAppDir);
46503928aee4356845252ac6b662d5c72c29903813eJake Slack                    web_app.copyTo(extractedWebAppDir);
46603928aee4356845252ac6b662d5c72c29903813eJake Slack                }
46703928aee4356845252ac6b662d5c72c29903813eJake Slack                else
46803928aee4356845252ac6b662d5c72c29903813eJake Slack                {
46903928aee4356845252ac6b662d5c72c29903813eJake Slack                    //Use a sentinel file that will exist only whilst the extraction is taking place.
47003928aee4356845252ac6b662d5c72c29903813eJake Slack                    //This will help us detect interrupted extractions.
47103928aee4356845252ac6b662d5c72c29903813eJake Slack                    File extractionLock = new File (context.getTempDirectory(), ".extract_lock");
47203928aee4356845252ac6b662d5c72c29903813eJake Slack
47303928aee4356845252ac6b662d5c72c29903813eJake Slack                    if (!extractedWebAppDir.exists())
47403928aee4356845252ac6b662d5c72c29903813eJake Slack                    {
47503928aee4356845252ac6b662d5c72c29903813eJake Slack                        //it hasn't been extracted before so extract it
47603928aee4356845252ac6b662d5c72c29903813eJake Slack                        extractionLock.createNewFile();
47703928aee4356845252ac6b662d5c72c29903813eJake Slack                        extractedWebAppDir.mkdir();
47803928aee4356845252ac6b662d5c72c29903813eJake Slack                        LOG.info("Extract " + web_app + " to " + extractedWebAppDir);
47903928aee4356845252ac6b662d5c72c29903813eJake Slack                        Resource jar_web_app = JarResource.newJarResource(web_app);
48003928aee4356845252ac6b662d5c72c29903813eJake Slack                        jar_web_app.copyTo(extractedWebAppDir);
48103928aee4356845252ac6b662d5c72c29903813eJake Slack                        extractionLock.delete();
48203928aee4356845252ac6b662d5c72c29903813eJake Slack                    }
48303928aee4356845252ac6b662d5c72c29903813eJake Slack                    else
48403928aee4356845252ac6b662d5c72c29903813eJake Slack                    {
48503928aee4356845252ac6b662d5c72c29903813eJake Slack                        //only extract if the war file is newer, or a .extract_lock file is left behind meaning a possible partial extraction
48603928aee4356845252ac6b662d5c72c29903813eJake Slack                        if (web_app.lastModified() > extractedWebAppDir.lastModified() || extractionLock.exists())
48703928aee4356845252ac6b662d5c72c29903813eJake Slack                        {
48803928aee4356845252ac6b662d5c72c29903813eJake Slack                            extractionLock.createNewFile();
48903928aee4356845252ac6b662d5c72c29903813eJake Slack                            IO.delete(extractedWebAppDir);
49003928aee4356845252ac6b662d5c72c29903813eJake Slack                            extractedWebAppDir.mkdir();
49103928aee4356845252ac6b662d5c72c29903813eJake Slack                            LOG.info("Extract " + web_app + " to " + extractedWebAppDir);
49203928aee4356845252ac6b662d5c72c29903813eJake Slack                            Resource jar_web_app = JarResource.newJarResource(web_app);
49303928aee4356845252ac6b662d5c72c29903813eJake Slack                            jar_web_app.copyTo(extractedWebAppDir);
49403928aee4356845252ac6b662d5c72c29903813eJake Slack                            extractionLock.delete();
49503928aee4356845252ac6b662d5c72c29903813eJake Slack                        }
49603928aee4356845252ac6b662d5c72c29903813eJake Slack                    }
49703928aee4356845252ac6b662d5c72c29903813eJake Slack                }
49803928aee4356845252ac6b662d5c72c29903813eJake Slack                web_app = Resource.newResource(extractedWebAppDir.getCanonicalPath());
49903928aee4356845252ac6b662d5c72c29903813eJake Slack            }
50003928aee4356845252ac6b662d5c72c29903813eJake Slack
50103928aee4356845252ac6b662d5c72c29903813eJake Slack            // Now do we have something usable?
50203928aee4356845252ac6b662d5c72c29903813eJake Slack            if (!web_app.exists() || !web_app.isDirectory())
50303928aee4356845252ac6b662d5c72c29903813eJake Slack            {
50403928aee4356845252ac6b662d5c72c29903813eJake Slack                LOG.warn("Web application not found " + war);
50503928aee4356845252ac6b662d5c72c29903813eJake Slack                throw new java.io.FileNotFoundException(war);
50603928aee4356845252ac6b662d5c72c29903813eJake Slack            }
50703928aee4356845252ac6b662d5c72c29903813eJake Slack
50803928aee4356845252ac6b662d5c72c29903813eJake Slack            context.setBaseResource(web_app);
50903928aee4356845252ac6b662d5c72c29903813eJake Slack
51003928aee4356845252ac6b662d5c72c29903813eJake Slack            if (LOG.isDebugEnabled())
51103928aee4356845252ac6b662d5c72c29903813eJake Slack                LOG.debug("webapp=" + web_app);
51203928aee4356845252ac6b662d5c72c29903813eJake Slack        }
51303928aee4356845252ac6b662d5c72c29903813eJake Slack
51403928aee4356845252ac6b662d5c72c29903813eJake Slack
51503928aee4356845252ac6b662d5c72c29903813eJake Slack        // Do we need to extract WEB-INF/lib?
51603928aee4356845252ac6b662d5c72c29903813eJake Slack        if (context.isCopyWebInf() && !context.isCopyWebDir())
51703928aee4356845252ac6b662d5c72c29903813eJake Slack        {
51803928aee4356845252ac6b662d5c72c29903813eJake Slack            Resource web_inf= web_app.addPath("WEB-INF/");
51903928aee4356845252ac6b662d5c72c29903813eJake Slack
52003928aee4356845252ac6b662d5c72c29903813eJake Slack            File extractedWebInfDir= new File(context.getTempDirectory(), "webinf");
52103928aee4356845252ac6b662d5c72c29903813eJake Slack            if (extractedWebInfDir.exists())
52203928aee4356845252ac6b662d5c72c29903813eJake Slack                IO.delete(extractedWebInfDir);
52303928aee4356845252ac6b662d5c72c29903813eJake Slack            extractedWebInfDir.mkdir();
52403928aee4356845252ac6b662d5c72c29903813eJake Slack            Resource web_inf_lib = web_inf.addPath("lib/");
52503928aee4356845252ac6b662d5c72c29903813eJake Slack            File webInfDir=new File(extractedWebInfDir,"WEB-INF");
52603928aee4356845252ac6b662d5c72c29903813eJake Slack            webInfDir.mkdir();
52703928aee4356845252ac6b662d5c72c29903813eJake Slack
52803928aee4356845252ac6b662d5c72c29903813eJake Slack            if (web_inf_lib.exists())
52903928aee4356845252ac6b662d5c72c29903813eJake Slack            {
53003928aee4356845252ac6b662d5c72c29903813eJake Slack                File webInfLibDir = new File(webInfDir, "lib");
53103928aee4356845252ac6b662d5c72c29903813eJake Slack                if (webInfLibDir.exists())
53203928aee4356845252ac6b662d5c72c29903813eJake Slack                    IO.delete(webInfLibDir);
53303928aee4356845252ac6b662d5c72c29903813eJake Slack                webInfLibDir.mkdir();
53403928aee4356845252ac6b662d5c72c29903813eJake Slack
53503928aee4356845252ac6b662d5c72c29903813eJake Slack                LOG.info("Copying WEB-INF/lib " + web_inf_lib + " to " + webInfLibDir);
53603928aee4356845252ac6b662d5c72c29903813eJake Slack                web_inf_lib.copyTo(webInfLibDir);
53703928aee4356845252ac6b662d5c72c29903813eJake Slack            }
53803928aee4356845252ac6b662d5c72c29903813eJake Slack
53903928aee4356845252ac6b662d5c72c29903813eJake Slack            Resource web_inf_classes = web_inf.addPath("classes/");
54003928aee4356845252ac6b662d5c72c29903813eJake Slack            if (web_inf_classes.exists())
54103928aee4356845252ac6b662d5c72c29903813eJake Slack            {
54203928aee4356845252ac6b662d5c72c29903813eJake Slack                File webInfClassesDir = new File(webInfDir, "classes");
54303928aee4356845252ac6b662d5c72c29903813eJake Slack                if (webInfClassesDir.exists())
54403928aee4356845252ac6b662d5c72c29903813eJake Slack                    IO.delete(webInfClassesDir);
54503928aee4356845252ac6b662d5c72c29903813eJake Slack                webInfClassesDir.mkdir();
54603928aee4356845252ac6b662d5c72c29903813eJake Slack                LOG.info("Copying WEB-INF/classes from "+web_inf_classes+" to "+webInfClassesDir.getAbsolutePath());
54703928aee4356845252ac6b662d5c72c29903813eJake Slack                web_inf_classes.copyTo(webInfClassesDir);
54803928aee4356845252ac6b662d5c72c29903813eJake Slack            }
54903928aee4356845252ac6b662d5c72c29903813eJake Slack
55003928aee4356845252ac6b662d5c72c29903813eJake Slack            web_inf=Resource.newResource(extractedWebInfDir.getCanonicalPath());
55103928aee4356845252ac6b662d5c72c29903813eJake Slack
55203928aee4356845252ac6b662d5c72c29903813eJake Slack            ResourceCollection rc = new ResourceCollection(web_inf,web_app);
55303928aee4356845252ac6b662d5c72c29903813eJake Slack
55403928aee4356845252ac6b662d5c72c29903813eJake Slack            if (LOG.isDebugEnabled())
55503928aee4356845252ac6b662d5c72c29903813eJake Slack                LOG.debug("context.resourcebase = "+rc);
55603928aee4356845252ac6b662d5c72c29903813eJake Slack
55703928aee4356845252ac6b662d5c72c29903813eJake Slack            context.setBaseResource(rc);
55803928aee4356845252ac6b662d5c72c29903813eJake Slack        }
55903928aee4356845252ac6b662d5c72c29903813eJake Slack    }
56003928aee4356845252ac6b662d5c72c29903813eJake Slack
56103928aee4356845252ac6b662d5c72c29903813eJake Slack
56203928aee4356845252ac6b662d5c72c29903813eJake Slack    public File findWorkDirectory (WebAppContext context) throws IOException
56303928aee4356845252ac6b662d5c72c29903813eJake Slack    {
56403928aee4356845252ac6b662d5c72c29903813eJake Slack        if (context.getBaseResource() != null)
56503928aee4356845252ac6b662d5c72c29903813eJake Slack        {
56603928aee4356845252ac6b662d5c72c29903813eJake Slack            Resource web_inf = context.getWebInf();
56703928aee4356845252ac6b662d5c72c29903813eJake Slack            if (web_inf !=null && web_inf.exists())
56803928aee4356845252ac6b662d5c72c29903813eJake Slack            {
56903928aee4356845252ac6b662d5c72c29903813eJake Slack               return new File(web_inf.getFile(),"work");
57003928aee4356845252ac6b662d5c72c29903813eJake Slack            }
57103928aee4356845252ac6b662d5c72c29903813eJake Slack        }
57203928aee4356845252ac6b662d5c72c29903813eJake Slack        return null;
57303928aee4356845252ac6b662d5c72c29903813eJake Slack    }
57403928aee4356845252ac6b662d5c72c29903813eJake Slack
57503928aee4356845252ac6b662d5c72c29903813eJake Slack
57603928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
57703928aee4356845252ac6b662d5c72c29903813eJake Slack     * Check if the tmpDir itself is called "work", or if the tmpDir
57803928aee4356845252ac6b662d5c72c29903813eJake Slack     * is in a directory called "work".
57903928aee4356845252ac6b662d5c72c29903813eJake Slack     * @return true if File is a temporary or work directory
58003928aee4356845252ac6b662d5c72c29903813eJake Slack     */
58103928aee4356845252ac6b662d5c72c29903813eJake Slack    public boolean isTempWorkDirectory (File tmpDir)
58203928aee4356845252ac6b662d5c72c29903813eJake Slack    {
58303928aee4356845252ac6b662d5c72c29903813eJake Slack        if (tmpDir == null)
58403928aee4356845252ac6b662d5c72c29903813eJake Slack            return false;
58503928aee4356845252ac6b662d5c72c29903813eJake Slack        if (tmpDir.getName().equalsIgnoreCase("work"))
58603928aee4356845252ac6b662d5c72c29903813eJake Slack            return true;
58703928aee4356845252ac6b662d5c72c29903813eJake Slack        File t = tmpDir.getParentFile();
58803928aee4356845252ac6b662d5c72c29903813eJake Slack        if (t == null)
58903928aee4356845252ac6b662d5c72c29903813eJake Slack            return false;
59003928aee4356845252ac6b662d5c72c29903813eJake Slack        return (t.getName().equalsIgnoreCase("work"));
59103928aee4356845252ac6b662d5c72c29903813eJake Slack    }
59203928aee4356845252ac6b662d5c72c29903813eJake Slack
59303928aee4356845252ac6b662d5c72c29903813eJake Slack
59403928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
59503928aee4356845252ac6b662d5c72c29903813eJake Slack     * Create a canonical name for a webapp temp directory.
59603928aee4356845252ac6b662d5c72c29903813eJake Slack     * The form of the name is:
59703928aee4356845252ac6b662d5c72c29903813eJake Slack     *  <code>"Jetty_"+host+"_"+port+"__"+resourceBase+"_"+context+"_"+virtualhost+base36_hashcode_of_whole_string</code>
59803928aee4356845252ac6b662d5c72c29903813eJake Slack     *
59903928aee4356845252ac6b662d5c72c29903813eJake Slack     *  host and port uniquely identify the server
60003928aee4356845252ac6b662d5c72c29903813eJake Slack     *  context and virtual host uniquely identify the webapp
60103928aee4356845252ac6b662d5c72c29903813eJake Slack     * @return the canonical name for the webapp temp directory
60203928aee4356845252ac6b662d5c72c29903813eJake Slack     */
60303928aee4356845252ac6b662d5c72c29903813eJake Slack    public static String getCanonicalNameForWebAppTmpDir (WebAppContext context)
60403928aee4356845252ac6b662d5c72c29903813eJake Slack    {
60503928aee4356845252ac6b662d5c72c29903813eJake Slack        StringBuffer canonicalName = new StringBuffer();
60603928aee4356845252ac6b662d5c72c29903813eJake Slack        canonicalName.append("jetty-");
60703928aee4356845252ac6b662d5c72c29903813eJake Slack
60803928aee4356845252ac6b662d5c72c29903813eJake Slack        //get the host and the port from the first connector
60903928aee4356845252ac6b662d5c72c29903813eJake Slack        Server server=context.getServer();
61003928aee4356845252ac6b662d5c72c29903813eJake Slack        if (server!=null)
61103928aee4356845252ac6b662d5c72c29903813eJake Slack        {
61203928aee4356845252ac6b662d5c72c29903813eJake Slack            Connector[] connectors = context.getServer().getConnectors();
61303928aee4356845252ac6b662d5c72c29903813eJake Slack
61403928aee4356845252ac6b662d5c72c29903813eJake Slack            if (connectors.length>0)
61503928aee4356845252ac6b662d5c72c29903813eJake Slack            {
61603928aee4356845252ac6b662d5c72c29903813eJake Slack                //Get the host
61703928aee4356845252ac6b662d5c72c29903813eJake Slack                String host = (connectors==null||connectors[0]==null?"":connectors[0].getHost());
61803928aee4356845252ac6b662d5c72c29903813eJake Slack                if (host == null)
61903928aee4356845252ac6b662d5c72c29903813eJake Slack                    host = "0.0.0.0";
62003928aee4356845252ac6b662d5c72c29903813eJake Slack                canonicalName.append(host);
62103928aee4356845252ac6b662d5c72c29903813eJake Slack
62203928aee4356845252ac6b662d5c72c29903813eJake Slack                //Get the port
62303928aee4356845252ac6b662d5c72c29903813eJake Slack                canonicalName.append("-");
62403928aee4356845252ac6b662d5c72c29903813eJake Slack                //try getting the real port being listened on
62503928aee4356845252ac6b662d5c72c29903813eJake Slack                int port = (connectors==null||connectors[0]==null?0:connectors[0].getLocalPort());
62603928aee4356845252ac6b662d5c72c29903813eJake Slack                //if not available (eg no connectors or connector not started),
62703928aee4356845252ac6b662d5c72c29903813eJake Slack                //try getting one that was configured.
62803928aee4356845252ac6b662d5c72c29903813eJake Slack                if (port < 0)
62903928aee4356845252ac6b662d5c72c29903813eJake Slack                    port = connectors[0].getPort();
63003928aee4356845252ac6b662d5c72c29903813eJake Slack                canonicalName.append(port);
63103928aee4356845252ac6b662d5c72c29903813eJake Slack                canonicalName.append("-");
63203928aee4356845252ac6b662d5c72c29903813eJake Slack            }
63303928aee4356845252ac6b662d5c72c29903813eJake Slack        }
63403928aee4356845252ac6b662d5c72c29903813eJake Slack
63503928aee4356845252ac6b662d5c72c29903813eJake Slack
63603928aee4356845252ac6b662d5c72c29903813eJake Slack        //Resource  base
63703928aee4356845252ac6b662d5c72c29903813eJake Slack        try
63803928aee4356845252ac6b662d5c72c29903813eJake Slack        {
63903928aee4356845252ac6b662d5c72c29903813eJake Slack            Resource resource = context.getBaseResource();
64003928aee4356845252ac6b662d5c72c29903813eJake Slack            if (resource == null)
64103928aee4356845252ac6b662d5c72c29903813eJake Slack            {
64203928aee4356845252ac6b662d5c72c29903813eJake Slack                if (context.getWar()==null || context.getWar().length()==0)
64303928aee4356845252ac6b662d5c72c29903813eJake Slack                    resource=context.newResource(context.getResourceBase());
64403928aee4356845252ac6b662d5c72c29903813eJake Slack
64503928aee4356845252ac6b662d5c72c29903813eJake Slack                // Set dir or WAR
64603928aee4356845252ac6b662d5c72c29903813eJake Slack                resource = context.newResource(context.getWar());
64703928aee4356845252ac6b662d5c72c29903813eJake Slack            }
64803928aee4356845252ac6b662d5c72c29903813eJake Slack
64903928aee4356845252ac6b662d5c72c29903813eJake Slack            String tmp = URIUtil.decodePath(resource.getURL().getPath());
65003928aee4356845252ac6b662d5c72c29903813eJake Slack            if (tmp.endsWith("/"))
65103928aee4356845252ac6b662d5c72c29903813eJake Slack                tmp = tmp.substring(0, tmp.length()-1);
65203928aee4356845252ac6b662d5c72c29903813eJake Slack            if (tmp.endsWith("!"))
65303928aee4356845252ac6b662d5c72c29903813eJake Slack                tmp = tmp.substring(0, tmp.length() -1);
65403928aee4356845252ac6b662d5c72c29903813eJake Slack            //get just the last part which is the filename
65503928aee4356845252ac6b662d5c72c29903813eJake Slack            int i = tmp.lastIndexOf("/");
65603928aee4356845252ac6b662d5c72c29903813eJake Slack            canonicalName.append(tmp.substring(i+1, tmp.length()));
65703928aee4356845252ac6b662d5c72c29903813eJake Slack            canonicalName.append("-");
65803928aee4356845252ac6b662d5c72c29903813eJake Slack        }
65903928aee4356845252ac6b662d5c72c29903813eJake Slack        catch (Exception e)
66003928aee4356845252ac6b662d5c72c29903813eJake Slack        {
66103928aee4356845252ac6b662d5c72c29903813eJake Slack            LOG.warn("Can't generate resourceBase as part of webapp tmp dir name", e);
66203928aee4356845252ac6b662d5c72c29903813eJake Slack        }
66303928aee4356845252ac6b662d5c72c29903813eJake Slack
66403928aee4356845252ac6b662d5c72c29903813eJake Slack        //Context name
66503928aee4356845252ac6b662d5c72c29903813eJake Slack        String contextPath = context.getContextPath();
66603928aee4356845252ac6b662d5c72c29903813eJake Slack        contextPath=contextPath.replace('/','_');
66703928aee4356845252ac6b662d5c72c29903813eJake Slack        contextPath=contextPath.replace('\\','_');
66803928aee4356845252ac6b662d5c72c29903813eJake Slack        canonicalName.append(contextPath);
66903928aee4356845252ac6b662d5c72c29903813eJake Slack
67003928aee4356845252ac6b662d5c72c29903813eJake Slack        //Virtual host (if there is one)
67103928aee4356845252ac6b662d5c72c29903813eJake Slack        canonicalName.append("-");
67203928aee4356845252ac6b662d5c72c29903813eJake Slack        String[] vhosts = context.getVirtualHosts();
67303928aee4356845252ac6b662d5c72c29903813eJake Slack        if (vhosts == null || vhosts.length <= 0)
67403928aee4356845252ac6b662d5c72c29903813eJake Slack            canonicalName.append("any");
67503928aee4356845252ac6b662d5c72c29903813eJake Slack        else
67603928aee4356845252ac6b662d5c72c29903813eJake Slack            canonicalName.append(vhosts[0]);
67703928aee4356845252ac6b662d5c72c29903813eJake Slack
67803928aee4356845252ac6b662d5c72c29903813eJake Slack        // sanitize
67903928aee4356845252ac6b662d5c72c29903813eJake Slack        for (int i=0;i<canonicalName.length();i++)
68003928aee4356845252ac6b662d5c72c29903813eJake Slack        {
68103928aee4356845252ac6b662d5c72c29903813eJake Slack            char c=canonicalName.charAt(i);
68203928aee4356845252ac6b662d5c72c29903813eJake Slack            if (!Character.isJavaIdentifierPart(c) && "-.".indexOf(c)<0)
68303928aee4356845252ac6b662d5c72c29903813eJake Slack                canonicalName.setCharAt(i,'.');
68403928aee4356845252ac6b662d5c72c29903813eJake Slack        }
68503928aee4356845252ac6b662d5c72c29903813eJake Slack
68603928aee4356845252ac6b662d5c72c29903813eJake Slack        canonicalName.append("-");
68703928aee4356845252ac6b662d5c72c29903813eJake Slack        return canonicalName.toString();
68803928aee4356845252ac6b662d5c72c29903813eJake Slack    }
68903928aee4356845252ac6b662d5c72c29903813eJake Slack
69003928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
69103928aee4356845252ac6b662d5c72c29903813eJake Slack     * Look for jars in WEB-INF/lib
69203928aee4356845252ac6b662d5c72c29903813eJake Slack     * @param context
69303928aee4356845252ac6b662d5c72c29903813eJake Slack     * @return the list of jar resources found within context
69403928aee4356845252ac6b662d5c72c29903813eJake Slack     * @throws Exception
69503928aee4356845252ac6b662d5c72c29903813eJake Slack     */
69603928aee4356845252ac6b662d5c72c29903813eJake Slack    protected List<Resource> findJars (WebAppContext context)
69703928aee4356845252ac6b662d5c72c29903813eJake Slack    throws Exception
69803928aee4356845252ac6b662d5c72c29903813eJake Slack    {
69903928aee4356845252ac6b662d5c72c29903813eJake Slack        List<Resource> jarResources = new ArrayList<Resource>();
70003928aee4356845252ac6b662d5c72c29903813eJake Slack
70103928aee4356845252ac6b662d5c72c29903813eJake Slack        Resource web_inf = context.getWebInf();
70203928aee4356845252ac6b662d5c72c29903813eJake Slack        if (web_inf==null || !web_inf.exists())
70303928aee4356845252ac6b662d5c72c29903813eJake Slack            return null;
70403928aee4356845252ac6b662d5c72c29903813eJake Slack
70503928aee4356845252ac6b662d5c72c29903813eJake Slack        Resource web_inf_lib = web_inf.addPath("/lib");
70603928aee4356845252ac6b662d5c72c29903813eJake Slack
70703928aee4356845252ac6b662d5c72c29903813eJake Slack
70803928aee4356845252ac6b662d5c72c29903813eJake Slack        if (web_inf_lib.exists() && web_inf_lib.isDirectory())
70903928aee4356845252ac6b662d5c72c29903813eJake Slack        {
71003928aee4356845252ac6b662d5c72c29903813eJake Slack            String[] files=web_inf_lib.list();
71103928aee4356845252ac6b662d5c72c29903813eJake Slack            for (int f=0;files!=null && f<files.length;f++)
71203928aee4356845252ac6b662d5c72c29903813eJake Slack            {
71303928aee4356845252ac6b662d5c72c29903813eJake Slack                try
71403928aee4356845252ac6b662d5c72c29903813eJake Slack                {
71503928aee4356845252ac6b662d5c72c29903813eJake Slack                    Resource file = web_inf_lib.addPath(files[f]);
71603928aee4356845252ac6b662d5c72c29903813eJake Slack                    String fnlc = file.getName().toLowerCase(Locale.ENGLISH);
71703928aee4356845252ac6b662d5c72c29903813eJake Slack                    int dot = fnlc.lastIndexOf('.');
71803928aee4356845252ac6b662d5c72c29903813eJake Slack                    String extension = (dot < 0 ? null : fnlc.substring(dot));
71903928aee4356845252ac6b662d5c72c29903813eJake Slack                    if (extension != null && (extension.equals(".jar") || extension.equals(".zip")))
72003928aee4356845252ac6b662d5c72c29903813eJake Slack                    {
72103928aee4356845252ac6b662d5c72c29903813eJake Slack                        jarResources.add(file);
72203928aee4356845252ac6b662d5c72c29903813eJake Slack                    }
72303928aee4356845252ac6b662d5c72c29903813eJake Slack                }
72403928aee4356845252ac6b662d5c72c29903813eJake Slack                catch (Exception ex)
72503928aee4356845252ac6b662d5c72c29903813eJake Slack                {
72603928aee4356845252ac6b662d5c72c29903813eJake Slack                    LOG.warn(Log.EXCEPTION,ex);
72703928aee4356845252ac6b662d5c72c29903813eJake Slack                }
72803928aee4356845252ac6b662d5c72c29903813eJake Slack            }
72903928aee4356845252ac6b662d5c72c29903813eJake Slack        }
73003928aee4356845252ac6b662d5c72c29903813eJake Slack        return jarResources;
73103928aee4356845252ac6b662d5c72c29903813eJake Slack    }
73203928aee4356845252ac6b662d5c72c29903813eJake Slack}
733