1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
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
271 LOG.error("exception occurred while destroying ObjectFactory ["+objectFactory+"]", e);
272 }
273 }
274
275
276 instance.set(null);
277
278
279 if (!dispatcherListeners.isEmpty()) {
280 for (DispatcherListener l : dispatcherListeners) {
281 l.dispatcherDestroyed(this);
282 }
283 }
284
285
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
302 ActionContext.setContext(null);
303
304
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
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();
412 init_TraditionalXmlConfigurations();
413 init_LegacyStrutsProperties();
414 init_CustomConfigurationProviders();
415 init_FilterInitParameters() ;
416 init_AliasStandardObjects() ;
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
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
484 if (mapping.getResult() != null) {
485 Result result = mapping.getResult();
486 result.execute(proxy.getInvocation());
487 } else {
488 proxy.execute();
489 }
490
491
492 if (!nullStack) {
493 request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);
494 }
495 } catch (ConfigurationException e) {
496
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
524 Map requestMap = new RequestMap(request);
525
526
527 Map params = new HashMap(request.getParameterMap());
528
529
530 Map session = new SessionMap(request);
531
532
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
577
578 extraContext.put(StrutsStatics.HTTP_REQUEST, request);
579 extraContext.put(StrutsStatics.HTTP_RESPONSE, response);
580 extraContext.put(StrutsStatics.SERVLET_CONTEXT, servletContext);
581
582
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");
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
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
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
755 }
756 }
757 } else {
758 try {
759
760 if (code == HttpServletResponse.SC_INTERNAL_SERVER_ERROR) {
761
762
763 request.setAttribute("javax.servlet.error.exception", e);
764
765
766 request.setAttribute("javax.servlet.jsp.jspException", e);
767 }
768
769
770 response.sendError(code, e.getMessage());
771 } catch (IOException e1) {
772
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 }