FileDocCategorySizeDatePackage
Dispatcher.javaAPI DocExample35194Mon Jul 23 13:26:38 BST 2007org.apache.struts2.dispatcher

Dispatcher

public class Dispatcher extends Object
A utility class the actual dispatcher delegates most of its tasks to. Each instance of the primary dispatcher holds an instance of this dispatcher to be shared for all requests.
see
org.apache.struts2.dispatcher.FilterDispatcher
see
org.apache.struts2.portlet.dispatcher.Jsr168Dispatcher

Fields Summary
private static final Log
LOG
Provide a logging instance.
private static ThreadLocal
instance
Provide a thread local instance.
private static List
dispatcherListeners
Store list of DispatcherListeners.
private com.opensymphony.xwork2.config.ConfigurationManager
configurationManager
Store ConfigurationManager instance, set on init.
private static boolean
portletSupportActive
Store whether portlet support is active (set to true by Jsr168Dispatcher).
private static boolean
devMode
Store state of StrutsConstants.STRUTS_DEVMODE setting.
private static String
defaultEncoding
Store state of StrutsConstants.STRUTS_I18N_ENCODING setting.
private static String
defaultLocale
Store state of StrutsConstants.STRUTS_LOCALE setting.
private static String
multipartSaveDir
Store state of StrutsConstants.STRUTS_MULTIPART_SAVEDIR setting.
private static final String
DEFAULT_CONFIGURATION_PATHS
Provide list of default configuration files.
private boolean
paramsWorkaroundEnabled
Store state of STRUTS_DISPATCHER_PARAMETERSWORKAROUND.

The workaround is for WebLogic. We try to autodect WebLogic on Dispatcher init. The workaround can also be enabled manually.

private ServletContext
servletContext
private Map
initParams
Constructors Summary
public Dispatcher(ServletContext servletContext, Map initParams)
Create the Dispatcher instance for a given ServletContext and set of initialization parameters.

param
servletContext Our servlet context
param
initParams The set of initialization parameters

        this.servletContext = servletContext;
        this.initParams = initParams;
    
Methods Summary
public static synchronized voidaddDispatcherListener(DispatcherListener listener)
Add a dispatcher lifecycle listener.

param
listener The listener to add

        dispatcherListeners.add(listener);
    
public voidcleanup()
Releases all instances bound to this dispatcher instance.


    	// clean up ObjectFactory
        ObjectFactory objectFactory = getContainer().getInstance(ObjectFactory.class);
        if (objectFactory == null) {
            LOG.warn("Object Factory is null, something is seriously wrong, no clean up will be performed");
        }
        if (objectFactory instanceof ObjectFactoryDestroyable) {
            try {
                ((ObjectFactoryDestroyable)objectFactory).destroy();
            }
            catch(Exception e) {
                // catch any exception that may occured during destroy() and log it
                LOG.error("exception occurred while destroying ObjectFactory ["+objectFactory+"]", e);
            }
        }

        // clean up Dispatcher itself for this thread
        instance.set(null);

        // clean up DispatcherListeners
        synchronized(Dispatcher.class) {
            if (dispatcherListeners.size() > 0) {
                for (DispatcherListener l : dispatcherListeners) {
                    l.dispatcherDestroyed(this);
                }
            }
        }

        // clean up configuration
    	configurationManager.destroyConfiguration();
    	configurationManager = null;
    
public java.util.MapcreateContextMap(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, org.apache.struts2.dispatcher.mapper.ActionMapping mapping, javax.servlet.ServletContext context)
Create a context map containing all the wrapped request objects

param
request The servlet request
param
response The servlet response
param
mapping The action mapping
param
context The servlet context
return
A map of context objects


        // request map wrapping the http request objects
        Map requestMap = new RequestMap(request);

        // parameters map wrapping the http paraneters.
        Map params = null;
        if (mapping != null) {
            params = mapping.getParams();
        }
        Map requestParams = new HashMap(request.getParameterMap());
        if (params != null) {
            params.putAll(requestParams);
        } else {
            params = requestParams;
        }

        // session map wrapping the http session
        Map session = new SessionMap(request);

        // application map wrapping the ServletContext
        Map application = new ApplicationMap(context);

        Map<String,Object> extraContext = createContextMap(requestMap, params, session, application, request, response, context);
        extraContext.put(ServletActionContext.ACTION_MAPPING, mapping);
        return extraContext;
    
public java.util.HashMapcreateContextMap(java.util.Map requestMap, java.util.Map parameterMap, java.util.Map sessionMap, java.util.Map applicationMap, javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, javax.servlet.ServletContext servletContext)
Merge all application and servlet attributes into a single HashMap to represent the entire Action context.

param
requestMap a Map of all request attributes.
param
parameterMap a Map of all request parameters.
param
sessionMap a Map of all session attributes.
param
applicationMap a Map of all servlet context attributes.
param
request the HttpServletRequest object.
param
response the HttpServletResponse object.
param
servletContext the ServletContextmapping object.
return
a HashMap representing the Action context.

        HashMap<String,Object> extraContext = new HashMap<String,Object>();
        extraContext.put(ActionContext.PARAMETERS, new HashMap(parameterMap));
        extraContext.put(ActionContext.SESSION, sessionMap);
        extraContext.put(ActionContext.APPLICATION, applicationMap);

        Locale locale;
        if (defaultLocale != null) {
            locale = LocalizedTextUtil.localeFromString(defaultLocale, request.getLocale());
        } else {
            locale = request.getLocale();
        }

        extraContext.put(ActionContext.LOCALE, locale);
        //extraContext.put(ActionContext.DEV_MODE, Boolean.valueOf(devMode));

        extraContext.put(StrutsStatics.HTTP_REQUEST, request);
        extraContext.put(StrutsStatics.HTTP_RESPONSE, response);
        extraContext.put(StrutsStatics.SERVLET_CONTEXT, servletContext);

        // helpers to get access to request/session/application scope
        extraContext.put("request", requestMap);
        extraContext.put("session", sessionMap);
        extraContext.put("application", applicationMap);
        extraContext.put("parameters", parameterMap);

        AttributeMap attrMap = new AttributeMap(extraContext);
        extraContext.put("attr", attrMap);

        return extraContext;
    
public com.opensymphony.xwork2.config.ConfigurationManagergetConfigurationManager()
Expose the ConfigurationManager instance.

return
The instance

        return configurationManager;
    
public com.opensymphony.xwork2.inject.ContainergetContainer()
Expose the dependency injection container.

return
Our dependency injection container

        ConfigurationManager mgr = getConfigurationManager();
        if (mgr == null) {
            throw new IllegalStateException("The configuration manager shouldn't be null");
        } else {
            Configuration config = mgr.getConfiguration();
            if (config == null) {
                throw new IllegalStateException("Unable to load configuration");
            } else {
                return config.getContainer();
            }
        }
    
public static org.apache.struts2.dispatcher.DispatchergetInstance()
Provide the dispatcher instance for the current thread.

return
The dispatcher instance


                     
        
        return instance.get();
    
private java.lang.StringgetSaveDir(javax.servlet.ServletContext servletContext)
Return the path to save uploaded files to (this is configurable).

return
the path to save uploaded files to
param
servletContext Our ServletContext

        String saveDir = multipartSaveDir.trim();

        if (saveDir.equals("")) {
            File tempdir = (File) servletContext.getAttribute("javax.servlet.context.tempdir");
            LOG.info("Unable to find 'struts.multipart.saveDir' property setting. Defaulting to javax.servlet.context.tempdir");

            if (tempdir != null) {
                saveDir = tempdir.toString();
            }
        } else {
            File multipartSaveDir = new File(saveDir);

            if (!multipartSaveDir.exists()) {
                multipartSaveDir.mkdir();
            }
        }

        if (LOG.isDebugEnabled()) {
            LOG.debug("saveDir=" + saveDir);
        }

        return saveDir;
    
public voidinit()
Load configurations, including both XML and zero-configuration strategies, and update optional settings, including whether to reload configurations and resource files.


    	if (configurationManager == null) {
    		configurationManager = new ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);
    	}

    	init_DefaultProperties(); // [1]
        init_TraditionalXmlConfigurations(); // [2]
        init_LegacyStrutsProperties(); // [3]
        init_ZeroConfiguration(); // [4]
        init_CustomConfigurationProviders(); // [5]
        init_MethodConfigurationProvider();
        init_FilterInitParameters() ; // [6]
        init_AliasStandardObjects() ; // [7]

        Container container = init_PreloadConfiguration();
        init_CheckConfigurationReloading(container);
        init_CheckWebLogicWorkaround(container);

    
private voidinit_AliasStandardObjects()

        configurationManager.addConfigurationProvider(new BeanSelectionProvider());
    
private voidinit_CheckConfigurationReloading(com.opensymphony.xwork2.inject.Container container)

        FileManager.setReloadingConfigs("true".equals(container.getInstance(String.class,
                StrutsConstants.STRUTS_CONFIGURATION_XML_RELOAD)));
    
private voidinit_CheckWebLogicWorkaround(com.opensymphony.xwork2.inject.Container container)

        // test whether param-access workaround needs to be enabled
        if (servletContext != null && servletContext.getServerInfo() != null
                && servletContext.getServerInfo().indexOf("WebLogic") >= 0) {
            LOG.info("WebLogic server detected. Enabling Struts parameter access work-around.");
            paramsWorkaroundEnabled = true;
        } else {
            paramsWorkaroundEnabled = "true".equals(container.getInstance(String.class,
                    StrutsConstants.STRUTS_DISPATCHER_PARAMETERSWORKAROUND));
        }

        synchronized(Dispatcher.class) {
            if (dispatcherListeners.size() > 0) {
                for (DispatcherListener l : dispatcherListeners) {
                    l.dispatcherInitialized(this);
                }
            }
        }

    
private voidinit_CustomConfigurationProviders()

        String configProvs = initParams.get("configProviders");
        if (configProvs != null) {
            String[] classes = configProvs.split("\\s*[,]\\s*");
            for (String cname : classes) {
                try {
                    Class cls = ClassLoaderUtils.loadClass(cname, this.getClass());
                    ConfigurationProvider prov = (ConfigurationProvider)cls.newInstance();
                    configurationManager.addConfigurationProvider(prov);
                } catch (InstantiationException e) {
                    throw new ConfigurationException("Unable to instantiate provider: "+cname, e);
                } catch (IllegalAccessException e) {
                    throw new ConfigurationException("Unable to access provider: "+cname, e);
                } catch (ClassNotFoundException e) {
                    throw new ConfigurationException("Unable to locate provider class: "+cname, e);
                }
            }
        }
    
private voidinit_DefaultProperties()

        configurationManager.addConfigurationProvider(new DefaultPropertiesProvider());
    
private voidinit_FilterInitParameters()

        configurationManager.addConfigurationProvider(new ConfigurationProvider() {
            public void destroy() {}
            public void init(Configuration configuration) throws ConfigurationException {}
            public void loadPackages() throws ConfigurationException {}
            public boolean needsReload() { return false; }

            public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException {
                props.putAll(initParams);
            }
        });
    
private voidinit_LegacyStrutsProperties()

        configurationManager.addConfigurationProvider(new LegacyPropertiesConfigurationProvider());
    
private voidinit_MethodConfigurationProvider()

        // See https://issues.apache.org/struts/browse/WW-1522
    /*
    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).
	at com.opensymphony.xwork2.inject.ContainerImpl.addInjectorsForMembers(ContainerImpl.java:135)
	at com.opensymphony.xwork2.inject.ContainerImpl.addInjectorsForMethods(ContainerImpl.java:104)
	at com.opensymphony.xwork2.inject.ContainerImpl.injectStatics(ContainerImpl.java:89)
	at com.opensymphony.xwork2.inject.ContainerBuilder.create(ContainerBuilder.java:494)
	at com.opensymphony.xwork2.config.impl.DefaultConfiguration.reload(DefaultConfiguration.java:140)
	at com.opensymphony.xwork2.config.ConfigurationManager.getConfiguration(ConfigurationManager.java:52)
	at org.apache.struts2.dispatcher.Dispatcher.init_MethodConfigurationProvider(Dispatcher.java:347)
	at org.apache.struts2.dispatcher.Dispatcher.init(Dispatcher.java:421)
	at org.apache.struts2.config.MethodConfigurationProviderTest.setUp(MethodConfigurationProviderTest.java:68)
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:40)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:90)
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).
	at com.opensymphony.xwork2.inject.ContainerImpl.createParameterInjector(ContainerImpl.java:217)
	at com.opensymphony.xwork2.inject.ContainerImpl.getParametersInjectors(ContainerImpl.java:207)
	at com.opensymphony.xwork2.inject.ContainerImpl$MethodInjector.<init>(ContainerImpl.java:260)
	at com.opensymphony.xwork2.inject.ContainerImpl$3.create(ContainerImpl.java:108)
	at com.opensymphony.xwork2.inject.ContainerImpl$3.create(ContainerImpl.java:106)
	at com.opensymphony.xwork2.inject.ContainerImpl.addInjectorsForMembers(ContainerImpl.java:132)
	... 26 more

        MethodConfigurationProvider provider = new MethodConfigurationProvider();
        provider.init(configurationManager.getConfiguration());
        provider.loadPackages();
   */
    
private com.opensymphony.xwork2.inject.Containerinit_PreloadConfiguration()

        Configuration config = configurationManager.getConfiguration();
        Container container = config.getContainer();

        boolean reloadi18n = Boolean.valueOf(container.getInstance(String.class, StrutsConstants.STRUTS_I18N_RELOAD));
        LocalizedTextUtil.setReloadBundles(reloadi18n);

        ObjectTypeDeterminer objectTypeDeterminer = container.getInstance(ObjectTypeDeterminer.class);
        ObjectTypeDeterminerFactory.setInstance(objectTypeDeterminer);

        return container;
    
private voidinit_TraditionalXmlConfigurations()

        String configPaths = initParams.get("config");
        if (configPaths == null) {
            configPaths = DEFAULT_CONFIGURATION_PATHS;
        }
        String[] files = configPaths.split("\\s*[,]\\s*");
        for (String file : files) {
            if (file.endsWith(".xml")) {
                if ("xwork.xml".equals(file)) {
                    configurationManager.addConfigurationProvider(new XmlConfigurationProvider(file, false));
                } else {
                    configurationManager.addConfigurationProvider(new StrutsXmlConfigurationProvider(file, false, servletContext));
                }
            } else {
                throw new IllegalArgumentException("Invalid configuration file name");
            }
        }
    
private voidinit_ZeroConfiguration()

        String packages = initParams.get("actionPackages");
        if (packages != null) {
            String[] names = packages.split("\\s*[,]\\s*");
            // Initialize the classloader scanner with the configured packages
            if (names.length > 0) {
                ClasspathConfigurationProvider provider = new ClasspathConfigurationProvider(names);
                provider.setPageLocator(new ServletContextPageLocator(servletContext));
                configurationManager.addConfigurationProvider(provider);
            }
        }
    
public booleanisPortletSupportActive()
Return true, if portlet support is active, false otherwise.

return
true, if portlet support is active, false otherwise.

        return portletSupportActive;
    
public voidprepare(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response)
Prepare a request, including setting the encoding and locale.

param
request The request
param
response The response

        String encoding = null;
        if (defaultEncoding != null) {
            encoding = defaultEncoding;
        }

        Locale locale = null;
        if (defaultLocale != null) {
            locale = LocalizedTextUtil.localeFromString(defaultLocale, request.getLocale());
        }

        if (encoding != null) {
            try {
                request.setCharacterEncoding(encoding);
            } catch (Exception e) {
                LOG.error("Error setting character encoding to '" + encoding + "' - ignoring.", e);
            }
        }

        if (locale != null) {
            response.setLocale(locale);
        }

        if (paramsWorkaroundEnabled) {
            request.getParameter("foo"); // simply read any parameter (existing or not) to "prime" the request
        }
    
public static synchronized voidremoveDispatcherListener(DispatcherListener listener)
Remove a specific dispatcher lifecycle listener.

param
listener The listener

        dispatcherListeners.remove(listener);
    
public voidsendError(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, javax.servlet.ServletContext ctx, int code, java.lang.Exception e)
Send an HTTP error response code.

param
request the HttpServletRequest object.
param
response the HttpServletResponse object.
param
code the HttpServletResponse error code (see {@link javax.servlet.http.HttpServletResponse} for possible error codes).
param
e the Exception that is reported.
param
ctx the ServletContext object.

        if (devMode) {
            response.setContentType("text/html");

            try {
                FreemarkerManager mgr = getContainer().getInstance(FreemarkerManager.class);

                freemarker.template.Configuration config = mgr.getConfiguration(ctx);
                Template template = config.getTemplate("/org/apache/struts2/dispatcher/error.ftl");

                List<Throwable> chain = new ArrayList<Throwable>();
                Throwable cur = e;
                chain.add(cur);
                while ((cur = cur.getCause()) != null) {
                    chain.add(cur);
                }

                HashMap<String,Object> data = new HashMap<String,Object>();
                data.put("exception", e);
                data.put("unknown", Location.UNKNOWN);
                data.put("chain", chain);
                data.put("locator", new Locator());
                template.process(data, response.getWriter());
                response.getWriter().close();
            } catch (Exception exp) {
                try {
                    response.sendError(code, "Unable to show problem report: " + exp);
                } catch (IOException ex) {
                    // we're already sending an error, not much else we can do if more stuff breaks
                }
            }
        } else {
            try {
                // send a http error response to use the servlet defined error handler
                // make the exception availible to the web.xml defined error page
                request.setAttribute("javax.servlet.error.exception", e);

                // for compatibility
                request.setAttribute("javax.servlet.jsp.jspException", e);

                // send the error response
                response.sendError(code, e.getMessage());
            } catch (IOException e1) {
                // we're already sending an error, not much else we can do if more stuff breaks
            }
        }
    
public voidserviceAction(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, javax.servlet.ServletContext context, org.apache.struts2.dispatcher.mapper.ActionMapping mapping)
Load Action class for mapping and invoke the appropriate Action method, or go directly to the Result.

This method first creates the action context from the given parameters, and then loads an ActionProxy from the given action name and namespace. After that, the Action method is executed and output channels through the response object. Actions not found are sent back to the user via the {@link Dispatcher#sendError} method, using the 404 return code. All other errors are reported by throwing a ServletException.

param
request the HttpServletRequest object
param
response the HttpServletResponse object
param
mapping the action mapping object
throws
ServletException when an unknown error occurs (not a 404, but typically something that would end up as a 5xx by the servlet container)
param
context Our ServletContext object


        Map<String, Object> extraContext = createContextMap(request, response, mapping, context);

        // If there was a previous value stack, then create a new copy and pass it in to be used by the new Action
        ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
        if (stack != null) {
            extraContext.put(ActionContext.VALUE_STACK, ValueStackFactory.getFactory().createValueStack(stack));
        }

        String timerKey = "Handling request from Dispatcher";
        try {
            UtilTimerStack.push(timerKey);
            String namespace = mapping.getNamespace();
            String name = mapping.getName();
            String method = mapping.getMethod();

            Configuration config = configurationManager.getConfiguration();
            ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(
                    namespace, name, extraContext, true, false);
            proxy.setMethod(method);
            request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());

            // if the ActionMapping says to go straight to a result, do it!
            if (mapping.getResult() != null) {
                Result result = mapping.getResult();
                result.execute(proxy.getInvocation());
            } else {
                proxy.execute();
            }

            // If there was a previous value stack then set it back onto the request
            if (stack != null) {
                request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);
            }
        } catch (ConfigurationException e) {
            LOG.error("Could not find action or result", e);
            sendError(request, response, context, HttpServletResponse.SC_NOT_FOUND, e);
        } catch (Exception e) {
            throw new ServletException(e);
        } finally {
            UtilTimerStack.pop(timerKey);
        }
    
public voidsetConfigurationManager(com.opensymphony.xwork2.config.ConfigurationManager mgr)
Modify the ConfigurationManager instance

param
mgr The configuration manager

        this.configurationManager = mgr;
    
public static voidsetDefaultEncoding(java.lang.String val)
Modify state of StrutsConstants.STRUTS_I18N_ENCODING setting.

param
val New setting

        defaultEncoding = val;
    
public static voidsetDefaultLocale(java.lang.String val)
Modify state of StrutsConstants.STRUTS_LOCALE setting.

param
val New setting

        defaultLocale = val;
    
public static voidsetDevMode(java.lang.String mode)
Modify state of StrutsConstants.STRUTS_DEVMODE setting.

param
mode New setting

        devMode = "true".equals(mode);
    
public static voidsetInstance(org.apache.struts2.dispatcher.Dispatcher instance)
Store the dispatcher instance for this thread.

param
instance The instance

        Dispatcher.instance.set(instance);

        // Tie the ObjectFactory threadlocal instance to this Dispatcher instance
        if (instance != null) {
            Container cont = instance.getContainer();
            if (cont != null) {
                ObjectFactory.setObjectFactory(cont.getInstance(ObjectFactory.class));
            } else {
                LOG.warn("This dispatcher instance doesn't have a container, so the object factory won't be set.");
            }
        } else {
            ObjectFactory.setObjectFactory(null);
        }
    
public static voidsetMultipartSaveDir(java.lang.String val)
Modify state of StrutsConstants.STRUTS_MULTIPART_SAVEDIR setting.

param
val New setting

        multipartSaveDir = val;
    
public static voidsetPortletSupportActive(boolean portletSupportActive)
Modify the portlet support mode.

param
portletSupportActive true or false

        Dispatcher.portletSupportActive = portletSupportActive;
    
public javax.servlet.http.HttpServletRequestwrapRequest(javax.servlet.http.HttpServletRequest request, javax.servlet.ServletContext servletContext)
Wrap and return the given request or return the original request object.

This method transparently handles multipart data as a wrapped class around the given request. Override this method to handle multipart requests in a special way or to handle other types of requests. Note, {@link org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper} is flexible - look first to that object before overriding this method to handle multipart data.

param
request the HttpServletRequest object.
param
servletContext Our ServletContext object
return
a wrapped request or original request.
see
org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper
throws
java.io.IOException on any error.

        // don't wrap more than once
        if (request instanceof StrutsRequestWrapper) {
            return request;
        }

        String content_type = request.getContentType();
        if (content_type != null && content_type.indexOf("multipart/form-data") != -1) {
            MultiPartRequest multi = getContainer().getInstance(MultiPartRequest.class);
            request = new MultiPartRequestWrapper(multi, request, getSaveDir(servletContext));
        } else {
            request = new StrutsRequestWrapper(request);
        }

        return request;