001: /*
002: * $Id: Dispatcher.java 566474 2007-08-16 02:55:44Z jholmes $
003: *
004: * Licensed to the Apache Software Foundation (ASF) under one
005: * or more contributor license agreements. See the NOTICE file
006: * distributed with this work for additional information
007: * regarding copyright ownership. The ASF licenses this file
008: * to you under the Apache License, Version 2.0 (the
009: * "License"); you may not use this file except in compliance
010: * with the License. You may obtain a copy of the License at
011: *
012: * http://www.apache.org/licenses/LICENSE-2.0
013: *
014: * Unless required by applicable law or agreed to in writing,
015: * software distributed under the License is distributed on an
016: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017: * KIND, either express or implied. See the License for the
018: * specific language governing permissions and limitations
019: * under the License.
020: */
021: package org.apache.struts2.dispatcher;
022:
023: import java.io.File;
024: import java.io.IOException;
025: import java.net.MalformedURLException;
026: import java.net.URL;
027: import java.util.ArrayList;
028: import java.util.HashMap;
029: import java.util.List;
030: import java.util.Locale;
031: import java.util.Map;
032:
033: import javax.servlet.ServletContext;
034: import javax.servlet.ServletException;
035: import javax.servlet.http.HttpServletRequest;
036: import javax.servlet.http.HttpServletResponse;
037:
038: import org.apache.commons.logging.Log;
039: import org.apache.commons.logging.LogFactory;
040: import org.apache.struts2.ServletActionContext;
041: import org.apache.struts2.StrutsConstants;
042: import org.apache.struts2.StrutsStatics;
043: import org.apache.struts2.config.*;
044: import org.apache.struts2.config.ClasspathConfigurationProvider.ClasspathPageLocator;
045: import org.apache.struts2.config.ClasspathConfigurationProvider.PageLocator;
046: import org.apache.struts2.dispatcher.mapper.ActionMapping;
047: import org.apache.struts2.dispatcher.multipart.MultiPartRequest;
048: import org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper;
049: import org.apache.struts2.util.AttributeMap;
050: import org.apache.struts2.util.ClassLoaderUtils;
051: import org.apache.struts2.util.ObjectFactoryDestroyable;
052: import org.apache.struts2.views.freemarker.FreemarkerManager;
053:
054: import com.opensymphony.xwork2.util.FileManager;
055: import com.opensymphony.xwork2.*;
056: import com.opensymphony.xwork2.Result;
057: import com.opensymphony.xwork2.config.Configuration;
058: import com.opensymphony.xwork2.config.ConfigurationException;
059: import com.opensymphony.xwork2.config.ConfigurationManager;
060: import com.opensymphony.xwork2.config.ConfigurationProvider;
061: import com.opensymphony.xwork2.config.providers.XmlConfigurationProvider;
062: import com.opensymphony.xwork2.inject.Container;
063: import com.opensymphony.xwork2.inject.ContainerBuilder;
064: import com.opensymphony.xwork2.inject.Inject;
065: import com.opensymphony.xwork2.util.LocalizedTextUtil;
066: import com.opensymphony.xwork2.util.ObjectTypeDeterminer;
067: import com.opensymphony.xwork2.util.ObjectTypeDeterminerFactory;
068: import com.opensymphony.xwork2.util.ValueStack;
069: import com.opensymphony.xwork2.util.ValueStackFactory;
070: import com.opensymphony.xwork2.util.location.Location;
071: import com.opensymphony.xwork2.util.location.LocationUtils;
072: import com.opensymphony.xwork2.util.location.LocatableProperties;
073: import com.opensymphony.xwork2.util.profiling.UtilTimerStack;
074:
075: import freemarker.template.Template;
076:
077: /**
078: * A utility class the actual dispatcher delegates most of its tasks to. Each instance
079: * of the primary dispatcher holds an instance of this dispatcher to be shared for
080: * all requests.
081: *
082: * @see org.apache.struts2.dispatcher.FilterDispatcher
083: * @see org.apache.struts2.portlet.dispatcher.Jsr168Dispatcher
084: */
085: public class Dispatcher {
086:
087: /**
088: * Provide a logging instance.
089: */
090: private static final Log LOG = LogFactory.getLog(Dispatcher.class);
091:
092: /**
093: * Provide a thread local instance.
094: */
095: private static ThreadLocal<Dispatcher> instance = new ThreadLocal<Dispatcher>();
096:
097: /**
098: * Store list of DispatcherListeners.
099: */
100: private static List<DispatcherListener> dispatcherListeners = new ArrayList<DispatcherListener>();
101:
102: /**
103: * Store ConfigurationManager instance, set on init.
104: */
105: private ConfigurationManager configurationManager;
106:
107: /**
108: * Store whether portlet support is active
109: * (set to true by Jsr168Dispatcher).
110: */
111: private static boolean portletSupportActive;
112:
113: /**
114: * Store state of StrutsConstants.STRUTS_DEVMODE setting.
115: */
116: private static boolean devMode;
117:
118: /**
119: * Store state of StrutsConstants.STRUTS_I18N_ENCODING setting.
120: */
121: private static String defaultEncoding;
122:
123: /**
124: * Store state of StrutsConstants.STRUTS_LOCALE setting.
125: */
126: private static String defaultLocale;
127:
128: /**
129: * Store state of StrutsConstants.STRUTS_MULTIPART_SAVEDIR setting.
130: */
131: private static String multipartSaveDir;
132:
133: /**
134: * Provide list of default configuration files.
135: */
136: private static final String DEFAULT_CONFIGURATION_PATHS = "struts-default.xml,struts-plugin.xml,struts.xml";
137:
138: /**
139: * Store state of STRUTS_DISPATCHER_PARAMETERSWORKAROUND.
140: * <p/>
141: * The workaround is for WebLogic.
142: * We try to autodect WebLogic on Dispatcher init.
143: * The workaround can also be enabled manually.
144: */
145: private boolean paramsWorkaroundEnabled = false;
146:
147: /**
148: * Provide the dispatcher instance for the current thread.
149: *
150: * @return The dispatcher instance
151: */
152: public static Dispatcher getInstance() {
153: return instance.get();
154: }
155:
156: /**
157: * Store the dispatcher instance for this thread.
158: *
159: * @param instance The instance
160: */
161: public static void setInstance(Dispatcher instance) {
162: Dispatcher.instance.set(instance);
163:
164: // Tie the ObjectFactory threadlocal instance to this Dispatcher instance
165: if (instance != null) {
166: Container cont = instance.getContainer();
167: if (cont != null) {
168: ObjectFactory.setObjectFactory(cont
169: .getInstance(ObjectFactory.class));
170: } else {
171: LOG
172: .warn("This dispatcher instance doesn't have a container, so the object factory won't be set.");
173: }
174: } else {
175: ObjectFactory.setObjectFactory(null);
176: }
177: }
178:
179: /**
180: * Add a dispatcher lifecycle listener.
181: *
182: * @param listener The listener to add
183: */
184: public static synchronized void addDispatcherListener(
185: DispatcherListener listener) {
186: dispatcherListeners.add(listener);
187: }
188:
189: /**
190: * Remove a specific dispatcher lifecycle listener.
191: *
192: * @param listener The listener
193: */
194: public static synchronized void removeDispatcherListener(
195: DispatcherListener listener) {
196: dispatcherListeners.remove(listener);
197: }
198:
199: private ServletContext servletContext;
200: private Map<String, String> initParams;
201:
202: /**
203: * Create the Dispatcher instance for a given ServletContext and set of initialization parameters.
204: *
205: * @param servletContext Our servlet context
206: * @param initParams The set of initialization parameters
207: */
208: public Dispatcher(ServletContext servletContext,
209: Map<String, String> initParams) {
210: this .servletContext = servletContext;
211: this .initParams = initParams;
212: }
213:
214: /**
215: * Modify state of StrutsConstants.STRUTS_DEVMODE setting.
216: * @param mode New setting
217: */
218: @Inject(StrutsConstants.STRUTS_DEVMODE)
219: public static void setDevMode(String mode) {
220: devMode = "true".equals(mode);
221: }
222:
223: /**
224: * Modify state of StrutsConstants.STRUTS_LOCALE setting.
225: * @param val New setting
226: */
227: @Inject(value=StrutsConstants.STRUTS_LOCALE,required=false)
228: public static void setDefaultLocale(String val) {
229: defaultLocale = val;
230: }
231:
232: /**
233: * Modify state of StrutsConstants.STRUTS_I18N_ENCODING setting.
234: * @param val New setting
235: */
236: @Inject(StrutsConstants.STRUTS_I18N_ENCODING)
237: public static void setDefaultEncoding(String val) {
238: defaultEncoding = val;
239: }
240:
241: /**
242: * Modify state of StrutsConstants.STRUTS_MULTIPART_SAVEDIR setting.
243: * @param val New setting
244: */
245: @Inject(StrutsConstants.STRUTS_MULTIPART_SAVEDIR)
246: public static void setMultipartSaveDir(String val) {
247: multipartSaveDir = val;
248: }
249:
250: /**
251: * Releases all instances bound to this dispatcher instance.
252: */
253: public void cleanup() {
254:
255: // clean up ObjectFactory
256: ObjectFactory objectFactory = getContainer().getInstance(
257: ObjectFactory.class);
258: if (objectFactory == null) {
259: LOG
260: .warn("Object Factory is null, something is seriously wrong, no clean up will be performed");
261: }
262: if (objectFactory instanceof ObjectFactoryDestroyable) {
263: try {
264: ((ObjectFactoryDestroyable) objectFactory).destroy();
265: } catch (Exception e) {
266: // catch any exception that may occured during destroy() and log it
267: LOG.error(
268: "exception occurred while destroying ObjectFactory ["
269: + objectFactory + "]", e);
270: }
271: }
272:
273: // clean up Dispatcher itself for this thread
274: instance.set(null);
275:
276: // clean up DispatcherListeners
277: synchronized (Dispatcher.class) {
278: if (dispatcherListeners.size() > 0) {
279: for (DispatcherListener l : dispatcherListeners) {
280: l.dispatcherDestroyed(this );
281: }
282: }
283: }
284:
285: // clean up configuration
286: configurationManager.destroyConfiguration();
287: configurationManager = null;
288: }
289:
290: private void init_DefaultProperties() {
291: configurationManager
292: .addConfigurationProvider(new DefaultPropertiesProvider());
293: }
294:
295: private void init_LegacyStrutsProperties() {
296: configurationManager
297: .addConfigurationProvider(new LegacyPropertiesConfigurationProvider());
298: }
299:
300: private void init_TraditionalXmlConfigurations() {
301: String configPaths = initParams.get("config");
302: if (configPaths == null) {
303: configPaths = DEFAULT_CONFIGURATION_PATHS;
304: }
305: String[] files = configPaths.split("\\s*[,]\\s*");
306: for (String file : files) {
307: if (file.endsWith(".xml")) {
308: if ("xwork.xml".equals(file)) {
309: configurationManager
310: .addConfigurationProvider(new XmlConfigurationProvider(
311: file, false));
312: } else {
313: configurationManager
314: .addConfigurationProvider(new StrutsXmlConfigurationProvider(
315: file, false, servletContext));
316: }
317: } else {
318: throw new IllegalArgumentException(
319: "Invalid configuration file name");
320: }
321: }
322: }
323:
324: private void init_ZeroConfiguration() {
325: String packages = initParams.get("actionPackages");
326: if (packages != null) {
327: String[] names = packages.split("\\s*[,]\\s*");
328: // Initialize the classloader scanner with the configured packages
329: if (names.length > 0) {
330: ClasspathConfigurationProvider provider = new ClasspathConfigurationProvider(
331: names);
332: provider.setPageLocator(new ServletContextPageLocator(
333: servletContext));
334: configurationManager.addConfigurationProvider(provider);
335: }
336: }
337: }
338:
339: private void init_CustomConfigurationProviders() {
340: String configProvs = initParams.get("configProviders");
341: if (configProvs != null) {
342: String[] classes = configProvs.split("\\s*[,]\\s*");
343: for (String cname : classes) {
344: try {
345: Class cls = ClassLoaderUtils.loadClass(cname, this
346: .getClass());
347: ConfigurationProvider prov = (ConfigurationProvider) cls
348: .newInstance();
349: configurationManager.addConfigurationProvider(prov);
350: } catch (InstantiationException e) {
351: throw new ConfigurationException(
352: "Unable to instantiate provider: " + cname,
353: e);
354: } catch (IllegalAccessException e) {
355: throw new ConfigurationException(
356: "Unable to access provider: " + cname, e);
357: } catch (ClassNotFoundException e) {
358: throw new ConfigurationException(
359: "Unable to locate provider class: " + cname,
360: e);
361: }
362: }
363: }
364: }
365:
366: private void init_MethodConfigurationProvider() {
367: // See https://issues.apache.org/struts/browse/WW-1522
368: /*
369: com.opensymphony.xwork2.inject.DependencyException: com.opensymphony.xwork2.inject.ContainerImpl$MissingDependencyException: No mapping found for dependency [type=org.apache.struts2.dispatcher.mapper.ActionMapper, name='default'] in public static void org.apache.struts2.dispatcher.FilterDispatcher.setActionMapper(org.apache.struts2.dispatcher.mapper.ActionMapper).
370: at com.opensymphony.xwork2.inject.ContainerImpl.addInjectorsForMembers(ContainerImpl.java:135)
371: at com.opensymphony.xwork2.inject.ContainerImpl.addInjectorsForMethods(ContainerImpl.java:104)
372: at com.opensymphony.xwork2.inject.ContainerImpl.injectStatics(ContainerImpl.java:89)
373: at com.opensymphony.xwork2.inject.ContainerBuilder.create(ContainerBuilder.java:494)
374: at com.opensymphony.xwork2.config.impl.DefaultConfiguration.reload(DefaultConfiguration.java:140)
375: at com.opensymphony.xwork2.config.ConfigurationManager.getConfiguration(ConfigurationManager.java:52)
376: at org.apache.struts2.dispatcher.Dispatcher.init_MethodConfigurationProvider(Dispatcher.java:347)
377: at org.apache.struts2.dispatcher.Dispatcher.init(Dispatcher.java:421)
378: at org.apache.struts2.config.MethodConfigurationProviderTest.setUp(MethodConfigurationProviderTest.java:68)
379: at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:40)
380: at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
381: at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
382: at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
383: at com.intellij.rt.execution.application.AppMain.main(AppMain.java:90)
384: Caused by: com.opensymphony.xwork2.inject.ContainerImpl$MissingDependencyException: No mapping found for dependency [type=org.apache.struts2.dispatcher.mapper.ActionMapper, name='default'] in public static void org.apache.struts2.dispatcher.FilterDispatcher.setActionMapper(org.apache.struts2.dispatcher.mapper.ActionMapper).
385: at com.opensymphony.xwork2.inject.ContainerImpl.createParameterInjector(ContainerImpl.java:217)
386: at com.opensymphony.xwork2.inject.ContainerImpl.getParametersInjectors(ContainerImpl.java:207)
387: at com.opensymphony.xwork2.inject.ContainerImpl$MethodInjector.<init>(ContainerImpl.java:260)
388: at com.opensymphony.xwork2.inject.ContainerImpl$3.create(ContainerImpl.java:108)
389: at com.opensymphony.xwork2.inject.ContainerImpl$3.create(ContainerImpl.java:106)
390: at com.opensymphony.xwork2.inject.ContainerImpl.addInjectorsForMembers(ContainerImpl.java:132)
391: ... 26 more
392:
393: MethodConfigurationProvider provider = new MethodConfigurationProvider();
394: provider.init(configurationManager.getConfiguration());
395: provider.loadPackages();
396: */
397: }
398:
399: private void init_FilterInitParameters() {
400: configurationManager
401: .addConfigurationProvider(new ConfigurationProvider() {
402: public void destroy() {
403: }
404:
405: public void init(Configuration configuration)
406: throws ConfigurationException {
407: }
408:
409: public void loadPackages()
410: throws ConfigurationException {
411: }
412:
413: public boolean needsReload() {
414: return false;
415: }
416:
417: public void register(ContainerBuilder builder,
418: LocatableProperties props)
419: throws ConfigurationException {
420: props.putAll(initParams);
421: }
422: });
423: }
424:
425: private void init_AliasStandardObjects() {
426: configurationManager
427: .addConfigurationProvider(new BeanSelectionProvider());
428: }
429:
430: private Container init_PreloadConfiguration() {
431: Configuration config = configurationManager.getConfiguration();
432: Container container = config.getContainer();
433:
434: boolean reloadi18n = Boolean.valueOf(container.getInstance(
435: String.class, StrutsConstants.STRUTS_I18N_RELOAD));
436: LocalizedTextUtil.setReloadBundles(reloadi18n);
437:
438: ObjectTypeDeterminer objectTypeDeterminer = container
439: .getInstance(ObjectTypeDeterminer.class);
440: ObjectTypeDeterminerFactory.setInstance(objectTypeDeterminer);
441:
442: return container;
443: }
444:
445: private void init_CheckConfigurationReloading(Container container) {
446: FileManager
447: .setReloadingConfigs("true"
448: .equals(container
449: .getInstance(
450: String.class,
451: StrutsConstants.STRUTS_CONFIGURATION_XML_RELOAD)));
452: }
453:
454: private void init_CheckWebLogicWorkaround(Container container) {
455: // test whether param-access workaround needs to be enabled
456: if (servletContext != null
457: && servletContext.getServerInfo() != null
458: && servletContext.getServerInfo().indexOf("WebLogic") >= 0) {
459: LOG
460: .info("WebLogic server detected. Enabling Struts parameter access work-around.");
461: paramsWorkaroundEnabled = true;
462: } else {
463: paramsWorkaroundEnabled = "true"
464: .equals(container
465: .getInstance(
466: String.class,
467: StrutsConstants.STRUTS_DISPATCHER_PARAMETERSWORKAROUND));
468: }
469:
470: synchronized (Dispatcher.class) {
471: if (dispatcherListeners.size() > 0) {
472: for (DispatcherListener l : dispatcherListeners) {
473: l.dispatcherInitialized(this );
474: }
475: }
476: }
477:
478: }
479:
480: /**
481: * Load configurations, including both XML and zero-configuration strategies,
482: * and update optional settings, including whether to reload configurations and resource files.
483: */
484: public void init() {
485:
486: if (configurationManager == null) {
487: configurationManager = new ConfigurationManager(
488: BeanSelectionProvider.DEFAULT_BEAN_NAME);
489: }
490:
491: init_DefaultProperties(); // [1]
492: init_TraditionalXmlConfigurations(); // [2]
493: init_LegacyStrutsProperties(); // [3]
494: init_ZeroConfiguration(); // [4]
495: init_CustomConfigurationProviders(); // [5]
496: init_MethodConfigurationProvider();
497: init_FilterInitParameters(); // [6]
498: init_AliasStandardObjects(); // [7]
499:
500: Container container = init_PreloadConfiguration();
501: init_CheckConfigurationReloading(container);
502: init_CheckWebLogicWorkaround(container);
503:
504: }
505:
506: /**
507: * Load Action class for mapping and invoke the appropriate Action method, or go directly to the Result.
508: * <p/>
509: * This method first creates the action context from the given parameters,
510: * and then loads an <tt>ActionProxy</tt> from the given action name and namespace.
511: * After that, the Action method is executed and output channels through the response object.
512: * Actions not found are sent back to the user via the {@link Dispatcher#sendError} method,
513: * using the 404 return code.
514: * All other errors are reported by throwing a ServletException.
515: *
516: * @param request the HttpServletRequest object
517: * @param response the HttpServletResponse object
518: * @param mapping the action mapping object
519: * @throws ServletException when an unknown error occurs (not a 404, but typically something that
520: * would end up as a 5xx by the servlet container)
521: * @param context Our ServletContext object
522: */
523: public void serviceAction(HttpServletRequest request,
524: HttpServletResponse response, ServletContext context,
525: ActionMapping mapping) throws ServletException {
526:
527: Map<String, Object> extraContext = createContextMap(request,
528: response, mapping, context);
529:
530: // If there was a previous value stack, then create a new copy and pass it in to be used by the new Action
531: ValueStack stack = (ValueStack) request
532: .getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
533: if (stack != null) {
534: extraContext.put(ActionContext.VALUE_STACK,
535: ValueStackFactory.getFactory().createValueStack(
536: stack));
537: }
538:
539: String timerKey = "Handling request from Dispatcher";
540: try {
541: UtilTimerStack.push(timerKey);
542: String namespace = mapping.getNamespace();
543: String name = mapping.getName();
544: String method = mapping.getMethod();
545:
546: Configuration config = configurationManager
547: .getConfiguration();
548: ActionProxy proxy = config.getContainer().getInstance(
549: ActionProxyFactory.class).createActionProxy(
550: namespace, name, extraContext, true, false);
551: proxy.setMethod(method);
552: request.setAttribute(
553: ServletActionContext.STRUTS_VALUESTACK_KEY, proxy
554: .getInvocation().getStack());
555:
556: // if the ActionMapping says to go straight to a result, do it!
557: if (mapping.getResult() != null) {
558: Result result = mapping.getResult();
559: result.execute(proxy.getInvocation());
560: } else {
561: proxy.execute();
562: }
563:
564: // If there was a previous value stack then set it back onto the request
565: if (stack != null) {
566: request.setAttribute(
567: ServletActionContext.STRUTS_VALUESTACK_KEY,
568: stack);
569: }
570: } catch (ConfigurationException e) {
571: LOG.error("Could not find action or result", e);
572: sendError(request, response, context,
573: HttpServletResponse.SC_NOT_FOUND, e);
574: } catch (Exception e) {
575: throw new ServletException(e);
576: } finally {
577: UtilTimerStack.pop(timerKey);
578: }
579: }
580:
581: /**
582: * Create a context map containing all the wrapped request objects
583: *
584: * @param request The servlet request
585: * @param response The servlet response
586: * @param mapping The action mapping
587: * @param context The servlet context
588: * @return A map of context objects
589: */
590: public Map<String, Object> createContextMap(
591: HttpServletRequest request, HttpServletResponse response,
592: ActionMapping mapping, ServletContext context) {
593:
594: // request map wrapping the http request objects
595: Map requestMap = new RequestMap(request);
596:
597: // parameters map wrapping the http paraneters.
598: Map params = null;
599: if (mapping != null) {
600: params = mapping.getParams();
601: }
602: Map requestParams = new HashMap(request.getParameterMap());
603: if (params != null) {
604: params.putAll(requestParams);
605: } else {
606: params = requestParams;
607: }
608:
609: // session map wrapping the http session
610: Map session = new SessionMap(request);
611:
612: // application map wrapping the ServletContext
613: Map application = new ApplicationMap(context);
614:
615: Map<String, Object> extraContext = createContextMap(requestMap,
616: params, session, application, request, response,
617: context);
618: extraContext.put(ServletActionContext.ACTION_MAPPING, mapping);
619: return extraContext;
620: }
621:
622: /**
623: * Merge all application and servlet attributes into a single <tt>HashMap</tt> to represent the entire
624: * <tt>Action</tt> context.
625: *
626: * @param requestMap a Map of all request attributes.
627: * @param parameterMap a Map of all request parameters.
628: * @param sessionMap a Map of all session attributes.
629: * @param applicationMap a Map of all servlet context attributes.
630: * @param request the HttpServletRequest object.
631: * @param response the HttpServletResponse object.
632: * @param servletContext the ServletContextmapping object.
633: * @return a HashMap representing the <tt>Action</tt> context.
634: */
635: public HashMap<String, Object> createContextMap(Map requestMap,
636: Map parameterMap, Map sessionMap, Map applicationMap,
637: HttpServletRequest request, HttpServletResponse response,
638: ServletContext servletContext) {
639: HashMap<String, Object> extraContext = new HashMap<String, Object>();
640: extraContext.put(ActionContext.PARAMETERS, new HashMap(
641: parameterMap));
642: extraContext.put(ActionContext.SESSION, sessionMap);
643: extraContext.put(ActionContext.APPLICATION, applicationMap);
644:
645: Locale locale;
646: if (defaultLocale != null) {
647: locale = LocalizedTextUtil.localeFromString(defaultLocale,
648: request.getLocale());
649: } else {
650: locale = request.getLocale();
651: }
652:
653: extraContext.put(ActionContext.LOCALE, locale);
654: //extraContext.put(ActionContext.DEV_MODE, Boolean.valueOf(devMode));
655:
656: extraContext.put(StrutsStatics.HTTP_REQUEST, request);
657: extraContext.put(StrutsStatics.HTTP_RESPONSE, response);
658: extraContext.put(StrutsStatics.SERVLET_CONTEXT, servletContext);
659:
660: // helpers to get access to request/session/application scope
661: extraContext.put("request", requestMap);
662: extraContext.put("session", sessionMap);
663: extraContext.put("application", applicationMap);
664: extraContext.put("parameters", parameterMap);
665:
666: AttributeMap attrMap = new AttributeMap(extraContext);
667: extraContext.put("attr", attrMap);
668:
669: return extraContext;
670: }
671:
672: /**
673: * Return the path to save uploaded files to (this is configurable).
674: *
675: * @return the path to save uploaded files to
676: * @param servletContext Our ServletContext
677: */
678: private String getSaveDir(ServletContext servletContext) {
679: String saveDir = multipartSaveDir.trim();
680:
681: if (saveDir.equals("")) {
682: File tempdir = (File) servletContext
683: .getAttribute("javax.servlet.context.tempdir");
684: LOG
685: .info("Unable to find 'struts.multipart.saveDir' property setting. Defaulting to javax.servlet.context.tempdir");
686:
687: if (tempdir != null) {
688: saveDir = tempdir.toString();
689: }
690: } else {
691: File multipartSaveDir = new File(saveDir);
692:
693: if (!multipartSaveDir.exists()) {
694: multipartSaveDir.mkdir();
695: }
696: }
697:
698: if (LOG.isDebugEnabled()) {
699: LOG.debug("saveDir=" + saveDir);
700: }
701:
702: return saveDir;
703: }
704:
705: /**
706: * Prepare a request, including setting the encoding and locale.
707: *
708: * @param request The request
709: * @param response The response
710: */
711: public void prepare(HttpServletRequest request,
712: HttpServletResponse response) {
713: String encoding = null;
714: if (defaultEncoding != null) {
715: encoding = defaultEncoding;
716: }
717:
718: Locale locale = null;
719: if (defaultLocale != null) {
720: locale = LocalizedTextUtil.localeFromString(defaultLocale,
721: request.getLocale());
722: }
723:
724: if (encoding != null) {
725: try {
726: request.setCharacterEncoding(encoding);
727: } catch (Exception e) {
728: LOG.error("Error setting character encoding to '"
729: + encoding + "' - ignoring.", e);
730: }
731: }
732:
733: if (locale != null) {
734: response.setLocale(locale);
735: }
736:
737: if (paramsWorkaroundEnabled) {
738: request.getParameter("foo"); // simply read any parameter (existing or not) to "prime" the request
739: }
740: }
741:
742: /**
743: * Wrap and return the given request or return the original request object.
744: * </p>
745: * This method transparently handles multipart data as a wrapped class around the given request.
746: * Override this method to handle multipart requests in a special way or to handle other types of requests.
747: * Note, {@link org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper} is
748: * flexible - look first to that object before overriding this method to handle multipart data.
749: *
750: * @param request the HttpServletRequest object.
751: * @param servletContext Our ServletContext object
752: * @return a wrapped request or original request.
753: * @see org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper
754: * @throws java.io.IOException on any error.
755: */
756: public HttpServletRequest wrapRequest(HttpServletRequest request,
757: ServletContext servletContext) throws IOException {
758: // don't wrap more than once
759: if (request instanceof StrutsRequestWrapper) {
760: return request;
761: }
762:
763: String content_type = request.getContentType();
764: if (content_type != null
765: && content_type.indexOf("multipart/form-data") != -1) {
766: MultiPartRequest multi = getContainer().getInstance(
767: MultiPartRequest.class);
768: request = new MultiPartRequestWrapper(multi, request,
769: getSaveDir(servletContext));
770: } else {
771: request = new StrutsRequestWrapper(request);
772: }
773:
774: return request;
775: }
776:
777: /**
778: * Send an HTTP error response code.
779: *
780: * @param request the HttpServletRequest object.
781: * @param response the HttpServletResponse object.
782: * @param code the HttpServletResponse error code (see {@link javax.servlet.http.HttpServletResponse} for possible error codes).
783: * @param e the Exception that is reported.
784: * @param ctx the ServletContext object.
785: */
786: public void sendError(HttpServletRequest request,
787: HttpServletResponse response, ServletContext ctx, int code,
788: Exception e) {
789: if (devMode) {
790: response.setContentType("text/html");
791:
792: try {
793: FreemarkerManager mgr = getContainer().getInstance(
794: FreemarkerManager.class);
795:
796: freemarker.template.Configuration config = mgr
797: .getConfiguration(ctx);
798: Template template = config
799: .getTemplate("/org/apache/struts2/dispatcher/error.ftl");
800:
801: List<Throwable> chain = new ArrayList<Throwable>();
802: Throwable cur = e;
803: chain.add(cur);
804: while ((cur = cur.getCause()) != null) {
805: chain.add(cur);
806: }
807:
808: HashMap<String, Object> data = new HashMap<String, Object>();
809: data.put("exception", e);
810: data.put("unknown", Location.UNKNOWN);
811: data.put("chain", chain);
812: data.put("locator", new Locator());
813: template.process(data, response.getWriter());
814: response.getWriter().close();
815: } catch (Exception exp) {
816: try {
817: response.sendError(code,
818: "Unable to show problem report: " + exp);
819: } catch (IOException ex) {
820: // we're already sending an error, not much else we can do if more stuff breaks
821: }
822: }
823: } else {
824: try {
825: // WW-1977: Only put errors in the request when code is a 500 error
826: if (code == HttpServletResponse.SC_INTERNAL_SERVER_ERROR) {
827: // send a http error response to use the servlet defined error handler
828: // make the exception availible to the web.xml defined error page
829: request.setAttribute(
830: "javax.servlet.error.exception", e);
831:
832: // for compatibility
833: request.setAttribute(
834: "javax.servlet.jsp.jspException", e);
835: }
836:
837: // send the error response
838: response.sendError(code, e.getMessage());
839: } catch (IOException e1) {
840: // we're already sending an error, not much else we can do if more stuff breaks
841: }
842: }
843: }
844:
845: /**
846: * Return <tt>true</tt>, if portlet support is active, <tt>false</tt> otherwise.
847: *
848: * @return <tt>true</tt>, if portlet support is active, <tt>false</tt> otherwise.
849: */
850: public boolean isPortletSupportActive() {
851: return portletSupportActive;
852: }
853:
854: /**
855: * Modify the portlet support mode.
856: * @param portletSupportActive <tt>true</tt> or <tt>false</tt>
857: */
858: public static void setPortletSupportActive(
859: boolean portletSupportActive) {
860: Dispatcher.portletSupportActive = portletSupportActive;
861: }
862:
863: /**
864: * Search classpath for a page.
865: */
866: private final class ServletContextPageLocator implements
867: PageLocator {
868: private final ServletContext context;
869: private ClasspathPageLocator classpathPageLocator = new ClasspathPageLocator();
870:
871: private ServletContextPageLocator(ServletContext context) {
872: this .context = context;
873: }
874:
875: public URL locate(String path) {
876: URL url = null;
877: try {
878: url = context.getResource(path);
879: if (url == null) {
880: url = classpathPageLocator.locate(path);
881: }
882: } catch (MalformedURLException e) {
883: if (LOG.isDebugEnabled()) {
884: LOG.debug("Unable to resolve path " + path
885: + " against the servlet context");
886: }
887: }
888: return url;
889: }
890: }
891:
892: /**
893: * Provide an accessor class for static XWork utility.
894: */
895: public class Locator {
896: public Location getLocation(Object obj) {
897: Location loc = LocationUtils.getLocation(obj);
898: if (loc == null) {
899: return Location.UNKNOWN;
900: }
901: return loc;
902: }
903: }
904:
905: /**
906: * Expose the ConfigurationManager instance.
907: *
908: * @return The instance
909: */
910: public ConfigurationManager getConfigurationManager() {
911: return configurationManager;
912: }
913:
914: /**
915: * Modify the ConfigurationManager instance
916: *
917: * @param mgr The configuration manager
918: */
919: public void setConfigurationManager(ConfigurationManager mgr) {
920: this .configurationManager = mgr;
921: }
922:
923: /**
924: * Expose the dependency injection container.
925: * @return Our dependency injection container
926: */
927: public Container getContainer() {
928: ConfigurationManager mgr = getConfigurationManager();
929: if (mgr == null) {
930: throw new IllegalStateException(
931: "The configuration manager shouldn't be null");
932: } else {
933: Configuration config = mgr.getConfiguration();
934: if (config == null) {
935: throw new IllegalStateException(
936: "Unable to load configuration");
937: } else {
938: return config.getContainer();
939: }
940: }
941: }
942: }
|