Package org.apache.struts2.dispatcher

Source Code of org.apache.struts2.dispatcher.FilterDispatcher

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

package org.apache.struts2.dispatcher;

import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

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.HttpServletResponse;

import org.apache.struts2.RequestUtils;
import org.apache.struts2.StrutsStatics;
import org.apache.struts2.dispatcher.mapper.ActionMapper;
import org.apache.struts2.dispatcher.mapper.ActionMapping;
import org.apache.struts2.dispatcher.ng.filter.FilterHostConfig;
import org.apache.struts2.util.ClassLoaderUtils;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.config.Configuration;
import com.opensymphony.xwork2.config.ConfigurationProvider;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.ValueStack;
import com.opensymphony.xwork2.util.ValueStackFactory;
import com.opensymphony.xwork2.util.logging.Logger;
import com.opensymphony.xwork2.util.logging.LoggerFactory;
import com.opensymphony.xwork2.util.profiling.UtilTimerStack;

/**
* Master filter for Struts that handles four distinct
* responsibilities:
* <p/>
* <ul>
* <p/>
* <li>Executing actions</li>
* <p/>
* <li>Cleaning up the {@link ActionContext} (see note)</li>
* <p/>
* <li>Serving static content</li>
* <p/>
* <li>Kicking off XWork's interceptor chain for the request lifecycle</li>
* <p/>
* </ul>
* <p/>
* <p/> <b>IMPORTANT</b>: this filter must be mapped to all requests. Unless you know exactly what you are doing, always
* map to this URL pattern: /*
* <p/>
* <p/> <b>Executing actions</b>
* <p/>
* <p/> This filter executes actions by consulting the {@link ActionMapper} and determining if the requested URL should
* invoke an action. If the mapper indicates it should, <b>the rest of the filter chain is stopped</b> and the action is
* invoked. This is important, as it means that filters like the SiteMesh filter must be placed <b>before</b> this
* filter or they will not be able to decorate the output of actions.
* <p/>
* <p/> <b>Cleaning up the {@link ActionContext}</b>
* <p/>
* <p/> This filter will also automatically clean up the {@link ActionContext} for you, ensuring that no memory leaks
* take place. However, this can sometimes cause problems integrating with other products like SiteMesh. See {@link
* ActionContextCleanUp} for more information on how to deal with this.
* <p/>
* <p/> <b>Serving static content</b>
* <p/>
* <p/> This filter also serves common static content needed when using various parts of Struts, such as JavaScript
* files, CSS files, etc. It works by looking for requests to /struts/*, and then mapping the value after "/struts/"
* to common packages in Struts and, optionally, in your class path. By default, the following packages are
* automatically searched:
* <p/>
* <ul>
* <p/>
* <li>org.apache.struts2.static</li>
* <p/>
* <li>template</li>
* <p/>
* </ul>
* <p/>
* <p/> This means that you can simply request /struts/xhtml/styles.css and the XHTML UI theme's default stylesheet
* will be returned. Likewise, many of the AJAX UI components require various JavaScript files, which are found in the
* org.apache.struts2.static package. If you wish to add additional packages to be searched, you can add a comma
* separated (space, tab and new line will do as well) list in the filter init parameter named "packages". <b>Be
* careful</b>, however, to expose any packages that may have sensitive information, such as properties file with
* database access credentials.
* <p/>
* <p/>
* <p/>
* <p>
* <p/>
* This filter supports the following init-params:
* <!-- START SNIPPET: params -->
* <p/>
* <ul>
* <p/>
* <li><b>config</b> - a comma-delimited list of XML configuration files to load.</li>
* <p/>
* <li><b>actionPackages</b> - a comma-delimited list of Java packages to scan for Actions.</li>
* <p/>
* <li><b>configProviders</b> - a comma-delimited list of Java classes that implement the
* {@link ConfigurationProvider} interface that should be used for building the {@link Configuration}.</li>
* <p/>
* <li><b>loggerFactory</b> - The class name of the {@link LoggerFactory} implementation.</li>
* <p/>
* <li><b>*</b> - any other parameters are treated as framework constants.</li>
* <p/>
* </ul>
* <p/>
* <!-- END SNIPPET: params -->
* <p/>
* </p>
* <p/>
* To use a custom {@link Dispatcher}, the <code>createDispatcher()</code> method could be overriden by
* the subclass.
*
* @version $Date: 2008-07-07 10:10:42 -0400 (Mon, 07 Jul 2008) $ $Id: FilterDispatcher.java 674498 2008-07-07 14:10:42Z mrdon $
* @deprecated Since Struts 2.1.3, use {@link org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter} instead or
* {@link org.apache.struts2.dispatcher.ng.filter.StrutsPrepareFilter} and {@link org.apache.struts2.dispatcher.ng.filter.StrutsExecuteFilter}
* if needing using the {@link ActionContextCleanUp} filter in addition to this one
*
* @see ActionMapper
* @see ActionContextCleanUp
* @see org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
* @see org.apache.struts2.dispatcher.ng.filter.StrutsPrepareFilter
* @see org.apache.struts2.dispatcher.ng.filter.StrutsExecuteFilter
*/
public class FilterDispatcher implements StrutsStatics, Filter {

    /**
     * Provide a logging instance.
     */
    private Logger log;

    /**
     * Provide ActionMapper instance, set by injection.
     */
    private ActionMapper actionMapper;

    /**
     * Provide FilterConfig instance, set on init.
     */
    private FilterConfig filterConfig;

    /**
     * Expose Dispatcher instance to subclass.
     */
    protected Dispatcher dispatcher;

    /**
     * Loads stattic resources, set by injection
     */
    protected StaticContentLoader staticResourceLoader;

    /**
     * Initializes the filter by creating a default dispatcher
     * and setting the default packages for static resources.
     *
     * @param filterConfig The filter configuration
     */
    public void init(FilterConfig filterConfig) throws ServletException {
        try {
            this.filterConfig = filterConfig;

            initLogging();

            dispatcher = createDispatcher(filterConfig);
            dispatcher.init();
            dispatcher.getContainer().inject(this);

            staticResourceLoader.setHostConfig(new FilterHostConfig(filterConfig));
        } finally {
            ActionContext.setContext(null);
        }
    }

    private void initLogging() {
        String factoryName = filterConfig.getInitParameter("loggerFactory");
        if (factoryName != null) {
            try {
                Class cls = ClassLoaderUtils.loadClass(factoryName, this.getClass());
                LoggerFactory fac = (LoggerFactory) cls.newInstance();
                LoggerFactory.setLoggerFactory(fac);
            } catch (InstantiationException e) {
                System.err.println("Unable to instantiate logger factory: " + factoryName + ", using default");
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                System.err.println("Unable to access logger factory: " + factoryName + ", using default");
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                System.err.println("Unable to locate logger factory class: " + factoryName + ", using default");
                e.printStackTrace();
            }
        }

        log = LoggerFactory.getLogger(FilterDispatcher.class);

    }

    /**
     * Calls dispatcher.cleanup,
     * which in turn releases local threads and destroys any DispatchListeners.
     *
     * @see javax.servlet.Filter#destroy()
     */
    public void destroy() {
        if (dispatcher == null) {
            log.warn("something is seriously wrong, Dispatcher is not initialized (null) ");
        } else {
            try {
                dispatcher.cleanup();
            } finally {
                ActionContext.setContext(null);
            }
        }
    }

    /**
     * Create a default {@link Dispatcher} that subclasses can override
     * with a custom Dispatcher, if needed.
     *
     * @param filterConfig Our FilterConfig
     * @return Initialized Dispatcher
     */
    protected Dispatcher createDispatcher(FilterConfig filterConfig) {
        Map<String, String> params = new HashMap<String, String>();
        for (Enumeration e = filterConfig.getInitParameterNames(); e.hasMoreElements();) {
            String name = (String) e.nextElement();
            String value = filterConfig.getInitParameter(name);
            params.put(name, value);
        }
        return new Dispatcher(filterConfig.getServletContext(), params);
    }

    /**
     * Modify state of StrutsConstants.STRUTS_STATIC_CONTENT_LOADER setting.
     * @param staticResourceLoader val New setting
     */
    @Inject
    public void setStaticResourceLoader(StaticContentLoader staticResourceLoader) {
        this.staticResourceLoader = staticResourceLoader;
    }

    /**
     * Modify ActionMapper instance.
     * @param mapper New instance
     */
    @Inject
    public void setActionMapper(ActionMapper mapper) {
        actionMapper = mapper;
    }

    /**
     * Provide a workaround for some versions of WebLogic.
     * <p/>
     * Servlet 2.3 specifies that the servlet context can be retrieved from the session. Unfortunately, some versions of
     * WebLogic can only retrieve the servlet context from the filter config. Hence, this method enables subclasses to
     * retrieve the servlet context from other sources.
     *
     * @return the servlet context.
     */
    protected ServletContext getServletContext() {
        return filterConfig.getServletContext();
    }

    /**
     * Expose the FilterConfig instance.
     *
     * @return Our FilterConfit instance
     */
    protected FilterConfig getFilterConfig() {
        return filterConfig;
    }

    /**
     * Wrap and return the given request, if needed, so as to to transparently
     * handle multipart data as a wrapped class around the given request.
     *
     * @param request  Our ServletRequest object
     * @param response Our ServerResponse object
     * @return Wrapped HttpServletRequest object
     * @throws ServletException on any error
     */
    protected HttpServletRequest prepareDispatcherAndWrapRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException {

        Dispatcher du = Dispatcher.getInstance();

        // Prepare and wrap the request if the cleanup filter hasn't already, cleanup filter should be
        // configured first before struts2 dispatcher filter, hence when its cleanup filter's turn,
        // static instance of Dispatcher should be null.
        if (du == null) {

            Dispatcher.setInstance(dispatcher);

            // prepare the request no matter what - this ensures that the proper character encoding
            // is used before invoking the mapper (see WW-9127)
            dispatcher.prepare(request, response);
        } else {
            dispatcher = du;
        }

        try {
            // Wrap request first, just in case it is multipart/form-data
            // parameters might not be accessible through before encoding (ww-1278)
            request = dispatcher.wrapRequest(request, getServletContext());
        } catch (IOException e) {
            String message = "Could not wrap servlet request with MultipartRequestWrapper!";
            log.error(message, e);
            throw new ServletException(message, e);
        }

        return request;
    }

    /**
     * Process an action or handle a request a static resource.
     * <p/>
     * The filter tries to match the request to an action mapping.
     * If mapping is found, the action processes is delegated to the dispatcher's serviceAction method.
     * If action processing fails, doFilter will try to create an error page via the dispatcher.
     * <p/>
     * Otherwise, if the request is for a static resource,
     * the resource is copied directly to the response, with the appropriate caching headers set.
     * <p/>
     * If the request does not match an action mapping, or a static resource page,
     * then it passes through.
     *
     * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
     */
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
        ServletContext servletContext = getServletContext();

        String timerKey = "FilterDispatcher_doFilter: ";
        try {

            // FIXME: this should be refactored better to not duplicate work with the action invocation
            ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
            ActionContext ctx = new ActionContext(stack.getContext());
            ActionContext.setContext(ctx);

            UtilTimerStack.push(timerKey);
            request = prepareDispatcherAndWrapRequest(request, response);
            ActionMapping mapping;
            try {
                mapping = actionMapper.getMapping(request, dispatcher.getConfigurationManager());
            } catch (Exception ex) {
                log.error("error getting ActionMapping", ex);
                dispatcher.sendError(request, response, servletContext, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);
                return;
            }

            if (mapping == null) {
                // there is no action in this request, should we look for a static resource?
                String resourcePath = RequestUtils.getServletPath(request);

                if ("".equals(resourcePath) && null != request.getPathInfo()) {
                    resourcePath = request.getPathInfo();
                }

                if (staticResourceLoader.canHandle(resourcePath)) {
                    staticResourceLoader.findStaticResource(resourcePath, request, response);
                } else {
                    // this is a normal request, let it pass through
                    chain.doFilter(request, response);
                }
                // The framework did its job here
                return;
            }

            dispatcher.serviceAction(request, response, servletContext, mapping);

        } finally {
            try {
                ActionContextCleanUp.cleanUp(req);
            } finally {
                UtilTimerStack.pop(timerKey);
            }
        }
    }
}
TOP

Related Classes of org.apache.struts2.dispatcher.FilterDispatcher

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.