Compare commits

...

73 Commits

Author SHA1 Message Date
Lukasz Lenart 941d88d115 [maven-release-plugin] prepare release STRUTS_6_0_3 2022-09-02 14:41:49 +02:00
Lukasz Lenart 8c4a90f6e3 Merge pull request #593 from apache/WW-5218-disable
[WW-5218] Allows to disable CSP related interceptors
2022-09-02 10:19:48 +02:00
Lukasz Lenart 16c2948f1a WW-5218 Allows to disable CSP related interceptors 2022-09-01 08:15:48 +02:00
Lukasz Lenart 58a76162a6 Merge pull request #592 from apache/upgrade-spring
[WW-5212] Upgrades to Spring 5.3.22
2022-08-29 08:29:17 +02:00
Lukasz Lenart b5e802174a WW-5212 Upgrades to Spring 5.3.22 2022-08-29 08:03:02 +02:00
Lukasz Lenart cdb70e6bed [maven-release-plugin] prepare for next development iteration 2022-08-25 07:39:48 +02:00
Lukasz Lenart 1380802b75 [maven-release-plugin] prepare release STRUTS_6_0_2 2022-08-25 07:39:40 +02:00
Lukasz Lenart 253d8558e5 Merge pull request #590 from apache/WW-5215-csp-session
[WW-5215] Checks if session was already created before applying CSP settings
2022-08-25 07:30:17 +02:00
Lukasz Lenart a97ee9e9cb Merge pull request #591 from sepe81/patch-1
Update maven-enforcer-plugin to 3.1.0
2022-08-25 07:30:04 +02:00
Sebastian Peters 23969abc19 Update maven-enforcer-plugin to 3.1.0 2022-08-24 22:54:37 +02:00
Lukasz Lenart 2707bf0489 WW-5215 Explicitly creates session in test 2022-08-23 20:50:39 +02:00
Lukasz Lenart 74d4e23716 WW-5215 Checks is session was already created before applying CSP settings 2022-08-23 20:31:39 +02:00
Yasser Zamani 2a03047e9e Merge pull request #573 from JCgH4164838Gh792C124B5/localS2_WW5173_Upd1
[WW-5173] - Attempt to fix DI behaviour for custom cache factories
2022-08-21 13:33:04 +04:30
Lukasz Lenart b1c1be6e40 [maven-release-plugin] prepare for next development iteration 2022-08-12 17:26:55 +02:00
Lukasz Lenart 6536dc65d8 [maven-release-plugin] prepare release STRUTS_6_0_1 2022-08-12 17:26:47 +02:00
Lukasz Lenart 915386816b Merge pull request #588 from apache/WW-5203-lazy-builder
[WW-5203] Re-builds policy string on each call
2022-08-11 13:43:18 +02:00
Lukasz Lenart 80c2de9e92 Merge pull request #586 from apache/WW-5207-asm-9
[WW-5207] Uses ASM 9 by default
2022-08-11 13:43:01 +02:00
Lukasz Lenart b62e583646 WW-5203 Re-builds policy string on each call 2022-08-09 15:19:02 +02:00
Lukasz Lenart 8b359383a1 Merge pull request #587 from sepe81/update-maven-surefire-plugin-to-3.0.0-M7
Update maven-surefire-plugin to 3.0.0-M7
2022-08-09 15:01:08 +02:00
Lukasz Lenart 11e716f979 Merge pull request #584 from sepe81/update-travis
Add openjdk17 to build, remove oraclejdk9 (EOL) and switch from oraclejdk to openjdk to fix Travis CI build
2022-08-09 15:00:34 +02:00
Sebastian Peters 71c7064f29 Update maven-surefire-plugin to 3.0.0-M7
see https://github.com/apache/maven-surefire/releases/tag/surefire-3.0.0-M7
2022-08-08 22:27:47 +02:00
Sebastian Peters 09472808aa Add openjdk17 to build, remove oraclejdk9 (EOL)
and switch from oraclejdk to openjdk
to fix Travis CI build
2022-08-08 22:20:53 +02:00
Sebastian Peters d119ba8e13 Migrate vom ubuntu trusty to jammy
see https://docs.travis-ci.com/user/reference/jammy/
2022-08-08 22:20:52 +02:00
Lukasz Lenart 86b45e96fc WW-5207 Uses ASM 9 by default 2022-08-08 17:00:42 +02:00
Lukasz Lenart 3c9465b857 Merge pull request #580 from rproserpio/patch-1
[WW-5205] - Reverting injection refactor of internal bean
2022-08-08 16:37:43 +02:00
Lukasz Lenart 0c73cf2e05 Merge pull request #583 from sepe81/update-hibernate-validator-to.6.2.x
WW-5208 Update hibernate-validator to 6.2.4
2022-08-07 10:47:38 +02:00
Lukasz Lenart 6785997412 Merge pull request #582 from sepe81/update-mvn-to-3.8.6
Update maven wrapper to 3.8.6
2022-08-07 10:45:12 +02:00
Sebastian Peters 5a7fce066f WW-5208 Update hibernate-validator to 6.2.4
b/c 6.1 has reached its end-of-life according to
https://hibernate.org/validator/releases/6.1/

6.2 is compatible with the given Java and Jakarta
versions and supports `javax.validation.*` according to
https://hibernate.org/validator/releases/6.2/
2022-08-06 14:33:00 +02:00
Sebastian Peters 4fbada4cd9 Update maven wrapper to 3.8.6 2022-08-06 14:09:54 +02:00
JCgH4164838Gh792C124B5 15bbf0ef1d Updated commit:
- Incorporate changes from Y. Zamani's PR #581 manually, which appears to
fix the previous issue that prevented customized cache factory
implementations from being used (tested with sample app).
Credit goes to Yasser Zamani for the fixes.
- Updated unit tests and slight modifications to the changes from the
PR #581.
2022-07-31 18:26:48 -04:00
rproserpio 3119eb65db Reverting injection refactor of internal bean
In commit e9d0a5518c ContentTypeHandlerManager has been changed from setter-injected to constructor-injected.
However, at least with spring as the object factory, that bean was an internal bean not available from the factory, injected post construction.
In the 6.0.0 implementation ContentTypeInterceptor cannot be instantiated since its dependency cannot be resolved:
```
org.springframework.beans.factory.UnsatisfiedDependencyException

Error creating bean with name 'org.apache.struts2.rest.ContentTypeInterceptor': Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.apache.struts2.rest.ContentTypeHandlerManager' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
```
This commit attempts to revert that change.
2022-07-25 01:11:46 +02:00
Lukasz Lenart de3dabbe44 Merge pull request #579 from apache/WW-5204-ognl-3-3-3
[WW-5204] Upgrades OGNL to version 3.3.3
2022-07-11 10:49:38 +02:00
Lukasz Lenart 82f7904206 WW-5204 Upgrades OGNL to version 3.3.3 2022-07-11 10:28:58 +02:00
Lukasz Lenart 42e3da550f Merge pull request #577 from apache/WW-5192-radio
[WW-5192] Fixes broken radio tag when using with enums
2022-07-11 10:27:06 +02:00
Lukasz Lenart f3cb89202d Merge pull request #571 from apache/WW-5190-match-action-proxy
[WW-5190] Fixes StackOverflowException when dispatching request
2022-07-11 10:25:16 +02:00
Lukasz Lenart 836a27692e Merge pull request #578 from sepe81/update-jasperreports
WW-5202 Update jasperreports to 6.19.1 and exclude optional itext from jasperreports
2022-07-08 17:19:41 +02:00
Sebastian Peters 6312572331 Exclude itext from jasperreports
b/c it's an optional dependency
and version 2.1.7.js9 is missing on maven central
2022-07-08 14:09:47 +02:00
Sebastian Peters e7db7ade62 Update jasperreports to 6.19.1 2022-07-08 14:08:46 +02:00
Lukasz Lenart b77c2f476a Uses different port for Jetty during integration testing to avoid clash 2022-07-08 08:21:16 +02:00
Lukasz Lenart e6c1cec7f8 WW-5192 Fixes brok radio tag when using with enums 2022-07-07 21:34:43 +02:00
Lukasz Lenart a781b5a27b Merge pull request #576 from apache/hiberante-validator
[WW-5193] Uses proper org.hibernate.validator groupId and upgrade to version 6.1.3.Final
2022-07-07 15:11:35 +02:00
Lukasz Lenart b71732fa14 WW-5193 Uses proper org.hibernate.validator groupId and upgrade to version 6.1.3.Final 2022-07-07 14:55:19 +02:00
Lukasz Lenart 3f79d64e6a Merge pull request #575 from apache/upgrade-log4j
[WW-5201] Upgrades log4j2 to version 2.18.0
2022-07-07 14:54:08 +02:00
Lukasz Lenart 039fd9f4ee WW-5201 Upgrades log4j2 to version 2.18.0 2022-07-07 14:12:12 +02:00
Lukasz Lenart 393d5be50c Merge pull request #574 from tobiasstadler/WW-5197
WW-5197 Adds support for java.sql.Date
2022-07-07 14:06:22 +02:00
JCgH4164838Gh792C124B5 24e279b165 Update:
- Added arbitrary code coverage test.
2022-07-03 17:37:58 -04:00
Lukasz Lenart cb59974903 WW-5190 Marks all Struts related filters with <async-supported/> 2022-06-27 09:44:51 +02:00
Lukasz Lenart ecfdee137d WW-5190 Disables test which won't pass without refactoring the prepare and execute filters 2022-06-27 09:37:17 +02:00
Lukasz Lenart 8c37b1523f WW-5190 Uses proper order of servlets and filters 2022-06-22 07:58:12 +02:00
Lukasz Lenart 62b83021a2 Merge pull request #569 from apache/WW-5188-since-6
[WW-5188] Replaces since 2.6 with 6.0
2022-06-21 10:39:47 +02:00
Tobias Stadler f3f2d52a1c Added test case for java.sql.Date 2022-06-21 10:23:17 +02:00
Tobias Stadler 61ded9a3e7 WW-5197 Adds support for java.sql.Date 2022-06-21 10:09:31 +02:00
JCgH4164838Gh792C124B5 963b473e6c Initial commit:
- Update to use consistent name for expression and BeanInfo factories in
default configuration implementation and provider.
- Add inject annotation to the non-default constructor to ensure the proper
constructor is called during DI for containers.
- Use fully-qualified factory names for defaults in default.properties.
- Ensure unique types for each cache factory in struts-default.xml
2022-06-20 17:07:57 -04:00
Lukasz Lenart d639b59f6d Merge branch 'master' into WW-5190-match-action-proxy 2022-06-18 09:45:20 +02:00
Lukasz Lenart 8e468a368e WW-5190 Defines two examples with Dispatcher result and integration tests 2022-06-18 09:45:10 +02:00
Lukasz Lenart ebe1b80ed2 WW-5190 Adds a new test to cover new logic of comparing ActionProxies 2022-06-18 08:12:29 +02:00
Lukasz Lenart 957292ecdb Simplifies logging pattern in Showcase app 2022-06-18 07:27:32 +02:00
Lukasz Lenart 06290cef91 Fixes showing configuration of an action in Showcase app 2022-06-18 07:26:13 +02:00
Lukasz Lenart a6a4529503 Fixes logging in Showcase app 2022-06-18 07:25:26 +02:00
Yasser Zamani 64d054221f Merge pull request #566 from apache/WW-5185-struts-resource
[WW-5185] Reintroduces proper implementation of Tiles resource to support wildcard matching of Tiles definitions
2022-06-17 11:16:38 +04:30
Yasser Zamani dc4e403d30 Merge pull request #570 from apache/WW-5187-velocity
[WW-5187] Comments out Velocity based PageFilter
2022-06-17 11:15:21 +04:30
Yasser Zamani e6bc93daf2 Merge pull request #572 from apache/WW-5191-textarea-max-length
[WW-5191] Fixes placement of maxLength and minLength attributes in <s:textarea/> tag
2022-06-17 11:13:09 +04:30
Lukasz Lenart 0e4d5962e1 WW-5191 Fixes placement of maxLength and minLength attributes in <s:textarea/> tag 2022-06-15 15:18:46 +02:00
Lukasz Lenart 69102e9075 WW-5190 Uses wrapped request only when processing request by Struts 2022-06-15 11:07:53 +02:00
Lukasz Lenart 1c2b491a27 WW-5190 Reuses action proxy if namespace, name and method are match 2022-06-15 11:07:29 +02:00
Lukasz Lenart 5af840b160 WW-5190 Extracts method name from config for known action name 2022-06-15 11:06:45 +02:00
Lukasz Lenart b6b74a46ea WW-5188 Replaces since 2.6 with 6.0 2022-06-14 13:34:27 +02:00
Lukasz Lenart fc2aebd10a WW-5187 Comments out Velocity based PageFilter 2022-06-14 13:17:19 +02:00
Lukasz Lenart ba041e1ee6 Merge pull request #568 from tobiasstadler/master
Add async and velocity plugin to bom
2022-06-14 11:43:49 +02:00
Tobias Stadler 0c3534e8b4 Added async and velocity plugin to bom 2022-06-14 09:53:10 +02:00
Lukasz Lenart 9a2996f9b7 Uses proper Servlet API 3.1 headers 2022-06-10 16:06:27 +02:00
Lukasz Lenart 3825229f44 WW-5185 Reintroduces proper implementation of Tiles resource to support wildcard matching of Tiles definitions
It was broken after refactoring related to WW-5011
2022-06-10 16:06:10 +02:00
Lukasz Lenart 4b3ee53f93 [maven-release-plugin] prepare for next development iteration 2022-06-02 09:11:11 +02:00
118 changed files with 1647 additions and 1097 deletions
+1 -1
View File
@@ -14,5 +14,5 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.5/apache-maven-3.8.5-bin.zip
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.6/apache-maven-3.8.6-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar
+5 -5
View File
@@ -1,10 +1,10 @@
dist: trusty
dist: jammy
language: java
sudo: false
jdk:
- oraclejdk9
- oraclejdk11
- openjdk11
- openjdk17
install: true
@@ -13,8 +13,8 @@ global:
- secure: iI7IpfDtS+LUyS2yNuRCR3KelNyvBHuoMQ3gb1UNmR5SSL7jO/p3olQWrQROs28FJ+dpE3lHyIjoHrebKQGJHHAgTG2XWxn+G3fDsf+wSSFSLoDGj0o2SgGXooBbR2dccnNZHCyQaOyE2cIPWaOxrQZFE4No70LQB4mrP/gdkoc=
matrix:
include:
- jdk: oraclejdk8
env: STRUTS_IT=true # do integration tests and coverage reports when jdk 9 and 11 tests prospered
- jdk: openjdk8
env: STRUTS_IT=true # do integration tests and coverage reports when jdk 11 and 17 tests prospered
script:
- if [ "$STRUTS_IT" == "true" ]; then
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>6.0.0</version>
<version>6.0.3</version>
</parent>
<artifactId>struts2-apps</artifactId>
<packaging>pom</packaging>
+2 -2
View File
@@ -24,12 +24,12 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-apps</artifactId>
<version>6.0.0</version>
<version>6.0.3</version>
</parent>
<artifactId>struts2-rest-showcase</artifactId>
<packaging>war</packaging>
<version>6.0.0</version>
<version>6.0.3</version>
<name>Struts 2 Rest Showcase Webapp</name>
<description>Struts 2 Rest Showcase Example</description>
@@ -19,13 +19,14 @@
* under the License.
*/
-->
<web-app id="starter" version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<display-name>Struts 2 Rest Example</display-name>
<!-- Filters -->
<!-- START SNIPPET: filter -->
<filter>
+14 -7
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-apps</artifactId>
<version>6.0.0</version>
<version>6.0.3</version>
</parent>
<artifactId>struts2-showcase</artifactId>
@@ -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>
@@ -150,9 +155,9 @@
<!-- BeanValidation Example -->
<dependency>
<groupId>org.hibernate</groupId>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.1.2.Final</version>
<version>${hibernate-validator.version}</version>
</dependency>
</dependencies>
@@ -167,6 +172,9 @@
<includes>
<include>it.org.apache.struts2.showcase.*Test</include>
</includes>
<systemPropertyVariables>
<http.port>8090</http.port>
</systemPropertyVariables>
</configuration>
<executions>
<execution>
@@ -191,15 +199,14 @@
<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>
</systemProperty>
</systemProperties>
<httpConnector>
<port>8090</port>
</httpConnector>
<scanIntervalSeconds>10</scanIntervalSeconds>
<webAppSourceDirectory>${basedir}/src/main/webapp/</webAppSourceDirectory>
<webAppConfig>
@@ -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>
+39 -34
View File
@@ -1,5 +1,6 @@
<?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
@@ -16,20 +17,16 @@
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
-->
<web-app id="starter" version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<display-name>Struts Showcase Application</display-name>
<context-param>
<param-name>org.apache.tiles.definition.DefinitionsFactory.DEFINITIONS_CONFIG</param-name>
<param-value>
/WEB-INF/tiles.xml
</param-value>
</context-param>
<filter>
<filter-name>async</filter-name>
<filter-class>org.apache.struts2.showcase.async.AsyncFilter</filter-class>
@@ -39,18 +36,20 @@
<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>
</filter>
<filter>
<filter-name>sitemesh</filter-name>
<filter-class>com.opensymphony.sitemesh.webapp.SiteMeshFilter</filter-class>
<async-supported>true</async-supported>
</filter>
<filter-mapping>
<filter-name>async</filter-name>
@@ -60,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>
@@ -81,18 +86,18 @@
org.apache.struts2.tiles.StrutsTilesListener
</listener-class>
</listener>
<!-- Chat Example in Showcase -->
<listener>
<listener-class>
org.apache.struts2.showcase.chat.ChatSessionListener
</listener-class>
<listener-class>
org.apache.struts2.showcase.chat.ChatSessionListener
</listener-class>
</listener>
<listener>
<listener-class>org.apache.struts2.dispatcher.listener.StrutsListener</listener-class>
</listener>
<!-- SNIPPET START: dwr -->
<servlet>
@@ -103,13 +108,20 @@
<param-value>true</param-value>
</init-param>
</servlet>
<servlet>
<servlet-name>JspSupportServlet</servlet-name>
<servlet-class>org.apache.struts2.views.JspSupportServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>JspSupportServlet</servlet-name>
<servlet-class>org.apache.struts2.views.JspSupportServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- Sitemesh Freemarker and Velocity Decorator Servlets. Shares configuration with Struts.-->
<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>
<servlet-class>org.apache.struts2.sitemesh.FreemarkerDecoratorServlet</servlet-class>
@@ -117,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>
@@ -127,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());
}
}
}
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>6.0.0</version>
<version>6.0.3</version>
</parent>
<artifactId>struts2-assembly</artifactId>
+13 -3
View File
@@ -29,7 +29,7 @@
</parent>
<artifactId>struts2-bom</artifactId>
<version>6.0.0</version>
<version>6.0.3</version>
<packaging>pom</packaging>
<name>Struts 2 Bill of Materials</name>
@@ -44,7 +44,7 @@
</licenses>
<properties>
<struts-version.version>6.0.0</struts-version.version>
<struts-version.version>6.0.3</struts-version.version>
<maven.site.skip>true</maven.site.skip>
<maven.site.deploy.skip>true</maven.site.deploy.skip>
</properties>
@@ -56,6 +56,11 @@
<artifactId>struts2-core</artifactId>
<version>${struts-version.version}</version>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-async-plugin</artifactId>
<version>${struts-version.version}</version>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-bean-validation-plugin</artifactId>
@@ -171,11 +176,16 @@
<artifactId>struts2-tiles-plugin</artifactId>
<version>${struts-version.version}</version>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-velocity-plugin</artifactId>
<version>${struts-version.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<scm>
<tag>STRUTS_6_0_0</tag>
<tag>STRUTS_6_0_3</tag>
<connection>scm:git:https://gitbox.apache.org/repos/asf/struts.git</connection>
<developerConnection>scm:git:https://gitbox.apache.org/repos/asf/struts.git</developerConnection>
<url>https://github.com/apache/struts/</url>
+2 -2
View File
@@ -24,12 +24,12 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-osgi-bundles</artifactId>
<version>6.0.0</version>
<version>6.0.3</version>
</parent>
<artifactId>struts2-osgi-admin-bundle</artifactId>
<packaging>bundle</packaging>
<name>DEPRECATED: Struts 2 OSGi Admin Bundle - since 2.6</name>
<name>DEPRECATED: Struts 2 OSGi Admin Bundle - since 6.0.0</name>
<dependencies>
<dependency>
+2 -2
View File
@@ -24,12 +24,12 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-osgi-bundles</artifactId>
<version>6.0.0</version>
<version>6.0.3</version>
</parent>
<artifactId>struts2-osgi-demo-bundle</artifactId>
<packaging>bundle</packaging>
<name>DEPRECATED: Struts 2 OSGi Demo Bundle - since 2.6</name>
<name>DEPRECATED: Struts 2 OSGi Demo Bundle - since 6.0.0</name>
<dependencies>
+2 -2
View File
@@ -24,12 +24,12 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>6.0.0</version>
<version>6.0.3</version>
</parent>
<artifactId>struts2-osgi-bundles</artifactId>
<packaging>pom</packaging>
<name>DEPRECATED: Struts 2 OSGi Bundles - since 2.6</name>
<name>DEPRECATED: Struts 2 OSGi Bundles - since 6.0.0</name>
<modules>
<module>admin</module>
+2 -2
View File
@@ -24,14 +24,14 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>6.0.0</version>
<version>6.0.3</version>
</parent>
<artifactId>struts2-core</artifactId>
<packaging>jar</packaging>
<name>Struts 2 Core</name>
<properties>
<tlib.version>2.6</tlib.version>
<tlib.version>6.0</tlib.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
@@ -24,7 +24,7 @@ import java.util.concurrent.Callable;
* Adds support for invoke async actions. This allows us to support action methods that return {@link Callable}
* as well as invoking them in separate not-container thread then executing the result in another container thread.
*
* @since 2.6
* @since 6.0.0
*/
public interface AsyncManager {
boolean hasAsyncActionResult();
@@ -298,8 +298,8 @@ public class DefaultConfiguration implements Configuration {
builder.factory(ObjectTypeDeterminer.class, DefaultObjectTypeDeterminer.class, Scope.SINGLETON);
builder.factory(PropertyAccessor.class, CompoundRoot.class.getName(), CompoundRootAccessor.class, Scope.SINGLETON);
builder.factory(ExpressionCacheFactory.class, "defaultOgnlExpressionCacheFactory", DefaultOgnlExpressionCacheFactory.class, Scope.SINGLETON);
builder.factory(BeanInfoCacheFactory.class, "defaultOgnlBeanInfoCacheFactory", DefaultOgnlBeanInfoCacheFactory.class, Scope.SINGLETON);
builder.factory(ExpressionCacheFactory.class, DefaultOgnlExpressionCacheFactory.class, Scope.SINGLETON);
builder.factory(BeanInfoCacheFactory.class, DefaultOgnlBeanInfoCacheFactory.class, Scope.SINGLETON);
builder.factory(OgnlUtil.class, Scope.SINGLETON);
builder.factory(ValueSubstitutor.class, EnvsValueSubstitutor.class, Scope.SINGLETON);
@@ -217,8 +217,8 @@ public class StrutsDefaultConfigurationProvider implements ConfigurationProvider
.factory(TextProviderFactory.class, StrutsTextProviderFactory.class, Scope.SINGLETON)
.factory(LocaleProviderFactory.class, DefaultLocaleProviderFactory.class, Scope.SINGLETON)
.factory(ExpressionCacheFactory.class, "defaultOgnlExpressionCacheFactory", DefaultOgnlExpressionCacheFactory.class, Scope.SINGLETON)
.factory(BeanInfoCacheFactory.class, "defaultOgnlBeanInfoCacheFactory", DefaultOgnlBeanInfoCacheFactory.class, Scope.SINGLETON)
.factory(ExpressionCacheFactory.class, DefaultOgnlExpressionCacheFactory.class, Scope.SINGLETON)
.factory(BeanInfoCacheFactory.class, DefaultOgnlBeanInfoCacheFactory.class, Scope.SINGLETON)
.factory(OgnlUtil.class, Scope.SINGLETON)
.factory(CollectionConverter.class, Scope.SINGLETON)
.factory(ArrayConverter.class, Scope.SINGLETON)
@@ -82,7 +82,9 @@ public class OgnlUtil {
*/
@Deprecated
public OgnlUtil() {
this(null, null); // Instantiate default Expression and BeanInfo caches (null factories)
// Instantiate default Expression and BeanInfo caches (factories must be non-null).
this(new DefaultOgnlExpressionCacheFactory<String, Object>(),
new DefaultOgnlBeanInfoCacheFactory<Class<?>, BeanInfo>());
}
/**
@@ -96,10 +98,17 @@ public class OgnlUtil {
* @param ognlExpressionCacheFactory factory for Expression cache instance. If null, it uses a default
* @param ognlBeanInfoCacheFactory factory for BeanInfo cache instance. If null, it uses a default
*/
@Inject
public OgnlUtil(
@Inject(value = StrutsConstants.STRUTS_OGNL_EXPRESSION_CACHE_FACTORY, required = false) ExpressionCacheFactory<String, Object> ognlExpressionCacheFactory,
@Inject(value = StrutsConstants.STRUTS_OGNL_BEANINFO_CACHE_FACTORY, required = false) BeanInfoCacheFactory<Class<?>, BeanInfo> ognlBeanInfoCacheFactory
@Inject ExpressionCacheFactory<String, Object> ognlExpressionCacheFactory,
@Inject BeanInfoCacheFactory<Class<?>, BeanInfo> ognlBeanInfoCacheFactory
) {
if (ognlExpressionCacheFactory == null) {
throw new IllegalArgumentException("ExpressionCacheFactory parameter cannot be null");
}
if (ognlBeanInfoCacheFactory == null) {
throw new IllegalArgumentException("BeanInfoCacheFactory parameter cannot be null");
}
excludedClasses = Collections.unmodifiableSet(new HashSet<>());
excludedPackageNamePatterns = Collections.unmodifiableSet(new HashSet<>());
excludedPackageNames = Collections.unmodifiableSet(new HashSet<>());
@@ -108,11 +117,8 @@ public class OgnlUtil {
devModeExcludedPackageNamePatterns = Collections.unmodifiableSet(new HashSet<>());
devModeExcludedPackageNames = Collections.unmodifiableSet(new HashSet<>());
OgnlCacheFactory<String, Object> ognlExpressionCacheFactory1 = (ognlExpressionCacheFactory != null ? ognlExpressionCacheFactory : new DefaultOgnlExpressionCacheFactory<>());
OgnlCacheFactory<Class<?>, BeanInfo> ognlBeanInfoCacheFactory1 = (ognlBeanInfoCacheFactory != null ? ognlBeanInfoCacheFactory : new DefaultOgnlBeanInfoCacheFactory<>());
this.expressionCache = ognlExpressionCacheFactory1.buildOgnlCache();
this.beanInfoCache = ognlBeanInfoCacheFactory1.buildOgnlCache();
this.expressionCache = ognlExpressionCacheFactory.buildOgnlCache();
this.beanInfoCache = ognlBeanInfoCacheFactory.buildOgnlCache();
}
@Inject
@@ -21,7 +21,7 @@ package com.opensymphony.xwork2.security;
/**
* Used across different places to check if given string is not excluded and is accepted
* @see <a href="https://securitylab.github.com/research/apache-struts-double-evaluation/">here</a>
* @since 2.6
* @since 6.0.0
*/
public interface NotExcludedAcceptedPatternsChecker extends ExcludedPatternsChecker, AcceptedPatternsChecker {
@@ -235,7 +235,7 @@ abstract class AbstractLocalizedTextProvider implements LocalizedTextProvider {
*
* @param bundleName The bundle to remove from the bundle map
*
* @deprecated A "no-op" since 2.6. Use {@link #clearBundle(java.lang.String, java.util.Locale)} instead.
* @deprecated A "no-op" since 6.0.0. Use {@link #clearBundle(java.lang.String, java.util.Locale)} instead.
*/
public void clearBundle(final String bundleName) {
LOG.debug("No-op. Did NOT clear resource bundle [{}], result: false.", bundleName);
@@ -248,7 +248,7 @@ abstract class AbstractLocalizedTextProvider implements LocalizedTextProvider {
* @param bundleName The bundle (combined with locale) to remove from the bundle map
* @param locale Provides the locale to combine with the bundle to get the key
*
* @since 2.6
* @since 6.0.0
*/
protected void clearBundle(final String bundleName, Locale locale) {
final String key = createMissesKey(String.valueOf(getCurrentThreadContextClassLoader().hashCode()), bundleName, locale);
@@ -266,7 +266,7 @@ abstract class AbstractLocalizedTextProvider implements LocalizedTextProvider {
* {@link AbstractLocalizedTextProvider} has already been used for failed bundle
* lookups of a given key, or some transitory state made a bundle lookup fail.
*
* @since 2.6
* @since 6.0.0
*/
protected void clearMissingBundlesCache() {
missingBundles.clear();
@@ -313,7 +313,7 @@ abstract class AbstractLocalizedTextProvider implements LocalizedTextProvider {
* The <code>clearCache()</code> methods have been available since Java 1.6, so
* it is anticipated the logic will work on any subsequent JVM versions.
*
* @since 2.6
* @since 6.0.0
*/
private void clearResourceBundleClassloaderCaches() {
final ClassLoader ccl = getCurrentThreadContextClassLoader();
@@ -436,7 +436,7 @@ abstract class AbstractLocalizedTextProvider implements LocalizedTextProvider {
*
* @param searchDefaultBundlesFirst provide {@link String} "true" or "false" to set the flag state accordingly.
*
* @since 2.6
* @since 6.0.0
*/
@Inject(value = StrutsConstants.STRUTS_I18N_SEARCH_DEFAULTBUNDLES_FIRST, required = false)
public void setSearchDefaultBundlesFirst(String searchDefaultBundlesFirst) {
@@ -29,11 +29,11 @@ import java.util.List;
/**
* ClassFinder searches the classpath of the specified ClassLoaderInterface for
* packages, classes, constructors, methods, or fields with specific annotations.
*
* <p>
* For security reasons ASM is used to find the annotations. Classes are not
* loaded unless they match the requirements of a called findAnnotated* method.
* Once loaded, these classes are cached.
*
* <p>
* The getClassesNotLoaded() method can be used immediately after any find*
* method to get a list of classes which matched the find requirements (i.e.
* contained the annotation), but were unable to be loaded.
@@ -67,32 +67,32 @@ public interface ClassFinder {
List<Package> findAnnotatedPackages(Class<? extends Annotation> annotation);
List<Class> findAnnotatedClasses(Class<? extends Annotation> annotation);
List<Class<?>> findAnnotatedClasses(Class<? extends Annotation> annotation);
List<Method> findAnnotatedMethods(Class<? extends Annotation> annotation);
List<Constructor> findAnnotatedConstructors(Class<? extends Annotation> annotation);
List<Constructor<?>> findAnnotatedConstructors(Class<? extends Annotation> annotation);
List<Field> findAnnotatedFields(Class<? extends Annotation> annotation);
List<Class> findClassesInPackage(String packageName, boolean recursive);
List<Class<?>> findClassesInPackage(String packageName, boolean recursive);
List<Class> findClasses(Test<ClassInfo> test);
List<Class<?>> findClasses(Test<ClassInfo> test);
List<Class> findClasses();
List<Class<?>> findClasses();
ClassLoaderInterface getClassLoaderInterface();
public static interface Info {
interface Info {
String getName();
List<AnnotationInfo> getAnnotations();
}
public class AnnotationInfo extends Annotatable implements Info {
class AnnotationInfo extends Annotatable implements Info {
private final String name;
public AnnotationInfo(Annotation annotation){
public AnnotationInfo(Annotation annotation) {
this(annotation.getClass().getName());
}
@@ -116,7 +116,7 @@ public interface ClassFinder {
}
}
public class Annotatable {
class Annotatable {
private final List<AnnotationInfo> annotations = new ArrayList<>();
public Annotatable(AnnotatedElement element) {
@@ -134,12 +134,12 @@ public interface ClassFinder {
}
public class PackageInfo extends Annotatable implements Info {
class PackageInfo extends Annotatable implements Info {
private final String name;
private final ClassInfo info;
private final Package pkg;
public PackageInfo(Package pkg){
public PackageInfo(Package pkg) {
super(pkg);
this.pkg = pkg;
this.name = pkg.getName();
@@ -157,11 +157,11 @@ public interface ClassFinder {
}
public Package get() throws ClassNotFoundException {
return (pkg != null)?pkg:info.get().getPackage();
return (pkg != null) ? pkg : info.get().getPackage();
}
}
public class ClassInfo extends Annotatable implements Info {
class ClassInfo extends Annotatable implements Info {
private final String name;
private final List<MethodInfo> methods = new ArrayList<>();
private final List<MethodInfo> constructors = new ArrayList<>();
@@ -169,17 +169,18 @@ public interface ClassFinder {
private final List<String> interfaces = new ArrayList<>();
private final List<String> superInterfaces = new ArrayList<>();
private final List<FieldInfo> fields = new ArrayList<>();
private final ClassFinder classFinder;
private Class<?> clazz;
private ClassFinder classFinder;
private ClassNotFoundException notFound;
public ClassInfo(Class clazz, ClassFinder classFinder) {
public ClassInfo(Class<?> clazz, ClassFinder classFinder) {
super(clazz);
this.clazz = clazz;
this.classFinder = classFinder;
this.name = clazz.getName();
Class superclass = clazz.getSuperclass();
this.superType = superclass != null ? superclass.getName(): null;
Class<?> superclass = clazz.getSuperclass();
this.superType = superclass != null ? superclass.getName() : null;
}
public ClassInfo(String name, String superType, ClassFinder classFinder) {
@@ -188,8 +189,8 @@ public interface ClassFinder {
this.classFinder = classFinder;
}
public String getPackageName(){
return name.indexOf('.') > 0 ? name.substring(0, name.lastIndexOf('.')) : "" ;
public String getPackageName() {
return name.indexOf('.') > 0 ? name.substring(0, name.lastIndexOf('.')) : "";
}
public List<MethodInfo> getConstructors() {
@@ -220,7 +221,7 @@ public interface ClassFinder {
return superType;
}
public Class get() throws ClassNotFoundException {
public Class<?> get() throws ClassNotFoundException {
if (clazz != null) return clazz;
if (notFound != null) throw notFound;
try {
@@ -239,20 +240,20 @@ public interface ClassFinder {
}
}
public class MethodInfo extends Annotatable implements Info {
class MethodInfo extends Annotatable implements Info {
private final ClassInfo declaringClass;
private final String returnType;
private final String name;
private final List<List<AnnotationInfo>> parameterAnnotations = new ArrayList<>();
public MethodInfo(ClassInfo info, Constructor constructor){
public MethodInfo(ClassInfo info, Constructor<?> constructor) {
super(constructor);
this.declaringClass = info;
this.name = "<init>";
this.returnType = Void.TYPE.getName();
}
public MethodInfo(ClassInfo info, Method method){
public MethodInfo(ClassInfo info, Method method) {
super(method);
this.declaringClass = info;
this.name = method.getName();
@@ -297,12 +298,12 @@ public interface ClassFinder {
}
}
public class FieldInfo extends Annotatable implements Info {
class FieldInfo extends Annotatable implements Info {
private final String name;
private final String type;
private final ClassInfo declaringClass;
public FieldInfo(ClassInfo info, Field field){
public FieldInfo(ClassInfo info, Field field) {
super(field);
this.declaringClass = info;
this.name = field.getName();
@@ -43,7 +43,7 @@ public final class StrutsConstants {
* Note: This control flag may not be meaningful to all provider implementations, and should be false by default.
* </p>
*
* @since 2.6
* @since 6.0.0
*/
public static final String STRUTS_I18N_SEARCH_DEFAULTBUNDLES_FIRST = "struts.i18n.search.defaultbundles.first";
@@ -254,7 +254,7 @@ public final class StrutsConstants {
* Specifies an OGNL expression cache factory implementation. A default implementation is provided, but
* could be replaced by a custom one if desired.
*
* @since 2.6
* @since 6.0.0
*/
public static final String STRUTS_OGNL_EXPRESSION_CACHE_FACTORY = "struts.ognl.expressionCacheFactory";
@@ -262,7 +262,7 @@ public final class StrutsConstants {
* Specifies an OGNL BeanInfo cache factory implementation. A default implementation is provided, but
* could be replaced by a custom one if desired.
*
* @since 2.6
* @since 6.0.0
*/
public static final String STRUTS_OGNL_BEANINFO_CACHE_FACTORY = "struts.ognl.beanInfoCacheFactory";
@@ -275,7 +275,7 @@ public final class StrutsConstants {
* For the optional LRU cache, once the maximum is reached, the least-recently-used (LRU) entry will be
* removed when a new entry needs to be added (cache is fully-utilized).
*
* @since 2.6
* @since 6.0.0
*/
public static final String STRUTS_OGNL_BEANINFO_CACHE_MAXSIZE = "struts.ognl.beanInfoCacheMaxSize";
@@ -287,32 +287,32 @@ public final class StrutsConstants {
* Note: When enabling LRU cache mode you must also set a maximum size (via {@link #STRUTS_OGNL_BEANINFO_CACHE_MAXSIZE})
* for it to be effective. Otherwise, there is no condition to evict a LRU entry (cache has no limit).
*
* @since 2.6
* @since 6.0.0
*/
public static final String STRUTS_OGNL_BEANINFO_CACHE_LRU_MODE = "struts.ognl.beanInfoCacheLRUMode";
/**
* Logs properties that are not found (very verbose)
* @since 2.6
* @since 6.0.0
*/
public static final String STRUTS_OGNL_LOG_MISSING_PROPERTIES = "struts.ognl.logMissingProperties";
/**
* Logs properties that are not found (very verbose)
* @deprecated as of 2.6. Use {@link #STRUTS_OGNL_LOG_MISSING_PROPERTIES} instead.
* @deprecated as of 6.0.0. Use {@link #STRUTS_OGNL_LOG_MISSING_PROPERTIES} instead.
*/
@Deprecated
public static final String STRUTS_LOG_MISSING_PROPERTIES = STRUTS_OGNL_LOG_MISSING_PROPERTIES;
/**
* Enables caching of parsed OGNL expressions
* @since 2.6
* @since 6.0.0
*/
public static final String STRUTS_OGNL_ENABLE_EXPRESSION_CACHE = "struts.ognl.enableExpressionCache";
/**
* Enables caching of parsed OGNL expressions
* @deprecated as of 2.6. Use {@link #STRUTS_OGNL_ENABLE_EXPRESSION_CACHE} instead.
* @deprecated as of 6.0.0. Use {@link #STRUTS_OGNL_ENABLE_EXPRESSION_CACHE} instead.
*/
public static final String STRUTS_ENABLE_OGNL_EXPRESSION_CACHE = STRUTS_OGNL_ENABLE_EXPRESSION_CACHE;
@@ -325,7 +325,7 @@ public final class StrutsConstants {
* For the optional LRU cache, once the maximum is reached, the least-recently-used (LRU) entry will be
* removed when a new entry needs to be added (cache is fully-utilized).
*
* @since 2.6
* @since 6.0.0
*/
public static final String STRUTS_OGNL_EXPRESSION_CACHE_MAXSIZE = "struts.ognl.expressionCacheMaxSize";
@@ -337,19 +337,19 @@ public final class StrutsConstants {
* Note: When enabling LRU cache mode you must also set a maximum size (via {@link #STRUTS_OGNL_EXPRESSION_CACHE_MAXSIZE})
* for it to be effective. Otherwise, there is no condition to evict a LRU entry (cache has no limit).
*
* @since 2.6
* @since 6.0.0
*/
public static final String STRUTS_OGNL_EXPRESSION_CACHE_LRU_MODE = "struts.ognl.expressionCacheLRUMode";
/**
* Enables evaluation of OGNL expressions
* @since 2.6
* @since 6.0.0
*/
public static final String STRUTS_OGNL_ENABLE_EVAL_EXPRESSION = "struts.ognl.enableEvalExpression";
/**
* Enables evaluation of OGNL expressions
* @deprecated as of 2.6. Use {@link #STRUTS_OGNL_ENABLE_EVAL_EXPRESSION} instead.
* @deprecated as of 6.0.0. Use {@link #STRUTS_OGNL_ENABLE_EVAL_EXPRESSION} instead.
*/
public static final String STRUTS_ENABLE_OGNL_EVAL_EXPRESSION = STRUTS_OGNL_ENABLE_EVAL_EXPRESSION;
@@ -29,7 +29,7 @@ import java.util.Map;
* Please note that the {@link CookieInterceptor} needs to be
* activated to receive a cookies map.
*
* @since 2.6
* @since 6.0.0
*/
public interface CookiesAware {
/**
@@ -37,4 +37,4 @@ public interface CookiesAware {
* @param cookies the cookies
*/
void withCookies(Map<String, String> cookies);
}
}
@@ -28,7 +28,7 @@ import org.apache.struts2.dispatcher.HttpParameters;
* One common use for this is to have the action propagate parameters to internally instantiated data
* objects.
*
* @since 2.6
* @since 6.0.0
*/
public interface ParametersAware {
@@ -23,7 +23,7 @@ import javax.servlet.ServletContext;
/**
* For components that have a dependence on the Servlet context.
*
* @since 2.6
* @since 6.0.0
*/
public interface ServletContextAware {
@@ -538,7 +538,7 @@ public class Component {
* Override to set if body content should be HTML-escaped.
*
* @return always true (default) for this component.
* @since 2.6
* @since 6.0.0
*/
public boolean escapeHtmlBody() {
return escapeHtmlBody;
@@ -600,7 +600,7 @@ public class Component {
*
* @param expression of the component
* @return true|false
* @since 2.6
* @since 6.0.0
*/
protected boolean isAcceptableExpression(String expression) {
NotExcludedAcceptedPatternsChecker.IsAllowed isAllowed = notExcludedAcceptedPatterns.isAllowed(expression);
@@ -59,7 +59,7 @@ import java.util.List;
* </p>
*
* <p>
* <b>Note</b>: Since Struts 2.6 a new Java 8 API has been used to format the Date, it's based on
* <b>Note</b>: Since Struts 6.0.0 a new Java 8 API has been used to format the Date, it's based on
* <a href="https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html">DateTimeFormatter</a>
* which uses a bit different patterns.
* </p>
@@ -293,7 +293,9 @@ public class Date extends ContextBean {
final ZoneId tz = getTimeZone();
// find the name on the valueStack
Object dateObject = findValue(name);
if (dateObject instanceof java.util.Date) {
if (dateObject instanceof java.sql.Date) {
date = ((java.sql.Date) dateObject).toLocalDate().atStartOfDay(tz);
} else if (dateObject instanceof java.util.Date) {
date = ((java.util.Date) dateObject).toInstant().atZone(tz);
} else if (dateObject instanceof Calendar) {
date = ((Calendar) dateObject).toInstant().atZone(tz);
@@ -80,4 +80,8 @@ public class Radio extends ListUIBean {
return true;
}
protected Class<?> getValueClassType() {
return String.class;
}
}
@@ -83,14 +83,14 @@ public class StrutsXmlConfigurationProvider extends XmlConfigurationProvider {
dtdMappings.put("-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN", "struts-2.1.7.dtd");
dtdMappings.put("-//Apache Software Foundation//DTD Struts Configuration 2.3//EN", "struts-2.3.dtd");
dtdMappings.put("-//Apache Software Foundation//DTD Struts Configuration 2.5//EN", "struts-2.5.dtd");
dtdMappings.put("-//Apache Software Foundation//DTD Struts Configuration 2.6//EN", "struts-2.6.dtd");
dtdMappings.put("-//Apache Software Foundation//DTD Struts Configuration 6.0//EN", "struts-6.0.dtd");
setDtdMappings(dtdMappings);
File file = new File(filename);
if (file.getParent() != null) {
this.baseDir = file.getParentFile();
}
}
/* (non-Javadoc)
* @see com.opensymphony.xwork2.config.providers.XmlConfigurationProvider#register(com.opensymphony.xwork2.inject.ContainerBuilder, java.util.Properties)
*/
@@ -175,7 +175,7 @@ public class StrutsXmlConfigurationProvider extends XmlConfigurationProvider {
}
}
public String toString() {
return ("Struts XML configuration provider ("+filename+")");
}
@@ -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);
}
@@ -52,21 +52,30 @@ public class CoepInterceptor extends AbstractInterceptor implements PreResultLis
@Override
public String intercept(ActionInvocation invocation) throws Exception {
invocation.addPreResultListener(this);
if (disabled) {
LOG.trace("COEP interceptor has been disabled");
} else {
invocation.addPreResultListener(this);
}
return invocation.invoke();
}
@Override
public void beforeResult(ActionInvocation invocation, String resultCode) {
if (disabled) {
return;
}
HttpServletRequest req = invocation.getInvocationContext().getServletRequest();
HttpServletResponse res = invocation.getInvocationContext().getServletResponse();
final String path = req.getContextPath();
if (exemptedPaths.contains(path)) {
// no need to add headers
LOG.debug("Skipping COEP header for exempted path {}", path);
} else if (!disabled) {
res.setHeader(header, REQUIRE_COEP_HEADER);
LOG.debug("Skipping COEP header for exempted path: {}", path);
} else {
LOG.trace("Applying COEP header: {} with value: {}", header, REQUIRE_COEP_HEADER);
HttpServletResponse response = invocation.getInvocationContext().getServletResponse();
response.setHeader(header, REQUIRE_COEP_HEADER);
}
}
@@ -49,25 +49,34 @@ public class CoopInterceptor extends AbstractInterceptor implements PreResultLis
private static final String COOP_HEADER = "Cross-Origin-Opener-Policy";
private final Set<String> exemptedPaths = new HashSet<>();
private boolean disabled = false;
private String mode = SAME_ORIGIN;
@Override
public String intercept(ActionInvocation invocation) throws Exception {
invocation.addPreResultListener(this);
if (disabled) {
LOG.trace("COOP interceptor has been disabled");
} else {
invocation.addPreResultListener(this);
}
return invocation.invoke();
}
@Override
public void beforeResult(ActionInvocation invocation, String resultCode) {
if (disabled) {
return;
}
HttpServletRequest request = invocation.getInvocationContext().getServletRequest();
HttpServletResponse response = invocation.getInvocationContext().getServletResponse();
String path = request.getContextPath();
if (isExempted(path)) {
// no need to add headers
LOG.debug("Skipping COOP header for exempted path {}", path);
} else {
response.setHeader(COOP_HEADER, getMode());
LOG.trace("Applying COOP header: {} with value: {}", COOP_HEADER, mode);
HttpServletResponse response = invocation.getInvocationContext().getServletResponse();
response.setHeader(COOP_HEADER, mode);
}
}
@@ -79,10 +88,6 @@ public class CoopInterceptor extends AbstractInterceptor implements PreResultLis
exemptedPaths.addAll(TextParseUtil.commaDelimitedStringToSet(paths));
}
private String getMode() {
return mode;
}
public void setMode(String mode) {
if (!(mode.equals(SAME_ORIGIN) || mode.equals(SAME_ORIGIN_ALLOW_POPUPS) || mode.equals(UNSAFE_NONE))) {
throw new IllegalArgumentException(String.format("Mode '%s' not recognized!", mode));
@@ -90,4 +95,7 @@ public class CoopInterceptor extends AbstractInterceptor implements PreResultLis
this.mode = mode;
}
public void setDisabled(String value) {
this.disabled = Boolean.parseBoolean(value);
}
}
@@ -18,24 +18,25 @@
*/
package org.apache.struts2.interceptor;
import static org.apache.struts2.interceptor.ResourceIsolationPolicy.SEC_FETCH_DEST_HEADER;
import static org.apache.struts2.interceptor.ResourceIsolationPolicy.SEC_FETCH_MODE_HEADER;
import static org.apache.struts2.interceptor.ResourceIsolationPolicy.SEC_FETCH_SITE_HEADER;
import static org.apache.struts2.interceptor.ResourceIsolationPolicy.SEC_FETCH_USER_HEADER;
import static org.apache.struts2.interceptor.ResourceIsolationPolicy.VARY_HEADER;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
import com.opensymphony.xwork2.util.TextParseUtil;
import java.util.HashSet;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashSet;
import java.util.Set;
import static org.apache.struts2.interceptor.ResourceIsolationPolicy.SEC_FETCH_DEST_HEADER;
import static org.apache.struts2.interceptor.ResourceIsolationPolicy.SEC_FETCH_MODE_HEADER;
import static org.apache.struts2.interceptor.ResourceIsolationPolicy.SEC_FETCH_SITE_HEADER;
import static org.apache.struts2.interceptor.ResourceIsolationPolicy.SEC_FETCH_USER_HEADER;
import static org.apache.struts2.interceptor.ResourceIsolationPolicy.VARY_HEADER;
/**
* Interceptor that implements Fetch Metadata policy on incoming requests used to protect against
* CSRF, XSSI, and cross-origin information leaks. Uses {@link StrutsResourceIsolationPolicy} to
@@ -46,6 +47,7 @@ import org.apache.logging.log4j.Logger;
**/
public class FetchMetadataInterceptor extends AbstractInterceptor {
private static final Logger LOG = LogManager.getLogger(FetchMetadataInterceptor.class);
private static final String VARY_HEADER_VALUE = String.format("%s,%s,%s,%s", SEC_FETCH_DEST_HEADER, SEC_FETCH_MODE_HEADER, SEC_FETCH_SITE_HEADER, SEC_FETCH_USER_HEADER);
private static final String SC_FORBIDDEN = String.valueOf(HttpServletResponse.SC_FORBIDDEN);
@@ -53,13 +55,19 @@ public class FetchMetadataInterceptor extends AbstractInterceptor {
private final Set<String> exemptedPaths = new HashSet<>();
private final ResourceIsolationPolicy resourceIsolationPolicy = new StrutsResourceIsolationPolicy();
@Inject (required=false)
private boolean disabled = false;
@Inject(required = false)
public void setExemptedPaths(String paths) {
this.exemptedPaths.addAll(TextParseUtil.commaDelimitedStringToSet(paths));
}
@Override
public String intercept(ActionInvocation invocation) throws Exception {
if (disabled) {
LOG.trace("Fetch Metadata interceptor has been disabled");
return invocation.invoke();
}
ActionContext context = invocation.getInvocationContext();
HttpServletRequest request = context.getServletRequest();
@@ -76,31 +84,34 @@ public class FetchMetadataInterceptor extends AbstractInterceptor {
return invocation.invoke();
}
LOG.info("Fetch metadata rejected cross-origin request to [{}]", contextPath);
LOG.warn("Fetch metadata rejected cross-origin request to: {}", contextPath);
return SC_FORBIDDEN;
}
/**
* Sets {@link SEC_FETCH_DEST_HEADER}, {@link SEC_FETCH_MODE_HEADER}, {@link SEC_FETCH_SITE_HEADER}, and {@link SEC_FETCH_USER_HEADER}
* elements in the provided ActionInvocation's HttpServletResponse {@link VARY_HEADER} response header.
*
* Sets {@link ResourceIsolationPolicy#SEC_FETCH_DEST_HEADER}, {@link ResourceIsolationPolicy#SEC_FETCH_MODE_HEADER},
* {@link ResourceIsolationPolicy#SEC_FETCH_SITE_HEADER}, and {@link ResourceIsolationPolicy#SEC_FETCH_USER_HEADER}
* elements in the provided ActionInvocation's HttpServletResponse {@link ResourceIsolationPolicy#VARY_HEADER} response header.
* <p>
* Note: This method will replace any previous Vary header content already set for the response.
* Note: In order to be effective, the Vary header modification must take place at (or very near) the start of this interceptor's processing.
*
* @param invocation Supplies the HttpServletResponse (if present) to which the SEC_FETCH_* header names are be added to its {@link VARY_HEADER} response header.
* Note: In order to be effective, the Vary header modification must take place at (or very near) the start of this
* interceptor's processing.
*
* @param invocation Supplies the HttpServletResponse (if present) to which the SEC_FETCH_* header names are be added
* to its {@link ResourceIsolationPolicy#VARY_HEADER} response header.
*/
private void addVaryHeaders(ActionInvocation invocation) {
HttpServletResponse response = invocation.getInvocationContext().getServletResponse();
if (response != null) {
// TODO: Whenever servlet 3.x becomes the baseline for Struts, consider revising this method to use
// getHeader(VARY_HEADER) and preserve any VARY_HEADER content already set in the response.
// This will probably require some tokenization logic for the header contents.
if (LOG.isDebugEnabled() && response.containsHeader(VARY_HEADER)) {
LOG.debug("HTTP response already has a [{}] header set, the old value will be overwritten (replaced)", VARY_HEADER);
}
response.setHeader(VARY_HEADER, VARY_HEADER_VALUE);
} else {
LOG.debug("HTTP response is null, cannot add a new [{}] header", VARY_HEADER);
// TODO: Whenever servlet 3.x becomes the baseline for Struts, consider revising this method to use
// getHeader(VARY_HEADER) and preserve any VARY_HEADER content already set in the response.
// This will probably require some tokenization logic for the header contents.
if (LOG.isDebugEnabled() && response.containsHeader(VARY_HEADER)) {
LOG.debug("HTTP response already has header: {} set, the old value will be overwritten (replaced)", VARY_HEADER);
}
response.setHeader(VARY_HEADER, VARY_HEADER_VALUE);
}
public void setDisabled(String value) {
this.disabled = Boolean.parseBoolean(value);
}
}
@@ -21,9 +21,13 @@ package org.apache.struts2.interceptor.csp;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
import com.opensymphony.xwork2.interceptor.PreResultListener;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.URI;
import java.util.Optional;
import javax.servlet.http.HttpServletResponse;
/**
* Interceptor that implements Content Security Policy on incoming requests used to protect against
@@ -36,17 +40,30 @@ import javax.servlet.http.HttpServletResponse;
* @see DefaultCspSettings
**/
public final class CspInterceptor extends AbstractInterceptor implements PreResultListener {
private static final Logger LOG = LogManager.getLogger(CspInterceptor.class);
private final CspSettings settings = new DefaultCspSettings();
private boolean disabled = false;
@Override
public String intercept(ActionInvocation invocation) throws Exception {
invocation.addPreResultListener(this);
if (disabled) {
LOG.trace("CSP interceptor has been disabled");
} else {
invocation.addPreResultListener(this);
}
return invocation.invoke();
}
public void beforeResult(ActionInvocation invocation, String resultCode) {
if (disabled) {
return;
}
HttpServletRequest request = invocation.getInvocationContext().getServletRequest();
HttpServletResponse response = invocation.getInvocationContext().getServletResponse();
settings.addCspHeaders(response);
settings.addCspHeaders(request, response);
}
public void setReportUri(String reportUri) {
@@ -71,8 +88,12 @@ public final class CspInterceptor extends AbstractInterceptor implements PreResu
return Optional.empty();
}
public void setEnforcingMode(String value){
public void setEnforcingMode(String value) {
boolean enforcingMode = Boolean.parseBoolean(value);
settings.setEnforcingMode(enforcingMode);
}
public void setDisabled(String value) {
this.disabled = Boolean.parseBoolean(value);
}
}
@@ -18,6 +18,7 @@
*/
package org.apache.struts2.interceptor.csp;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
@@ -42,9 +43,17 @@ public interface CspSettings {
String HTTPS = "https:";
String CSP_REPORT_TYPE = "application/csp-report";
/**
* @deprecated use {@link #addCspHeaders(HttpServletRequest, HttpServletResponse)} instead
*/
@Deprecated
void addCspHeaders(HttpServletResponse response);
void addCspHeaders(HttpServletRequest request, HttpServletResponse response);
// sets the uri where csp violation reports will be sent
void setReportUri(String uri);
// sets CSP headers in enforcing mode when true, and report-only when false
void setEnforcingMode(boolean value);
}
@@ -18,16 +18,16 @@
*/
package org.apache.struts2.interceptor.csp;
import static java.lang.String.format;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.opensymphony.xwork2.ActionContext;
import java.util.function.Supplier;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.Map;
import java.util.Objects;
import static java.lang.String.format;
/**
* Default implementation of {@link CspSettings}.
@@ -37,57 +37,62 @@ import java.util.Map;
* @see CspInterceptor
*/
public class DefaultCspSettings implements CspSettings {
private final SecureRandom sRand = new SecureRandom();
// this lazy supplier computes a policy format the first time it's called and caches the result
// to reduce string operations when attaching policies to HTTP responses
private final Supplier<String> lazyPolicyBuilder = new Supplier<String>() {
boolean hasBeenCalled;
String policyFormat;
@Override
public String get() {
if (!hasBeenCalled) {
StringBuilder policyFormatBuilder = new StringBuilder()
.append(OBJECT_SRC)
.append(format(" '%s'; ", NONE))
.append(SCRIPT_SRC)
.append(" 'nonce-%s' ") // nonce placeholder
.append(format("'%s' ", STRICT_DYNAMIC))
.append(format("%s %s; ", HTTP, HTTPS))
.append(BASE_URI)
.append(format(" '%s'; ", NONE));
private final static Logger LOG = LogManager.getLogger(DefaultCspSettings.class);
if (reportUri != null) {
policyFormatBuilder
.append(REPORT_URI)
.append(format(" %s", reportUri));
}
policyFormat = policyFormatBuilder.toString();
}
return format(policyFormat, getNonceString());
}
};
private final SecureRandom sRand = new SecureRandom();
private String reportUri;
// default to reporting mode
private String cspHeader = CSP_REPORT_HEADER;
@Override
public void addCspHeaders(HttpServletResponse response) {
associateNonceWithSession();
response.setHeader(cspHeader, lazyPolicyBuilder.get());
throw new UnsupportedOperationException("Unsupported implementation, use #addCspHeaders(HttpServletRequest request, HttpServletResponse response)");
}
private String getNonceString() {
Map<String, Object> session = ActionContext.getContext().getSession();
return (String) session.get("nonce");
public void addCspHeaders(HttpServletRequest request, HttpServletResponse response) {
if (isSessionActive(request)) {
LOG.trace("Session is active, applying CSP settings");
associateNonceWithSession(request);
response.setHeader(cspHeader, cratePolicyFormat(request));
} else {
LOG.trace("Session is not active, ignoring CSP settings");
}
}
private void associateNonceWithSession() {
Map<String, Object> session = ActionContext.getContext().getSession();
private boolean isSessionActive(HttpServletRequest request) {
return request.getSession(false) != null;
}
private void associateNonceWithSession(HttpServletRequest request) {
String nonceValue = Base64.getUrlEncoder().encodeToString(getRandomBytes());
session.put("nonce", nonceValue);
request.getSession().setAttribute("nonce", nonceValue);
}
private String cratePolicyFormat(HttpServletRequest request) {
StringBuilder policyFormatBuilder = new StringBuilder()
.append(OBJECT_SRC)
.append(format(" '%s'; ", NONE))
.append(SCRIPT_SRC)
.append(" 'nonce-%s' ") // nonce placeholder
.append(format("'%s' ", STRICT_DYNAMIC))
.append(format("%s %s; ", HTTP, HTTPS))
.append(BASE_URI)
.append(format(" '%s'; ", NONE));
if (reportUri != null) {
policyFormatBuilder
.append(REPORT_URI)
.append(format(" %s", reportUri));
}
return format(policyFormatBuilder.toString(), getNonceString(request));
}
private String getNonceString(HttpServletRequest request) {
Object nonce = request.getSession().getAttribute("nonce");
return Objects.toString(nonce);
}
private byte[] getRandomBytes() {
@@ -105,4 +110,5 @@ public class DefaultCspSettings implements CspSettings {
public void setReportUri(String reportUri) {
this.reportUri = reportUri;
}
}
@@ -295,7 +295,7 @@ public class StreamResult extends StrutsResultSupport {
*
* @param expression of result
* @return true|false
* @since 2.6
* @since 6.0.0
*/
protected boolean isAcceptableExpression(String expression) {
NotExcludedAcceptedPatternsChecker.IsAllowed isAllowed = notExcludedAcceptedPatterns.isAllowed(expression);
@@ -154,7 +154,7 @@ public class AnchorTag extends AbstractClosingTag {
*
* @param escapeHtmlBody
*
* @since 2.6
* @since 6.0.0
*/
public void setEscapeHtmlBody(String escapeHtmlBody) {
this.escapeHtmlBody = escapeHtmlBody;
@@ -81,7 +81,7 @@ public class SubmitTag extends AbstractClosingTag {
*
* @param escapeHtmlBody
*
* @since 2.6
* @since 6.0.0
*/
public void setEscapeHtmlBody(boolean escapeHtmlBody) {
this.escapeHtmlBody = escapeHtmlBody;
@@ -73,7 +73,7 @@ public class TextareaTag extends AbstractUITag {
public void setWrap(String wrap) {
this.wrap = wrap;
}
public void setMaxlength(String maxlength) {
this.maxlength = maxlength;
}
@@ -100,6 +100,8 @@ public class TextareaTag extends AbstractUITag {
this.readonly = null;
this.rows = null;
this.wrap = null;
this.maxlength = null;
this.minlength = null;
}
}
@@ -231,8 +231,8 @@ struts.ognl.enableExpressionCache=true
### Specify the OGNL expression cache factory and BeanInfo cache factory to use.
### Currently, the default implementations are used, but can be replaced with custom ones if desired.
struts.ognl.expressionCacheFactory=defaultOgnlExpressionCacheFactory
struts.ognl.beanInfoCacheFactory=defaultOgnlBeanInfoCacheFactory
# struts.ognl.expressionCacheFactory=customOgnlExpressionCacheFactory
# struts.ognl.beanInfoCacheFactory=customOgnlBeanInfoCacheFactory
### Specify a limit to the number of entries in the OGNL expressionCache.
### For the standard expressionCache mode, when the limit is exceeded the entire cache's
@@ -26,8 +26,8 @@
Use the following DOCTYPE
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.6//EN"
"http://struts.apache.org/dtds/struts-2.6.dtd">
"-//Apache Software Foundation//DTD Struts Configuration 6.0//EN"
"http://struts.apache.org/dtds/struts-6.0.dtd">
-->
<!ELEMENT struts ((package|include|bean|constant)*,bean-selection?, unknown-handler-stack?)>
+10 -6
View File
@@ -31,8 +31,8 @@
and {@link com.opensymphony.xwork2.inject.Inject}
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.6//EN"
"http://struts.apache.org/dtds/struts-2.6.dtd">
"-//Apache Software Foundation//DTD Struts Configuration 6.0//EN"
"struts-6.0.dtd">
<struts>
@@ -231,8 +231,8 @@
<bean type="org.apache.struts2.components.date.DateFormatter" name="simpleDateFormatter" class="org.apache.struts2.components.date.SimpleDateFormatAdapter" scope="singleton"/>
<bean type="org.apache.struts2.components.date.DateFormatter" name="dateTimeFormatter" class="org.apache.struts2.components.date.DateTimeFormatterAdapter" scope="singleton"/>
<bean type="com.opensymphony.xwork2.ognl.OgnlCacheFactory" name="ognlExpressionCacheFactory" class="com.opensymphony.xwork2.ognl.DefaultOgnlExpressionCacheFactory" scope="singleton"/>
<bean type="com.opensymphony.xwork2.ognl.OgnlCacheFactory" name="ognlBeanInfoCacheFactory" class="com.opensymphony.xwork2.ognl.DefaultOgnlBeanInfoCacheFactory" scope="singleton"/>
<bean type="com.opensymphony.xwork2.ognl.ExpressionCacheFactory" name="struts" class="com.opensymphony.xwork2.ognl.DefaultOgnlExpressionCacheFactory" scope="singleton" />
<bean type="com.opensymphony.xwork2.ognl.BeanInfoCacheFactory" name="struts" class="com.opensymphony.xwork2.ognl.DefaultOgnlBeanInfoCacheFactory" scope="singleton" />
<package name="struts-default" abstract="true">
<result-types>
@@ -392,6 +392,7 @@
<interceptor-ref name="servletConfig"/>
<interceptor-ref name="i18n"/>
<interceptor-ref name="cspInterceptor">
<param name="disabled">false</param>
<param name="enforcingMode">false</param>
</interceptor-ref>
<interceptor-ref name="prepare"/>
@@ -407,15 +408,18 @@
<interceptor-ref name="params"/>
<interceptor-ref name="conversionError"/>
<interceptor-ref name="coepInterceptor">
<param name="enforcingMode">false</param>
<param name="disabled">false</param>
<param name="enforcingMode">false</param>
<param name="exemptedPaths"/>
</interceptor-ref>
<interceptor-ref name="coopInterceptor">
<param name="disabled">false</param>
<param name="exemptedPaths"/>
<param name="mode">same-origin</param>
</interceptor-ref>
<interceptor-ref name="fetchMetadata"/>
<interceptor-ref name="fetchMetadata">
<param name="disabled">false</param>
</interceptor-ref>
<interceptor-ref name="validation">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
@@ -45,6 +45,12 @@
<#if parameters.title?has_content>
title="${parameters.title}"<#rt/>
</#if>
<#if parameters.maxlength?has_content>
maxlength="${parameters.maxlength}"<#rt/>
</#if>
<#if parameters.minlength?has_content>
minlength="${parameters.minlength}"<#rt/>
</#if>
<#include "/${parameters.templateDir}/${parameters.expandTheme}/scripting-events.ftl" />
<#include "/${parameters.templateDir}/${parameters.expandTheme}/common-attributes.ftl" />
<#include "/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" />
@@ -52,10 +58,4 @@
<#if parameters.nameValue??>
<@s.property value="parameters.nameValue"/><#t/>
</#if>
<#if parameters.maxlength?has_content>
maxlength="${parameters.maxlength}"<#rt/>
</#if>
<#if parameters.minlength?has_content>
minlength="${parameters.minlength}"<#rt/>
</#if>
</textarea>
</textarea>
@@ -1313,11 +1313,20 @@ public class OgnlUtilTest extends XWorkTestCase {
internalTestOgnlUtilExclusionsImmutable(basicOgnlUtil);
}
public void testDefaultOgnlUtilExclusionsAlternateConstructor() {
OgnlUtil basicOgnlUtil = new OgnlUtil(null, null);
internalTestInitialEmptyOgnlUtilExclusions(basicOgnlUtil);
internalTestOgnlUtilExclusionsImmutable(basicOgnlUtil);
public void testDefaultOgnlUtilAlternateConstructorArguments() {
// Code coverage test for the OgnlUtil alternate constructor method, and verify expected behaviour.
try {
OgnlUtil basicOgnlUtil = new OgnlUtil(new DefaultOgnlExpressionCacheFactory<String, Object>(), null);
fail("null beanInfoCacheFactory should result in exception");
} catch (IllegalArgumentException iaex) {
// expected result
}
try {
OgnlUtil basicOgnlUtil = new OgnlUtil(null, new DefaultOgnlBeanInfoCacheFactory<Class<?>, BeanInfo>());
fail("null expressionCacheFactory should result in exception");
} catch (IllegalArgumentException iaex) {
// expected result
}
}
public void testDefaultOgnlUtilExclusionsAlternateConstructorPopulated() {
@@ -1690,20 +1699,6 @@ public class OgnlUtilTest extends XWorkTestCase {
}
}
public void testGetExcludedPackageNamesAlternateConstructor() {
// Getter should return an immutable collection
OgnlUtil util = new OgnlUtil(null, null);
util.setExcludedPackageNames("java.lang,java.awt");
assertEquals(util.getExcludedPackageNames().size(), 2);
try {
util.getExcludedPackageNames().clear();
} catch (Exception ex) {
assertTrue(ex instanceof UnsupportedOperationException);
} finally {
assertEquals(util.getExcludedPackageNames().size(), 2);
}
}
public void testGetExcludedPackageNamesAlternateConstructorPopulated() {
// Getter should return an immutable collection
OgnlUtil util = new OgnlUtil(new DefaultOgnlExpressionCacheFactory<String, Object>(), new DefaultOgnlBeanInfoCacheFactory<Class<?>, BeanInfo>());
@@ -1732,20 +1727,6 @@ public class OgnlUtilTest extends XWorkTestCase {
}
}
public void testGetExcludedClassesAlternateConstructor() {
// Getter should return an immutable collection
OgnlUtil util = new OgnlUtil(null, null);
util.setExcludedClasses("java.lang.Runtime,java.lang.ProcessBuilder,java.net.URL");
assertEquals(util.getExcludedClasses().size(), 3);
try {
util.getExcludedClasses().clear();
} catch (Exception ex) {
assertTrue(ex instanceof UnsupportedOperationException);
} finally {
assertEquals(util.getExcludedClasses().size(), 3);
}
}
public void testGetExcludedClassesAlternateConstructorPopulated() {
// Getter should return an immutable collection
OgnlUtil util = new OgnlUtil(new DefaultOgnlExpressionCacheFactory<String, Object>(), new DefaultOgnlBeanInfoCacheFactory<Class<?>, BeanInfo>());
@@ -1774,20 +1755,6 @@ public class OgnlUtilTest extends XWorkTestCase {
}
}
public void testGetExcludedPackageNamePatternsAlternateConstructor() {
// Getter should return an immutable collection
OgnlUtil util = new OgnlUtil(null, null);
util.setExcludedPackageNamePatterns("java.lang.");
assertEquals(util.getExcludedPackageNamePatterns().size(), 1);
try {
util.getExcludedPackageNamePatterns().clear();
} catch (Exception ex) {
assertTrue(ex instanceof UnsupportedOperationException);
} finally {
assertEquals(util.getExcludedPackageNamePatterns().size(), 1);
}
}
public void testGetExcludedPackageNamePatternsAlternateConstructorPopulated() {
// Getter should return an immutable collection
OgnlUtil util = new OgnlUtil(new DefaultOgnlExpressionCacheFactory<String, Object>(), new DefaultOgnlBeanInfoCacheFactory<Class<?>, BeanInfo>());
@@ -264,7 +264,7 @@ public class StrutsLocalizedTextProviderTest extends XWorkTestCase {
* Unit test to confirm expected behaviour of "clearing methods" provided to
* StrutsLocalizedTextProvider (from AbstractLocalizedTextProvider).
*
* @since 2.6
* @since 6.0.0
*/
public void testLocalizedTextProviderClearingMethods() {
TestStrutsLocalizedTextProvider testStrutsLocalizedTextProvider = new TestStrutsLocalizedTextProvider();
@@ -331,7 +331,7 @@ public class StrutsLocalizedTextProviderTest extends XWorkTestCase {
* Unit test to confirm the basic behaviour of bundle reload methods provided to
* StrutsLocalizedTextProvider (from AbstractLocalizedTextProvider).
*
* @since 2.6
* @since 6.0.0
*/
public void testLocalizedTextProviderReloadMethods() {
TestStrutsLocalizedTextProvider testStrutsLocalizedTextProvider = new TestStrutsLocalizedTextProvider();
@@ -582,7 +582,7 @@ public class StrutsLocalizedTextProviderTest extends XWorkTestCase {
/**
* Basic test class to allow specific testing of StrutsLocalizedTextProvider.
*
* @since 2.6
* @since 6.0.0
*/
class TestStrutsLocalizedTextProvider extends StrutsLocalizedTextProvider {
@@ -27,11 +27,12 @@ import com.opensymphony.xwork2.validator.annotations.Validations;
import com.opensymphony.xwork2.validator.annotations.ValidatorType;
import org.apache.struts2.views.jsp.ui.User;
import java.util.*;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*/
public class TestAction extends ActionSupport {
private static final long serialVersionUID = -8891365561914451494L;
@@ -50,22 +51,22 @@ public class TestAction extends ActionSupport {
private SomeEnum status = SomeEnum.COMPLETED;
private Float floatNumber;
private Long id;
private List<SomeEnum> enumList;
private List<Integer> intList;
private final Map<String, String> texts = new HashMap<String, String>();
private final Map<String, String> texts = new HashMap<>();
/**
* Define a text resource within this action that will be returned by the getText methods
* here before delegating to the default TextProvider
*
* call
* @param key
* @param value
* here before delegating to the default TextProvider call
*/
public void setText(String key, String value) {
this.texts.put(key, value);
}
/** Returns the test value if defined otherwise delegates to the default TextProvider */
/**
* Returns the test value if defined otherwise delegates to the default TextProvider
*/
public String getText(String key) {
if (this.texts.containsKey(key)) {
return this.texts.get(key);
@@ -73,8 +74,10 @@ public class TestAction extends ActionSupport {
return super.getText(key);
}
/** This is the method invoked by the {@link org.apache.struts2.util.TextProviderHelper}.
* Returns the test value if defined otherwise delegates to the default TextProvider */
/**
* This is the method invoked by the {@link org.apache.struts2.util.TextProviderHelper}.
* Returns the test value if defined otherwise delegates to the default TextProvider
*/
public String getText(String key, String defaultValue, List<?> args, ValueStack stack) {
if (this.texts.containsKey(key)) {
return this.texts.get(key);
@@ -180,20 +183,21 @@ public class TestAction extends ActionSupport {
}
@Validations(
requiredFields = {
@RequiredFieldValidator(type = ValidatorType.SIMPLE, fieldName = "status", message = "You must enter a value for field.")
},
requiredStrings = {
@RequiredStringValidator(type = ValidatorType.SIMPLE, fieldName = "result", message = "You must enter a value for field.")
}
requiredFields = {
@RequiredFieldValidator(type = ValidatorType.SIMPLE, fieldName = "status", message = "You must enter a value for field.")
},
requiredStrings = {
@RequiredStringValidator(type = ValidatorType.SIMPLE, fieldName = "result", message = "You must enter a value for field.")
}
)
public String annotatedExecute1() throws Exception {
return Action.SUCCESS;
}
@Validations(
requiredFields = {
@RequiredFieldValidator(type = ValidatorType.SIMPLE, fieldName = "status", message = "You must enter a value for field.")
}
requiredFields = {
@RequiredFieldValidator(type = ValidatorType.SIMPLE, fieldName = "status", message = "You must enter a value for field.")
}
)
public String annotatedExecute2() throws Exception {
return Action.SUCCESS;
@@ -207,16 +211,16 @@ public class TestAction extends ActionSupport {
return INPUT;
}
public SomeEnum getStatus() {
return status;
}
public SomeEnum getStatus() {
return status;
}
public void setStatus(SomeEnum status) {
this.status = status;
}
public void setStatus(SomeEnum status) {
this.status = status;
}
public List<SomeEnum> getStatusList() {
return Arrays.asList(SomeEnum.values());
return Arrays.asList(SomeEnum.values());
}
public Float getFloatNumber() {
@@ -235,4 +239,19 @@ public class TestAction extends ActionSupport {
this.id = id;
}
public List<SomeEnum> getEnumList() {
return enumList;
}
public void setEnumList(List<SomeEnum> enumList) {
this.enumList = enumList;
}
public List<Integer> getIntList() {
return intList;
}
public void setIntList(List<Integer> intList) {
this.intList = intList;
}
}
@@ -81,6 +81,28 @@ public class DateTest extends StrutsInternalTestCase {
assertEquals(expected, writer.toString());
}
public void testJavaSqlDate() {
// given
Date date = new Date(stack);
date.setDateFormatter(new SimpleDateFormatAdapter());
java.sql.Date now = new java.sql.Date(System.currentTimeMillis());
String expected = SimpleDateFormat.getDateInstance(DateFormat.MEDIUM, ActionContext.getContext().getLocale()).format(now);
context.put("myDate", now);
Writer writer = new StringWriter();
// when
date.setName("myDate");
date.setNice(false);
date.start(writer);
date.end(writer, "");
// then
assertEquals(expected, writer.toString());
}
@Override
public void setUp() throws Exception {
super.setUp();
@@ -104,4 +104,60 @@ public class StrutsJavaConfigurationProviderTest {
Assert.assertTrue(names.contains("struts"));
Assert.assertTrue(names.contains("struts.test.bean"));
}
@Test
/**
* This test is purely to provide code coverage for {@link AbstractBeanSelectionProvider}.
* It uses an arbitrary setup to ensure a code path not followed in the registration test
* is traversed.
*/
public void testAbstractBeanProviderCoverage() throws Exception {
final ConstantConfig constantConfig = new ConstantConfig();
final String expectedUnknownHandler = "expectedUnknownHandler";
StrutsJavaConfiguration javaConfig = new StrutsJavaConfiguration() {
@Override
public List<String> unknownHandlerStack() {
return Collections.singletonList(expectedUnknownHandler);
}
@Override
public List<ConstantConfig> constants() {
return Collections.singletonList(constantConfig);
}
@Override
public List<BeanConfig> beans() {
return Arrays.asList(
new BeanConfig(TestBean.class, "struts")
);
}
@Override
public Optional<BeanSelectionConfig> beanSelection() {
return Optional.of(new BeanSelectionConfig(TestBeanSelectionProvider.class, "testBeans"));
}
};
StrutsJavaConfigurationProvider provider = new StrutsJavaConfigurationProvider(javaConfig);
Configuration configuration = new MockConfiguration();
ContainerBuilder builder = new ContainerBuilder();
LocatableProperties props = new LocatableProperties();
provider.init(configuration);
provider.register(builder, props);
props.put(CodeCoverageTestClass1.ALIAS_KEY, CodeCoverageTestClass1.ALIAS_VALUE);
TestBeanSelectionProvider testBeanSelectionProvider = new TestBeanSelectionProvider();
testBeanSelectionProvider.aliasCallCoverage(CodeCoverageTestClass1.class, builder, props, CodeCoverageTestClass1.ALIAS_KEY, Scope.THREAD);
}
final class CodeCoverageTestClass1 extends Object {
public static final String ALIAS_KEY = "testAliasKey";
public static final String ALIAS_VALUE = "testAliasValue";
public CodeCoverageTestClass1() {
super();
}
}
}
@@ -21,6 +21,7 @@ package org.apache.struts2.config;
import com.opensymphony.xwork2.TestBean;
import com.opensymphony.xwork2.config.ConfigurationException;
import com.opensymphony.xwork2.inject.ContainerBuilder;
import com.opensymphony.xwork2.inject.Scope;
import com.opensymphony.xwork2.util.location.LocatableProperties;
public class TestBeanSelectionProvider extends AbstractBeanSelectionProvider {
@@ -30,4 +31,9 @@ public class TestBeanSelectionProvider extends AbstractBeanSelectionProvider {
alias(TestBean.class, "struts.test.bean", builder, props);
}
public void aliasCallCoverage(Class aliasClass, ContainerBuilder builder, LocatableProperties props, String aliasKey, Scope scope) throws ConfigurationException {
// Allow for coverage testing of AbstractBeanSelectionProvider.
alias(aliasClass, aliasKey, builder, props, scope);
}
}
@@ -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 {
@@ -34,7 +34,6 @@ public class CoopInterceptorTest extends StrutsInternalTestCase {
private final MockHttpServletResponse response = new MockHttpServletResponse();
String SAME_ORIGIN = "same-origin";
String SAME_SITE = "same-site";
String UNSAFE_NONE = "unsafe-none";
String COOP_HEADER = "Cross-Origin-Opener-Policy";
@@ -65,7 +64,7 @@ public class CoopInterceptorTest extends StrutsInternalTestCase {
assertEquals("Coop header is not same-origin", UNSAFE_NONE, header);
}
public void testErrorNotRecognizedMode() throws Exception {
public void testErrorNotRecognizedMode() {
request.setContextPath("/some");
try{
@@ -76,6 +75,15 @@ public class CoopInterceptorTest extends StrutsInternalTestCase {
}
}
public void testDisabled() throws Exception {
interceptor.setDisabled("true");
interceptor.intercept(mai);
String header = response.getHeader(COOP_HEADER);
assertTrue("COOP is not disabled", Strings.isEmpty(header));
}
@Override
protected void setUp() throws Exception {
super.setUp();
@@ -21,16 +21,25 @@ package org.apache.struts2.interceptor;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.mock.MockActionInvocation;
import org.apache.logging.log4j.util.Strings;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.StrutsInternalTestCase;
import org.apache.struts2.dispatcher.SessionMap;
import org.apache.struts2.interceptor.csp.CspInterceptor;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpSession;
import static org.apache.struts2.interceptor.csp.CspSettings.*;
import static org.apache.struts2.interceptor.csp.CspSettings.BASE_URI;
import static org.apache.struts2.interceptor.csp.CspSettings.CSP_ENFORCE_HEADER;
import static org.apache.struts2.interceptor.csp.CspSettings.CSP_REPORT_HEADER;
import static org.apache.struts2.interceptor.csp.CspSettings.HTTP;
import static org.apache.struts2.interceptor.csp.CspSettings.HTTPS;
import static org.apache.struts2.interceptor.csp.CspSettings.NONE;
import static org.apache.struts2.interceptor.csp.CspSettings.OBJECT_SRC;
import static org.apache.struts2.interceptor.csp.CspSettings.REPORT_URI;
import static org.apache.struts2.interceptor.csp.CspSettings.SCRIPT_SRC;
import static org.apache.struts2.interceptor.csp.CspSettings.STRICT_DYNAMIC;
import static org.junit.Assert.assertNotEquals;
public class CspInterceptorTest extends StrutsInternalTestCase {
@@ -38,7 +47,8 @@ public class CspInterceptorTest extends StrutsInternalTestCase {
private final MockActionInvocation mai = new MockActionInvocation();
private final MockHttpServletRequest request = new MockHttpServletRequest();
private final MockHttpServletResponse response = new MockHttpServletResponse();
private final Map<String, Object> session = new HashMap<>();
private HttpSession session;
public void test_whenRequestReceived_thenNonceIsSetInSession_andCspHeaderContainsIt() throws Exception {
String reportUri = "/barfoo";
@@ -48,8 +58,8 @@ public class CspInterceptorTest extends StrutsInternalTestCase {
interceptor.intercept(mai);
assertTrue("Nonce key does not exist", session.containsKey("nonce"));
assertFalse("Nonce value is empty", Strings.isEmpty((String) session.get("nonce")));
assertNotNull("Nonce key does not exist", session.getAttribute("nonce"));
assertFalse("Nonce value is empty", Strings.isEmpty((String) session.getAttribute("nonce")));
checkHeader(reportUri, reporting);
}
@@ -58,13 +68,13 @@ public class CspInterceptorTest extends StrutsInternalTestCase {
String enforcingMode = "true";
interceptor.setReportUri(reportUri);
interceptor.setEnforcingMode(enforcingMode);
session.put("nonce", "foo");
session.setAttribute("nonce", "foo");
interceptor.intercept(mai);
assertTrue("Nonce key does not exist", session.containsKey("nonce"));
assertFalse("Nonce value is empty", Strings.isEmpty((String) session.get("nonce")));
assertFalse("New nonce value couldn't be set", session.get("nonce").equals("foo"));
assertNotNull("Nonce key does not exist", session.getAttribute("nonce"));
assertFalse("Nonce value is empty", Strings.isEmpty((String) session.getAttribute("nonce")));
assertNotEquals("New nonce value couldn't be set", "foo", session.getAttribute("nonce"));
checkHeader(reportUri, enforcingMode);
}
@@ -73,13 +83,13 @@ public class CspInterceptorTest extends StrutsInternalTestCase {
String enforcingMode = "true";
interceptor.setReportUri(reportUri);
interceptor.setEnforcingMode(enforcingMode);
session.put("nonce", "foo");
session.setAttribute("nonce", "foo");
interceptor.intercept(mai);
assertTrue("Nonce key does not exist", session.containsKey("nonce"));
assertFalse("Nonce value is empty", Strings.isEmpty((String) session.get("nonce")));
assertFalse("New nonce value couldn't be set", session.get("nonce").equals("foo"));
assertNotNull("Nonce key does not exist", session.getAttribute("nonce"));
assertFalse("Nonce value is empty", Strings.isEmpty((String) session.getAttribute("nonce")));
assertNotEquals("New nonce value couldn't be set", "foo", session.getAttribute("nonce"));
checkHeader(reportUri, enforcingMode);
}
@@ -88,13 +98,12 @@ public class CspInterceptorTest extends StrutsInternalTestCase {
String enforcingMode = "false";
interceptor.setReportUri(reportUri);
interceptor.setEnforcingMode(enforcingMode);
session.put("nonce", "foo");
session.setAttribute("nonce", "foo");
interceptor.intercept(mai);
assertTrue("Nonce key does not exist", session.containsKey("nonce"));
assertFalse("Nonce value is empty", Strings.isEmpty((String) session.get("nonce")));
assertFalse("New nonce value couldn't be set", session.get("nonce").equals("foo"));
assertNotNull("Nonce value is empty", session.getAttribute("nonce"));
assertNotEquals("New nonce value couldn't be set", "foo", session.getAttribute("nonce"));
checkHeader(reportUri, enforcingMode);
}
@@ -112,49 +121,60 @@ public class CspInterceptorTest extends StrutsInternalTestCase {
checkHeader(reportUri, enforcingMode);
}
public void testCannotParseUri() throws Exception {
public void testCannotParseUri() {
String enforcingMode = "false";
interceptor.setEnforcingMode(enforcingMode);
try{
try {
interceptor.setReportUri("ww w. google.@com");
assert(false);
} catch (IllegalArgumentException e){
assert(true);
assert (false);
} catch (IllegalArgumentException e) {
assert (true);
}
}
public void testCannotParseRelativeUri() throws Exception {
public void testCannotParseRelativeUri() {
String enforcingMode = "false";
interceptor.setEnforcingMode(enforcingMode);
try{
try {
interceptor.setReportUri("some-uri");
assert(false);
} catch (IllegalArgumentException e){
assert(true);
assert (false);
} catch (IllegalArgumentException e) {
assert (true);
}
}
public void checkHeader(String reportUri, String enforcingMode){
String expectedCspHeader = "";
public void testDisabled() throws Exception {
interceptor.setDisabled("true");
interceptor.intercept(mai);
String header = response.getHeader(CSP_ENFORCE_HEADER);
assertTrue("CSP is not disabled", Strings.isEmpty(header));
header = response.getHeader(CSP_REPORT_HEADER);
assertTrue("CSP is not disabled", Strings.isEmpty(header));
}
public void checkHeader(String reportUri, String enforcingMode) {
String expectedCspHeader;
if (Strings.isEmpty(reportUri)) {
expectedCspHeader = String.format("%s '%s'; %s 'nonce-%s' '%s' %s %s; %s '%s'; ",
OBJECT_SRC, NONE,
SCRIPT_SRC, session.get("nonce"), STRICT_DYNAMIC, HTTP, HTTPS,
BASE_URI, NONE
OBJECT_SRC, NONE,
SCRIPT_SRC, session.getAttribute("nonce"), STRICT_DYNAMIC, HTTP, HTTPS,
BASE_URI, NONE
);
} else {
expectedCspHeader = String.format("%s '%s'; %s 'nonce-%s' '%s' %s %s; %s '%s'; %s %s",
OBJECT_SRC, NONE,
SCRIPT_SRC, session.get("nonce"), STRICT_DYNAMIC, HTTP, HTTPS,
BASE_URI, NONE,
REPORT_URI, reportUri
OBJECT_SRC, NONE,
SCRIPT_SRC, session.getAttribute("nonce"), STRICT_DYNAMIC, HTTP, HTTPS,
BASE_URI, NONE,
REPORT_URI, reportUri
);
}
String header = "";
if (enforcingMode.equals("true")){
String header;
if (enforcingMode.equals("true")) {
header = response.getHeader(CSP_ENFORCE_HEADER);
} else {
header = response.getHeader(CSP_REPORT_HEADER);
@@ -168,10 +188,12 @@ public class CspInterceptorTest extends StrutsInternalTestCase {
protected void setUp() throws Exception {
super.setUp();
container.inject(interceptor);
ServletActionContext.setRequest(request);
ServletActionContext.setResponse(response);
ActionContext context = ServletActionContext.getActionContext().bind();
context.withSession(session);
ActionContext context = ActionContext.getContext()
.withServletRequest(request)
.withServletResponse(response)
.withSession(new SessionMap<>(request))
.bind();
mai.setInvocationContext(context);
session = request.getSession();
}
}
@@ -18,6 +18,27 @@
*/
package org.apache.struts2.interceptor;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.XWorkTestCase;
import com.opensymphony.xwork2.config.RuntimeConfiguration;
import com.opensymphony.xwork2.config.entities.ActionConfig;
import com.opensymphony.xwork2.config.entities.InterceptorMapping;
import com.opensymphony.xwork2.config.entities.InterceptorStackConfig;
import com.opensymphony.xwork2.config.entities.PackageConfig;
import com.opensymphony.xwork2.config.providers.XmlConfigurationProvider;
import com.opensymphony.xwork2.mock.MockActionInvocation;
import org.apache.logging.log4j.util.Strings;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.config.StrutsXmlConfigurationProvider;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.Objects;
import static org.apache.struts2.interceptor.ResourceIsolationPolicy.DEST_EMBED;
import static org.apache.struts2.interceptor.ResourceIsolationPolicy.DEST_OBJECT;
import static org.apache.struts2.interceptor.ResourceIsolationPolicy.DEST_SCRIPT;
@@ -32,25 +53,6 @@ import static org.apache.struts2.interceptor.ResourceIsolationPolicy.SITE_SAME_S
import static org.apache.struts2.interceptor.ResourceIsolationPolicy.VARY_HEADER;
import static org.junit.Assert.assertNotEquals;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.XWorkTestCase;
import com.opensymphony.xwork2.config.RuntimeConfiguration;
import com.opensymphony.xwork2.config.entities.ActionConfig;
import com.opensymphony.xwork2.config.entities.InterceptorMapping;
import com.opensymphony.xwork2.config.entities.InterceptorStackConfig;
import com.opensymphony.xwork2.config.entities.PackageConfig;
import com.opensymphony.xwork2.config.providers.XmlConfigurationProvider;
import com.opensymphony.xwork2.mock.MockActionInvocation;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.config.StrutsXmlConfigurationProvider;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import javax.servlet.http.HttpServletResponse;
public class FetchMetadataInterceptorTest extends XWorkTestCase {
private final FetchMetadataInterceptor interceptor = new FetchMetadataInterceptor();
@@ -79,7 +81,7 @@ public class FetchMetadataInterceptorTest extends XWorkTestCase {
}
public void testValidSite() throws Exception {
for (String header : Arrays.asList(SITE_SAME_ORIGIN, SITE_SAME_SITE, SITE_NONE)){
for (String header : Arrays.asList(SITE_SAME_ORIGIN, SITE_SAME_SITE, SITE_NONE)) {
request.addHeader(SEC_FETCH_SITE_HEADER, header);
assertNotEquals("Expected interceptor to accept this request", SC_FORBIDDEN, interceptor.intercept(mai));
@@ -147,12 +149,12 @@ public class FetchMetadataInterceptorTest extends XWorkTestCase {
interceptor.intercept(mai);
assertTrue("Expected vary header to be included", response.containsHeader(VARY_HEADER));
assertFalse("Expected original vary header content to be replaced", response.getHeader(VARY_HEADER).contains(ACCEPT_ENCODING_VALUE));
assertTrue("Expected added vary header content to be present", response.getHeader(VARY_HEADER).contains(VARY_HEADER_VALUE));
assertFalse("Expected original vary header content to be replaced", Objects.requireNonNull(response.getHeader(VARY_HEADER)).contains(ACCEPT_ENCODING_VALUE));
assertTrue("Expected added vary header content to be present", Objects.requireNonNull(response.getHeader(VARY_HEADER)).contains(VARY_HEADER_VALUE));
}
public void testSetExemptedPathsInjectionIndirectly() throws Exception {
// Perform a multi-step test to confirm (indirectly) that the method parameter injection of setExemptedPaths() for
// Perform a multistep test to confirm (indirectly) that the method parameter injection of setExemptedPaths() for
// the FetchMetadataInterceptor is functioning as expected, when configured appropriately.
// Ensure we're using the specific test configuration, not the default simple configuration.
XmlConfigurationProvider configurationProvider = new StrutsXmlConfigurationProvider("struts-testing.xml");
@@ -259,4 +261,12 @@ public class FetchMetadataInterceptorTest extends XWorkTestCase {
assertNotEquals("Expected interceptor to accept this request [" + "/" + fetchMetadataExemptedGlobalActionConfig.getName() + "]", SC_FORBIDDEN, configuredFetchMetadataInterceptor.intercept(mai));
}
public void testDisabled() throws Exception {
interceptor.setDisabled("true");
interceptor.intercept(mai);
String header = response.getHeader(VARY_HEADER);
assertTrue("Fetch Metadata is not disabled", Strings.isEmpty(header));
}
}
@@ -259,6 +259,8 @@ public class FreemarkerResultMockedTest extends StrutsInternalTestCase {
EasyMock.replay(servletContext);
init();
// create session
request.getSession();
request.setRequestURI("/tutorial/test10.action");
ActionMapping mapping = container.getInstance(ActionMapper.class).getMapping(request, configurationManager);
@@ -18,36 +18,31 @@
*/
package org.apache.struts2.views.jsp;
import java.io.File;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.JspWriter;
import com.mockobjects.dynamic.Mock;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.util.ValueStack;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.StrutsInternalTestCase;
import org.apache.struts2.TestAction;
import org.apache.struts2.dispatcher.ApplicationMap;
import org.apache.struts2.dispatcher.Dispatcher;
import org.apache.struts2.dispatcher.MockDispatcher;
import org.apache.struts2.dispatcher.HttpParameters;
import org.apache.struts2.dispatcher.MockDispatcher;
import org.apache.struts2.dispatcher.RequestMap;
import org.apache.struts2.dispatcher.SessionMap;
import com.mockobjects.dynamic.Mock;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.util.ValueStack;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.JspWriter;
import java.io.File;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;
/**
* Base class to extend for unit testing UI Tags.
*
*/
public abstract class AbstractTagTest extends StrutsInternalTestCase {
protected Action action;
@@ -62,7 +57,7 @@ public abstract class AbstractTagTest extends StrutsInternalTestCase {
protected StrutsMockHttpServletRequest request;
protected StrutsMockPageContext pageContext;
protected HttpServletResponse response;
protected Mock mockContainer;
/**
@@ -114,11 +109,11 @@ public abstract class AbstractTagTest extends StrutsInternalTestCase {
Dispatcher.setInstance(du);
session = new SessionMap<>(request);
Map<String, Object> extraContext = du.createContextMap(new RequestMap(request),
HttpParameters.create(request.getParameterMap()).build(),
session,
new ApplicationMap(pageContext.getServletContext()),
request,
response);
HttpParameters.create(request.getParameterMap()).build(),
session,
new ApplicationMap(pageContext.getServletContext()),
request,
response);
// let's not set the locale -- there is a test that checks if Dispatcher actually picks this up...
// ... but generally we want to just use no locale (let it stay system default)
extraContext = ActionContext.of(extraContext).withLocale(null).getContextMap();
@@ -149,18 +144,18 @@ public abstract class AbstractTagTest extends StrutsInternalTestCase {
}
/**
* Compare if two component tags are considered equal according to their fields as accessed
* Compare if two component tags are considered equal according to their fields as accessed
* via reflection.
*
* Utilizes {@link EqualsBuilder#reflectionEquals(java.lang.Object, java.lang.Object, boolean)} to perform
* <p>
* Utilizes {@link EqualsBuilder#reflectionEquals(java.lang.Object, java.lang.Object, boolean)} to perform
* the check, and compares transient fields as well. This may fail when run while a security manager is
* active, due to a need to user reflection.
*
* <p>
* This method may be useful for checking if the state of a tag is what is expected after a given set of operations,
* or after clearing state such as for calls involving {@link StrutsBodyTagSupport#clearTagStateForTagPoolingServers()}
* has taken place following {@link StrutsBodyTagSupport#doEndTag()} processing. When making comparisons, keep in mind the
* pageContext and parent Tag state are not cleared by clearTagStateForTagPoolingServers().
*
*
* @param tag1 the first {@link StrutsBodyTagSupport} to compare against the other.
* @param tag2 the second {@link StrutsBodyTagSupport} to compare against the other.
* @return true if the Tags are equal based on field comparisons by reflection, false otherwise.
@@ -170,15 +165,16 @@ public abstract class AbstractTagTest extends StrutsInternalTestCase {
}
/**
* Helper method to simplify setting the performClearTagStateForTagPoolingServers state for a
* {@link ComponentTagSupport} tag's {@link Component} to match expectations for the test.
*
* Helper method to simplify setting the performClearTagStateForTagPoolingServers state for a
* {@link ComponentTagSupport} tag's {@link import org.apache.struts2.components.Component} to match expectations
* for the test.
* <p>
* The component reference is not available to the tag until after the doStartTag() method is called.
* We need to ensure the component's {@link Component#performClearTagStateForTagPoolingServers} state matches
* what we set for the Tag when a non-default (true) value is used, so this method accesses the component instance,
* sets the value specified and forces the tag's parameters to be repopulated again.
*
* @param tag The ComponentTagSupport tag upon whose component we will set the performClearTagStateForTagPoolingServers state.
* We need to ensure the component's {@link import org.apache.struts2.components.Component#performClearTagStateForTagPoolingServers}
* state matches what we set for the Tag when a non-default (true) value is used, so this method accesses
* the component instance, sets the value specified and forces the tag's parameters to be repopulated again.
*
* @param tag The ComponentTagSupport tag upon whose component we will set the performClearTagStateForTagPoolingServers state.
* @param performClearTagStateForTagPoolingServers true to clear tag state, false otherwise
*/
protected void setComponentTagClearTagState(ComponentTagSupport tag, boolean performClearTagStateForTagPoolingServers) {
@@ -18,77 +18,82 @@
*/
package org.apache.struts2.views.jsp.ui;
import org.apache.struts2.SomeEnum;
import org.apache.struts2.TestAction;
import org.apache.struts2.views.jsp.AbstractUITagTest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
/**
*
*/
public class RadioTest extends AbstractUITagTest {
public void testMapWithBooleanAsKey() throws Exception {
TestAction testAction = (TestAction) action;
HashMap map = new LinkedHashMap();
map.put(Boolean.TRUE, "male");
map.put(Boolean.FALSE, "female");
testAction.setMap(map);
RadioTag tag = new RadioTag();
tag.setPageContext(pageContext);
tag.setLabel("mylabel");
tag.setName("myname");
tag.setValue("%{true}");
tag.setList("map");
tag.doStartTag();
tag.doEndTag();
verify(RadioTag.class.getResource("Radio-3.txt"));
// Basic sanity check of clearTagStateForTagPoolingServers() behaviour for Struts Tags after doEndTag().
RadioTag freshTag = new RadioTag();
freshTag.setPageContext(pageContext);
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testMapWithBooleanAsKey() throws Exception {
TestAction testAction = (TestAction) action;
public void testMapWithBooleanAsKey_clearTagStateSet() throws Exception {
TestAction testAction = (TestAction) action;
Map<Boolean, String> map = new LinkedHashMap<>();
map.put(Boolean.TRUE, "male");
map.put(Boolean.FALSE, "female");
testAction.setMap(map);
HashMap map = new LinkedHashMap();
map.put(Boolean.TRUE, "male");
map.put(Boolean.FALSE, "female");
testAction.setMap(map);
RadioTag tag = new RadioTag();
tag.setPageContext(pageContext);
tag.setLabel("mylabel");
tag.setName("myname");
tag.setValue("%{true}");
tag.setList("map");
RadioTag tag = new RadioTag();
tag.setPerformClearTagStateForTagPoolingServers(true); // Explicitly request tag state clearing.
tag.setPageContext(pageContext);
tag.setLabel("mylabel");
tag.setName("myname");
tag.setValue("%{true}");
tag.setList("map");
tag.doStartTag();
tag.doEndTag();
tag.doStartTag();
setComponentTagClearTagState(tag, true); // Ensure component tag state clearing is set true (to match tag).
tag.doEndTag();
verify(RadioTag.class.getResource("Radio-3.txt"));
verify(RadioTag.class.getResource("Radio-3.txt"));
// Basic sanity check of clearTagStateForTagPoolingServers() behaviour for Struts Tags after doEndTag().
RadioTag freshTag = new RadioTag();
freshTag.setPageContext(pageContext);
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
// Basic sanity check of clearTagStateForTagPoolingServers() behaviour for Struts Tags after doEndTag().
RadioTag freshTag = new RadioTag();
freshTag.setPerformClearTagStateForTagPoolingServers(true);
freshTag.setPageContext(pageContext);
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testMapWithBooleanAsKey_clearTagStateSet() throws Exception {
TestAction testAction = (TestAction) action;
Map<Boolean, String> map = new LinkedHashMap<>();
map.put(Boolean.TRUE, "male");
map.put(Boolean.FALSE, "female");
testAction.setMap(map);
RadioTag tag = new RadioTag();
tag.setPerformClearTagStateForTagPoolingServers(true); // Explicitly request tag state clearing.
tag.setPageContext(pageContext);
tag.setLabel("mylabel");
tag.setName("myname");
tag.setValue("%{true}");
tag.setList("map");
tag.doStartTag();
setComponentTagClearTagState(tag, true); // Ensure component tag state clearing is set true (to match tag).
tag.doEndTag();
verify(RadioTag.class.getResource("Radio-3.txt"));
// Basic sanity check of clearTagStateForTagPoolingServers() behaviour for Struts Tags after doEndTag().
RadioTag freshTag = new RadioTag();
freshTag.setPerformClearTagStateForTagPoolingServers(true);
freshTag.setPageContext(pageContext);
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testMapChecked() throws Exception {
TestAction testAction = (TestAction) action;
@@ -103,7 +108,7 @@ public class RadioTest extends AbstractUITagTest {
tag.setPageContext(pageContext);
tag.setLabel("mylabel");
tag.setName("myname");
tag.setValue("\"1\"");
tag.setValue("1");
tag.setList("map");
tag.setListKey("key");
tag.setListValue("value");
@@ -118,7 +123,7 @@ public class RadioTest extends AbstractUITagTest {
freshTag.setPageContext(pageContext);
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testMapChecked_clearTagStateSet() throws Exception {
@@ -135,7 +140,7 @@ public class RadioTest extends AbstractUITagTest {
tag.setPageContext(pageContext);
tag.setLabel("mylabel");
tag.setName("myname");
tag.setValue("\"1\"");
tag.setValue("1");
tag.setList("map");
tag.setListKey("key");
tag.setListValue("value");
@@ -152,14 +157,122 @@ public class RadioTest extends AbstractUITagTest {
freshTag.setPageContext(pageContext);
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testMapCheckedUsingEnum() throws Exception {
TestAction testAction = (TestAction) action;
List<SomeEnum> enumList = new ArrayList<>(Arrays.asList(SomeEnum.values()));
testAction.setEnumList(enumList);
RadioTag tag = new RadioTag();
tag.setTheme("simple");
tag.setPageContext(pageContext);
tag.setName("status");
tag.setValue("INIT");
tag.setList("enumList");
tag.doStartTag();
tag.doEndTag();
verify(RadioTag.class.getResource("Radio-9.txt"));
// Basic sanity check of clearTagStateForTagPoolingServers() behaviour for Struts Tags after doEndTag().
RadioTag freshTag = new RadioTag();
freshTag.setPageContext(pageContext);
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testMapCheckedUsingEnum_clearTagStateSet() throws Exception {
TestAction testAction = (TestAction) action;
List<SomeEnum> enumList = new ArrayList<>(Arrays.asList(SomeEnum.values()));
testAction.setEnumList(enumList);
RadioTag tag = new RadioTag();
tag.setPerformClearTagStateForTagPoolingServers(true); // Explicitly request tag state clearing.
tag.setTheme("simple");
tag.setPageContext(pageContext);
tag.setName("status");
tag.setValue("INIT");
tag.setList("enumList");
tag.doStartTag();
tag.doEndTag();
verify(RadioTag.class.getResource("Radio-9.txt"));
// Basic sanity check of clearTagStateForTagPoolingServers() behaviour for Struts Tags after doEndTag().
RadioTag freshTag = new RadioTag();
freshTag.setPerformClearTagStateForTagPoolingServers(true);
freshTag.setPageContext(pageContext);
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testMapCheckedUsingInteger() throws Exception {
TestAction testAction = (TestAction) action;
List<Integer> intList = new ArrayList<>(Arrays.asList(1, 2));
testAction.setIntList(intList);
RadioTag tag = new RadioTag();
tag.setTheme("simple");
tag.setPageContext(pageContext);
tag.setName("status");
tag.setValue("2");
tag.setList("intList");
tag.doStartTag();
tag.doEndTag();
verify(RadioTag.class.getResource("Radio-10.txt"));
// Basic sanity check of clearTagStateForTagPoolingServers() behaviour for Struts Tags after doEndTag().
RadioTag freshTag = new RadioTag();
freshTag.setPageContext(pageContext);
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testMapCheckedUsingInt_clearTagStateSet() throws Exception {
TestAction testAction = (TestAction) action;
List<Integer> intList = new ArrayList<>(Arrays.asList(1, 2));
testAction.setIntList(intList);
RadioTag tag = new RadioTag();
tag.setPerformClearTagStateForTagPoolingServers(true); // Explicitly request tag state clearing.
tag.setTheme("simple");
tag.setPageContext(pageContext);
tag.setName("status");
tag.setValue("2");
tag.setList("intList");
tag.doStartTag();
tag.doEndTag();
verify(RadioTag.class.getResource("Radio-10.txt"));
// Basic sanity check of clearTagStateForTagPoolingServers() behaviour for Struts Tags after doEndTag().
RadioTag freshTag = new RadioTag();
freshTag.setPerformClearTagStateForTagPoolingServers(true);
freshTag.setPageContext(pageContext);
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testMapCheckedNull() throws Exception {
TestAction testAction = (TestAction) action;
testAction.setFoo("bar");
HashMap map = new HashMap();
Map<String, String> map = new HashMap<>();
map.put("1", "One");
map.put("2", "Two");
testAction.setMap(map);
@@ -181,14 +294,14 @@ public class RadioTest extends AbstractUITagTest {
freshTag.setPageContext(pageContext);
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testMapCheckedNull_clearTagStateSet() throws Exception {
TestAction testAction = (TestAction) action;
testAction.setFoo("bar");
HashMap map = new HashMap();
Map<String, String> map = new HashMap<>();
map.put("1", "One");
map.put("2", "Two");
testAction.setMap(map);
@@ -213,15 +326,15 @@ public class RadioTest extends AbstractUITagTest {
freshTag.setPageContext(pageContext);
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testSimple() throws Exception {
TestAction testAction = (TestAction) action;
testAction.setFoo("bar");
testAction.setList(new String[][]{
{"hello", "world"},
{"foo", "bar"}
{"hello", "world"},
{"foo", "bar"}
});
RadioTag tag = new RadioTag();
@@ -243,15 +356,15 @@ public class RadioTest extends AbstractUITagTest {
freshTag.setPageContext(pageContext);
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testSimple_clearTagStateSet() throws Exception {
TestAction testAction = (TestAction) action;
testAction.setFoo("bar");
testAction.setList(new String[][]{
{"hello", "world"},
{"foo", "bar"}
{"hello", "world"},
{"foo", "bar"}
});
RadioTag tag = new RadioTag();
@@ -276,13 +389,15 @@ public class RadioTest extends AbstractUITagTest {
freshTag.setPageContext(pageContext);
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testSimpleWithStringMap() throws Exception {
final Map<String, String> myMap = new TreeMap<String, String>();
final Map<String, String> myMap = new TreeMap<>();
myMap.put("name", "Std.");
stack.push(new HashMap() {{ put ("myMap", myMap); }});
stack.push(new HashMap<String, Map<String, String>>() {{
put("myMap", myMap);
}});
RadioTag tag = new RadioTag();
tag.setPageContext(pageContext);
@@ -298,13 +413,15 @@ public class RadioTest extends AbstractUITagTest {
freshTag.setPageContext(pageContext);
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testSimpleWithStringMap_clearTagStateSet() throws Exception {
final Map<String, String> myMap = new TreeMap<String, String>();
final Map<String, String> myMap = new TreeMap<>();
myMap.put("name", "Std.");
stack.push(new HashMap() {{ put ("myMap", myMap); }});
stack.push(new HashMap<String, Map<String, String>>() {{
put("myMap", myMap);
}});
RadioTag tag = new RadioTag();
tag.setPerformClearTagStateForTagPoolingServers(true); // Explicitly request tag state clearing.
@@ -323,15 +440,15 @@ public class RadioTest extends AbstractUITagTest {
freshTag.setPageContext(pageContext);
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testSimpleWithLabelSeparator() throws Exception {
TestAction testAction = (TestAction) action;
testAction.setFoo("bar");
testAction.setList(new String[][]{
{"hello", "world"},
{"foo", "bar"}
{"hello", "world"},
{"foo", "bar"}
});
RadioTag tag = new RadioTag();
@@ -354,15 +471,15 @@ public class RadioTest extends AbstractUITagTest {
freshTag.setPageContext(pageContext);
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testSimpleWithLabelSeparator_clearTagStateSet() throws Exception {
TestAction testAction = (TestAction) action;
testAction.setFoo("bar");
testAction.setList(new String[][]{
{"hello", "world"},
{"foo", "bar"}
{"hello", "world"},
{"foo", "bar"}
});
RadioTag tag = new RadioTag();
@@ -388,27 +505,27 @@ public class RadioTest extends AbstractUITagTest {
freshTag.setPageContext(pageContext);
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testGenericSimple() throws Exception {
RadioTag tag = new RadioTag();
prepareTagGeneric(tag);
verifyGenericProperties(tag, "simple", new String[]{"id","value"});
verifyGenericProperties(tag, "simple", new String[]{"id", "value"});
}
public void testGenericXhtml() throws Exception {
RadioTag tag = new RadioTag();
prepareTagGeneric(tag);
verifyGenericProperties(tag, "xhtml", new String[]{"id","value"});
verifyGenericProperties(tag, "xhtml", new String[]{"id", "value"});
}
public void testDynamicAttributes() throws Exception {
TestAction testAction = (TestAction) action;
testAction.setFoo("bar");
testAction.setList(new String[][]{
{"hello", "world"},
{"foo", "bar"}
{"hello", "world"},
{"foo", "bar"}
});
RadioTag tag = new RadioTag();
@@ -431,15 +548,15 @@ public class RadioTest extends AbstractUITagTest {
freshTag.setPageContext(pageContext);
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testDynamicAttributes_clearTagStateSet() throws Exception {
TestAction testAction = (TestAction) action;
testAction.setFoo("bar");
testAction.setList(new String[][]{
{"hello", "world"},
{"foo", "bar"}
{"hello", "world"},
{"foo", "bar"}
});
RadioTag tag = new RadioTag();
@@ -465,7 +582,7 @@ public class RadioTest extends AbstractUITagTest {
freshTag.setPageContext(pageContext);
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testNotExistingListValueKey() throws Exception {
@@ -487,7 +604,7 @@ public class RadioTest extends AbstractUITagTest {
freshTag.setPageContext(pageContext);
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testNotExistingListValueKey_clearTagStateSet() throws Exception {
@@ -512,15 +629,15 @@ public class RadioTest extends AbstractUITagTest {
freshTag.setPageContext(pageContext);
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
private void prepareTagGeneric(RadioTag tag) {
TestAction testAction = (TestAction) action;
testAction.setFoo("bar");
testAction.setList(new String[][]{
{"hello", "world"},
{"foo", "bar"}
{"hello", "world"},
{"foo", "bar"}
});
tag.setList("list");
tag.setListKey("top[0]");
@@ -47,6 +47,8 @@ public class TextareaTest extends AbstractUITagTest {
tag.setId("the_id");
tag.setOnkeyup("alert('hello');");
tag.setReadonly("true");
tag.setMaxlength("100");
tag.setMinlength("10");
tag.doStartTag();
tag.doEndTag();
@@ -81,6 +83,8 @@ public class TextareaTest extends AbstractUITagTest {
tag.setId("the_id");
tag.setOnkeyup("alert('hello');");
tag.setReadonly("true");
tag.setMaxlength("100");
tag.setMinlength("10");
tag.doStartTag();
setComponentTagClearTagState(tag, true); // Ensure component tag state clearing is set true (to match tag).
@@ -20,6 +20,6 @@
*/
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.6//EN"
"http://struts.apache.org/dtds/struts-2.6.dtd">
<struts />
"-//Apache Software Foundation//DTD Struts Configuration 6.0//EN"
"struts-6.0.dtd">
<struts/>
@@ -21,14 +21,14 @@
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.6//EN"
"http://struts.apache.org/dtds/struts-2.6.dtd">
"-//Apache Software Foundation//DTD Struts Configuration 6.0//EN"
"struts-6.0.dtd">
<struts>
<!-- CUSTOM ACTION MAPPER -->
<bean type="org.apache.struts2.dispatcher.mapper.ActionMapper"
name="WW3090" class="org.apache.struts2.views.jsp.URLTagTest$RedBlueActionMapper" />
<constant name="struts.mapper.class" value="WW3090" />
<!-- CUSTOM ACTION MAPPER -->
<bean type="org.apache.struts2.dispatcher.mapper.ActionMapper"
name="WW3090" class="org.apache.struts2.views.jsp.URLTagTest$RedBlueActionMapper"/>
<constant name="struts.mapper.class" value="WW3090"/>
</struts>
@@ -0,0 +1,4 @@
<input type="radio" name="status" id="status1" value="1"/>
<label for="status1">1</label>
<input type="radio" name="status" id="status2" checked="checked" value="2"/>
<label for="status2">2</label>
@@ -0,0 +1,4 @@
<input type="radio" name="status" id="statusINIT" checked="checked" value="INIT"/>
<label for="statusINIT">INIT</label>
<input type="radio" name="status" id="statusCOMPLETED" value="COMPLETED"/>
<label for="statusCOMPLETED">COMPLETED</label>
@@ -1,4 +1,4 @@
<tr>
<td class="tdLabel"><label for="the_id" class="label">mylabel:</label></td>
<td class="tdInput"><textarea name="myname" cols="20" rows="30" disabled="disabled" readonly="readonly" tabindex="5" id="the_id" title="mytitle" onclick="alert('onclick');" onkeyup="alert('hello');" onchange="alert('goodbye');">bar</textarea></td>
<td class="tdInput"><textarea name="myname" cols="20" rows="30" disabled="disabled" readonly="readonly" tabindex="5" id="the_id" title="mytitle" maxlength="100" minlength="10" onclick="alert('onclick');" onkeyup="alert('hello');" onchange="alert('goodbye');">bar</textarea></td>
</tr>
@@ -20,8 +20,8 @@
*/
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.6//EN"
"http://struts.apache.org/dtds/struts-2.6.dtd">
"-//Apache Software Foundation//DTD Struts Configuration 6.0//EN"
"struts-6.0.dtd">
<struts>
<constant name="struts.ui.escapeHtmlBody" value="true"/>
+6 -6
View File
@@ -20,8 +20,8 @@
*/
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.6//EN"
"http://struts.apache.org/dtds/struts-2.6.dtd">
"-//Apache Software Foundation//DTD Struts Configuration 6.0//EN"
"struts-6.0.dtd">
<struts>
<package name="default" extends="struts-default">
<action name="hello" class="com.opensymphony.xwork2.ActionSupport">
@@ -56,25 +56,25 @@
</result>
</action>
<action name="test5" class="com.opensymphony.xwork2.ActionSupport">
<action name="test5" class="com.opensymphony.xwork2.ActionSupport">
<result type="freemarker">
<param name="location">org/apache/struts2/views/freemarker/callActionFreeMarker2.ftl</param>
</result>
</action>
<action name="test6" class="com.opensymphony.xwork2.ActionSupport">
<action name="test6" class="com.opensymphony.xwork2.ActionSupport">
<result type="freemarker">
<param name="location">org/apache/struts2/views/freemarker/dynaAttributes.ftl</param>
</result>
</action>
<action name="test7" class="com.opensymphony.xwork2.ActionSupport">
<action name="test7" class="com.opensymphony.xwork2.ActionSupport">
<result type="freemarker">
<param name="location">org/apache/struts2/views/freemarker/manual-list.ftl</param>
</result>
</action>
<action name="test8" class="com.opensymphony.xwork2.ActionSupport">
<action name="test8" class="com.opensymphony.xwork2.ActionSupport">
<result type="freemarker">
<param name="location">org/apache/struts2/views/freemarker/customTextField.ftl</param>
</result>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.0.0</version>
<version>6.0.3</version>
</parent>
<artifactId>struts2-async-plugin</artifactId>
@@ -24,7 +24,7 @@ import java.util.concurrent.Executor;
/**
* A {@link Callable} with a timeout value and an {@link Executor}.
*
* @since 2.6
* @since 6.0.0
*/
public class AsyncAction implements Callable {
@@ -35,7 +35,7 @@ import java.util.concurrent.atomic.AtomicInteger;
/**
* Implements {@link AsyncManager} to add support for invoke async actions via Servlet 3's API.
*
* @since 2.6
* @since 6.0.0
*/
public class DefaultAsyncManager implements AsyncManager, AsyncListener {
private static final Logger LOG = LogManager.getLogger(DefaultAsyncManager.class);
+3 -3
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.0.0</version>
<version>6.0.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -45,9 +45,9 @@
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.1.2.Final</version>
<version>${hibernate-validator.version}</version>
<scope>test</scope>
</dependency>
<dependency>
+1 -1
View File
@@ -25,7 +25,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.0.0</version>
<version>6.0.3</version>
</parent>
<artifactId>struts2-cdi-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.0.0</version>
<version>6.0.3</version>
</parent>
<artifactId>struts2-config-browser-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.0.0</version>
<version>6.0.3</version>
</parent>
<artifactId>struts2-convention-plugin</artifactId>
@@ -38,6 +38,7 @@ import org.objectweb.asm.Opcodes;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
@@ -45,7 +46,15 @@ import java.lang.reflect.Method;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
@@ -57,9 +66,9 @@ public class DefaultClassFinder implements ClassFinder {
private final List<String> classesNotLoaded = new ArrayList<>();
private boolean extractBaseInterfaces;
private ClassLoaderInterface classLoaderInterface;
private FileManager fileManager;
private final ClassLoaderInterface classLoaderInterface;
private final boolean extractBaseInterfaces;
private final FileManager fileManager;
public DefaultClassFinder(ClassLoaderInterface classLoaderInterface, Collection<URL> urls, boolean extractBaseInterfaces, Set<String> protocols, Test<String> classNameFilter) {
this.classLoaderInterface = classLoaderInterface;
@@ -97,46 +106,6 @@ public class DefaultClassFinder implements ClassFinder {
}
}
public DefaultClassFinder(Class... classes){
this(Arrays.asList(classes));
}
public DefaultClassFinder(List<Class> classes){
this.classLoaderInterface = null;
List<Info> infos = new ArrayList<>();
List<Package> packages = new ArrayList<>();
for (Class clazz : classes) {
Package aPackage = clazz.getPackage();
if (aPackage != null && !packages.contains(aPackage)){
infos.add(new PackageInfo(aPackage));
packages.add(aPackage);
}
ClassInfo classInfo = new ClassInfo(clazz, this);
infos.add(classInfo);
classInfos.put(classInfo.getName(), classInfo);
for (Method method : clazz.getDeclaredMethods()) {
infos.add(new MethodInfo(classInfo, method));
}
for (Constructor constructor : clazz.getConstructors()) {
infos.add(new MethodInfo(classInfo, constructor));
}
for (Field field : clazz.getDeclaredFields()) {
infos.add(new FieldInfo(classInfo, field));
}
}
for (Info info : infos) {
for (AnnotationInfo annotation : info.getAnnotations()) {
List<Info> annotationInfos = getAnnotationInfos(annotation.getName());
annotationInfos.add(info);
}
}
}
public ClassLoaderInterface getClassLoaderInterface() {
return classLoaderInterface;
}
@@ -171,15 +140,15 @@ public class DefaultClassFinder implements ClassFinder {
return packages;
}
public List<Class> findAnnotatedClasses(Class<? extends Annotation> annotation) {
public List<Class<?>> findAnnotatedClasses(Class<? extends Annotation> annotation) {
classesNotLoaded.clear();
List<Class> classes = new ArrayList<>();
List<Class<?>> classes = new ArrayList<>();
List<Info> infos = getAnnotationInfos(annotation.getName());
for (Info info : infos) {
if (info instanceof ClassInfo) {
ClassInfo classInfo = (ClassInfo) info;
try {
Class clazz = classInfo.get();
Class<?> clazz = classInfo.get();
// double check via proper reflection
if (clazz.isAnnotationPresent(annotation)) {
classes.add(clazz);
@@ -208,7 +177,7 @@ public class DefaultClassFinder implements ClassFinder {
seen.add(classInfo);
try {
Class clazz = classInfo.get();
Class<?> clazz = classInfo.get();
for (Method method : clazz.getDeclaredMethods()) {
if (method.isAnnotationPresent(annotation)) {
methods.add(method);
@@ -223,10 +192,10 @@ public class DefaultClassFinder implements ClassFinder {
return methods;
}
public List<Constructor> findAnnotatedConstructors(Class<? extends Annotation> annotation) {
public List<Constructor<?>> findAnnotatedConstructors(Class<? extends Annotation> annotation) {
classesNotLoaded.clear();
List<ClassInfo> seen = new ArrayList<>();
List<Constructor> constructors = new ArrayList<>();
List<Constructor<?>> constructors = new ArrayList<>();
List<Info> infos = getAnnotationInfos(annotation.getName());
for (Info info : infos) {
if (info instanceof MethodInfo && "<init>".equals(info.getName())) {
@@ -238,8 +207,8 @@ public class DefaultClassFinder implements ClassFinder {
seen.add(classInfo);
try {
Class clazz = classInfo.get();
for (Constructor constructor : clazz.getConstructors()) {
Class<?> clazz = classInfo.get();
for (Constructor<?> constructor : clazz.getConstructors()) {
if (constructor.isAnnotationPresent(annotation)) {
constructors.add(constructor);
}
@@ -270,7 +239,7 @@ public class DefaultClassFinder implements ClassFinder {
seen.add(classInfo);
try {
Class clazz = classInfo.get();
Class<?> clazz = classInfo.get();
for (Field field : clazz.getDeclaredFields()) {
if (field.isAnnotationPresent(annotation)) {
fields.add(field);
@@ -285,14 +254,14 @@ public class DefaultClassFinder implements ClassFinder {
return fields;
}
public List<Class> findClassesInPackage(String packageName, boolean recursive) {
public List<Class<?>> findClassesInPackage(String packageName, boolean recursive) {
classesNotLoaded.clear();
List<Class> classes = new ArrayList<>();
List<Class<?>> classes = new ArrayList<>();
for (ClassInfo classInfo : classInfos.values()) {
try {
if (recursive && classInfo.getPackageName().startsWith(packageName)){
if (recursive && classInfo.getPackageName().startsWith(packageName)) {
classes.add(classInfo.get());
} else if (classInfo.getPackageName().equals(packageName)){
} else if (classInfo.getPackageName().equals(packageName)) {
classes.add(classInfo.get());
}
} catch (Throwable e) {
@@ -303,9 +272,9 @@ public class DefaultClassFinder implements ClassFinder {
return classes;
}
public List<Class> findClasses(Test<ClassInfo> test) {
public List<Class<?>> findClasses(Test<ClassInfo> test) {
classesNotLoaded.clear();
List<Class> classes = new ArrayList<>();
List<Class<?>> classes = new ArrayList<>();
for (ClassInfo classInfo : classInfos.values()) {
try {
if (test.test(classInfo)) {
@@ -319,9 +288,9 @@ public class DefaultClassFinder implements ClassFinder {
return classes;
}
public List<Class> findClasses() {
public List<Class<?>> findClasses() {
classesNotLoaded.clear();
List<Class> classes = new ArrayList<>();
List<Class<?>> classes = new ArrayList<>();
for (ClassInfo classInfo : classInfos.values()) {
try {
classes.add(classInfo.get());
@@ -333,9 +302,9 @@ public class DefaultClassFinder implements ClassFinder {
return classes;
}
private List<String> file(URL location) {
private List<String> file(URL location) throws UnsupportedEncodingException {
List<String> classNames = new ArrayList<>();
File dir = new File(URLDecoder.decode(location.getPath()));
File dir = new File(URLDecoder.decode(location.getPath(), "UTF-8"));
if ("META-INF".equals(dir.getName())) {
dir = dir.getParentFile(); // Scrape "META-INF" off
}
@@ -347,15 +316,17 @@ public class DefaultClassFinder implements ClassFinder {
private void scanDir(File dir, List<String> classNames, String packageName) {
File[] files = dir.listFiles();
for (File file : files) {
if (file.isDirectory()) {
scanDir(file, classNames, packageName + file.getName() + ".");
} else if (file.getName().endsWith(".class")) {
String name = file.getName();
name = name.replaceFirst(".class$", "");
// Classes packaged in an exploded .war (e.g. in a VFS file system) should not
// have WEB-INF.classes in their package name.
classNames.add(StringUtils.removeStart(packageName, "WEB-INF.classes.") + name);
if (files != null) {
for (File file : files) {
if (file.isDirectory()) {
scanDir(file, classNames, packageName + file.getName() + ".");
} else if (file.getName().endsWith(".class")) {
String name = file.getName();
name = name.replaceFirst(".class$", "");
// Classes packaged in an exploded .war (e.g. in a VFS file system) should not
// have WEB-INF.classes in their package name.
classNames.add(StringUtils.removeStart(packageName, "WEB-INF.classes.") + name);
}
}
}
}
@@ -363,12 +334,9 @@ public class DefaultClassFinder implements ClassFinder {
private List<String> jar(URL location) throws IOException {
URL url = fileManager.normalizeToFileProtocol(location);
if (url != null) {
InputStream in = url.openStream();
try {
try (InputStream in = url.openStream()) {
JarInputStream jarStream = new JarInputStream(in);
return jar(jarStream);
} finally {
in.close();
}
} else {
LOG.debug("Unable to read [{}]", location.toExternalForm());
@@ -388,7 +356,7 @@ public class DefaultClassFinder implements ClassFinder {
className = className.replaceFirst(".class$", "");
//war files are treated as .jar files, so takeout WEB-INF/classes
className = StringUtils.removeStart(className, "WEB-INF/classes/");
className = StringUtils.removeStart(className, "WEB-INF/classes/");
className = className.replace('/', '.');
classNames.add(className);
@@ -397,40 +365,8 @@ public class DefaultClassFinder implements ClassFinder {
return classNames;
}
public class PackageInfo extends Annotatable implements Info {
private final String name;
private final ClassInfo info;
private final Package pkg;
public PackageInfo(Package pkg){
super(pkg);
this.pkg = pkg;
this.name = pkg.getName();
this.info = null;
}
public PackageInfo(String name, ClassFinder classFinder) {
info = new ClassInfo(name, null, classFinder);
this.name = name;
this.pkg = null;
}
public String getName() {
return name;
}
public Package get() throws ClassNotFoundException {
return (pkg != null)?pkg:info.get().getPackage();
}
}
private List<Info> getAnnotationInfos(String name) {
List<Info> infos = annotated.get(name);
if (infos == null) {
infos = new ArrayList<>();
annotated.put(name, infos);
}
return infos;
return annotated.computeIfAbsent(name, k -> new ArrayList<>());
}
private void readClassDef(String className) {
@@ -454,19 +390,16 @@ public class DefaultClassFinder implements ClassFinder {
}
public class InfoBuildingVisitor extends ClassVisitor {
private final ClassFinder classFinder;
private Info info;
private ClassFinder classFinder;
public InfoBuildingVisitor(ClassFinder classFinder) {
super(Opcodes.ASM7);
super(Opcodes.ASM9);
this.classFinder = classFinder;
}
public InfoBuildingVisitor(Info info, ClassFinder classFinder) {
this(classFinder);
this.info = info;
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
if (name.endsWith("package-info")) {
@@ -480,8 +413,9 @@ public class DefaultClassFinder implements ClassFinder {
info = classInfo;
classInfos.put(classInfo.getName(), classInfo);
if (extractBaseInterfaces)
if (extractBaseInterfaces) {
extractSuperInterfaces(classInfo);
}
}
}
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.0.0</version>
<version>6.0.3</version>
</parent>
<artifactId>struts2-dwr-plugin</artifactId>
+2 -2
View File
@@ -24,12 +24,12 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.0.0</version>
<version>6.0.3</version>
</parent>
<artifactId>struts2-embeddedjsp-plugin</artifactId>
<packaging>jar</packaging>
<name>DEPRECATED: Struts 2 Embedded JSP Plugin (since 2.6)</name>
<name>DEPRECATED: Struts 2 Embedded JSP Plugin, since 6.0.0</name>
<dependencies>
<dependency>
+2 -2
View File
@@ -24,12 +24,12 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.0.0</version>
<version>6.0.3</version>
</parent>
<artifactId>struts2-gxp-plugin</artifactId>
<packaging>jar</packaging>
<name>DEPRECATED: Struts 2 GXP Plugin - since 2.6</name>
<name>DEPRECATED: Struts 2 GXP Plugin - since 6.0.0</name>
<dependencies>
<dependency>
+6 -2
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.0.0</version>
<version>6.0.3</version>
</parent>
<artifactId>struts2-jasperreports-plugin</artifactId>
@@ -39,7 +39,7 @@
<dependency>
<groupId>net.sf.jasperreports</groupId>
<artifactId>jasperreports</artifactId>
<version>6.17.0</version>
<version>6.19.1</version>
<scope>provided</scope>
<exclusions>
<!-- not necessary to compile and it force dependency convergence issues -->
@@ -51,6 +51,10 @@
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</exclusion>
<exclusion>
<groupId>com.lowagie</groupId>
<artifactId>itext</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
@@ -512,7 +512,7 @@ public class JasperReportsResult extends StrutsResultSupport implements JasperRe
*
* @param expression of result
* @return true|false
* @since 2.6
* @since 6.0.0
*/
protected boolean isAcceptableExpression(String expression) {
NotExcludedAcceptedPatternsChecker.IsAllowed isAllowed = notExcludedAcceptedPatterns.isAllowed(expression);
+1 -1
View File
@@ -25,7 +25,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.0.0</version>
<version>6.0.3</version>
</parent>
<artifactId>struts2-javatemplates-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.0.0</version>
<version>6.0.3</version>
</parent>
<artifactId>struts2-jfreechart-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.0.0</version>
<version>6.0.3</version>
</parent>
<artifactId>struts2-json-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.0.0</version>
<version>6.0.3</version>
</parent>
<artifactId>struts2-junit-plugin</artifactId>
+2 -2
View File
@@ -24,12 +24,12 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.0.0</version>
<version>6.0.3</version>
</parent>
<artifactId>struts2-osgi-plugin</artifactId>
<packaging>jar</packaging>
<name>DEPRECATED: Struts 2 OSGi Plugin - since 2.6</name>
<name>DEPRECATED: Struts 2 OSGi Plugin - since 6.0.0</name>
<dependencies>
<!-- Define OSGi Core and Compendium dependencies directly here for the plugin.
@@ -25,7 +25,7 @@ import java.util.List;
* registered with the type of the parameterized type
*
* @param <T> The type of the service
* @since 2.6
* @since 6.0.0
*/
public interface ServiceAware<T> {
+2 -2
View File
@@ -24,12 +24,12 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.0.0</version>
<version>6.0.3</version>
</parent>
<artifactId>struts2-oval-plugin</artifactId>
<packaging>jar</packaging>
<name>DEPRECATED: Struts 2 OVal Plugin (since 2.6)</name>
<name>DEPRECATED: Struts 2 OVal Plugin, since 6.0.0</name>
<dependencies>
<!-- OVal -->
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.0.0</version>
<version>6.0.3</version>
</parent>
<artifactId>struts2-pell-multipart-plugin</artifactId>
+2 -2
View File
@@ -24,12 +24,12 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.0.0</version>
<version>6.0.3</version>
</parent>
<artifactId>struts2-plexus-plugin</artifactId>
<packaging>jar</packaging>
<name>DEPRECATED: Struts 2 Plexus Plugin - since 2.6</name>
<name>DEPRECATED: Struts 2 Plexus Plugin - since 6.0.0</name>
<dependencies>
<dependency>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>6.0.0</version>
<version>6.0.3</version>
</parent>
<artifactId>struts2-plugins</artifactId>
+2 -2
View File
@@ -24,12 +24,12 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.0.0</version>
<version>6.0.3</version>
</parent>
<artifactId>struts2-portlet-mocks-plugin</artifactId>
<packaging>jar</packaging>
<name>DEPRECATED: Struts 2 Portlet Mocks Plugin - since 2.6</name>
<name>DEPRECATED: Struts 2 Portlet Mocks Plugin - since 6.0.0</name>
<dependencies>
<dependency>
+2 -2
View File
@@ -24,12 +24,12 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.0.0</version>
<version>6.0.3</version>
</parent>
<artifactId>struts2-portlet-tiles-plugin</artifactId>
<packaging>jar</packaging>
<name>DEPRECATED: Struts 2 Portlet Tiles Plugin - since 2.6</name>
<name>DEPRECATED: Struts 2 Portlet Tiles Plugin - since 6.0.0</name>
<dependencies>
<dependency>
+2 -2
View File
@@ -24,12 +24,12 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.0.0</version>
<version>6.0.3</version>
</parent>
<artifactId>struts2-portlet-plugin</artifactId>
<packaging>jar</packaging>
<name>DEPRECATED: Struts 2 Portlet Plugin - since 2.6</name>
<name>DEPRECATED: Struts 2 Portlet Plugin - since 6.0.0</name>
<dependencies>
<!-- junit and related JARs are needed for 'compile'! -->

Some files were not shown because too many files have changed in this diff Show More