Merge pull request #571 from apache/WW-5190-match-action-proxy

[WW-5190] Fixes StackOverflowException when dispatching request
This commit is contained in:
Lukasz Lenart
2022-07-11 10:25:16 +02:00
committed by GitHub
14 changed files with 583 additions and 387 deletions
+5 -4
View File
@@ -121,6 +121,11 @@
<artifactId>log4j-jcl</artifactId>
<version>${log4j2.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>${log4j2.version}</version>
</dependency>
<dependency>
<groupId>opensymphony</groupId>
@@ -191,10 +196,6 @@
<stopKey>CTRL+C</stopKey>
<stopPort>8999</stopPort>
<systemProperties>
<systemProperty>
<name>log4j.configuration</name>
<value>file:${basedir}/src/main/resources/log4j2.xml</value>
</systemProperty>
<systemProperty>
<name>slf4j</name>
<value>false</value>
@@ -22,16 +22,13 @@ package org.apache.struts2.showcase.source;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.util.ClassLoaderUtil;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.action.ServletContextAware;
import javax.servlet.ServletContext;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
@@ -42,202 +39,192 @@ import java.util.List;
*/
public class ViewSourceAction extends ActionSupport implements ServletContextAware {
private String page;
private String className;
private String config;
private String page;
private String className;
private String config;
private List pageLines;
private List classLines;
private List configLines;
private List<String> pageLines;
private List<String> classLines;
private List<String> configLines;
private int configLine;
private int padding = 10;
private int configLine;
private int padding = 10;
private ServletContext servletContext;
private ServletContext servletContext;
public String execute() throws MalformedURLException, IOException {
public String execute() throws IOException {
if (page != null) {
if (page != null) {
InputStream in = ClassLoaderUtil.getResourceAsStream(page.substring(page.indexOf("//") + 1), getClass());
page = page.replace("//", "/");
InputStream in = ClassLoaderUtil.getResourceAsStream(page.substring(page.indexOf("//") + 1), getClass());
page = page.replace("//", "/");
if (in == null) {
in = servletContext.getResourceAsStream(page);
while (in == null && page.indexOf('/', 1) > 0) {
page = page.substring(page.indexOf('/', 1));
in = servletContext.getResourceAsStream(page);
}
}
pageLines = read(in, -1);
if (in == null) {
in = servletContext.getResourceAsStream(page);
while (in == null && page.indexOf('/', 1) > 0) {
page = page.substring(page.indexOf('/', 1));
in = servletContext.getResourceAsStream(page);
}
}
pageLines = read(in, -1);
if (in != null) {
in.close();
}
}
if (in != null) {
in.close();
}
}
if (className != null) {
className = "/" + className.replace('.', '/') + ".java";
InputStream in = getClass().getResourceAsStream(className);
if (in == null) {
in = servletContext.getResourceAsStream("/WEB-INF/src" + className);
}
classLines = read(in, -1);
if (className != null) {
className = "/" + className.replace('.', '/') + ".java";
InputStream in = getClass().getResourceAsStream(className);
if (in == null) {
in = servletContext.getResourceAsStream("/WEB-INF/src/java" + className);
}
classLines = read(in, -1);
if (in != null) {
in.close();
}
}
if (in != null) {
in.close();
}
}
final String rootPath = ServletActionContext.getServletContext().getRealPath("/");
final String rootPathUnix = (rootPath != null ? rootPath.replace(File.separator, "/") : null); // Make path Unix-like for comparison (e.g. on Windows)
final String rootPathFileURI = "file://" + rootPathUnix;
final String collapsedRootPathFileURI = rootPathFileURI.replace("//", "/"); // Config string may have been transformed
final String rootPathWarFileURI = "war:file://" + rootPathUnix;
final String collapsedRootPathWarFileURI = rootPathWarFileURI.replace("//", "/"); // Config string may have been transformed
if (config != null && (rootPath == null || config.startsWith(rootPath) ||
config.startsWith(rootPathFileURI) || config.startsWith(collapsedRootPathFileURI) ||
config.startsWith(rootPathWarFileURI) || config.startsWith(collapsedRootPathWarFileURI))) {
int pos = config.lastIndexOf(':');
configLine = Integer.parseInt(config.substring(pos + 1));
config = config.substring(0, pos).replace("//", "/");
configLines = read(new URL(config).openStream(), configLine);
}
return SUCCESS;
}
if (config != null && config.startsWith("file:/")) {
int pos = config.lastIndexOf(':');
configLine = Integer.parseInt(config.substring(pos + 1));
configLines = read(new URL(config.substring(0, pos)).openStream(), configLine);
}
return SUCCESS;
}
/**
* @param className the className to set
*/
public void setClassName(String className) {
if (className != null && className.trim().length() > 0) {
this.className = className;
}
}
/**
* @param className the className to set
*/
public void setClassName(String className) {
if (className != null && className.trim().length() > 0) {
this.className = className;
}
}
/**
* @param config the config to set
*/
public void setConfig(String config) {
if (config != null && config.trim().length() > 0) {
this.config = config;
}
}
/**
* @param config the config to set
*/
public void setConfig(String config) {
if (config != null && config.trim().length() > 0) {
this.config = config;
}
}
/**
* @param page the page to set
*/
public void setPage(String page) {
if (page != null && page.trim().length() > 0) {
this.page = page;
}
}
/**
* @param page the page to set
*/
public void setPage(String page) {
if (page != null && page.trim().length() > 0) {
this.page = page;
}
}
/**
* @param padding the padding to set
*/
public void setPadding(int padding) {
this.padding = padding;
}
/**
* @param padding the padding to set
*/
public void setPadding(int padding) {
this.padding = padding;
}
/**
* @return the classLines
*/
public List getClassLines() {
return classLines;
}
/**
* @return the classLines
*/
public List<String> getClassLines() {
return classLines;
}
/**
* @return the configLines
*/
public List getConfigLines() {
return configLines;
}
/**
* @return the configLines
*/
public List<String> getConfigLines() {
return configLines;
}
/**
* @return the pageLines
*/
public List getPageLines() {
return pageLines;
}
/**
* @return the pageLines
*/
public List<String> getPageLines() {
return pageLines;
}
/**
* @return the className
*/
public String getClassName() {
return className;
}
/**
* @return the className
*/
public String getClassName() {
return className;
}
/**
* @return the config
*/
public String getConfig() {
return config;
}
/**
* @return the config
*/
public String getConfig() {
return config;
}
/**
* @return the page
*/
public String getPage() {
return page;
}
/**
* @return the page
*/
public String getPage() {
return page;
}
/**
* @return the configLine
*/
public int getConfigLine() {
return configLine;
}
/**
* @return the configLine
*/
public int getConfigLine() {
return configLine;
}
/**
* @return the padding
*/
public int getPadding() {
return padding;
}
/**
* @return the padding
*/
public int getPadding() {
return padding;
}
/**
* Reads in a stream, optionally only including the target line number
* and its padding
*
* @param in The input stream
* @param targetLineNumber The target line number, negative to read all
* @return A list of lines
*/
private List read(InputStream in, int targetLineNumber) {
List snippet = null;
if (in != null) {
snippet = new ArrayList();
int startLine = 0;
int endLine = Integer.MAX_VALUE;
if (targetLineNumber > 0) {
startLine = targetLineNumber - padding;
endLine = targetLineNumber + padding;
}
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
/**
* Reads in a stream, optionally only including the target line number
* and its padding
*
* @param in The input stream
* @param targetLineNumber The target line number, negative to read all
* @return A list of lines
*/
private List<String> read(InputStream in, int targetLineNumber) {
List<String> snippet = null;
if (in != null) {
snippet = new ArrayList<>();
int startLine = 0;
int endLine = Integer.MAX_VALUE;
if (targetLineNumber > 0) {
startLine = targetLineNumber - padding;
endLine = targetLineNumber + padding;
}
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
int lineno = 0;
String line;
while ((line = reader.readLine()) != null) {
lineno++;
if (lineno >= startLine && lineno <= endLine) {
snippet.add(line);
}
}
} catch (Exception ex) {
// ignoring as snippet not available isn't a big deal
}
}
return snippet;
}
int lineno = 0;
String line;
while ((line = reader.readLine()) != null) {
lineno++;
if (lineno >= startLine && lineno <= endLine) {
snippet.add(line);
}
}
} catch (Exception ex) {
// ignoring as snippet not available isn't a big deal
}
}
return snippet;
}
public void withServletContext(ServletContext arg0) {
this.servletContext = arg0;
}
public void withServletContext(ServletContext arg0) {
this.servletContext = arg0;
}
}
+4 -5
View File
@@ -22,15 +22,14 @@
<Configuration>
<Appenders>
<Console name="STDOUT" target="SYSTEM_OUT">
<PatternLayout pattern="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/>
<PatternLayout pattern="[%-5p] %C{2} (%F:%L) - %m%n"/>
</Console>
</Appenders>
<Loggers>
<Logger name="com.opensymphony.xwork2" level="info"/>
<Logger name="org.apache.struts2" level="info"/>
<Logger name="org.springframework" level="info"/>
<Root level="info">
<AppenderRef ref="STDOUT"/>
</Root>
<Logger name="org.apache.struts2" level="info"/>
<Logger name="com.opensymphony.xwork2" level="info"/>
</Loggers>
</Configuration>
</Configuration>
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="dispatcher" extends="struts-default" namespace="/dispatcher">
<action name="dispatch">
<result type="dispatcher">
/WEB-INF/dispatcher/dispatch-result.jsp
</result>
</action>
<action name="forward">
<result type="dispatcher">/dispatcher/dispatch.action</result>
</action>
</package>
</struts>
@@ -78,6 +78,8 @@
<include file="struts-async.xml" />
<include file="struts-dispatcher.xml" />
<package name="default" extends="struts-default">
<interceptors>
<interceptor-stack name="crudStack">
@@ -244,6 +244,8 @@
<li><s:url var="url" namespace="/modelDriven" action="modelDriven"/><s:a
href="%{url}">Model Driven</s:a></li>
<li><s:a value="/async/index.html">Async</s:a></li>
<li><s:a value="/dispatcher/dispatch.action">Dispatcher result - dispatch</s:a></li>
<li><s:a value="/dispatcher/forward.action">Dispatcher result - forward</s:a></li>
</ul>
</li>
<li class="dropdown">
@@ -0,0 +1,42 @@
<!--
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
-->
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Struts2 Showcase - Dispatcher result Example</title>
<s:head theme="xhtml"/>
</head>
<body>
<div class="page-header">
<h1>Dispatcher Result Example</h1>
</div>
<div class="container-fluid">
<div class="row">
<div id="dispatcher-result" class="col-md-12">
This page is a result of &quot;dispatching&quot; to it from an action
</div>
</div>
</div>
</body>
</html>
@@ -1,19 +1,19 @@
<!--
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
@@ -26,49 +26,46 @@
<body>
<div class="container-fluid">
<div class="row">
<div class="col-md-12">
<h1>View Sources</h1>
<div class="row">
<div class="col-md-12">
<h1>View Sources</h1>
<ul class="nav nav-tabs" id="codeTab">
<li class="active"><a href="#page">Page</a></li>
<li><a href="#config">Configuration</a></li>
<li><a href="#java">Java Action</a></li>
</ul>
<ul class="nav nav-tabs" id="codeTab">
<li class="active"><a href="#page">Page</a></li>
<li><a href="#config">Configuration</a></li>
<li><a href="#java">Java Action</a></li>
</ul>
<div class="tab-content">
<div class="tab-pane active" id="page">
<h3><s:property default="Unknown page" value="page"/></h3>
<pre class="prettyprint lang-html linenums">
<s:iterator value="pageLines" status="row">
<div class="tab-content">
<div class="tab-pane active" id="page">
<h3><s:property default="Unknown page" value="page"/></h3>
<pre class="prettyprint lang-html linenums"><s:iterator value="pageLines" status="row">
<s:property/></s:iterator>
</pre>
</div>
<div class="tab-pane" id="config">
<h3><s:property default="Unknown configuration" value="config"/></h3>
<pre class="prettyprint lang-xml linenums">
<s:iterator value="configLines" status="row">
</pre>
</div>
<div class="tab-pane" id="config">
<h3><s:property default="Unknown configuration" value="config"/></h3>
<pre class="prettyprint lang-xml linenums"><s:iterator value="configLines" status="row">
<s:property/></s:iterator>
</pre>
</div>
<div class="tab-pane" id="java">
<h3><s:property default="Unknown or unavailable Action class" value="className"/></h3>
<pre class="prettyprint lang-java linenums">
<s:iterator value="classLines" status="row">
</pre>
</div>
<div class="tab-pane" id="java">
<h3><s:property default="Unknown or unavailable Action class" value="className"/></h3>
<pre class="prettyprint lang-java linenums"><s:iterator value="classLines" status="row">
<s:property/></s:iterator>
</pre>
</div>
</div>
</div>
</div>
</pre>
</div>
</div>
</div>
</div>
</div>
<s:script>
$('#codeTab a').click(function (e) {
e.preventDefault();
$(this).tab('show');
})
$('#codeTab a').click(function (e) {
e.preventDefault();
$(this).tab('show');
})
</s:script>
</body>
</html>
+18 -9
View File
@@ -36,16 +36,19 @@
<filter>
<filter-name>struts-prepare</filter-name>
<filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareFilter</filter-class>
<async-supported>true</async-supported>
</filter>
<filter>
<filter-name>struts-execute</filter-name>
<filter-class>org.apache.struts2.dispatcher.filter.StrutsExecuteFilter</filter-class>
<async-supported>true</async-supported>
</filter>
<filter>
<filter-name>sitemesh</filter-name>
<filter-class>com.opensymphony.sitemesh.webapp.SiteMeshFilter</filter-class>
<async-supported>true</async-supported>
</filter>
<filter-mapping>
@@ -56,16 +59,22 @@
<filter-mapping>
<filter-name>struts-prepare</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>FORWARD</dispatcher>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
<filter-mapping>
<filter-name>sitemesh</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>FORWARD</dispatcher>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
<filter-mapping>
<filter-name>struts-execute</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>FORWARD</dispatcher>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
<listener>
@@ -105,6 +114,13 @@
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>strutsServlet</servlet-name>
<servlet-class>org.apache.struts2.dispatcher.servlet.StrutsServlet</servlet-class>
<load-on-startup>2</load-on-startup>
<async-supported>true</async-supported>
</servlet>
<!-- Sitemesh Freemarker and Velocity Decorator Servlets. Shares configuration with Struts.-->
<servlet>
<servlet-name>sitemesh-freemarker</servlet-name>
@@ -113,7 +129,7 @@
<param-name>default_encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<load-on-startup>3</load-on-startup>
</servlet>
<servlet>
@@ -123,14 +139,7 @@
<param-name>default_encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>strutsServlet</servlet-name>
<servlet-class>org.apache.struts2.dispatcher.servlet.StrutsServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
<load-on-startup>4</load-on-startup>
</servlet>
<servlet-mapping>
@@ -0,0 +1,56 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package it.org.apache.struts2.showcase;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.DomElement;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import org.junit.Assert;
import org.junit.Test;
public class DispatcherResultTest {
@Test
public void testDispatchingToJSP() throws Exception {
try (final WebClient webClient = new WebClient()) {
final HtmlPage page = webClient.getPage(ParameterUtils.getBaseUrl() + "/dispatcher/dispatch.action");
DomElement div = page.getElementById("dispatcher-result");
Assert.assertEquals("This page is a result of \"dispatching\" to it from an action", div.asNormalizedText());
}
}
@Test
public void testDispatchingToAction() throws Exception {
try (final WebClient webClient = new WebClient()) {
webClient.getOptions().setThrowExceptionOnFailingStatusCode(false);
final HtmlPage page = webClient.getPage(ParameterUtils.getBaseUrl() + "/dispatcher/forward.action");
//DomElement div = page.getElementById("dispatcher-result");
//Assert.assertEquals("This page is a result of \"dispatching\" to it from an action", div.asNormalizedText());
// support for forwarding to another action is broken on StrutsPrepareFilter/StrutsExecuteFilter
// it only works in StrutsPrepareAndExecuteFilter
// this will be fixed in Struts 6.1.x
Assert.assertEquals(404, page.getWebResponse().getStatusCode());
}
}
}
@@ -83,6 +83,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.regex.Pattern;
@@ -612,20 +613,12 @@ public class Dispatcher {
}
try {
String namespace = mapping.getNamespace();
String name = mapping.getName();
String method = mapping.getMethod();
String actionNamespace = mapping.getNamespace();
String actionName = mapping.getName();
String actionMethod = mapping.getMethod();
ActionProxy proxy;
//check if we are probably in an async resuming
ActionInvocation invocation = ActionContext.getContext().getActionInvocation();
if (invocation == null || invocation.isExecuted()) {
proxy = getContainer().getInstance(ActionProxyFactory.class).createActionProxy(namespace, name, method,
extraContext, true, false);
} else {
proxy = invocation.getProxy();
}
LOG.trace("Processing action, namespace: {}, name: {}, method: {}", actionNamespace, actionName, actionMethod);
ActionProxy proxy = prepareActionProxy(extraContext, actionNamespace, actionName, actionMethod);
request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());
@@ -656,6 +649,36 @@ public class Dispatcher {
}
}
private ActionProxy prepareActionProxy(Map<String, Object> extraContext, String actionNamespace, String actionName, String actionMethod) {
ActionProxy proxy;
//check if we are probably in an async resuming
ActionInvocation invocation = ActionContext.getContext().getActionInvocation();
if (invocation == null || invocation.isExecuted()) {
LOG.trace("Creating a new action, namespace: {}, name: {}, method: {}", actionNamespace, actionName, actionMethod);
proxy = createActionProxy(actionNamespace, actionName, actionMethod, extraContext);
} else {
proxy = invocation.getProxy();
if (isSameAction(proxy, actionNamespace, actionName, actionMethod)) {
LOG.trace("Proxy: {} matches requested action, namespace: {}, name: {}, method: {} - reusing proxy", proxy, actionNamespace, actionName, actionMethod);
} else {
LOG.trace("Proxy: {} doesn't match action namespace: {}, name: {}, method: {} - creating new proxy", proxy, actionNamespace, actionName, actionMethod);
proxy = createActionProxy(actionNamespace, actionName, actionMethod, extraContext);
}
}
return proxy;
}
private ActionProxy createActionProxy(String namespace, String name, String method, Map<String, Object> extraContext) {
ActionProxyFactory actionProxyFactory = getContainer().getInstance(ActionProxyFactory.class);
return actionProxyFactory.createActionProxy(namespace, name, method, extraContext, true, false);
}
private boolean isSameAction(ActionProxy actionProxy, String namespace, String actionName, String method) {
return Objects.equals(namespace, actionProxy.getNamespace())
&& Objects.equals(actionName, actionProxy.getActionName())
&& Objects.equals(method, actionProxy.getMethod());
}
/**
* Performs logging of missing action/result configuration exception
*
@@ -126,18 +126,18 @@ public class StrutsPrepareAndExecuteFilter implements StrutsStatics, Filter {
LOG.trace("Checking if {} is a static resource", uri);
boolean handled = execute.executeStaticResourceRequest(request, response);
if (!handled) {
LOG.trace("Assuming uri {} as a normal action", uri);
LOG.trace("Uri {} is not a static resource, assuming action", uri);
prepare.setEncodingAndLocale(request, response);
prepare.createActionContext(request, response);
prepare.assignDispatcherToThread();
request = prepare.wrapRequest(request);
ActionMapping mapping = prepare.findActionMapping(request, response, true);
HttpServletRequest wrappedRequest = prepare.wrapRequest(request);
ActionMapping mapping = prepare.findActionMapping(wrappedRequest, response, true);
if (mapping == null) {
LOG.trace("Cannot find mapping for {}, passing to other filters", uri);
chain.doFilter(request, response);
} else {
LOG.trace("Found mapping {} for {}", mapping, uri);
execute.executeAction(request, response, mapping);
execute.executeAction(wrappedRequest, response, mapping);
}
}
}
@@ -21,6 +21,7 @@ package org.apache.struts2.dispatcher.mapper;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.config.Configuration;
import com.opensymphony.xwork2.config.ConfigurationManager;
import com.opensymphony.xwork2.config.entities.ActionConfig;
import com.opensymphony.xwork2.config.entities.PackageConfig;
import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.inject.Inject;
@@ -29,7 +30,6 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.RequestUtils;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.StrutsConstants;
import org.apache.struts2.util.PrefixTrie;
@@ -115,7 +115,7 @@ public class DefaultActionMapper implements ActionMapper {
protected boolean allowDynamicMethodCalls = false;
protected boolean allowSlashesInActionNames = false;
protected boolean alwaysSelectFullNamespace = false;
protected PrefixTrie prefixTrie = null;
protected PrefixTrie prefixTrie;
protected Pattern allowedNamespaceNames = Pattern.compile("[a-zA-Z0-9._/\\-]*");
protected String defaultNamespaceName = "/";
@@ -139,39 +139,35 @@ public class DefaultActionMapper implements ActionMapper {
public DefaultActionMapper() {
prefixTrie = new PrefixTrie() {
{
put(METHOD_PREFIX, new ParameterAction() {
public void execute(String key, ActionMapping mapping) {
if (allowDynamicMethodCalls) {
mapping.setMethod(cleanupMethodName(key.substring(METHOD_PREFIX.length())));
}
put(METHOD_PREFIX, (ParameterAction) (key, mapping) -> {
if (allowDynamicMethodCalls) {
mapping.setMethod(cleanupMethodName(key.substring(METHOD_PREFIX.length())));
}
});
put(ACTION_PREFIX, new ParameterAction() {
public void execute(final String key, ActionMapping mapping) {
if (allowActionPrefix) {
String name = key.substring(ACTION_PREFIX.length());
if (allowDynamicMethodCalls) {
int bang = name.indexOf('!');
if (bang != -1) {
String method = cleanupMethodName(name.substring(bang + 1));
mapping.setMethod(method);
name = name.substring(0, bang);
}
put(ACTION_PREFIX, (ParameterAction) (key, mapping) -> {
if (allowActionPrefix) {
String name = key.substring(ACTION_PREFIX.length());
if (allowDynamicMethodCalls) {
int bang = name.indexOf('!');
if (bang != -1) {
String method = cleanupMethodName(name.substring(bang + 1));
mapping.setMethod(method);
name = name.substring(0, bang);
}
String actionName = cleanupActionName(name);
if (allowSlashesInActionNames && !allowActionCrossNamespaceAccess) {
if (actionName.startsWith("/")) {
actionName = actionName.substring(1);
}
}
if (!allowSlashesInActionNames && !allowActionCrossNamespaceAccess) {
if (actionName.lastIndexOf('/') != -1) {
actionName = actionName.substring(actionName.lastIndexOf('/') + 1);
}
}
mapping.setName(actionName);
}
String actionName = cleanupActionName(name);
if (allowSlashesInActionNames && !allowActionCrossNamespaceAccess) {
if (actionName.startsWith("/")) {
actionName = actionName.substring(1);
}
}
if (!allowSlashesInActionNames && !allowActionCrossNamespaceAccess) {
if (actionName.lastIndexOf('/') != -1) {
actionName = actionName.substring(actionName.lastIndexOf('/') + 1);
}
}
mapping.setName(actionName);
}
});
@@ -293,6 +289,7 @@ public class DefaultActionMapper implements ActionMapper {
}
parseNameAndNamespace(uri, mapping, configManager);
extractMethodName(mapping, configManager);
handleSpecialParameters(request, mapping);
return parseActionName(mapping);
}
@@ -324,9 +321,8 @@ public class DefaultActionMapper implements ActionMapper {
public void handleSpecialParameters(HttpServletRequest request, ActionMapping mapping) {
// handle special parameter prefixes.
Set<String> uniqueParameters = new HashSet<>();
Map parameterMap = request.getParameterMap();
for (Object o : parameterMap.keySet()) {
String key = (String) o;
Map<String, String[]> parameterMap = request.getParameterMap();
for (String key : parameterMap.keySet()) {
// Strip off the image button location info, if found
if (key.endsWith(".x") || key.endsWith(".y")) {
@@ -353,33 +349,33 @@ public class DefaultActionMapper implements ActionMapper {
* @param configManager configuration manager
*/
protected void parseNameAndNamespace(String uri, ActionMapping mapping, ConfigurationManager configManager) {
String namespace, name;
String actionNamespace, actionName;
int lastSlash = uri.lastIndexOf('/');
if (lastSlash == -1) {
namespace = "";
name = uri;
actionNamespace = "";
actionName = uri;
} else if (lastSlash == 0) {
// ww-1046, assume it is the root namespace, it will fallback to
// default
// namespace anyway if not found in root namespace.
namespace = "/";
name = uri.substring(lastSlash + 1);
actionNamespace = "/";
actionName = uri.substring(lastSlash + 1);
} else if (alwaysSelectFullNamespace) {
// Simply select the namespace as everything before the last slash
namespace = uri.substring(0, lastSlash);
name = uri.substring(lastSlash + 1);
actionNamespace = uri.substring(0, lastSlash);
actionName = uri.substring(lastSlash + 1);
} else {
// Try to find the namespace in those defined, defaulting to ""
Configuration config = configManager.getConfiguration();
String prefix = uri.substring(0, lastSlash);
namespace = "";
actionNamespace = "";
boolean rootAvailable = false;
// Find the longest matching namespace, defaulting to the default
for (PackageConfig cfg : config.getPackageConfigs().values()) {
String ns = cfg.getNamespace();
if (ns != null && prefix.startsWith(ns) && (prefix.length() == ns.length() || prefix.charAt(ns.length()) == '/')) {
if (ns.length() > namespace.length()) {
namespace = ns;
if (ns.length() > actionNamespace.length()) {
actionNamespace = ns;
}
}
if ("/".equals(ns)) {
@@ -387,23 +383,23 @@ public class DefaultActionMapper implements ActionMapper {
}
}
name = uri.substring(namespace.length() + 1);
actionName = uri.substring(actionNamespace.length() + 1);
// Still none found, use root namespace if found
if (rootAvailable && "".equals(namespace)) {
namespace = "/";
if (rootAvailable && "".equals(actionNamespace)) {
actionNamespace = "/";
}
}
if (!allowSlashesInActionNames) {
int pos = name.lastIndexOf('/');
if (pos > -1 && pos < name.length() - 1) {
name = name.substring(pos + 1);
int pos = actionName.lastIndexOf('/');
if (pos > -1 && pos < actionName.length() - 1) {
actionName = actionName.substring(pos + 1);
}
}
mapping.setNamespace(cleanupNamespaceName(namespace));
mapping.setName(cleanupActionName(name));
mapping.setNamespace(cleanupNamespaceName(actionNamespace));
mapping.setName(cleanupActionName(actionName));
}
/**
@@ -454,6 +450,30 @@ public class DefaultActionMapper implements ActionMapper {
}
}
/**
* Reads defined method name for a given action from configuration
*
* @param mapping current instance of {@link ActionMapping}
* @param configurationManager current instance of {@link ConfigurationManager}
*/
protected void extractMethodName(ActionMapping mapping, ConfigurationManager configurationManager) {
String methodName = null;
for (PackageConfig cfg : configurationManager.getConfiguration().getPackageConfigs().values()) {
if (cfg.getNamespace().equals(mapping.getNamespace())) {
ActionConfig actionCfg = cfg.getActionConfigs().get(mapping.getName());
if (actionCfg != null) {
methodName = actionCfg.getMethodName();
LOG.trace("Using method: {} for action mapping: {}", methodName, mapping);
} else {
LOG.debug("No action config for action mapping: {}", mapping);
}
break;
}
}
mapping.setMethod(methodName);
}
/**
* Drops the extension from the action name, storing it in the mapping for later use
*
@@ -551,7 +571,7 @@ public class DefaultActionMapper implements ActionMapper {
String extension = lookupExtension(mapping.getExtension());
if (extension != null) {
if (extension.length() == 0 || (extension.length() > 0 && uri.indexOf('.' + extension) == -1)) {
if (extension.length() == 0 || uri.indexOf('.' + extension) == -1) {
if (extension.length() > 0) {
uri.append(".").append(extension);
}
@@ -21,6 +21,7 @@ package org.apache.struts2.dispatcher;
import com.mockobjects.dynamic.C;
import com.mockobjects.dynamic.Mock;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.LocalizedTextProvider;
import com.opensymphony.xwork2.ObjectFactory;
import com.opensymphony.xwork2.StubValueStack;
import com.opensymphony.xwork2.config.Configuration;
@@ -30,7 +31,6 @@ import com.opensymphony.xwork2.config.entities.InterceptorStackConfig;
import com.opensymphony.xwork2.config.entities.PackageConfig;
import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.interceptor.Interceptor;
import com.opensymphony.xwork2.LocalizedTextProvider;
import com.opensymphony.xwork2.mock.MockActionInvocation;
import com.opensymphony.xwork2.mock.MockActionProxy;
import org.apache.struts2.ServletActionContext;
@@ -54,24 +54,23 @@ import java.util.Map;
/**
* Test case for Dispatcher.
*
*/
public class DispatcherTest extends StrutsInternalTestCase {
public void testDefaultResurceBundlePropertyLoaded() throws Exception {
public void testDefaultResourceBundlePropertyLoaded() {
LocalizedTextProvider localizedTextProvider = container.getInstance(LocalizedTextProvider.class);
// some i18n messages from xwork-messages.properties
assertEquals(localizedTextProvider.findDefaultText("xwork.error.action.execution", Locale.US),
"Error during Action invocation");
"Error during Action invocation");
// some i18n messages from struts-messages.properties
assertEquals(localizedTextProvider.findDefaultText("struts.messages.error.uploading", Locale.US,
new Object[] { "some error messages" }),
"Error uploading: some error messages");
new Object[]{"some error messages"}),
"Error uploading: some error messages");
}
public void testPrepareSetEncodingProperly() throws Exception {
public void testPrepareSetEncodingProperly() {
HttpServletRequest req = new MockHttpServletRequest();
HttpServletResponse res = new MockHttpServletResponse();
@@ -84,7 +83,7 @@ public class DispatcherTest extends StrutsInternalTestCase {
assertEquals(res.getCharacterEncoding(), "utf-8");
}
public void testEncodingForXMLHttpRequest() throws Exception {
public void testEncodingForXMLHttpRequest() {
// given
MockHttpServletRequest req = new MockHttpServletRequest();
req.addHeader("X-Requested-With", "XMLHttpRequest");
@@ -103,7 +102,7 @@ public class DispatcherTest extends StrutsInternalTestCase {
assertEquals(res.getCharacterEncoding(), "UTF-8");
}
public void testSetEncodingIfDiffer() throws Exception {
public void testSetEncodingIfDiffer() {
// given
Mock mock = new Mock(HttpServletRequest.class);
mock.expectAndReturn("getCharacterEncoding", "utf-8");
@@ -127,7 +126,7 @@ public class DispatcherTest extends StrutsInternalTestCase {
mock.verify();
}
public void testPrepareSetEncodingPropertyWithMultipartRequest() throws Exception {
public void testPrepareSetEncodingPropertyWithMultipartRequest() {
MockHttpServletRequest req = new MockHttpServletRequest();
MockHttpServletResponse res = new MockHttpServletResponse();
@@ -147,7 +146,7 @@ public class DispatcherTest extends StrutsInternalTestCase {
req.setMethod("post");
req.setContentType("multipart/form-data; boundary=asdcvb345asd");
Dispatcher du = initDispatcher(Collections.<String, String>emptyMap());
Dispatcher du = initDispatcher(Collections.emptyMap());
du.prepare(req, res);
HttpServletRequest wrapped = du.wrapRequest(req);
@@ -160,7 +159,7 @@ public class DispatcherTest extends StrutsInternalTestCase {
req.setMethod("post");
req.setContentType("multipart/form-data; boundary=01=23a.bC:D((e)d'z?p+o_r,e-");
Dispatcher du = initDispatcher(Collections.<String, String>emptyMap());
Dispatcher du = initDispatcher(Collections.emptyMap());
du.prepare(req, res);
HttpServletRequest wrapped = du.wrapRequest(req);
@@ -173,46 +172,46 @@ public class DispatcherTest extends StrutsInternalTestCase {
req.setMethod("post");
req.setContentType("multipart/form-data; boundary=01=2;3a.bC:D((e)d'z?p+o_r,e-");
Dispatcher du = initDispatcher(Collections.<String, String>emptyMap());
Dispatcher du = initDispatcher(Collections.emptyMap());
du.prepare(req, res);
HttpServletRequest wrapped = du.wrapRequest(req);
assertFalse(wrapped instanceof MultiPartRequestWrapper);
}
public void testDispatcherListener() throws Exception {
public void testDispatcherListener() {
final DispatcherListenerState state = new DispatcherListenerState();
final DispatcherListenerState state = new DispatcherListenerState();
Dispatcher.addDispatcherListener(new DispatcherListener() {
public void dispatcherDestroyed(Dispatcher du) {
state.isDestroyed = true;
}
public void dispatcherInitialized(Dispatcher du) {
state.isInitialized = true;
}
});
Dispatcher.addDispatcherListener(new DispatcherListener() {
public void dispatcherDestroyed(Dispatcher du) {
state.isDestroyed = true;
}
public void dispatcherInitialized(Dispatcher du) {
state.isInitialized = true;
}
});
assertFalse(state.isDestroyed);
assertFalse(state.isInitialized);
assertFalse(state.isDestroyed);
assertFalse(state.isInitialized);
Dispatcher du = initDispatcher(new HashMap<String, String>() );
Dispatcher du = initDispatcher(new HashMap<>());
assertTrue(state.isInitialized);
assertTrue(state.isInitialized);
du.cleanup();
du.cleanup();
assertTrue(state.isDestroyed);
assertTrue(state.isDestroyed);
}
public void testConfigurationManager() {
Dispatcher du;
final InternalConfigurationManager configurationManager = new InternalConfigurationManager(Container.DEFAULT_NAME);
try {
du = new MockDispatcher(new MockServletContext(), new HashMap<String, String>(), configurationManager);
du.init();
Dispatcher du;
final InternalConfigurationManager configurationManager = new InternalConfigurationManager(Container.DEFAULT_NAME);
try {
du = new MockDispatcher(new MockServletContext(), new HashMap<>(), configurationManager);
du.init();
Dispatcher.setInstance(du);
assertFalse(configurationManager.destroyConfiguration);
@@ -221,18 +220,17 @@ public class DispatcherTest extends StrutsInternalTestCase {
assertTrue(configurationManager.destroyConfiguration);
}
finally {
Dispatcher.setInstance(null);
}
} finally {
Dispatcher.setInstance(null);
}
}
public void testInitLoadsDefaultConfig() {
Dispatcher du = new Dispatcher(new MockServletContext(), new HashMap<String, String>());
Dispatcher du = new Dispatcher(new MockServletContext(), new HashMap<>());
du.init();
Configuration config = du.getConfigurationManager().getConfiguration();
assertNotNull(config);
HashSet<String> expected = new HashSet<String>();
HashSet<String> expected = new HashSet<>();
expected.add("struts-default.xml");
expected.add("struts-plugin.xml");
expected.add("struts.xml");
@@ -243,17 +241,17 @@ public class DispatcherTest extends StrutsInternalTestCase {
assertTrue(packageConfig.getResultTypeConfigs().size() > 0);
}
public void testObjectFactoryDestroy() throws Exception {
public void testObjectFactoryDestroy() {
ConfigurationManager cm = new ConfigurationManager(Container.DEFAULT_NAME);
Dispatcher du = new MockDispatcher(new MockServletContext(), new HashMap<String, String>(), cm);
Dispatcher du = new MockDispatcher(new MockServletContext(), new HashMap<>(), cm);
Mock mockConfiguration = new Mock(Configuration.class);
cm.setConfiguration((Configuration)mockConfiguration.proxy());
cm.setConfiguration((Configuration) mockConfiguration.proxy());
Mock mockContainer = new Mock(Container.class);
String reloadConfigs = container.getInstance(String.class, StrutsConstants.STRUTS_CONFIGURATION_XML_RELOAD);
mockContainer.expectAndReturn("getInstance", C.args(C.eq(String.class), C.eq(StrutsConstants.STRUTS_CONFIGURATION_XML_RELOAD)),
reloadConfigs);
reloadConfigs);
final InnerDestroyableObjectFactory destroyedObjectFactory = new InnerDestroyableObjectFactory();
destroyedObjectFactory.setContainer((Container) mockContainer.proxy());
mockContainer.expectAndReturn("getInstance", C.args(C.eq(ObjectFactory.class)), destroyedObjectFactory);
@@ -271,7 +269,7 @@ public class DispatcherTest extends StrutsInternalTestCase {
mockContainer.verify();
}
public void testInterceptorDestroy() throws Exception {
public void testInterceptorDestroy() {
Mock mockInterceptor = new Mock(Interceptor.class);
mockInterceptor.matchAndReturn("hashCode", 0);
mockInterceptor.expect("destroy");
@@ -282,14 +280,14 @@ public class DispatcherTest extends StrutsInternalTestCase {
PackageConfig packageConfig = new PackageConfig.Builder("test").addInterceptorStackConfig(isc).build();
Map<String, PackageConfig> packageConfigs = new HashMap<String, PackageConfig>();
Map<String, PackageConfig> packageConfigs = new HashMap<>();
packageConfigs.put("test", packageConfig);
Mock mockContainer = new Mock(Container.class);
mockContainer.matchAndReturn("getInstance", C.args(C.eq(ObjectFactory.class)), new ObjectFactory());
String reloadConfigs = container.getInstance(String.class, StrutsConstants.STRUTS_CONFIGURATION_XML_RELOAD);
mockContainer.expectAndReturn("getInstance", C.args(C.eq(String.class), C.eq(StrutsConstants.STRUTS_CONFIGURATION_XML_RELOAD)),
reloadConfigs);
reloadConfigs);
Mock mockConfiguration = new Mock(Configuration.class);
mockConfiguration.matchAndReturn("getPackageConfigs", packageConfigs);
@@ -299,7 +297,7 @@ public class DispatcherTest extends StrutsInternalTestCase {
ConfigurationManager configurationManager = new ConfigurationManager(Container.DEFAULT_NAME);
configurationManager.setConfiguration((Configuration) mockConfiguration.proxy());
Dispatcher dispatcher = new MockDispatcher(new MockServletContext(), new HashMap<String, String>(), configurationManager);
Dispatcher dispatcher = new MockDispatcher(new MockServletContext(), new HashMap<>(), configurationManager);
dispatcher.init();
dispatcher.cleanup();
@@ -308,22 +306,22 @@ public class DispatcherTest extends StrutsInternalTestCase {
mockConfiguration.verify();
}
public void testMultipartSupportEnabledByDefault() throws Exception {
public void testMultipartSupportEnabledByDefault() {
HttpServletRequest req = new MockHttpServletRequest();
HttpServletResponse res = new MockHttpServletResponse();
Dispatcher du = initDispatcher(Collections.<String, String>emptyMap());
Dispatcher du = initDispatcher(Collections.emptyMap());
du.prepare(req, res);
assertTrue(du.isMultipartSupportEnabled(req));
}
public void testIsMultipartRequest() throws Exception {
public void testIsMultipartRequest() {
MockHttpServletRequest req = new MockHttpServletRequest();
HttpServletResponse res = new MockHttpServletResponse();
req.setMethod("POST");
Dispatcher du = initDispatcher(Collections.<String, String>emptyMap());
Dispatcher du = initDispatcher(Collections.emptyMap());
du.prepare(req, res);
req.setContentType("multipart/form-data");
@@ -370,7 +368,7 @@ public class DispatcherTest extends StrutsInternalTestCase {
}
public void testServiceActionResumePreviousProxy() throws Exception {
Dispatcher du = initDispatcher(Collections.<String, String>emptyMap());
Dispatcher du = initDispatcher(Collections.emptyMap());
MockActionInvocation mai = new MockActionInvocation();
ActionContext.getContext().withActionInvocation(mai);
@@ -393,14 +391,41 @@ public class DispatcherTest extends StrutsInternalTestCase {
assertTrue("should execute previous proxy", actionProxy.isExecutedCalled());
}
public void testServiceActionCreatesNewProxyIfDifferentMapping() throws Exception {
Dispatcher du = initDispatcher(Collections.emptyMap());
container.inject(du);
MockActionInvocation mai = new MockActionInvocation();
ActionContext.getContext().withActionInvocation(mai);
MockActionProxy previousActionProxy = new MockActionProxy();
previousActionProxy.setActionName("first-action");
previousActionProxy.setNamespace("namespace1");
previousActionProxy.setInvocation(mai);
mai.setProxy(previousActionProxy);
mai.setStack(new StubValueStack());
HttpServletRequest request = new MockHttpServletRequest();
request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, mai.getStack());
HttpServletResponse response = new MockHttpServletResponse();
assertFalse(previousActionProxy.isExecutedCalled());
ActionMapping newActionMapping = new ActionMapping();
newActionMapping.setName("hello");
du.serviceAction(request, response, newActionMapping);
assertFalse(previousActionProxy.isExecutedCalled());
}
/**
* Verify proper default (true) handleExceptionState for Dispatcher and that
* it properly reflects a manually configured change to false.
*
* @throws Exception
*/
public void testHandleException() throws Exception {
Dispatcher du = initDispatcher(new HashMap<String, String>());
public void testHandleException() {
Dispatcher du = initDispatcher(new HashMap<>());
assertTrue("Default Dispatcher handleException state not true ?", du.isHandleException());
Dispatcher du2 = initDispatcher(new HashMap<String, String>() {{
@@ -412,11 +437,9 @@ public class DispatcherTest extends StrutsInternalTestCase {
/**
* Verify proper default (false) devMode for Dispatcher and that
* it properly reflects a manually configured change to true.
*
* @throws Exception
*/
public void testDevMode() throws Exception {
Dispatcher du = initDispatcher(new HashMap<String, String>());
public void testDevMode() {
Dispatcher du = initDispatcher(new HashMap<>());
assertFalse("Default Dispatcher devMode state not false ?", du.isDevMode());
Dispatcher du2 = initDispatcher(new HashMap<String, String>() {{
@@ -425,7 +448,7 @@ public class DispatcherTest extends StrutsInternalTestCase {
assertTrue("Modified Dispatcher devMode state not true ?", du2.isDevMode());
}
public void testGetLocale_With_DefaultLocale_FromConfiguration() throws Exception {
public void testGetLocale_With_DefaultLocale_FromConfiguration() {
// Given
Mock mock = new Mock(HttpServletRequest.class);
MockHttpSession mockHttpSession = new MockHttpSession();
@@ -451,7 +474,7 @@ public class DispatcherTest extends StrutsInternalTestCase {
mock.verify();
}
public void testGetLocale_With_DefaultLocale_fr_CA() throws Exception {
public void testGetLocale_With_DefaultLocale_fr_CA() {
// Given
Mock mock = new Mock(HttpServletRequest.class);
MockHttpSession mockHttpSession = new MockHttpSession();
@@ -477,7 +500,7 @@ public class DispatcherTest extends StrutsInternalTestCase {
mock.verify();
}
public void testGetLocale_With_BadDefaultLocale_RequestLocale_en_UK() throws Exception {
public void testGetLocale_With_BadDefaultLocale_RequestLocale_en_UK() {
// Given
Mock mock = new Mock(HttpServletRequest.class);
MockHttpSession mockHttpSession = new MockHttpSession();
@@ -505,7 +528,7 @@ public class DispatcherTest extends StrutsInternalTestCase {
mock.verify();
}
public void testGetLocale_With_BadDefaultLocale_And_RuntimeException() throws Exception {
public void testGetLocale_With_BadDefaultLocale_And_RuntimeException() {
// Given
Mock mock = new Mock(HttpServletRequest.class);
MockHttpSession mockHttpSession = new MockHttpSession();
@@ -533,7 +556,7 @@ public class DispatcherTest extends StrutsInternalTestCase {
mock.verify();
}
public void testGetLocale_With_NullDefaultLocale() throws Exception {
public void testGetLocale_With_NullDefaultLocale() {
// Given
Mock mock = new Mock(HttpServletRequest.class);
MockHttpSession mockHttpSession = new MockHttpSession();
@@ -563,7 +586,7 @@ public class DispatcherTest extends StrutsInternalTestCase {
mock.verify();
}
public void testGetLocale_With_NullDefaultLocale_And_RuntimeException() throws Exception {
public void testGetLocale_With_NullDefaultLocale_And_RuntimeException() {
// Given
Mock mock = new Mock(HttpServletRequest.class);
MockHttpSession mockHttpSession = new MockHttpSession();
@@ -595,19 +618,14 @@ public class DispatcherTest extends StrutsInternalTestCase {
/**
* Create a test context Map from a Dispatcher instance.
*
* <p>
* The method directly calls getParameterMap() and getSession(true) on the HttpServletRequest.
*
* <p>
* The method indirectly calls getLocale(request) on the Dispatcher instance, allowing a test of that code path.
* The derived Struts Dispatcher Locale can be retrieved from the Map afterwards.
*
* @param dispatcher
* @param request
* @param response
* @return
*/
protected static Map<String, Object> createTestContextMap(Dispatcher dispatcher,
HttpServletRequest request, HttpServletResponse response) {
HttpServletRequest request, HttpServletResponse response) {
if (dispatcher == null) {
throw new IllegalArgumentException("Cannot create a test ContextMap from a null Dispatcher");
}
@@ -619,31 +637,31 @@ public class DispatcherTest extends StrutsInternalTestCase {
}
return dispatcher.createContextMap(new RequestMap(request),
HttpParameters.create(request.getParameterMap()).build(),
new SessionMap(request),
new ApplicationMap(request.getSession(true).getServletContext()),
request,
response);
HttpParameters.create(request.getParameterMap()).build(),
new SessionMap(request),
new ApplicationMap(request.getSession(true).getServletContext()),
request,
response);
}
class InternalConfigurationManager extends ConfigurationManager {
public boolean destroyConfiguration = false;
static class InternalConfigurationManager extends ConfigurationManager {
public boolean destroyConfiguration = false;
public InternalConfigurationManager(String name) {
super(name);
}
@Override
public synchronized void destroyConfiguration() {
super.destroyConfiguration();
destroyConfiguration = true;
}
public synchronized void destroyConfiguration() {
super.destroyConfiguration();
destroyConfiguration = true;
}
}
class DispatcherListenerState {
public boolean isInitialized = false;
public boolean isDestroyed = false;
static class DispatcherListenerState {
public boolean isInitialized = false;
public boolean isDestroyed = false;
}
public static class InnerDestroyableObjectFactory extends ObjectFactory implements ObjectFactoryDestroyable {