View Javadoc

1   /*
2    * $Id: Dispatcher.java 783012 2009-06-09 14:15:54Z wesw $
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.File;
25  import java.io.IOException;
26  import java.util.ArrayList;
27  import java.util.Collection;
28  import java.util.HashMap;
29  import java.util.HashSet;
30  import java.util.List;
31  import java.util.Locale;
32  import java.util.Map;
33  import java.util.Set;
34  import java.util.concurrent.CopyOnWriteArrayList;
35  
36  import javax.servlet.ServletContext;
37  import javax.servlet.ServletException;
38  import javax.servlet.http.HttpServletRequest;
39  import javax.servlet.http.HttpServletResponse;
40  
41  import org.apache.struts2.ServletActionContext;
42  import org.apache.struts2.StrutsConstants;
43  import org.apache.struts2.StrutsStatics;
44  import org.apache.struts2.StrutsException;
45  import org.apache.struts2.config.BeanSelectionProvider;
46  import org.apache.struts2.config.DefaultPropertiesProvider;
47  import org.apache.struts2.config.LegacyPropertiesConfigurationProvider;
48  import org.apache.struts2.config.StrutsXmlConfigurationProvider;
49  import org.apache.struts2.dispatcher.mapper.ActionMapping;
50  import org.apache.struts2.dispatcher.multipart.MultiPartRequest;
51  import org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper;
52  import org.apache.struts2.util.AttributeMap;
53  import org.apache.struts2.util.ClassLoaderUtils;
54  import org.apache.struts2.util.ObjectFactoryDestroyable;
55  import org.apache.struts2.views.freemarker.FreemarkerManager;
56  
57  import com.opensymphony.xwork2.ActionContext;
58  import com.opensymphony.xwork2.ActionProxy;
59  import com.opensymphony.xwork2.ActionProxyFactory;
60  import com.opensymphony.xwork2.ObjectFactory;
61  import com.opensymphony.xwork2.Result;
62  import com.opensymphony.xwork2.config.Configuration;
63  import com.opensymphony.xwork2.config.ConfigurationException;
64  import com.opensymphony.xwork2.config.ConfigurationManager;
65  import com.opensymphony.xwork2.config.ConfigurationProvider;
66  import com.opensymphony.xwork2.config.entities.InterceptorMapping;
67  import com.opensymphony.xwork2.config.entities.InterceptorStackConfig;
68  import com.opensymphony.xwork2.config.entities.PackageConfig;
69  import com.opensymphony.xwork2.config.providers.XmlConfigurationProvider;
70  import com.opensymphony.xwork2.inject.Container;
71  import com.opensymphony.xwork2.inject.ContainerBuilder;
72  import com.opensymphony.xwork2.inject.Inject;
73  import com.opensymphony.xwork2.interceptor.Interceptor;
74  import com.opensymphony.xwork2.util.FileManager;
75  import com.opensymphony.xwork2.util.LocalizedTextUtil;
76  import com.opensymphony.xwork2.util.ValueStack;
77  import com.opensymphony.xwork2.util.ValueStackFactory;
78  import com.opensymphony.xwork2.util.location.LocatableProperties;
79  import com.opensymphony.xwork2.util.location.Location;
80  import com.opensymphony.xwork2.util.location.LocationUtils;
81  import com.opensymphony.xwork2.util.logging.Logger;
82  import com.opensymphony.xwork2.util.logging.LoggerFactory;
83  import com.opensymphony.xwork2.util.profiling.UtilTimerStack;
84  
85  import freemarker.template.Template;
86  
87  /***
88   * A utility class the actual dispatcher delegates most of its tasks to. Each instance
89   * of the primary dispatcher holds an instance of this dispatcher to be shared for
90   * all requests.
91   *
92   * @see org.apache.struts2.dispatcher.FilterDispatcher
93   */
94  public class Dispatcher {
95  
96      /***
97       * Provide a logging instance.
98       */
99      private static final Logger LOG = LoggerFactory.getLogger(Dispatcher.class);
100 
101     /***
102      * Provide a thread local instance.
103      */
104     private static ThreadLocal<Dispatcher> instance = new ThreadLocal<Dispatcher>();
105 
106     /***
107      * Store list of DispatcherListeners.
108      */
109     private static List<DispatcherListener> dispatcherListeners =
110         new CopyOnWriteArrayList<DispatcherListener>();
111 
112     /***
113      * Store ConfigurationManager instance, set on init.
114      */
115     private ConfigurationManager configurationManager;
116 
117     /***
118      * Store state of  StrutsConstants.STRUTS_DEVMODE setting.
119      */
120     private boolean devMode;
121 
122     /***
123      * Store state of StrutsConstants.STRUTS_I18N_ENCODING setting.
124      */
125     private String defaultEncoding;
126 
127     /***
128      * Store state of StrutsConstants.STRUTS_LOCALE setting.
129      */
130     private String defaultLocale;
131 
132     /***
133      * Store state of StrutsConstants.STRUTS_MULTIPART_SAVEDIR setting.
134      */
135     private String multipartSaveDir;
136 
137     /***
138      * Stores the value of StrutsConstants.STRUTS_MULTIPART_HANDLER setting
139      */
140     private String multipartHandlerName;
141 
142     /***
143      * Provide list of default configuration files.
144      */
145     private static final String DEFAULT_CONFIGURATION_PATHS = "struts-default.xml,struts-plugin.xml,struts.xml";
146 
147     /***
148      * Store state of STRUTS_DISPATCHER_PARAMETERSWORKAROUND.
149      * <p/>
150      * The workaround is for WebLogic.
151      * We try to autodect WebLogic on Dispatcher init.
152      * The workaround can also be enabled manually.
153      */
154     private boolean paramsWorkaroundEnabled = false;
155 
156     /***
157      * Provide the dispatcher instance for the current thread.
158      *
159      * @return The dispatcher instance
160      */
161     public static Dispatcher getInstance() {
162         return instance.get();
163     }
164 
165     /***
166      * Store the dispatcher instance for this thread.
167      *
168      * @param instance The instance
169      */
170     public static void setInstance(Dispatcher instance) {
171         Dispatcher.instance.set(instance);
172     }
173 
174     /***
175      * Add a dispatcher lifecycle listener.
176      *
177      * @param listener The listener to add
178      */
179     public static void addDispatcherListener(DispatcherListener listener) {
180         dispatcherListeners.add(listener);
181     }
182 
183     /***
184      * Remove a specific dispatcher lifecycle listener.
185      *
186      * @param listener The listener
187      */
188     public static void removeDispatcherListener(DispatcherListener listener) {
189         dispatcherListeners.remove(listener);
190     }
191 
192     private ServletContext servletContext;
193     private Map<String, String> initParams;
194 
195     private ValueStackFactory valueStackFactory;
196 
197 
198     /***
199      * Create the Dispatcher instance for a given ServletContext and set of initialization parameters.
200      *
201      * @param servletContext Our servlet context
202      * @param initParams The set of initialization parameters
203      */
204     public Dispatcher(ServletContext servletContext, Map<String, String> initParams) {
205         this.servletContext = servletContext;
206         this.initParams = initParams;
207     }
208 
209     /***
210      * Modify state of StrutsConstants.STRUTS_DEVMODE setting.
211      * @param mode New setting
212      */
213     @Inject(StrutsConstants.STRUTS_DEVMODE)
214     public void setDevMode(String mode) {
215         devMode = "true".equals(mode);
216     }
217 
218     /***
219      * Modify state of StrutsConstants.STRUTS_LOCALE setting.
220      * @param val New setting
221      */
222     @Inject(value=StrutsConstants.STRUTS_LOCALE, required=false)
223     public void setDefaultLocale(String val) {
224         defaultLocale = val;
225     }
226 
227     /***
228      * Modify state of StrutsConstants.STRUTS_I18N_ENCODING setting.
229      * @param val New setting
230      */
231     @Inject(StrutsConstants.STRUTS_I18N_ENCODING)
232     public void setDefaultEncoding(String val) {
233         defaultEncoding = val;
234     }
235 
236     /***
237      * Modify state of StrutsConstants.STRUTS_MULTIPART_SAVEDIR setting.
238      * @param val New setting
239      */
240     @Inject(StrutsConstants.STRUTS_MULTIPART_SAVEDIR)
241     public void setMultipartSaveDir(String val) {
242         multipartSaveDir = val;
243     }
244 
245     @Inject(StrutsConstants.STRUTS_MULTIPART_HANDLER)
246     public void setMultipartHandler(String val) {
247         multipartHandlerName = val;
248     }
249 
250     @Inject
251     public void setValueStackFactory(ValueStackFactory valueStackFactory) {
252         this.valueStackFactory = valueStackFactory;
253     }
254 
255     /***
256      * Releases all instances bound to this dispatcher instance.
257      */
258     public void cleanup() {
259 
260     	// clean up ObjectFactory
261         ObjectFactory objectFactory = getContainer().getInstance(ObjectFactory.class);
262         if (objectFactory == null) {
263             LOG.warn("Object Factory is null, something is seriously wrong, no clean up will be performed");
264         }
265         if (objectFactory instanceof ObjectFactoryDestroyable) {
266             try {
267                 ((ObjectFactoryDestroyable)objectFactory).destroy();
268             }
269             catch(Exception e) {
270                 // catch any exception that may occured during destroy() and log it
271                 LOG.error("exception occurred while destroying ObjectFactory ["+objectFactory+"]", e);
272             }
273         }
274 
275         // clean up Dispatcher itself for this thread
276         instance.set(null);
277 
278         // clean up DispatcherListeners
279         if (!dispatcherListeners.isEmpty()) {
280             for (DispatcherListener l : dispatcherListeners) {
281                 l.dispatcherDestroyed(this);
282             }
283         }
284 
285         // clean up all interceptors by calling their destroy() method
286         Set<Interceptor> interceptors = new HashSet<Interceptor>();
287         Collection<PackageConfig> packageConfigs = configurationManager.getConfiguration().getPackageConfigs().values();
288         for (PackageConfig packageConfig : packageConfigs) {
289             for (Object config : packageConfig.getAllInterceptorConfigs().values()) {
290                 if (config instanceof InterceptorStackConfig) {
291                     for (InterceptorMapping interceptorMapping : ((InterceptorStackConfig) config).getInterceptors()) {
292                 	    interceptors.add(interceptorMapping.getInterceptor());
293                     }
294                 }
295             }
296         }
297         for (Interceptor interceptor : interceptors) {
298         	interceptor.destroy();
299         }
300 
301         //cleanup action context
302         ActionContext.setContext(null);
303 
304         // clean up configuration
305     	configurationManager.destroyConfiguration();
306     	configurationManager = null;
307     }
308 
309     private void init_DefaultProperties() {
310         configurationManager.addConfigurationProvider(new DefaultPropertiesProvider());
311     }
312     
313     private void init_LegacyStrutsProperties() {
314         configurationManager.addConfigurationProvider(new LegacyPropertiesConfigurationProvider());
315     }
316 
317     private void init_TraditionalXmlConfigurations() {
318         String configPaths = initParams.get("config");
319         if (configPaths == null) {
320             configPaths = DEFAULT_CONFIGURATION_PATHS;
321         }
322         String[] files = configPaths.split("//s*[,]//s*");
323         for (String file : files) {
324             if (file.endsWith(".xml")) {
325                 if ("xwork.xml".equals(file)) {
326                     configurationManager.addConfigurationProvider(new XmlConfigurationProvider(file, false));
327                 } else {
328                     configurationManager.addConfigurationProvider(new StrutsXmlConfigurationProvider(file, false, servletContext));
329                 }
330             } else {
331                 throw new IllegalArgumentException("Invalid configuration file name");
332             }
333         }
334     }
335 
336     private void init_CustomConfigurationProviders() {
337         String configProvs = initParams.get("configProviders");
338         if (configProvs != null) {
339             String[] classes = configProvs.split("//s*[,]//s*");
340             for (String cname : classes) {
341                 try {
342                     Class cls = ClassLoaderUtils.loadClass(cname, this.getClass());
343                     ConfigurationProvider prov = (ConfigurationProvider)cls.newInstance();
344                     configurationManager.addConfigurationProvider(prov);
345                 } catch (InstantiationException e) {
346                     throw new ConfigurationException("Unable to instantiate provider: "+cname, e);
347                 } catch (IllegalAccessException e) {
348                     throw new ConfigurationException("Unable to access provider: "+cname, e);
349                 } catch (ClassNotFoundException e) {
350                     throw new ConfigurationException("Unable to locate provider class: "+cname, e);
351                 }
352             }
353         }
354     }
355 
356     private void init_FilterInitParameters() {
357         configurationManager.addConfigurationProvider(new ConfigurationProvider() {
358             public void destroy() {}
359             public void init(Configuration configuration) throws ConfigurationException {}
360             public void loadPackages() throws ConfigurationException {}
361             public boolean needsReload() { return false; }
362 
363             public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException {
364                 props.putAll(initParams);
365             }
366         });
367     }
368 
369     private void init_AliasStandardObjects() {
370         configurationManager.addConfigurationProvider(new BeanSelectionProvider());
371     }
372 
373     private Container init_PreloadConfiguration() {
374         Configuration config = configurationManager.getConfiguration();
375         Container container = config.getContainer();
376 
377         boolean reloadi18n = Boolean.valueOf(container.getInstance(String.class, StrutsConstants.STRUTS_I18N_RELOAD));
378         LocalizedTextUtil.setReloadBundles(reloadi18n);
379 
380         return container;
381     }
382 
383     private void init_CheckConfigurationReloading(Container container) {
384         FileManager.setReloadingConfigs("true".equals(container.getInstance(String.class,
385                 StrutsConstants.STRUTS_CONFIGURATION_XML_RELOAD)));
386     }
387 
388     private void init_CheckWebLogicWorkaround(Container container) {
389         // test whether param-access workaround needs to be enabled
390         if (servletContext != null && servletContext.getServerInfo() != null
391                 && servletContext.getServerInfo().indexOf("WebLogic") >= 0) {
392             LOG.info("WebLogic server detected. Enabling Struts parameter access work-around.");
393             paramsWorkaroundEnabled = true;
394         } else {
395             paramsWorkaroundEnabled = "true".equals(container.getInstance(String.class,
396                     StrutsConstants.STRUTS_DISPATCHER_PARAMETERSWORKAROUND));
397         }
398     }
399 
400     /***
401      * Load configurations, including both XML and zero-configuration strategies,
402      * and update optional settings, including whether to reload configurations and resource files.
403      */
404     public void init() {
405 
406     	if (configurationManager == null) {
407     		configurationManager = new ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);
408     	}
409 
410         try {
411             init_DefaultProperties(); // [1]
412             init_TraditionalXmlConfigurations(); // [2]
413             init_LegacyStrutsProperties(); // [3]
414             init_CustomConfigurationProviders(); // [5]
415             init_FilterInitParameters() ; // [6]
416             init_AliasStandardObjects() ; // [7]
417 
418             Container container = init_PreloadConfiguration();
419             container.inject(this);
420             init_CheckConfigurationReloading(container);
421             init_CheckWebLogicWorkaround(container);
422 
423             if (!dispatcherListeners.isEmpty()) {
424                 for (DispatcherListener l : dispatcherListeners) {
425                     l.dispatcherInitialized(this);
426                 }
427             }
428         } catch (Exception ex) {
429             if (LOG.isErrorEnabled())
430                 LOG.error("Dispatcher initialization failed", ex);
431             throw new StrutsException(ex);
432         }
433     }
434 
435     /***
436      * Load Action class for mapping and invoke the appropriate Action method, or go directly to the Result.
437      * <p/>
438      * This method first creates the action context from the given parameters,
439      * and then loads an <tt>ActionProxy</tt> from the given action name and namespace.
440      * After that, the Action method is executed and output channels through the response object.
441      * Actions not found are sent back to the user via the {@link Dispatcher#sendError} method,
442      * using the 404 return code.
443      * All other errors are reported by throwing a ServletException.
444      *
445      * @param request  the HttpServletRequest object
446      * @param response the HttpServletResponse object
447      * @param mapping  the action mapping object
448      * @throws ServletException when an unknown error occurs (not a 404, but typically something that
449      *                          would end up as a 5xx by the servlet container)
450      * @param context Our ServletContext object
451      */
452     public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,
453                               ActionMapping mapping) throws ServletException {
454 
455         Map<String, Object> extraContext = createContextMap(request, response, mapping, context);
456 
457         // If there was a previous value stack, then create a new copy and pass it in to be used by the new Action
458         ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
459         boolean nullStack = stack == null;
460         if (nullStack) {
461             ActionContext ctx = ActionContext.getContext();
462             if (ctx != null) {
463                 stack = ctx.getValueStack();
464             }
465         }
466         if (stack != null) {
467             extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack));
468         }
469 
470         String timerKey = "Handling request from Dispatcher";
471         try {
472             UtilTimerStack.push(timerKey);
473             String namespace = mapping.getNamespace();
474             String name = mapping.getName();
475             String method = mapping.getMethod();
476 
477             Configuration config = configurationManager.getConfiguration();
478             ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(
479                     namespace, name, method, extraContext, true, false);
480 
481             request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());
482 
483             // if the ActionMapping says to go straight to a result, do it!
484             if (mapping.getResult() != null) {
485                 Result result = mapping.getResult();
486                 result.execute(proxy.getInvocation());
487             } else {
488                 proxy.execute();
489             }
490 
491             // If there was a previous value stack then set it back onto the request
492             if (!nullStack) {
493                 request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);
494             }
495         } catch (ConfigurationException e) {
496         	// WW-2874 Only log error if in devMode
497         	if(devMode) {
498         		LOG.error("Could not find action or result", e);
499         	}
500         	else {
501         		LOG.warn("Could not find action or result", e);
502         	}
503             sendError(request, response, context, HttpServletResponse.SC_NOT_FOUND, e);
504         } catch (Exception e) {
505             sendError(request, response, context, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
506         } finally {
507             UtilTimerStack.pop(timerKey);
508         }
509     }
510 
511     /***
512      * Create a context map containing all the wrapped request objects
513      *
514      * @param request The servlet request
515      * @param response The servlet response
516      * @param mapping The action mapping
517      * @param context The servlet context
518      * @return A map of context objects
519      */
520     public Map<String,Object> createContextMap(HttpServletRequest request, HttpServletResponse response,
521             ActionMapping mapping, ServletContext context) {
522 
523         // request map wrapping the http request objects
524         Map requestMap = new RequestMap(request);
525 
526         // parameters map wrapping the http parameters.  ActionMapping parameters are now handled and applied separately
527         Map params = new HashMap(request.getParameterMap());
528 
529         // session map wrapping the http session
530         Map session = new SessionMap(request);
531 
532         // application map wrapping the ServletContext
533         Map application = new ApplicationMap(context);
534 
535         Map<String,Object> extraContext = createContextMap(requestMap, params, session, application, request, response, context);
536 
537         if (mapping != null) {
538             extraContext.put(ServletActionContext.ACTION_MAPPING, mapping);
539         }
540         return extraContext;
541     }
542 
543     /***
544      * Merge all application and servlet attributes into a single <tt>HashMap</tt> to represent the entire
545      * <tt>Action</tt> context.
546      *
547      * @param requestMap     a Map of all request attributes.
548      * @param parameterMap   a Map of all request parameters.
549      * @param sessionMap     a Map of all session attributes.
550      * @param applicationMap a Map of all servlet context attributes.
551      * @param request        the HttpServletRequest object.
552      * @param response       the HttpServletResponse object.
553      * @param servletContext the ServletContextmapping object.
554      * @return a HashMap representing the <tt>Action</tt> context.
555      */
556     public HashMap<String,Object> createContextMap(Map requestMap,
557                                     Map parameterMap,
558                                     Map sessionMap,
559                                     Map applicationMap,
560                                     HttpServletRequest request,
561                                     HttpServletResponse response,
562                                     ServletContext servletContext) {
563         HashMap<String,Object> extraContext = new HashMap<String,Object>();
564         extraContext.put(ActionContext.PARAMETERS, new HashMap(parameterMap));
565         extraContext.put(ActionContext.SESSION, sessionMap);
566         extraContext.put(ActionContext.APPLICATION, applicationMap);
567 
568         Locale locale;
569         if (defaultLocale != null) {
570             locale = LocalizedTextUtil.localeFromString(defaultLocale, request.getLocale());
571         } else {
572             locale = request.getLocale();
573         }
574 
575         extraContext.put(ActionContext.LOCALE, locale);
576         //extraContext.put(ActionContext.DEV_MODE, Boolean.valueOf(devMode));
577 
578         extraContext.put(StrutsStatics.HTTP_REQUEST, request);
579         extraContext.put(StrutsStatics.HTTP_RESPONSE, response);
580         extraContext.put(StrutsStatics.SERVLET_CONTEXT, servletContext);
581 
582         // helpers to get access to request/session/application scope
583         extraContext.put("request", requestMap);
584         extraContext.put("session", sessionMap);
585         extraContext.put("application", applicationMap);
586         extraContext.put("parameters", parameterMap);
587 
588         AttributeMap attrMap = new AttributeMap(extraContext);
589         extraContext.put("attr", attrMap);
590 
591         return extraContext;
592     }
593 
594     /***
595      * Return the path to save uploaded files to (this is configurable).
596      *
597      * @return the path to save uploaded files to
598      * @param servletContext Our ServletContext
599      */
600     private String getSaveDir(ServletContext servletContext) {
601         String saveDir = multipartSaveDir.trim();
602 
603         if (saveDir.equals("")) {
604             File tempdir = (File) servletContext.getAttribute("javax.servlet.context.tempdir");
605             LOG.info("Unable to find 'struts.multipart.saveDir' property setting. Defaulting to javax.servlet.context.tempdir");
606 
607             if (tempdir != null) {
608                 saveDir = tempdir.toString();
609                 setMultipartSaveDir(saveDir);
610             }
611         } else {
612             File multipartSaveDir = new File(saveDir);
613 
614             if (!multipartSaveDir.exists()) {
615                 if (multipartSaveDir.mkdir() == false) {
616                     String logMessage;
617         	    try {
618                         logMessage = "Could not find create multipart save directory '"+multipartSaveDir.getCanonicalPath()+"'.";
619         	    } catch (IOException e) {
620                         logMessage = "Could not find create multipart save directory '"+multipartSaveDir.toString()+"'.";
621         	    }
622         	    if(devMode) {
623                         LOG.error(logMessage);
624         	    }
625         	    else {
626                         LOG.warn(logMessage);
627         	    }
628                 }
629             }
630         }
631 
632         if (LOG.isDebugEnabled()) {
633             LOG.debug("saveDir=" + saveDir);
634         }
635 
636         return saveDir;
637     }
638 
639     /***
640      * Prepare a request, including setting the encoding and locale.
641      *
642      * @param request The request
643      * @param response The response
644      */
645     public void prepare(HttpServletRequest request, HttpServletResponse response) {
646         String encoding = null;
647         if (defaultEncoding != null) {
648             encoding = defaultEncoding;
649         }
650 
651         Locale locale = null;
652         if (defaultLocale != null) {
653             locale = LocalizedTextUtil.localeFromString(defaultLocale, request.getLocale());
654         }
655 
656         if (encoding != null) {
657             try {
658                 request.setCharacterEncoding(encoding);
659             } catch (Exception e) {
660                 LOG.error("Error setting character encoding to '" + encoding + "' - ignoring.", e);
661             }
662         }
663 
664         if (locale != null) {
665             response.setLocale(locale);
666         }
667 
668         if (paramsWorkaroundEnabled) {
669             request.getParameter("foo"); // simply read any parameter (existing or not) to "prime" the request
670         }
671     }
672 
673     /***
674      * Wrap and return the given request or return the original request object.
675      * </p>
676      * This method transparently handles multipart data as a wrapped class around the given request.
677      * Override this method to handle multipart requests in a special way or to handle other types of requests.
678      * Note, {@link org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper} is
679      * flexible - look first to that object before overriding this method to handle multipart data.
680      *
681      * @param request the HttpServletRequest object.
682      * @param servletContext Our ServletContext object
683      * @return a wrapped request or original request.
684      * @see org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper
685      * @throws java.io.IOException on any error.
686      */
687     public HttpServletRequest wrapRequest(HttpServletRequest request, ServletContext servletContext) throws IOException {
688         // don't wrap more than once
689         if (request instanceof StrutsRequestWrapper) {
690             return request;
691         }
692 
693         String content_type = request.getContentType();
694         if (content_type != null && content_type.indexOf("multipart/form-data") != -1) {
695             MultiPartRequest mpr = null;
696             //check for alternate implementations of MultiPartRequest
697             Set<String> multiNames = getContainer().getInstanceNames(MultiPartRequest.class);
698             if (multiNames != null) {
699                 for (String multiName : multiNames) {
700                     if (multiName.equals(multipartHandlerName)) {
701                         mpr = getContainer().getInstance(MultiPartRequest.class, multiName);
702                     }
703                 }
704             }
705             if (mpr == null ) {
706                 mpr = getContainer().getInstance(MultiPartRequest.class);
707             }
708             request = new MultiPartRequestWrapper(mpr, request, getSaveDir(servletContext));
709         } else {
710             request = new StrutsRequestWrapper(request);
711         }
712 
713         return request;
714     }
715 
716     /***
717      * Send an HTTP error response code.
718      *
719      * @param request  the HttpServletRequest object.
720      * @param response the HttpServletResponse object.
721      * @param code     the HttpServletResponse error code (see {@link javax.servlet.http.HttpServletResponse} for possible error codes).
722      * @param e        the Exception that is reported.
723      * @param ctx      the ServletContext object.
724      */
725     public void sendError(HttpServletRequest request, HttpServletResponse response,
726             ServletContext ctx, int code, Exception e) {
727         if (devMode) {
728             response.setContentType("text/html");
729 
730             try {
731                 FreemarkerManager mgr = getContainer().getInstance(FreemarkerManager.class);
732 
733                 freemarker.template.Configuration config = mgr.getConfiguration(ctx);
734                 Template template = config.getTemplate("/org/apache/struts2/dispatcher/error.ftl");
735 
736                 List<Throwable> chain = new ArrayList<Throwable>();
737                 Throwable cur = e;
738                 chain.add(cur);
739                 while ((cur = cur.getCause()) != null) {
740                     chain.add(cur);
741                 }
742 
743                 HashMap<String,Object> data = new HashMap<String,Object>();
744                 data.put("exception", e);
745                 data.put("unknown", Location.UNKNOWN);
746                 data.put("chain", chain);
747                 data.put("locator", new Locator());
748                 template.process(data, response.getWriter());
749                 response.getWriter().close();
750             } catch (Exception exp) {
751                 try {
752                     response.sendError(code, "Unable to show problem report: " + exp);
753                 } catch (IOException ex) {
754                     // we're already sending an error, not much else we can do if more stuff breaks
755                 }
756             }
757         } else {
758             try {
759                 // WW-1977: Only put errors in the request when code is a 500 error
760                 if (code == HttpServletResponse.SC_INTERNAL_SERVER_ERROR) {
761                     // send a http error response to use the servlet defined error handler
762                     // make the exception availible to the web.xml defined error page
763                     request.setAttribute("javax.servlet.error.exception", e);
764 
765                     // for compatibility
766                     request.setAttribute("javax.servlet.jsp.jspException", e);
767                 }
768 
769                 // send the error response
770                 response.sendError(code, e.getMessage());
771             } catch (IOException e1) {
772                 // we're already sending an error, not much else we can do if more stuff breaks
773             }
774         }
775     }
776 
777     
778 
779     /***
780      * Provide an accessor class for static XWork utility.
781      */
782     public static class Locator {
783         public Location getLocation(Object obj) {
784             Location loc = LocationUtils.getLocation(obj);
785             if (loc == null) {
786                 return Location.UNKNOWN;
787             }
788             return loc;
789         }
790     }
791 
792     /***
793      * Expose the ConfigurationManager instance.
794      *
795      * @return The instance
796      */
797     public ConfigurationManager getConfigurationManager() {
798         return configurationManager;
799     }
800 
801     /***
802      * Modify the ConfigurationManager instance
803      *
804      * @param mgr The configuration manager
805      */
806     public void setConfigurationManager(ConfigurationManager mgr) {
807         this.configurationManager = mgr;
808     }
809 
810     /***
811      * Expose the dependency injection container.
812      * @return Our dependency injection container
813      */
814     public Container getContainer() {
815         ConfigurationManager mgr = getConfigurationManager();
816         if (mgr == null) {
817             throw new IllegalStateException("The configuration manager shouldn't be null");
818         } else {
819             Configuration config = mgr.getConfiguration();
820             if (config == null) {
821                 throw new IllegalStateException("Unable to load configuration");
822             } else {
823                 return config.getContainer();
824             }
825         }
826     }
827 }