Package net.sourceforge.stripes.controller

Source Code of net.sourceforge.stripes.controller.StripesFilter

/* Copyright 2005-2006 Tim Fennell
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.sourceforge.stripes.controller;

import net.sourceforge.stripes.config.BootstrapPropertyResolver;
import net.sourceforge.stripes.config.Configuration;
import net.sourceforge.stripes.config.RuntimeConfiguration;
import net.sourceforge.stripes.exception.StripesRuntimeException;
import net.sourceforge.stripes.exception.StripesServletException;
import net.sourceforge.stripes.util.HttpUtil;
import net.sourceforge.stripes.util.Log;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.beans.Introspector;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Locale;
import java.util.Set;

/**
* The Stripes filter is used to ensure that all requests coming to a Stripes application
* are handled in the same way.  It detects and wraps any requests that contain multipart/form
* data, so that they may be treated much like any other request.  Also ensures that
* all downstream components have access to essential configuration and services whether
* the request goes through the dispatcher, or straight to a JSP.
*
* @author Tim Fennell
*/
public class StripesFilter implements Filter {
    /** Key used to lookup the name of the Configuration class used to configure Stripes. */
    public static final String CONFIG_CLASS = "Configuration.Class";

    /** Log used throughout the class. */
    private static final Log log = Log.getInstance(StripesFilter.class);

    /** The configuration instance for Stripes. */
    private Configuration configuration;

    /** The servlet context */
    private ServletContext servletContext;

    /**
     * A place to stash the Configuration object so that other classes in Stripes can access it
     * without resorting to ferrying it, or the request, to every class that needs access to the
     * Configuration.  Doing this allows multiple Stripes Configurations to exist in a single
     * Classloader since the Configuration is not located statically.
     */
    private static final ThreadLocal<Configuration> configurationStash = new ThreadLocal<Configuration>();

    /**
     * A set of weak references to all the Configuration objects that this class has ever
     * seen. Uses weak references to allow garbage collection to reap these objects if this
     * is the only reference left.  Used to determine if there is only one active Configuration
     * for the VM, and if so return it even when the Configuration isn't set in the thread local.
     */
    private static final Set<WeakReference<Configuration>> configurations =
            new HashSet<WeakReference<Configuration>>();

    /**
     * Some operations should only be done if the current invocation of
     * {@link #doFilter(ServletRequest, ServletResponse, FilterChain)} is the
     * first in the filter chain. This {@link ThreadLocal} keeps track of
     * whether such operations should be done or not.
     */
    private static final ThreadLocal<Boolean> initialInvocation = new ThreadLocal<Boolean>() {
        @Override
        protected Boolean initialValue() {
            return true;
        }
    };

    /**
     * Performs the necessary initialization for the StripesFilter.  Mainly this involves deciding
     * what configuration class to use, and then instantiating and initializing the chosen
     * Configuration.
     *
     * @throws ServletException thrown if a problem is encountered initializing Stripes
     */
    public void init(FilterConfig filterConfig) throws ServletException {
        BootstrapPropertyResolver bootstrap = new BootstrapPropertyResolver(filterConfig);

        // Set up the Configuration - if one isn't found by the bootstrapper then
        // we'll just use the default: RuntimeConfiguration
        Class<? extends Configuration> clazz = bootstrap.getClassProperty(CONFIG_CLASS,
                Configuration.class);

        if (clazz == null)
            clazz = RuntimeConfiguration.class;

        try {
            this.configuration = (Configuration) clazz.newInstance();
        }
        catch (Exception e) {
            log.fatal(e,
                    "Could not instantiate specified Configuration. Class name specified was ",
                    "[", clazz.getName(), "].");
            throw new StripesServletException("Could not instantiate specified Configuration. "
                    + "Class name specified was [" + clazz.getName() + "].", e);
        }

        this.configuration.setBootstrapPropertyResolver(bootstrap);
        this.configuration.init();
        StripesFilter.configurations.add(new WeakReference<Configuration>(this.configuration));

        this.servletContext = filterConfig.getServletContext();
        this.servletContext.setAttribute(StripesFilter.class.getName(), this);

        Package pkg = getClass().getPackage();
        log.info("Stripes Initialization Complete. Version: ", pkg.getSpecificationVersion(),
                 ", Build: ", pkg.getImplementationVersion());
    }

    /**
     * Returns the Configuration that is being used to process the current request.
     */
    public static Configuration getConfiguration() {
        Configuration configuration = StripesFilter.configurationStash.get();

        // If the configuration wasn't available in thread local, check to see if we only
        // know about one configuration in total, and if so use that one
        if (configuration == null) {
            synchronized (StripesFilter.configurations) {
                // Remove any references from the set that have been cleared
                Iterator<WeakReference<Configuration>> iterator = StripesFilter.configurations.iterator();
                while (iterator.hasNext()) {
                    WeakReference<Configuration> ref = iterator.next();
                    if (ref.get() == null) iterator.remove();
                }

                // If there is one and only one Configuration active, take it
                if (StripesFilter.configurations.size() == 1) {
                    configuration = StripesFilter.configurations.iterator().next().get();
                }
            }
        }

        if (configuration == null) {
            StripesRuntimeException sre = new StripesRuntimeException(
                    "Something is trying to access the current Stripes configuration but the " +
                    "current request was never routed through the StripesFilter! As a result " +
                    "the appropriate Configuration object cannot be located. Please take a look " +
                    "at the exact URL in your browser's address bar and ensure that any " +
                    "requests to that URL will be filtered through the StripesFilter according " +
                    "to the filter mappings in your web.xml."
            );
            log.error(sre)// log through an exception so that users get a stracktrace
        }

        return configuration;
    }

    /**
     * Returns the configuration for this instance of the StripesFilter for any class
     * that has a reference to the filter. For normal runtime access to the configuration
     * during a request cycle, call getConfiguration() instead.
     *
     * @return the Configuration of this instance of the StripesFilter
     */
    public Configuration getInstanceConfiguration() {
        return this.configuration;
    }

    /**
     * Performs the primary work of the filter, including constructing a StripesRequestWrapper to
     * wrap the HttpServletRequest, and using the configured LocalePicker to decide which
     * Locale will be used to process the request.
     */
    public void doFilter(ServletRequest servletRequest,
                         ServletResponse servletResponse,
                         FilterChain filterChain) throws IOException, ServletException {

        HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
        HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;

        // check the flag that indicates if this is the initial invocation
        boolean initial = initialInvocation.get();
        if (initial) {
            initialInvocation.set(false);
        }

        // Wrap pretty much everything in a try/catch so that we can funnel even the most
        // bizarre or unexpected exceptions into the exception handler
        try {
            log.trace("Intercepting request to URL: ", HttpUtil.getRequestedPath(httpRequest));

            if (initial) {
                // Pop the configuration into thread local
                StripesFilter.configurationStash.set(this.configuration);

                // Figure out the locale and character encoding to use. The ordering of things here
                // is very important!! We pick the locale first since picking the encoding is
                // locale dependent, but the encoding *must* be set on the request before any
                // parameters or parts are accessed, and wrapping the request accesses stuff.
                Locale locale = this.configuration.getLocalePicker().pickLocale(httpRequest);
                log.debug("LocalePicker selected locale: ", locale);

                String encoding = this.configuration.getLocalePicker().pickCharacterEncoding(
                        httpRequest, locale);
                if (encoding != null) {
                    httpRequest.setCharacterEncoding(encoding);
                    log.debug("LocalePicker selected character encoding: ", encoding);
                }
                else {
                    log.debug("LocalePicker did not pick a character encoding, using default: ",
                            httpRequest.getCharacterEncoding());
                }

                StripesRequestWrapper request = wrapRequest(httpRequest);
                request.setLocale(locale);
                httpResponse.setLocale(locale);
                if (encoding != null) {
                    httpResponse.setCharacterEncoding(encoding);
                }
                httpRequest = request;
            }
            else {
                // process URI parameters on subsequent invocations
                StripesRequestWrapper.findStripesWrapper(httpRequest).pushUriParameters(
                        (HttpServletRequestWrapper) httpRequest);
            }

            // Execute the rest of the chain
            flashInbound(httpRequest);
            filterChain.doFilter(httpRequest, servletResponse);
        }
        catch (Throwable t) {
            this.configuration.getExceptionHandler().handle(t, httpRequest, httpResponse);
        }
        finally {
            // reset the flag that indicates if this is the initial invocation
            if (initial) {
                initialInvocation.set(true);
                flashOutbound(httpRequest);

                // Once the request is processed, take the Configuration back out of thread local
                StripesFilter.configurationStash.remove();
            }
            else {
                // restore URI parameters to their previous state
                StripesRequestWrapper.findStripesWrapper(httpRequest).popUriParameters();
            }
        }
    }

    /**
     * Wraps the HttpServletRequest with a StripesServletRequest.  This is done to ensure that any
     * form posts that contain file uploads get handled appropriately.
     *
     * @param servletRequest the HttpServletRequest handed to the dispatcher by the container
     * @return an instance of StripesRequestWrapper, which is an HttpServletRequestWrapper
     * @throws StripesServletException if the wrapper cannot be constructed
     */
    protected StripesRequestWrapper wrapRequest(HttpServletRequest servletRequest)
            throws StripesServletException {
        try {
            return StripesRequestWrapper.findStripesWrapper(servletRequest);
        }
        catch (IllegalStateException e) {
            return new StripesRequestWrapper(servletRequest);
        }
    }

    /**
     * <p>Checks to see if there is a flash scope identified by a parameter to the current
     * request, and if there is, retrieves items from the flash scope and moves them
     * back to request attributes.</p>
     */
    protected void flashInbound(HttpServletRequest req) {
        // Copy the attributes from the previous flash scope to the new request
        FlashScope flash = FlashScope.getPrevious(req);
        if (flash != null) {
            flash.beginRequest(req);
        }
    }

    /**
     * Manages the work that ensures that flash scopes get cleaned up properly when
     * requests go missing.  Firstly timestamps the current flash scope (if one exists)
     * to record the time that the request exited the container.  Then checks all
     * flash scopes to make sure none have been hanging out for more than a minute.
     */
    protected void flashOutbound(HttpServletRequest req) {
        // Start the timer on the current flash scope
        FlashScope flash = FlashScope.getCurrent(req, false);
        if (flash != null) {
            flash.completeRequest();
        }
    }

    /** Calls the cleanup() method on the log to release resources held by commons logging. */
    public void destroy() {
        this.servletContext.removeAttribute(StripesFilter.class.getName());
        Log.cleanup();
        Introspector.flushCaches(); // Not 100% sure this is necessary, but it doesn't  hurt
    }
}
TOP

Related Classes of net.sourceforge.stripes.controller.StripesFilter

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