com.adito.server.jetty.CustomWebApplicationContext.java Source code

Java tutorial

Introduction

Here is the source code for com.adito.server.jetty.CustomWebApplicationContext.java

Source

/*
*  Adito
*
*  Copyright (C) 2003-2006 3SP LTD. All Rights Reserved
*
*  This program is free software; you can redistribute it and/or
*  modify it under the terms of the GNU General Public License
*  as published by the Free Software Foundation; either version 2 of
*  the License, or (at your option) any later version.
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU General Public License for more details.
*
*  You should have received a copy of the GNU General Public
*  License along with this program; if not, write to the Free Software
*  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

package com.adito.server.jetty;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.mortbay.http.ResourceCache;
import org.mortbay.jetty.servlet.AbstractSessionManager;
import org.mortbay.jetty.servlet.ServletHolder;
import org.mortbay.jetty.servlet.WebApplicationContext;
import org.mortbay.util.Resource;

import com.adito.boot.Branding;
import com.adito.boot.ContextHolder;
import com.adito.boot.SystemProperties;

/**
 * <p>
 * An extension to the standard Jetty
 * {@link org.mortbay.jetty.servlet.WebApplicationContext} that allows resources
 * to be loaded from multiple {@link org.mortbay.http.ResourceCache}s.
 * 
 * <p>
 * This is necessary for the plugin architecture so that plugins may register
 * their own <b>webapp</b> directories which can then be overlaid onto the
 * namespace of the main Adito webapp.
 * 
 * <p>
 * Other Adito specific webapp configuration is also performed here,
 * including setting up the special /defaultStyle.css alias that is used to
 * workaround the change to the way the CSS file is load. If this alias was not
 * set up, upgraders would have lost their CSS as /defaultStyle.css no longer
 * really exists.
 * 
 * <p>
 * Plugins also register new classpaths here so that any Java bytecode they may
 * require (be it in .CLASS format or .JAR format) may be loaded in the same
 * Class Loader as the main webapp.
 */
public class CustomWebApplicationContext extends WebApplicationContext {

    final static Log log = LogFactory.getLog(CustomWebApplicationContext.class);
    private List<ResourceCache> resourceCaches;
    private String additionalClasspath;
    private List<ResourceCache> reverseCaches;
    private Map<String, ResourceCache> resourceCacheMap;
    private ResourceCache mainWebappResourceCache;
    private Map<String, CacheState> cacheState;

    /**
     * Constructor
     * 
     * @param useDevConfig <code>true</code> if running in development mode
     * @throws Exception on any error
     */
    public CustomWebApplicationContext(boolean useDevConfig, ClassLoader bootLoader) throws Exception {
        super("webapp");
        Resource webInf = getWebInf();
        additionalClasspath = "";
        resourceCacheMap = new HashMap<String, ResourceCache>();
        reverseCaches = new ArrayList<ResourceCache>();
        cacheState = new HashMap<String, CacheState>();
        setContextPath("/");
        setDefaultsDescriptor("/com/adito/boot/webdefault.xml");
        setDisplayName(Branding.PRODUCT_NAME);
        setTempDirectory(ContextHolder.getContext().getTempDirectory());
        setResourceAlias("/defaultStyle.css", "/css/defaultStyle.jsp");
        setParentClassLoader(bootLoader);
        setWelcomeFiles(new String[] { "showHome.do" });
        resourceCaches = new ArrayList<ResourceCache>();

        if ("true".equals(SystemProperties.get("adito.paranoidSessionManager", "true"))) {
            ((AbstractSessionManager) getServletHandler().getSessionManager()).setUseRequestedId(false);
            ((AbstractSessionManager) getServletHandler().getSessionManager()).setSecureCookies(true);
        }
    }

    @Override
    public void setClassPath(String classPath) {
        super.setClassPath(classPath);
        File webappBuild = new File(new File("build"), "webapp");
        if (webappBuild.exists()) {
            addClassPath(webappBuild.toURI().toString());
        }
    }

    /**
     * Get all resource caches
     * 
     * @return resource caches
     */
    public Collection<ResourceCache> getResourceCaches() {
        return resourceCaches;
    }

    /**
     * <p>
     * Add a new Resource Cache. Whenever a resource is requested, this handler
     * will search all registered resource caches until one can locate it.
     * 
     * <p>
     * This shouldn't be called directly, but through
     * {@link com.adito.boot.Context#addResourceBase(URL)}
     * 
     * @param cache cache to add
     */
    public void addResourceCache(ResourceCache cache) {
        resourceCaches.add(cache);
        reverseCaches.clear();
        cacheState.clear();
        reverseCaches.addAll(resourceCaches);
        Collections.reverse(reverseCaches);
    }

    /**
     * <p>
     * Remove a Resrouce Cache. Whenever a resource is requested, this handler
     * will no longer use this cache.
     * 
     * <p>
     * This shouldn't be called directly, but through
     * {@link com.adito.boot.Context#removeResourceBase(URL)}
     * 
     * @param cache cache to remove
     */
    public void removeResourceCache(ResourceCache cache) {
        resourceCaches.remove(cache);
        reverseCaches.clear();
        cacheState.clear();
        reverseCaches.addAll(resourceCaches);
        Collections.reverse(reverseCaches);
    }

    protected void addComponent(Object o) {
        if (o instanceof ResourceCache) {
            mainWebappResourceCache = ((ResourceCache) o);
        }
        super.addComponent(o);
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mortbay.http.ResourceCache#getResource(java.lang.String)
     */
    public Resource getResource(String pathInContext) throws IOException {
        boolean fullResourceCache = SystemProperties
                .get("adito.fullResourceCache",
                        String.valueOf(!(SystemProperties.get("adito.useDevConfig", "false").equals("true"))))
                .equals("true");
        Resource r;
        if (log.isDebugEnabled())
            log.debug("Request for " + pathInContext);

        // This is a work around to prevent WEB-INF getting listed by using the
        // path //WEB-INF
        if (pathInContext.indexOf("//WEB-INF") != -1) {
            return null;
        }

        /*
         * When in 'Full resource cache' mode, check if we already have cached
         * the resource
         */
        if (fullResourceCache && cacheState.containsKey(pathInContext)) {
            r = cacheState.get(pathInContext).getResource();
            if (log.isDebugEnabled())
                if (r == null)
                    log.debug("Resource " + pathInContext + " is permanently as missing.");
                else
                    log.debug("Resource " + pathInContext + " found in permanent cache");
            return r;
        }

        /*
         * Determine if the resource has already been found in a resource cache
         * (be it an extensions resource cache or the cores)
         */

        ResourceCache o = fullResourceCache ? null : (ResourceCache) resourceCacheMap.get(pathInContext);
        if (o == null) {

            /*
             * The existence of the resource has not yet been determined. Search
             * all resource caches in reverse until the extension is found. When
             * found, store which cache it was found in for quick look up in the
             * future.
             */
            if (log.isDebugEnabled())
                log.debug("Resource " + pathInContext + " not found in any resource cache, checking in plugins");

            for (Iterator i = reverseCaches.iterator(); i.hasNext();) {
                ResourceCache cache = (ResourceCache) i.next();
                r = cache.getResource(pathInContext);
                if (r != null && r.exists() && !r.isDirectory()) {
                    if (fullResourceCache) {
                        if (log.isDebugEnabled())
                            log.debug("    Found in " + cache.getBaseResource().toString());
                        cacheState.put(pathInContext, new CacheState(CacheState.FOUND, pathInContext, r));
                    } else {
                        if (log.isDebugEnabled())
                            log.debug("    Found in " + cache.getBaseResource().toString());
                        resourceCacheMap.put(pathInContext, cache);
                    }
                    return r;
                }
            }

            /*
             * The resource cannot be found in this caches base directory
             */
            if (log.isDebugEnabled())
                log.debug("   Not found");
        } else {
            /*
             * We know what cache the resource came from so check it still
             * exists and return. This will only happen when not in full cache
             * mode
             */
            r = o.getResource(pathInContext);
            if (r != null && r.exists() && !r.isDirectory()) {
                if (log.isDebugEnabled())
                    log.debug("    Found in " + o.getBaseResource().toString());
                return r;
            }
        }

        if (log.isDebugEnabled())
            log.debug("Checking for alias in plugins");
        String resourceAlias = getResourceAlias(pathInContext);
        if (resourceAlias != null) {

            /*
             * The resource was not found with its real name in any caches base
             * directory, so repeat the operation but look for the alias
             */

            if (log.isDebugEnabled())
                log.debug("    Found alias of " + resourceAlias + ", checking in plugins");
            for (Iterator i = reverseCaches.iterator(); i.hasNext();) {
                ResourceCache cache = (ResourceCache) i.next();
                r = cache.getResource(resourceAlias);

                /*
                 * When checking for resource modification, check for existence
                 * of file. This allows file to be removed at runtime without
                 * adding overhead when used on deployed server
                 */

                if (r != null && r.exists() && !r.isDirectory()) {
                    if (fullResourceCache) {
                        if (log.isDebugEnabled())
                            log.debug("    Found in " + cache.getBaseResource().toString());
                        cacheState.put(pathInContext, new CacheState(CacheState.FOUND, pathInContext, r));
                        return r;
                    } else {
                        if (log.isDebugEnabled())
                            log.debug("    Found in " + cache.getBaseResource().toString());
                        resourceCacheMap.put(pathInContext, cache);
                        return r;
                    }
                }
            }
            if (log.isDebugEnabled())
                log.debug("   Not found");
        }

        /*
         * The resource could not be found in any caches base directory, so pass
         * to the main webapp
         */

        if (log.isDebugEnabled())
            log.debug("Passing to main webapp");
        r = super.getResource(pathInContext);
        if (r != null && r.exists() && !r.isDirectory()) {

            /*
             * The resource has been found in the main webapps base directory,
             * store where it was found for quick lookup in future requests
             */

            if (log.isDebugEnabled())
                log.debug("    Found in main webapp");

            if (fullResourceCache) {
                cacheState.put(pathInContext, new CacheState(CacheState.FOUND, pathInContext, r));
            } else {
                resourceCacheMap.put(pathInContext, mainWebappResourceCache);
            }
            return r;
        } else {
            if (fullResourceCache) {
                if (log.isDebugEnabled())
                    log.debug("    Not found, caching as missing");
                cacheState.put(pathInContext, new CacheState(CacheState.MISSING, pathInContext, r));
            }
        }

        /* Not found at all */

        if (log.isDebugEnabled())
            log.debug("    Found in main webapp");

        return r;
    }
}