Save This Page
Home » struts-2.1.8.1-src » org.apache » struts2 » dispatcher » [javadoc | source]
    1   /*
    2    * $Id: FilterDispatcher.java 674498 2008-07-07 14:10:42Z mrdon $
    3    *
    4    * Licensed to the Apache Software Foundation (ASF) under one
    5    * or more contributor license agreements.  See the NOTICE file
    6    * distributed with this work for additional information
    7    * regarding copyright ownership.  The ASF licenses this file
    8    * to you under the Apache License, Version 2.0 (the
    9    * "License"); you may not use this file except in compliance
   10    * with the License.  You may obtain a copy of the License at
   11    *
   12    *  http://www.apache.org/licenses/LICENSE-2.0
   13    *
   14    * Unless required by applicable law or agreed to in writing,
   15    * software distributed under the License is distributed on an
   16    * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   17    * KIND, either express or implied.  See the License for the
   18    * specific language governing permissions and limitations
   19    * under the License.
   20    */
   21   
   22   package org.apache.struts2.dispatcher;
   23   
   24   import java.io.IOException;
   25   import java.util.Enumeration;
   26   import java.util.HashMap;
   27   import java.util.Map;
   28   
   29   import javax.servlet.Filter;
   30   import javax.servlet.FilterChain;
   31   import javax.servlet.FilterConfig;
   32   import javax.servlet.ServletContext;
   33   import javax.servlet.ServletException;
   34   import javax.servlet.ServletRequest;
   35   import javax.servlet.ServletResponse;
   36   import javax.servlet.http.HttpServletRequest;
   37   import javax.servlet.http.HttpServletResponse;
   38   
   39   import org.apache.struts2.RequestUtils;
   40   import org.apache.struts2.StrutsStatics;
   41   import org.apache.struts2.dispatcher.mapper.ActionMapper;
   42   import org.apache.struts2.dispatcher.mapper.ActionMapping;
   43   import org.apache.struts2.dispatcher.ng.filter.FilterHostConfig;
   44   import org.apache.struts2.util.ClassLoaderUtils;
   45   
   46   import com.opensymphony.xwork2.ActionContext;
   47   import com.opensymphony.xwork2.config.Configuration;
   48   import com.opensymphony.xwork2.config.ConfigurationProvider;
   49   import com.opensymphony.xwork2.inject.Inject;
   50   import com.opensymphony.xwork2.util.ValueStack;
   51   import com.opensymphony.xwork2.util.ValueStackFactory;
   52   import com.opensymphony.xwork2.util.logging.Logger;
   53   import com.opensymphony.xwork2.util.logging.LoggerFactory;
   54   import com.opensymphony.xwork2.util.profiling.UtilTimerStack;
   55   
   56   /**
   57    * Master filter for Struts that handles four distinct
   58    * responsibilities:
   59    * <p/>
   60    * <ul>
   61    * <p/>
   62    * <li>Executing actions</li>
   63    * <p/>
   64    * <li>Cleaning up the {@link ActionContext} (see note)</li>
   65    * <p/>
   66    * <li>Serving static content</li>
   67    * <p/>
   68    * <li>Kicking off XWork's interceptor chain for the request lifecycle</li>
   69    * <p/>
   70    * </ul>
   71    * <p/>
   72    * <p/> <b>IMPORTANT</b>: this filter must be mapped to all requests. Unless you know exactly what you are doing, always
   73    * map to this URL pattern: /*
   74    * <p/>
   75    * <p/> <b>Executing actions</b>
   76    * <p/>
   77    * <p/> This filter executes actions by consulting the {@link ActionMapper} and determining if the requested URL should
   78    * invoke an action. If the mapper indicates it should, <b>the rest of the filter chain is stopped</b> and the action is
   79    * invoked. This is important, as it means that filters like the SiteMesh filter must be placed <b>before</b> this
   80    * filter or they will not be able to decorate the output of actions.
   81    * <p/>
   82    * <p/> <b>Cleaning up the {@link ActionContext}</b>
   83    * <p/>
   84    * <p/> This filter will also automatically clean up the {@link ActionContext} for you, ensuring that no memory leaks
   85    * take place. However, this can sometimes cause problems integrating with other products like SiteMesh. See {@link
   86    * ActionContextCleanUp} for more information on how to deal with this.
   87    * <p/>
   88    * <p/> <b>Serving static content</b>
   89    * <p/>
   90    * <p/> This filter also serves common static content needed when using various parts of Struts, such as JavaScript
   91    * files, CSS files, etc. It works by looking for requests to /struts/*, and then mapping the value after "/struts/"
   92    * to common packages in Struts and, optionally, in your class path. By default, the following packages are
   93    * automatically searched:
   94    * <p/>
   95    * <ul>
   96    * <p/>
   97    * <li>org.apache.struts2.static</li>
   98    * <p/>
   99    * <li>template</li>
  100    * <p/>
  101    * </ul>
  102    * <p/>
  103    * <p/> This means that you can simply request /struts/xhtml/styles.css and the XHTML UI theme's default stylesheet
  104    * will be returned. Likewise, many of the AJAX UI components require various JavaScript files, which are found in the
  105    * org.apache.struts2.static package. If you wish to add additional packages to be searched, you can add a comma
  106    * separated (space, tab and new line will do as well) list in the filter init parameter named "packages". <b>Be
  107    * careful</b>, however, to expose any packages that may have sensitive information, such as properties file with
  108    * database access credentials.
  109    * <p/>
  110    * <p/>
  111    * <p/>
  112    * <p>
  113    * <p/>
  114    * This filter supports the following init-params:
  115    * <!-- START SNIPPET: params -->
  116    * <p/>
  117    * <ul>
  118    * <p/>
  119    * <li><b>config</b> - a comma-delimited list of XML configuration files to load.</li>
  120    * <p/>
  121    * <li><b>actionPackages</b> - a comma-delimited list of Java packages to scan for Actions.</li>
  122    * <p/>
  123    * <li><b>configProviders</b> - a comma-delimited list of Java classes that implement the
  124    * {@link ConfigurationProvider} interface that should be used for building the {@link Configuration}.</li>
  125    * <p/>
  126    * <li><b>loggerFactory</b> - The class name of the {@link LoggerFactory} implementation.</li>
  127    * <p/>
  128    * <li><b>*</b> - any other parameters are treated as framework constants.</li>
  129    * <p/>
  130    * </ul>
  131    * <p/>
  132    * <!-- END SNIPPET: params -->
  133    * <p/>
  134    * </p>
  135    * <p/>
  136    * To use a custom {@link Dispatcher}, the <code>createDispatcher()</code> method could be overriden by
  137    * the subclass.
  138    *
  139    * @version $Date: 2008-07-07 10:10:42 -0400 (Mon, 07 Jul 2008) $ $Id: FilterDispatcher.java 674498 2008-07-07 14:10:42Z mrdon $
  140    * @deprecated Since Struts 2.1.3, use {@link org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter} instead or
  141    * {@link org.apache.struts2.dispatcher.ng.filter.StrutsPrepareFilter} and {@link org.apache.struts2.dispatcher.ng.filter.StrutsExecuteFilter}
  142    * if needing using the {@link ActionContextCleanUp} filter in addition to this one
  143    *
  144    * @see ActionMapper
  145    * @see ActionContextCleanUp
  146    * @see org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
  147    * @see org.apache.struts2.dispatcher.ng.filter.StrutsPrepareFilter
  148    * @see org.apache.struts2.dispatcher.ng.filter.StrutsExecuteFilter
  149    */
  150   public class FilterDispatcher implements StrutsStatics, Filter {
  151   
  152       /**
  153        * Provide a logging instance.
  154        */
  155       private Logger log;
  156   
  157       /**
  158        * Provide ActionMapper instance, set by injection.
  159        */
  160       private ActionMapper actionMapper;
  161   
  162       /**
  163        * Provide FilterConfig instance, set on init.
  164        */
  165       private FilterConfig filterConfig;
  166   
  167       /**
  168        * Expose Dispatcher instance to subclass.
  169        */
  170       protected Dispatcher dispatcher;
  171   
  172       /**
  173        * Loads stattic resources, set by injection
  174        */
  175       protected StaticContentLoader staticResourceLoader;
  176   
  177       /**
  178        * Initializes the filter by creating a default dispatcher
  179        * and setting the default packages for static resources.
  180        *
  181        * @param filterConfig The filter configuration
  182        */
  183       public void init(FilterConfig filterConfig) throws ServletException {
  184           try {
  185               this.filterConfig = filterConfig;
  186   
  187               initLogging();
  188   
  189               dispatcher = createDispatcher(filterConfig);
  190               dispatcher.init();
  191               dispatcher.getContainer().inject(this);
  192   
  193               staticResourceLoader.setHostConfig(new FilterHostConfig(filterConfig));
  194           } finally {
  195               ActionContext.setContext(null);
  196           }
  197       }
  198   
  199       private void initLogging() {
  200           String factoryName = filterConfig.getInitParameter("loggerFactory");
  201           if (factoryName != null) {
  202               try {
  203                   Class cls = ClassLoaderUtils.loadClass(factoryName, this.getClass());
  204                   LoggerFactory fac = (LoggerFactory) cls.newInstance();
  205                   LoggerFactory.setLoggerFactory(fac);
  206               } catch (InstantiationException e) {
  207                   System.err.println("Unable to instantiate logger factory: " + factoryName + ", using default");
  208                   e.printStackTrace();
  209               } catch (IllegalAccessException e) {
  210                   System.err.println("Unable to access logger factory: " + factoryName + ", using default");
  211                   e.printStackTrace();
  212               } catch (ClassNotFoundException e) {
  213                   System.err.println("Unable to locate logger factory class: " + factoryName + ", using default");
  214                   e.printStackTrace();
  215               }
  216           }
  217   
  218           log = LoggerFactory.getLogger(FilterDispatcher.class);
  219   
  220       }
  221   
  222       /**
  223        * Calls dispatcher.cleanup,
  224        * which in turn releases local threads and destroys any DispatchListeners.
  225        *
  226        * @see javax.servlet.Filter#destroy()
  227        */
  228       public void destroy() {
  229           if (dispatcher == null) {
  230               log.warn("something is seriously wrong, Dispatcher is not initialized (null) ");
  231           } else {
  232               try {
  233                   dispatcher.cleanup();
  234               } finally {
  235                   ActionContext.setContext(null);
  236               }
  237           }
  238       }
  239   
  240       /**
  241        * Create a default {@link Dispatcher} that subclasses can override
  242        * with a custom Dispatcher, if needed.
  243        *
  244        * @param filterConfig Our FilterConfig
  245        * @return Initialized Dispatcher
  246        */
  247       protected Dispatcher createDispatcher(FilterConfig filterConfig) {
  248           Map<String, String> params = new HashMap<String, String>();
  249           for (Enumeration e = filterConfig.getInitParameterNames(); e.hasMoreElements();) {
  250               String name = (String) e.nextElement();
  251               String value = filterConfig.getInitParameter(name);
  252               params.put(name, value);
  253           }
  254           return new Dispatcher(filterConfig.getServletContext(), params);
  255       }
  256   
  257       /**
  258        * Modify state of StrutsConstants.STRUTS_STATIC_CONTENT_LOADER setting.
  259        * @param staticResourceLoader val New setting
  260        */
  261       @Inject
  262       public void setStaticResourceLoader(StaticContentLoader staticResourceLoader) {
  263           this.staticResourceLoader = staticResourceLoader;
  264       }
  265   
  266       /**
  267        * Modify ActionMapper instance.
  268        * @param mapper New instance
  269        */
  270       @Inject
  271       public void setActionMapper(ActionMapper mapper) {
  272           actionMapper = mapper;
  273       }
  274   
  275       /**
  276        * Provide a workaround for some versions of WebLogic.
  277        * <p/>
  278        * Servlet 2.3 specifies that the servlet context can be retrieved from the session. Unfortunately, some versions of
  279        * WebLogic can only retrieve the servlet context from the filter config. Hence, this method enables subclasses to
  280        * retrieve the servlet context from other sources.
  281        *
  282        * @return the servlet context.
  283        */
  284       protected ServletContext getServletContext() {
  285           return filterConfig.getServletContext();
  286       }
  287   
  288       /**
  289        * Expose the FilterConfig instance.
  290        *
  291        * @return Our FilterConfit instance
  292        */
  293       protected FilterConfig getFilterConfig() {
  294           return filterConfig;
  295       }
  296   
  297       /**
  298        * Wrap and return the given request, if needed, so as to to transparently
  299        * handle multipart data as a wrapped class around the given request.
  300        *
  301        * @param request  Our ServletRequest object
  302        * @param response Our ServerResponse object
  303        * @return Wrapped HttpServletRequest object
  304        * @throws ServletException on any error
  305        */
  306       protected HttpServletRequest prepareDispatcherAndWrapRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException {
  307   
  308           Dispatcher du = Dispatcher.getInstance();
  309   
  310           // Prepare and wrap the request if the cleanup filter hasn't already, cleanup filter should be
  311           // configured first before struts2 dispatcher filter, hence when its cleanup filter's turn,
  312           // static instance of Dispatcher should be null.
  313           if (du == null) {
  314   
  315               Dispatcher.setInstance(dispatcher);
  316   
  317               // prepare the request no matter what - this ensures that the proper character encoding
  318               // is used before invoking the mapper (see WW-9127)
  319               dispatcher.prepare(request, response);
  320           } else {
  321               dispatcher = du;
  322           }
  323   
  324           try {
  325               // Wrap request first, just in case it is multipart/form-data
  326               // parameters might not be accessible through before encoding (ww-1278)
  327               request = dispatcher.wrapRequest(request, getServletContext());
  328           } catch (IOException e) {
  329               String message = "Could not wrap servlet request with MultipartRequestWrapper!";
  330               log.error(message, e);
  331               throw new ServletException(message, e);
  332           }
  333   
  334           return request;
  335       }
  336   
  337       /**
  338        * Process an action or handle a request a static resource.
  339        * <p/>
  340        * The filter tries to match the request to an action mapping.
  341        * If mapping is found, the action processes is delegated to the dispatcher's serviceAction method.
  342        * If action processing fails, doFilter will try to create an error page via the dispatcher.
  343        * <p/>
  344        * Otherwise, if the request is for a static resource,
  345        * the resource is copied directly to the response, with the appropriate caching headers set.
  346        * <p/>
  347        * If the request does not match an action mapping, or a static resource page,
  348        * then it passes through.
  349        *
  350        * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
  351        */
  352       public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
  353   
  354           HttpServletRequest request = (HttpServletRequest) req;
  355           HttpServletResponse response = (HttpServletResponse) res;
  356           ServletContext servletContext = getServletContext();
  357   
  358           String timerKey = "FilterDispatcher_doFilter: ";
  359           try {
  360   
  361               // FIXME: this should be refactored better to not duplicate work with the action invocation
  362               ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
  363               ActionContext ctx = new ActionContext(stack.getContext());
  364               ActionContext.setContext(ctx);
  365   
  366               UtilTimerStack.push(timerKey);
  367               request = prepareDispatcherAndWrapRequest(request, response);
  368               ActionMapping mapping;
  369               try {
  370                   mapping = actionMapper.getMapping(request, dispatcher.getConfigurationManager());
  371               } catch (Exception ex) {
  372                   log.error("error getting ActionMapping", ex);
  373                   dispatcher.sendError(request, response, servletContext, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);
  374                   return;
  375               }
  376   
  377               if (mapping == null) {
  378                   // there is no action in this request, should we look for a static resource?
  379                   String resourcePath = RequestUtils.getServletPath(request);
  380   
  381                   if ("".equals(resourcePath) && null != request.getPathInfo()) {
  382                       resourcePath = request.getPathInfo();
  383                   }
  384   
  385                   if (staticResourceLoader.canHandle(resourcePath)) {
  386                       staticResourceLoader.findStaticResource(resourcePath, request, response);
  387                   } else {
  388                       // this is a normal request, let it pass through
  389                       chain.doFilter(request, response);
  390                   }
  391                   // The framework did its job here
  392                   return;
  393               }
  394   
  395               dispatcher.serviceAction(request, response, servletContext, mapping);
  396   
  397           } finally {
  398               try {
  399                   ActionContextCleanUp.cleanUp(req);
  400               } finally {
  401                   UtilTimerStack.pop(timerKey);
  402               }
  403           }
  404       }
  405   }

Save This Page
Home » struts-2.1.8.1-src » org.apache » struts2 » dispatcher » [javadoc | source]