Compare commits

...

42 Commits

Author SHA1 Message Date
Lukasz Lenart 5038a9209f [maven-release-plugin] prepare release STRUTS_6_7_2 2025-02-04 07:39:35 +01:00
Lukasz Lenart 2135ebf651 WW-5501 Ignores DMI related action field (#1211) 2025-02-04 06:47:43 +01:00
Kusal Kithul-Godage c0dd8dd80c Merge pull request #1210 from apache/WW-5516-attrmap-npe-67 2025-02-04 01:19:16 +11:00
Kusal Kithul-Godage c36eafa37a WW-5516 Fix AttributeMap NPE when PageContext has no request 2025-02-04 01:00:36 +11:00
Lukasz Lenart 07603b9591 [maven-release-plugin] prepare for next development iteration 2025-02-02 10:16:30 +01:00
Lukasz Lenart 3227d5929a [maven-release-plugin] prepare release STRUTS_6_7_1 2025-02-02 10:16:20 +01:00
Lukasz Lenart 4a8ddbb274 Merge pull request #1202 from apache/fix/bom-version
Defines proper snapshot version for BOM
2025-02-02 10:10:35 +01:00
Lukasz Lenart 67ba458d03 Defines proper snapshot version for BOM 2025-02-02 10:00:09 +01:00
Lukasz Lenart 959b2182b2 Merge pull request #1195 from apache/fix/proper-version
Defines proper snapshot version
2025-01-24 08:28:32 +01:00
Lukasz Lenart 6de93e77db Defines proper snapshot version 2025-01-24 07:58:39 +01:00
Lukasz Lenart 59dc9301e0 Merge pull request #1194 from apache/fix/jenkinsfile-67x
Simplifies Jenkinsfile to build Struts 6.x
2025-01-22 08:18:56 +01:00
Lukasz Lenart 2fc4a822a4 Simplifies Jenkinsfile to build Struts 6.x 2025-01-22 07:52:34 +01:00
Kusal Kithul-Godage 6a6270f62a Merge pull request #1190 from apache/WW-5516-fix-temp-theme-fallback
WW-5516 Fix TemplateDir and Theme fallback to Request/Session/Application attributes
2025-01-22 13:24:09 +11:00
Lukasz Lenart 9153228b6a Merge pull request #1192 from apache/fix/nightlies-67x
Fixes nightlies builds
2025-01-21 20:31:59 +01:00
Lukasz Lenart 5713a74edc Fixes nightlies builds 2025-01-21 18:06:11 +01:00
Kusal Kithul-Godage 32071c71ad WW-5516 Fix TemplateDir and Theme fallback to Request/Session/Application attributes 2025-01-21 20:05:24 +11:00
Lukasz Lenart 7e4791985b Merge pull request #1175 from apache/feature/WW-5512-optional-inject
WW-5512 Extends the container to support injecting optional parameters into constructor
2025-01-21 07:25:58 +01:00
Lukasz Lenart 3c1e44ca2d WW-5512 Supports mixing required and optional parameters in any order 2025-01-19 09:53:01 +01:00
Lukasz Lenart 2ba8d97ad0 WW-5512 Supports mixing required and optional parameters 2025-01-19 09:36:27 +01:00
Lukasz Lenart cba0db0b07 Merge pull request #1181 from apache/fix/WW-5501-exclude
WW-5501 Only exclude malicious file names
2025-01-16 07:28:46 +01:00
Lukasz Lenart 688162cae2 WW-5501 Only exclude malicious file names 2025-01-14 07:47:39 +01:00
Lukasz Lenart a33c162f53 WW-5512 Extends the container to support injecting optional parameters into constructor 2025-01-05 17:45:11 +01:00
Lukasz Lenart c7a6daf5ce Merge pull request #1169 from apache/fix/WW-5500
WW-5500 Extends pattern to validate multipart uploads
2025-01-05 09:20:33 +01:00
Lukasz Lenart 370c7ce17a Merge pull request #1168 from apache/fix/WW-5498
WW-5498 Adds devMode errors as action messages to avoid breaking validation logic
2025-01-05 09:16:44 +01:00
Lukasz Lenart 55989befce Merge pull request #1167 from apache/fix/excluded-packages
Excludes new & old packages
2025-01-04 14:37:47 +01:00
Lukasz Lenart 2e4a4b5e36 Excludes new & old packages 2025-01-04 13:56:31 +01:00
Lukasz Lenart 9d07c3b4c3 WW-5498 Adds devMode errors as action messages to avoid breaking validation logic 2025-01-03 17:14:12 +01:00
Lukasz Lenart 575e4e8c9b WW-5500 Extends pattern to validate multipart uploads 2025-01-03 17:10:59 +01:00
Lukasz Lenart 74388dfba7 Merge pull request #1158 from apache/fix/updates-asf-yaml
Updates .asf.yaml to match main
2024-12-23 16:11:55 +01:00
Lukasz Lenart 4ee8b02262 Updates .asf.yaml to match main 2024-12-23 15:24:10 +01:00
Lukasz Lenart c8febc2050 Merge pull request #1156 from apache/feature/WW-5501-exclude
WW-5501 Exclude malicious names
2024-12-23 15:08:58 +01:00
Lukasz Lenart 3ea126388c WW-5501 Uses StringUtils.normalizeSpace instead of sanitizeNewlines 2024-12-23 14:13:26 +01:00
Lukasz Lenart 09993e3476 WW-5501 Exclude malicious names 2024-12-23 12:05:15 +01:00
Lukasz Lenart f5688e680b Merge pull request #1150 from apache/feature/prepare-development
Sets version to 6.7.1-SNAPSHOT to prepare for another development cycle
2024-12-18 15:42:21 +01:00
Lukasz Lenart 60a3db0a63 Sets version to 6.7.1-SNAPSHOT to prepare for another development cycle 2024-12-18 14:43:29 +01:00
Lukasz Lenart 4714d27678 Merge pull request #1141 from apache/fix/WW-5493-parameters
WW-5493 Restores parameters and uses getAttributes()
2024-12-18 07:42:47 +01:00
Lukasz Lenart 4feceee5f1 WW-5493 Restores parameters and uses getAttributes()
Instead of using parameters field directly, this PR uses getAttributes() instead to avoid coupling with internal Component state
2024-12-04 08:19:34 +01:00
Lukasz Lenart 1f5305c249 [maven-release-plugin] prepare release STRUTS_6_7_0 2024-11-17 10:57:09 +01:00
Lukasz Lenart 1903e0ef33 Merge pull request #1126 from apache/feature/WW-5484-dwr
WW-5484 Marks the DWR plugin as deprecated
2024-11-14 15:55:01 +01:00
Lukasz Lenart 8a2f494c12 WW-5484 Marks the DWR plugin as deprecated 2024-11-11 17:49:54 +01:00
Lukasz Lenart 6b2e5ecd9b Merge pull request #1125 from apache/dependabot/github_actions/github/codeql-action-3.27.1
Bump github/codeql-action from 3.27.0 to 3.27.1
2024-11-11 07:37:48 +01:00
dependabot[bot] cb08d18b5a Bump github/codeql-action from 3.27.0 to 3.27.1
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.27.0 to 3.27.1.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Commits](https://github.com/github/codeql-action/compare/v3.27.0...v3.27.1)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-11 01:45:38 +00:00
74 changed files with 726 additions and 237 deletions
+11 -1
View File
@@ -11,9 +11,19 @@ notifications:
jira_options: link label worklog
github:
description: "Apache Struts is a free, open-source, MVC framework for creating elegant, modern Java web applications"
homepage: https://struts.apache.org/
del_branch_on_merge: true
protected_branches:
master:
main:
# contexts are the names of checks that must pass.
contexts:
- build
required_pull_request_reviews:
# it does not work because our github teams are private/secret, see INFRA-25666
require_code_owner_reviews: false
required_approving_review_count: 0
release/*:
# contexts are the names of checks that must pass.
contexts:
- build
+3 -3
View File
@@ -44,12 +44,12 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v4
- name: Initialize CodeQL
uses: github/codeql-action/init@v3.27.0
uses: github/codeql-action/init@v3.27.1
with:
languages: ${{ matrix.language }}
- name: Autobuild
uses: github/codeql-action/autobuild@v3.27.0
uses: github/codeql-action/autobuild@v3.27.1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3.27.0
uses: github/codeql-action/analyze@v3.27.1
with:
category: "/language:${{matrix.language}}"
+1 -1
View File
@@ -64,6 +64,6 @@ jobs:
retention-days: 5
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@3aa71356c75a8edd8430d54dff2982203a28be45 # 2.22.11
uses: github/codeql-action/upload-sarif@acb9cb18eec7e3a113ef83cff0be91e75cfd9526 # 2.22.11
with:
sarif_file: results.sarif
Vendored
+11 -54
View File
@@ -69,7 +69,7 @@ pipeline {
stages {
stage('Test & Coverage') {
steps {
sh './mvnw -B verify -Pcoverage -DskipAssembly --no-transfer-progress'
sh './mvnw -B verify -Pcoverage -DskipAssembly'
}
post {
always {
@@ -81,58 +81,15 @@ pipeline {
stage('Code Quality') {
when {
anyOf {
branch 'master'; branch 'release/struts-7-0-x'
branch 'release/struts-6-7-x'
}
}
steps {
withCredentials([string(credentialsId: 'asf-struts-sonarcloud', variable: 'SONARCLOUD_TOKEN')]) {
sh './mvnw -B -Pcoverage -DskipAssembly -Dsonar.login=${SONARCLOUD_TOKEN} verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar --no-transfer-progress'
sh './mvnw -B -Pcoverage -DskipAssembly -Dsonar.login=${SONARCLOUD_TOKEN} verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar'
}
}
}
stage('Build Source & JavaDoc') {
when {
branch 'release/struts-7-0-x'
}
steps {
dir("local-snapshots-dir/") {
deleteDir()
}
sh './mvnw -B source:jar javadoc:jar -DskipTests -DskipAssembly --no-transfer-progress'
}
}
stage('Deploy Snapshot') {
when {
branch 'release/struts-7-0-x'
}
steps {
withCredentials([file(credentialsId: 'lukaszlenart-repository-access-token', variable: 'CUSTOM_SETTINGS')]) {
sh './mvnw -s \${CUSTOM_SETTINGS} deploy -DskipTests -DskipAssembly --no-transfer-progress'
}
}
}
stage('Upload nightlies') {
when {
branch 'release/struts-7-0-x'
}
steps {
sh './mvnw -B package -DskipTests --no-transfer-progress'
sshPublisher(publishers: [
sshPublisherDesc(
configName: 'Nightlies',
transfers: [
sshTransfer(
remoteDirectory: '/struts/snapshot',
removePrefix: 'assembly/target/assembly/out',
sourceFiles: 'assembly/target/assembly/out/struts-*.zip',
cleanRemote: true
)
],
verbose: true
)
])
}
}
}
post {
always {
@@ -154,12 +111,12 @@ pipeline {
stages {
stage('Build') {
steps {
sh './mvnw -B clean install -DskipTests -DskipAssembly --no-transfer-progress'
sh './mvnw -B clean install -DskipTests -DskipAssembly'
}
}
stage('Test') {
steps {
sh './mvnw -B verify --no-transfer-progress'
sh './mvnw -B verify -DskipAssembly'
}
post {
always {
@@ -170,31 +127,31 @@ pipeline {
}
stage('Build Source & JavaDoc') {
when {
branch 'master'
branch 'release/struts-6-7-x'
}
steps {
dir("local-snapshots-dir/") {
deleteDir()
}
sh './mvnw -B source:jar javadoc:jar -DskipTests -DskipAssembly --no-transfer-progress'
sh './mvnw -B source:jar javadoc:jar -DskipTests -DskipAssembly'
}
}
stage('Deploy Snapshot') {
when {
branch 'master'
branch 'release/struts-6-7-x'
}
steps {
withCredentials([file(credentialsId: 'lukaszlenart-repository-access-token', variable: 'CUSTOM_SETTINGS')]) {
sh './mvnw -s \${CUSTOM_SETTINGS} deploy -DskipTests -DskipAssembly --no-transfer-progress'
sh './mvnw -s \${CUSTOM_SETTINGS} deploy -DskipTests -DskipAssembly'
}
}
}
stage('Upload nightlies') {
when {
branch 'master'
branch 'release/struts-6-7-x'
}
steps {
sh './mvnw -B package -DskipTests --no-transfer-progress'
sh './mvnw -B package -DskipTests'
sshPublisher(publishers: [
sshPublisherDesc(
configName: 'Nightlies',
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>6.7.0-SNAPSHOT</version>
<version>6.7.2</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.7.0-SNAPSHOT</version>
<version>6.7.2</version>
</parent>
<artifactId>struts2-rest-showcase</artifactId>
<packaging>war</packaging>
<version>6.7.0-SNAPSHOT</version>
<version>6.7.2</version>
<name>Struts 2 Rest Showcase Webapp</name>
<description>Struts 2 Rest Showcase Example</description>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-apps</artifactId>
<version>6.7.0-SNAPSHOT</version>
<version>6.7.2</version>
</parent>
<artifactId>struts2-showcase</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>6.7.0-SNAPSHOT</version>
<version>6.7.2</version>
</parent>
<artifactId>struts2-assembly</artifactId>
+4 -4
View File
@@ -25,11 +25,11 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>6.7.0-SNAPSHOT</version>
<version>6.7.2</version>
</parent>
<artifactId>struts2-bom</artifactId>
<version>6.7.0-SNAPSHOT</version>
<version>6.7.2</version>
<packaging>pom</packaging>
<name>Struts 2 Bill of Materials</name>
@@ -44,7 +44,7 @@
</licenses>
<properties>
<struts-version.version>6.7.0-SNAPSHOT</struts-version.version>
<struts-version.version>6.7.2</struts-version.version>
<maven.site.skip>true</maven.site.skip>
<maven.site.deploy.skip>true</maven.site.deploy.skip>
</properties>
@@ -190,7 +190,7 @@
</dependencyManagement>
<scm>
<tag>STRUTS_6_3_0_1</tag>
<tag>STRUTS_6_7_2</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>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-osgi-bundles</artifactId>
<version>6.7.0-SNAPSHOT</version>
<version>6.7.2</version>
</parent>
<artifactId>struts2-osgi-admin-bundle</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-osgi-bundles</artifactId>
<version>6.7.0-SNAPSHOT</version>
<version>6.7.2</version>
</parent>
<artifactId>struts2-osgi-demo-bundle</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>6.7.0-SNAPSHOT</version>
<version>6.7.2</version>
</parent>
<artifactId>struts2-osgi-bundles</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>6.7.0-SNAPSHOT</version>
<version>6.7.2</version>
</parent>
<artifactId>struts2-core</artifactId>
<packaging>jar</packaging>
@@ -242,6 +242,41 @@ class ContainerImpl implements Container {
return toArray(parameterInjectors);
}
/**
* Gets parameter injectors with nulls for optional dependencies.
*
* @param member to which the parameters belong
* @param annotations on the parameters
* @param parameterTypes parameter types
* @return injections
*/
<M extends AccessibleObject & Member> ParameterInjector<?>[] getParametersInjectorsWithNulls(
M member,
Annotation[][] annotations,
Class<?>[] parameterTypes,
String defaultName
) throws MissingDependencyException {
final List<ParameterInjector<?>> parameterInjectors = new ArrayList<>();
final Iterator<Annotation[]> annotationsIterator = Arrays.asList(annotations).iterator();
for (Class<?> parameterType : parameterTypes) {
Inject annotation = findInject(annotationsIterator.next());
String name = annotation == null ? defaultName : annotation.value();
Key<?> key = Key.newInstance(parameterType, name);
try {
parameterInjectors.add(createParameterInjector(key, member));
} catch (MissingDependencyException e) {
if (annotation != null && annotation.required()) {
throw e;
} else {
parameterInjectors.add(createNullParameterInjector(key, member));
}
}
}
return toArray(parameterInjectors);
}
<T> ParameterInjector<T> createParameterInjector(Key<T> key, Member member) throws MissingDependencyException {
final InternalFactory<? extends T> factory = getFactory(key);
if (factory == null) {
@@ -252,6 +287,23 @@ class ContainerImpl implements Container {
return new ParameterInjector<>(externalContext, factory);
}
<T> ParameterInjector<T> createNullParameterInjector(Key<T> key, Member member) throws MissingDependencyException {
final InternalFactory<? extends T> factory = new InternalFactory<T>() {
@Override
public T create(InternalContext context) {
return null;
}
@Override
public Class<? extends T> type() {
return key.getType();
}
};
final ExternalContext<T> externalContext = ExternalContext.newInstance(member, key, this);
return new ParameterInjector<>(externalContext, factory);
}
private ParameterInjector<?>[] toArray(List<ParameterInjector<?>> parameterInjections) {
return parameterInjections.toArray(new ParameterInjector[0]);
}
@@ -361,7 +413,7 @@ class ContainerImpl implements Container {
Inject inject, ContainerImpl container, Constructor<T> constructor) throws MissingDependencyException {
return constructor.getParameterTypes().length == 0
? null // default constructor.
: container.getParametersInjectors(
: container.getParametersInjectorsWithNulls(
constructor,
constructor.getParameterAnnotations(),
constructor.getParameterTypes(),
@@ -36,6 +36,7 @@ public class DefaultExcludedPatternsChecker implements ExcludedPatternsChecker {
private static final Logger LOG = LogManager.getLogger(DefaultExcludedPatternsChecker.class);
public static final String[] EXCLUDED_PATTERNS = {
"(^|\\%\\{)(#?top\\.)[^\\s]*",
"(^|\\%\\{)((#?)(top(\\.|\\['|\\[\")|\\[\\d\\]\\.)?)(dojo|struts|session|request|response|application|servlet(Request|Response|Context)|parameters|context|_memberAccess)(\\.|\\[).*",
".*(^|\\.|\\[|\\'|\"|get)class(\\(\\.|\\[|\\'|\").*",
"actionErrors|actionMessages|fieldErrors"
@@ -35,7 +35,7 @@ public final class DebugUtils {
log.error(message);
if (action instanceof ValidationAware) {
ValidationAware validationAware = (ValidationAware) action;
validationAware.addActionError(message);
validationAware.addActionMessage(message);
}
}
@@ -215,8 +215,8 @@ public class ActionComponent extends ContextBean {
HttpParameters.Builder builder = HttpParameters.create().withParent(parentParams);
if (attributes != null) {
builder = builder.withExtraParams(attributes);
if (getAttributes() != null) {
builder = builder.withExtraParams(getAttributes());
}
return builder.build();
}
@@ -71,7 +71,11 @@ public class Component {
protected boolean devMode = false;
protected boolean escapeHtmlBody = false;
protected ValueStack stack;
protected Map<String, Object> attributes;
/**
* @deprecated use {@link #getAttributes} instead of directly depending on this field
*/
@Deprecated
protected Map<String, Object> parameters;
protected ActionMapper actionMapper;
protected boolean throwExceptionOnELFailure;
protected boolean performClearTagStateForTagPoolingServers = false;
@@ -86,7 +90,7 @@ public class Component {
*/
public Component(ValueStack stack) {
this.stack = stack;
this.attributes = new LinkedHashMap<>();
this.parameters = new LinkedHashMap<>();
getComponentStack().push(this);
}
@@ -279,7 +283,7 @@ public class Component {
*/
protected StrutsException fieldError(String field, String errorMsg, Exception e) {
String msg = "tag '" + getComponentName() + "', field '" + field +
(attributes != null && attributes.containsKey("name") ? "', name '" + attributes.get("name") : "") +
(getAttributes() != null && getAttributes().containsKey("name") ? "', name '" + getAttributes().get("name") : "") +
"': " + errorMsg;
throw new StrutsException(msg, e);
}
@@ -457,7 +461,7 @@ public class Component {
* @param params the parameters to copy.
*/
public void copyParams(Map<String, Object> params) {
stack.push(attributes);
stack.push(getAttributes());
stack.push(this);
try {
for (Map.Entry<String, Object> entry : params.entrySet()) {
@@ -467,7 +471,7 @@ public class Component {
// UI component attributes may contain hypens (e.g. data-ajax), but ognl
// can't handle that, and there can't be a component property with a hypen
// so into the parameters map it goes. See WW-4493
attributes.put(key, entry.getValue());
getAttributes().put(key, entry.getValue());
} else {
stack.setValue(key, entry.getValue());
}
@@ -500,7 +504,7 @@ public class Component {
*/
@Deprecated
public Map<String, Object> getParameters() {
return attributes;
return parameters;
}
/**
@@ -509,7 +513,7 @@ public class Component {
* @return the parameters. Is never <tt>null</tt>.
*/
public Map<String, Object> getAttributes() {
return attributes;
return parameters;
}
/**
@@ -518,7 +522,7 @@ public class Component {
* @param params the parameters to add.
*/
public void addAllParameters(Map<String, Object> params) {
attributes.putAll(params);
getAttributes().putAll(params);
}
/**
@@ -204,7 +204,7 @@ public class Form extends ClosingUIBean {
// keep a collection of the tag names for anything special the templates might want to do (such as pure client
// side validation)
if (!attributes.containsKey("tagNames")) {
if (!getAttributes().containsKey("tagNames")) {
// we have this if check so we don't do this twice (on open and close of the template)
addParameter("tagNames", new ArrayList());
}
@@ -137,14 +137,13 @@ public class Include extends Component {
urlBuf.append(page);
// Add request parameters
if (attributes.size() > 0) {
if (!getAttributes().isEmpty()) {
urlBuf.append('?');
String concat = "";
// Set parameters
for (Object next : attributes.entrySet()) {
Map.Entry entry = (Map.Entry) next;
for (Map.Entry<String, Object> entry : getAttributes().entrySet()) {
Object name = entry.getKey();
List values = (List) entry.getValue();
@@ -234,11 +233,11 @@ public class Include extends Component {
// instead, include tag requires that each parameter be a list of objects,
// just like the HTTP servlet interfaces are (String[])
if (value != null) {
List currentValues = (List) attributes.get(key);
List currentValues = (List) getAttributes().get(key);
if (currentValues == null) {
currentValues = new ArrayList();
attributes.put(key, currentValues);
getAttributes().put(key, currentValues);
}
currentValues.add(value);
@@ -81,8 +81,8 @@ public class Label extends UIBean {
if (value != null) {
addParameter("nameValue", findString(value));
} else if (key != null) {
Object nameValue = attributes.get("nameValue");
if (nameValue == null || nameValue.toString().length() == 0) {
Object nameValue = getAttributes().get("nameValue");
if (nameValue == null || nameValue.toString().isEmpty()) {
// get the label from a TextProvider (default value is the key)
String providedLabel = TextProviderHelper.getText(key, key, stack);
addParameter("nameValue", providedLabel);
@@ -67,7 +67,7 @@ public abstract class ListUIBean extends UIBean {
Object value = null;
if (list == null) {
list = attributes.get("list");
list = getAttributes().get("list");
}
if (list instanceof String) {
@@ -174,7 +174,7 @@ public class ServletUrlRenderer implements UrlRenderer {
namespace, actionName);
if (actionConfig != null) {
ActionMapping mapping = new ActionMapping(actionName, namespace, actionMethod, formComponent.attributes);
ActionMapping mapping = new ActionMapping(actionName, namespace, actionMethod, formComponent.getAttributes());
String result = urlHelper.buildUrl(formComponent.actionMapper.getUriFromActionMapping(mapping),
formComponent.request, formComponent.response, queryStringResult.getQueryParams(), scheme, formComponent.includeContext, true, false, false);
formComponent.addParameter("action", result);
@@ -23,7 +23,6 @@ import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.TextParseUtil;
import com.opensymphony.xwork2.util.ValueStack;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.StrutsConstants;
@@ -32,6 +31,7 @@ import org.apache.struts2.components.template.Template;
import org.apache.struts2.components.template.TemplateEngine;
import org.apache.struts2.components.template.TemplateEngineManager;
import org.apache.struts2.components.template.TemplateRenderingContext;
import org.apache.struts2.dispatcher.AttributeMap;
import org.apache.struts2.dispatcher.StaticContentLoader;
import org.apache.struts2.util.ComponentUtils;
import org.apache.struts2.util.TextProviderHelper;
@@ -48,6 +48,10 @@ import java.util.List;
import java.util.Map;
import java.util.function.Function;
import static java.util.Collections.emptyMap;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.struts2.dispatcher.DispatcherConstants.ATTRIBUTES;
/**
* <p>
* UIBean is the standard superclass of all Struts UI components.
@@ -440,6 +444,9 @@ public abstract class UIBean extends Component {
private static final Logger LOG = LogManager.getLogger(UIBean.class);
static final String TEMPLATE_DIR = "templateDir";
static final String THEME = "theme";
protected static final String ATTR_FIELD_VALUE = "fieldValue";
protected static final String ATTR_NAME_VALUE = "nameValue";
protected static final String ATTR_VALUE = "value";
@@ -602,13 +609,18 @@ public abstract class UIBean extends Component {
result = findString(this.templateDir);
}
// Check Request, Session, Application scopes
if (isBlank(result)) {
result = (String) getAttrMap().get(TEMPLATE_DIR);
}
// Default template set
if (StringUtils.isBlank(result)) {
if (isBlank(result)) {
result = defaultTemplateDir;
}
// Defaults to 'template'
if (StringUtils.isBlank(result)) {
if (isBlank(result)) {
result = "template";
}
@@ -622,26 +634,36 @@ public abstract class UIBean extends Component {
result = findString(this.theme);
}
if (StringUtils.isBlank(result)) {
if (isBlank(result)) {
Form form = (Form) findAncestor(Form.class);
if (form != null) {
result = form.getTheme();
}
}
// Check Request, Session, Application scopes
if (isBlank(result)) {
result = (String) getAttrMap().get(THEME);
}
// Default theme set
if (StringUtils.isBlank(result)) {
if (isBlank(result)) {
result = defaultUITheme;
}
return result;
}
private Map<String, Object> getAttrMap() {
AttributeMap attrMap = (AttributeMap) getStack().getContext().get(ATTRIBUTES);
return attrMap != null ? attrMap : emptyMap();
}
public void evaluateParams() {
String gotTheme = getTheme();
addParameter("templateDir", getTemplateDir());
addParameter("theme", gotTheme);
addParameter(TEMPLATE_DIR, getTemplateDir());
addParameter(THEME, gotTheme);
addParameter("template", template != null ? findString(template) : getDefaultTemplate());
addParameter("dynamicAttributes", dynamicAttributes);
addParameter("themeExpansionToken", uiThemeExpansionToken);
@@ -882,8 +904,8 @@ public abstract class UIBean extends Component {
*/
protected void applyValueParameter(String translatedName) {
// see if the value has been specified as a parameter already
if (attributes.containsKey(ATTR_VALUE)) {
attributes.put(ATTR_NAME_VALUE, attributes.get(ATTR_VALUE));
if (getAttributes().containsKey(ATTR_VALUE)) {
getAttributes().put(ATTR_NAME_VALUE, getAttributes().get(ATTR_VALUE));
} else {
if (evaluateNameValue()) {
final Class<?> valueClazz = getValueClassType();
@@ -111,7 +111,7 @@ public class URL extends ContextBean {
public URL(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
super(stack);
urlProvider = new ComponentUrlProvider(this, this.attributes);
urlProvider = new ComponentUrlProvider(this, this.getAttributes());
urlProvider.setHttpServletRequest(req);
urlProvider.setHttpServletResponse(res);
}
@@ -86,7 +86,7 @@ public class AttributeMap extends AbstractMap<String, Object> {
PageContext pc = getPageContext();
if (pc == null) {
if (pc == null || pc.getRequest() == null) {
RequestMap request = (RequestMap) context.get(DispatcherConstants.REQUEST);
SessionMap session = (SessionMap) context.get(DispatcherConstants.SESSION);
ApplicationMap application = (ApplicationMap) context.get(DispatcherConstants.APPLICATION);
@@ -112,7 +112,7 @@ public class Dispatcher {
*/
public static final String REQUEST_POST_METHOD = "POST";
public static final String MULTIPART_FORM_DATA_REGEX = "^multipart/form-data(?:\\s*;\\s*boundary=[0-9a-zA-Z'()+_,\\-./:=?]{1,70})?(?:\\s*;\\s*charset=[a-zA-Z\\-0-9]{3,14})?";
public static final String MULTIPART_FORM_DATA_REGEX = "^multipart/form-data(?:\\s*;\\s*boundary=[0-9a-zA-Z'\"()+_,\\-./:=?]{1,70})?(?:\\s*;\\s*charset=[a-zA-Z\\-0-9]{3,14})?";
private static final String CONFIG_SPLIT_REGEX = "\\s*,\\s*";
@@ -20,6 +20,9 @@ package org.apache.struts2.dispatcher.multipart;
import com.opensymphony.xwork2.LocaleProviderFactory;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.security.DefaultExcludedPatternsChecker;
import com.opensymphony.xwork2.security.ExcludedPatternsChecker;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.StrutsConstants;
@@ -38,13 +41,16 @@ public abstract class AbstractMultiPartRequest implements MultiPartRequest {
private static final Logger LOG = LogManager.getLogger(AbstractMultiPartRequest.class);
private static final String EXCLUDED_FILE_PATTERN = "^(.*[<>&\"'|;\\\\/?*:]+.*|.*\\.\\..*)$";
private static final String EXCLUDED_FILE_PATTERN_WITH_DMI_SUPPORT = "^(?!action:[^<>&\"'|;\\\\/?*:]+(![^<>&\"'|;\\\\/?*:]+)?$)(.*[<>&\"'|;\\\\/?*:]+.*|.*\\.\\..*)$\n";
/**
* Defines the internal buffer size used during streaming operations.
*/
public static final int BUFFER_SIZE = 10240;
/**
* Internal list of raised errors to be passed to the the Struts2 framework.
* Internal list of raised errors to be passed to the Struts2 framework.
*/
protected List<LocalizedMessage> errors = new ArrayList<>();
@@ -80,6 +86,17 @@ public abstract class AbstractMultiPartRequest implements MultiPartRequest {
*/
protected Locale defaultLocale = Locale.ENGLISH;
private final ExcludedPatternsChecker patternsChecker;
protected AbstractMultiPartRequest(String dmiValue) {
patternsChecker = new DefaultExcludedPatternsChecker();
if (BooleanUtils.toBoolean(dmiValue)) {
((DefaultExcludedPatternsChecker) patternsChecker).setAdditionalExcludePatterns(EXCLUDED_FILE_PATTERN_WITH_DMI_SUPPORT);
} else {
((DefaultExcludedPatternsChecker) patternsChecker).setAdditionalExcludePatterns(EXCLUDED_FILE_PATTERN);
}
}
/**
* @param bufferSize Sets the buffer size to be used.
*/
@@ -123,7 +140,7 @@ public abstract class AbstractMultiPartRequest implements MultiPartRequest {
/**
* @param request Inspect the servlet request and set the locale if one wasn't provided by
* the Struts2 framework.
* the Struts2 framework.
*/
protected void setLocale(HttpServletRequest request) {
if (defaultLocale == null) {
@@ -134,7 +151,7 @@ public abstract class AbstractMultiPartRequest implements MultiPartRequest {
/**
* Build error message.
*
* @param e the Throwable/Exception
* @param e the Throwable/Exception
* @param args arguments
* @return error message
*/
@@ -147,7 +164,7 @@ public abstract class AbstractMultiPartRequest implements MultiPartRequest {
/* (non-Javadoc)
* @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getErrors()
*/
*/
public List<LocalizedMessage> getErrors() {
return errors;
}
@@ -169,4 +186,12 @@ public abstract class AbstractMultiPartRequest implements MultiPartRequest {
return fileName;
}
/**
* @param fileName file name to check
* @return true if the file name is excluded
*/
protected boolean isExcluded(String fileName) {
return patternsChecker.isExcluded(fileName).isExcluded();
}
}
@@ -18,6 +18,7 @@
*/
package org.apache.struts2.dispatcher.multipart;
import com.opensymphony.xwork2.inject.Inject;
import org.apache.commons.fileupload.FileCountLimitExceededException;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadBase;
@@ -29,6 +30,7 @@ import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.StrutsConstants;
import org.apache.struts2.dispatcher.LocalizedMessage;
import javax.servlet.http.HttpServletRequest;
@@ -44,6 +46,8 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.apache.commons.lang3.StringUtils.normalizeSpace;
/**
* Multipart form data request adapter for Jakarta Commons Fileupload package.
*/
@@ -56,6 +60,14 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
// maps parameter name -> List of param values
protected Map<String, List<String>> params = new HashMap<>();
public JakartaMultiPartRequest() {
super(Boolean.FALSE.toString());
}
@Inject(value = StrutsConstants.STRUTS_ENABLE_DYNAMIC_METHOD_INVOCATION, required = false)
public JakartaMultiPartRequest(String dmiValue) {
super(dmiValue);
}
/**
* Creates a new request wrapper to handle multi-part data using methods adapted from Jason Pell's
@@ -100,7 +112,7 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
protected void processUpload(HttpServletRequest request, String saveDir) throws FileUploadException, UnsupportedEncodingException {
if (ServletFileUpload.isMultipartContent(request)) {
for (FileItem item : parseRequest(request, saveDir)) {
LOG.debug("Found file item: [{}]", sanitizeNewlines(item.getFieldName()));
LOG.debug("Found file item: [{}]", normalizeSpace(item.getFieldName()));
if (item.isFormField()) {
processNormalFormField(item, request.getCharacterEncoding());
} else {
@@ -113,9 +125,19 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
protected void processFileField(FileItem item) {
LOG.debug("Item is a file upload");
if (isExcluded(item.getName())) {
LOG.warn("File name [{}] is not accepted", normalizeSpace(item.getName()));
return;
}
if (isExcluded(item.getFieldName())) {
LOG.warn("Field name [{}] is not accepted", normalizeSpace(item.getFieldName()));
return;
}
// Skip file uploads that don't have a file name - meaning that no file was selected.
if (item.getName() == null || item.getName().trim().isEmpty()) {
LOG.debug("No file has been uploaded for the field: {}", sanitizeNewlines(item.getFieldName()));
LOG.debug("No file has been uploaded for the field: {}", normalizeSpace(item.getFieldName()));
return;
}
@@ -134,6 +156,11 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
try {
LOG.debug("Item is a normal form field");
if (isExcluded(item.getFieldName())) {
LOG.warn("Form field name [{}] is not accepted", normalizeSpace(item.getFieldName()));
return;
}
List<String> values;
if (params.get(item.getFieldName()) != null) {
values = params.get(item.getFieldName());
@@ -143,7 +170,7 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
long size = item.getSize();
if (maxStringLength != null && size > maxStringLength) {
LOG.debug("Form field {} of size {} bytes exceeds limit of {}.", sanitizeNewlines(item.getFieldName()), size, maxStringLength);
LOG.debug("Form field [{}] of size [{}] bytes exceeds limit of [{}].", normalizeSpace(item.getFieldName()), size, maxStringLength);
String errorKey = "struts.messages.upload.error.parameter.too.long";
LocalizedMessage localizedMessage = new LocalizedMessage(this.getClass(), errorKey, null,
new Object[]{item.getFieldName(), maxStringLength, size});
@@ -359,7 +386,7 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
for (String name : names) {
List<FileItem> items = files.get(name);
for (FileItem item : items) {
LOG.debug("Removing file {} {}", name, item);
LOG.debug("Removing file [{}]", normalizeSpace(name));
if (!item.isInMemory()) {
item.delete();
}
@@ -367,7 +394,4 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
}
}
private String sanitizeNewlines(String before) {
return before.replaceAll("[\n\r]", "_");
}
}
@@ -18,6 +18,7 @@
*/
package org.apache.struts2.dispatcher.multipart;
import com.opensymphony.xwork2.inject.Inject;
import org.apache.commons.fileupload.FileItemIterator;
import org.apache.commons.fileupload.FileItemStream;
import org.apache.commons.fileupload.FileUploadBase;
@@ -27,6 +28,7 @@ import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.fileupload.util.Streams;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.StrutsConstants;
import org.apache.struts2.dispatcher.LocalizedMessage;
import javax.servlet.http.HttpServletRequest;
@@ -45,6 +47,8 @@ import java.util.List;
import java.util.Map;
import java.util.UUID;
import static org.apache.commons.lang3.StringUtils.normalizeSpace;
/**
* Multi-part form data request adapter for Jakarta Commons FileUpload package that
* leverages the streaming API rather than the traditional non-streaming API.
@@ -77,7 +81,7 @@ public class JakartaStreamMultiPartRequest extends AbstractMultiPartRequest {
File file = fileInfo.getFile();
LOG.debug("Deleting file '{}'.", file.getName());
if (!file.delete()) {
LOG.warn("There was a problem attempting to delete file '{}'.", file.getName());
LOG.warn("There was a problem attempting to delete file [{}].", file.getName());
}
}
}
@@ -204,6 +208,15 @@ public class JakartaStreamMultiPartRequest extends AbstractMultiPartRequest {
}
}
public JakartaStreamMultiPartRequest() {
super(Boolean.FALSE.toString());
}
@Inject(value = StrutsConstants.STRUTS_ENABLE_DYNAMIC_METHOD_INVOCATION, required = false)
public JakartaStreamMultiPartRequest(String dmiValue) {
super(dmiValue);
}
/**
* Processes the upload.
*
@@ -252,7 +265,7 @@ public class JakartaStreamMultiPartRequest extends AbstractMultiPartRequest {
// prevent processing file field item if request size not allowed.
if (!requestSizePermitted) {
addFileSkippedError(itemStream.getName(), request);
LOG.debug("Skipped stream '{}', request maximum size ({}) exceeded.", itemStream.getName(), maxSize);
LOG.debug("Skipped stream [{}], request maximum size ({}) exceeded.", normalizeSpace(itemStream.getName()), maxSize);
continue;
}
@@ -296,7 +309,7 @@ public class JakartaStreamMultiPartRequest extends AbstractMultiPartRequest {
* @param request the servlet request
*/
protected void addFileSkippedError(String fileName, HttpServletRequest request) {
String exceptionMessage = "Skipped file " + fileName + "; request size limit exceeded.";
String exceptionMessage = "Skipped file " + normalizeSpace(fileName) + "; request size limit exceeded.";
long allowedMaxSize = maxSize != null ? maxSize : -1;
FileSizeLimitExceededException exception = new FileUploadBase.FileSizeLimitExceededException(exceptionMessage, getRequestSize(request), allowedMaxSize);
LocalizedMessage message = buildErrorMessage(exception, new Object[]{fileName, getRequestSize(request), allowedMaxSize});
@@ -312,6 +325,10 @@ public class JakartaStreamMultiPartRequest extends AbstractMultiPartRequest {
*/
protected void processFileItemStreamAsFormField(FileItemStream itemStream) {
String fieldName = itemStream.getFieldName();
if (isExcluded(fieldName)) {
LOG.warn("Form field [{}] rejected!", normalizeSpace(fieldName));
return;
}
try {
List<String> values;
String fieldValue = Streams.asString(itemStream.openStream());
@@ -323,7 +340,7 @@ public class JakartaStreamMultiPartRequest extends AbstractMultiPartRequest {
}
values.add(fieldValue);
} catch (IOException e) {
LOG.warn("Failed to handle form field '{}'.", fieldName, e);
LOG.warn("Failed to handle form field [{}]", normalizeSpace(fieldName), e);
}
}
@@ -336,7 +353,12 @@ public class JakartaStreamMultiPartRequest extends AbstractMultiPartRequest {
protected void processFileItemStreamAsFileField(FileItemStream itemStream, String location) {
// Skip file uploads that don't have a file name - meaning that no file was selected.
if (itemStream.getName() == null || itemStream.getName().trim().isEmpty()) {
LOG.debug("No file has been uploaded for the field: {}", itemStream.getFieldName());
LOG.debug("No file has been uploaded for the field: {}", normalizeSpace(itemStream.getFieldName()));
return;
}
if (isExcluded(itemStream.getName())) {
LOG.warn("File field [{}] rejected", normalizeSpace(itemStream.getName()));
return;
}
@@ -353,7 +375,7 @@ public class JakartaStreamMultiPartRequest extends AbstractMultiPartRequest {
try {
file.delete();
} catch (SecurityException se) {
LOG.warn("Failed to delete '{}' due to security exception above.", file.getName(), se);
LOG.warn("Failed to delete [{}] due to security exception above.", normalizeSpace(file.getName()), se);
}
}
}
@@ -385,7 +407,9 @@ public class JakartaStreamMultiPartRequest extends AbstractMultiPartRequest {
}
File file = File.createTempFile(prefix + "_", suffix, new File(location));
LOG.debug("Creating temporary file '{}' (originally '{}').", file.getName(), fileName);
if (LOG.isDebugEnabled()) {
LOG.debug("Creating temporary file [{}] (originally [{}]).", file.getName(), normalizeSpace(fileName));
}
return file;
}
@@ -29,6 +29,8 @@ import org.apache.struts2.dispatcher.AttributeMap;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.PageContext;
import static org.apache.struts2.dispatcher.DispatcherConstants.ATTRIBUTES;
public class TagUtils {
private static final Logger LOG = LogManager.getLogger(TagUtils.class);
@@ -46,8 +48,8 @@ public class TagUtils {
} else {
LOG.trace("Adds the current PageContext to ActionContext");
stack.getActionContext()
.withPageContext(pageContext)
.with("attr", new AttributeMap(stack.getContext()));
.withPageContext(pageContext)
.with(ATTRIBUTES, new AttributeMap(stack.getContext()));
}
return stack;
@@ -29,6 +29,7 @@
<constant name="struts.excludedClasses"
value="
com.opensymphony.xwork2.ActionContext,
org.apache.struts2.ActionContext,
java.lang.Class,
java.lang.ClassLoader,
java.lang.InheritableThreadLocal,
@@ -75,10 +76,15 @@
value="
antlr.build,
com.opensymphony.xwork2.config,
org.apache.struts2.config,
com.opensymphony.xwork2.inject,
org.apache.struts2.inject,
com.opensymphony.xwork2.ognl,
org.apache.struts2.ognl,
com.opensymphony.xwork2.security,
org.apache.struts2.security,
com.opensymphony.xwork2.util,
org.apache.struts2.util,
freemarker.core,
freemarker.ext.jsp,
freemarker.ext.rhino,
@@ -108,10 +114,15 @@
value="
antlr.build,
com.opensymphony.xwork2.config,
org.apache.struts2.config,
com.opensymphony.xwork2.inject,
org.apache.struts2.inject,
com.opensymphony.xwork2.ognl,
org.apache.struts2.ognl,
com.opensymphony.xwork2.security,
org.apache.struts2.security,
com.opensymphony.xwork2.util,
org.apache.struts2.util,
freemarker.core,
freemarker.ext.jsp,
freemarker.ext.rhino,
@@ -28,7 +28,9 @@ import java.util.concurrent.Callable;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
@@ -49,6 +51,7 @@ public class ContainerImplTest {
ContainerBuilder cb = new ContainerBuilder();
cb.constant("methodCheck.name", "Lukasz");
cb.constant("fieldCheck.name", "Lukasz");
cb.constant("constructorCheck.name", "Lukasz");
cb.factory(EarlyInitializable.class, EarlyInitializableBean.class, Scope.SINGLETON);
cb.factory(Initializable.class, InitializableBean.class, Scope.SINGLETON);
cb.factory(EarlyInitializable.class, "prototypeEarlyInitializable", EarlyInitializableBean.class, Scope.PROTOTYPE);
@@ -65,15 +68,43 @@ public class ContainerImplTest {
}
@Test
public void fieldInjector() throws Exception {
public void fieldInjector() {
FieldCheck fieldCheck = new FieldCheck();
c.inject(fieldCheck);
assertEquals(fieldCheck.getName(), "Lukasz");
assertEquals("Lukasz", fieldCheck.getName());
}
@Test
public void methodInjector() throws Exception {
c.inject(new MethodCheck());
public void methodInjector() {
MethodCheck methodCheck = new MethodCheck();
c.inject(methodCheck);
assertEquals("Lukasz", methodCheck.getName());
}
@Test
public void constructorInjector() {
ConstructorCheck constructorCheck = c.inject(ConstructorCheck.class);
assertEquals("Lukasz", constructorCheck.getName());
}
@Test
public void optionalConstructorInjector() {
OptionalConstructorCheck constructorCheck = c.inject(OptionalConstructorCheck.class);
assertNull(constructorCheck.getName());
}
@Test
public void requiredOptionalConstructorInjector() {
RequiredOptionalConstructorCheck constructorCheck = c.inject(RequiredOptionalConstructorCheck.class);
assertNotNull(constructorCheck.getExistingName());
assertNull(constructorCheck.getNonExitingName());
}
@Test
public void optionalRequiredConstructorInjector() {
OptionalRequiredConstructorCheck constructorCheck = c.inject(OptionalRequiredConstructorCheck.class);
assertNull(constructorCheck.getNonExitingName());
assertNotNull(constructorCheck.getExistingName());
}
/**
@@ -92,7 +123,7 @@ public class ContainerImplTest {
* Inject values into method under SecurityManager
*/
@Test
public void testMethodInjectorWithSecurityEnabled() throws Exception {
public void testMethodInjectorWithSecurityEnabled() {
assumeTrue(SystemUtils.isJavaVersionAtMost(JavaVersion.JAVA_20));
System.setSecurityManager(new TestSecurityManager());
assertThrows(DependencyException.class, () -> c.inject(new MethodCheck()));
@@ -101,7 +132,7 @@ public class ContainerImplTest {
}
@Test
public void testEarlyInitializable() throws Exception {
public void testEarlyInitializable() {
assertTrue("should being initialized already", EarlyInitializableBean.initializedEarly);
EarlyInitializableCheck earlyInitializableCheck = new EarlyInitializableCheck();
@@ -148,22 +179,19 @@ public class ContainerImplTest {
final InitializableCheck initializableCheck3 = new InitializableCheck();
final TestScopeStrategy testScopeStrategy = new TestScopeStrategy();
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
ContainerBuilder cb2 = new ContainerBuilder();
cb2.factory(EarlyInitializable.class, EarlyInitializableBean.class, Scope.SINGLETON);
cb2.factory(Initializable.class, InitializableBean.class, Scope.SINGLETON);
cb2.factory(EarlyInitializable.class, "prototypeEarlyInitializable", EarlyInitializableBean.class, Scope.PROTOTYPE);
cb2.factory(Initializable.class, "prototypeInitializable", InitializableBean.class, Scope.PROTOTYPE);
cb2.factory(Initializable.class, "requestInitializable", InitializableBean.class, Scope.REQUEST);
cb2.factory(Initializable.class, "sessionInitializable", InitializableBean.class, Scope.SESSION);
cb2.factory(Initializable.class, "threadInitializable", InitializableBean.class, Scope.THREAD);
cb2.factory(Initializable.class, "wizardInitializable", InitializableBean.class, Scope.WIZARD);
Container c2 = cb2.create(false);
c2.setScopeStrategy(testScopeStrategy);
c2.inject(initializableCheck3);
}
Thread thread = new Thread(() -> {
ContainerBuilder cb2 = new ContainerBuilder();
cb2.factory(EarlyInitializable.class, EarlyInitializableBean.class, Scope.SINGLETON);
cb2.factory(Initializable.class, InitializableBean.class, Scope.SINGLETON);
cb2.factory(EarlyInitializable.class, "prototypeEarlyInitializable", EarlyInitializableBean.class, Scope.PROTOTYPE);
cb2.factory(Initializable.class, "prototypeInitializable", InitializableBean.class, Scope.PROTOTYPE);
cb2.factory(Initializable.class, "requestInitializable", InitializableBean.class, Scope.REQUEST);
cb2.factory(Initializable.class, "sessionInitializable", InitializableBean.class, Scope.SESSION);
cb2.factory(Initializable.class, "threadInitializable", InitializableBean.class, Scope.THREAD);
cb2.factory(Initializable.class, "wizardInitializable", InitializableBean.class, Scope.WIZARD);
Container c2 = cb2.create(false);
c2.setScopeStrategy(testScopeStrategy);
c2.inject(initializableCheck3);
});
thread.run();
thread.join();
@@ -205,6 +233,76 @@ public class ContainerImplTest {
}
public static class ConstructorCheck {
private String name;
@Inject("constructorCheck.name")
public ConstructorCheck(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public static class OptionalConstructorCheck {
private String name;
@Inject(value = "nonExistingConstant", required = false)
public OptionalConstructorCheck(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public static class RequiredOptionalConstructorCheck {
private final String existingName;
private final String nonExitingName;
@Inject(required = false)
public RequiredOptionalConstructorCheck(
@Inject("constructorCheck.name") String existingName,
@Inject(value = "nonExistingConstant", required = false) String nonExitingName
) {
this.existingName = existingName;
this.nonExitingName = nonExitingName;
}
public String getExistingName() {
return existingName;
}
public String getNonExitingName() {
return nonExitingName;
}
}
public static class OptionalRequiredConstructorCheck {
private final String existingName;
private final String nonExitingName;
@Inject(required = false)
public OptionalRequiredConstructorCheck(
@Inject(value = "nonExistingConstant", required = false) String nonExitingName,
@Inject("constructorCheck.name") String existingName
) {
this.existingName = existingName;
this.nonExitingName = nonExitingName;
}
public String getExistingName() {
return existingName;
}
public String getNonExitingName() {
return nonExitingName;
}
}
class InitializableCheck {
private Initializable initializable;
@@ -93,7 +93,7 @@ public class DefaultExcludedPatternsCheckerTest extends XWorkTestCase {
public void testDefaultExcludePatterns() throws Exception {
// given
List<String> prefixes = Arrays.asList("#[0].%s", "[0].%s", "top.%s", "%{[0].%s}", "%{#[0].%s}", "%{top.%s}", "%{#top.%s}", "%{#%s}", "%{%s}", "#%s");
List<String> prefixes = Arrays.asList("#[0].%s", "[0].%s", "top.%s", "%{[0].%s}", "%{#[0].%s}", "%{top.%s}", "%{#top.%s}", "%{#%s}", "%{%s}", "#%s", "top.param", "top.request");
List<String> inners = Arrays.asList("servletRequest", "servletResponse", "servletContext", "application", "session", "struts", "request", "response", "dojo", "parameters");
List<String> suffixes = Arrays.asList("['test']", "[\"test\"]", ".test");
@@ -0,0 +1,43 @@
/*
* 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 com.opensymphony.xwork2.test;
public class StrutsUtilBean {
public String getMyString() {
return "myString";
}
public boolean getMyBoolean(boolean bool) {
return bool;
}
public String getFoo() {
return "bar";
}
public String getBarExpression() {
return "%{bar}";
}
public String getBar() {
return "bar";
}
}
@@ -0,0 +1,88 @@
/*
* 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 org.apache.struts2.components;
import org.apache.struts2.ActionContext;
import org.apache.struts2.dispatcher.DispatcherConstants;
import org.apache.struts2.views.jsp.AbstractTagTest;
import java.util.Map;
import static org.apache.struts2.components.UIBean.TEMPLATE_DIR;
import static org.apache.struts2.components.UIBean.THEME;
public class UIBeanTagTest extends AbstractTagTest {
private UIBean bean;
@Override
public void setUp() throws Exception {
super.setUp();
bean = new UIBean(stack, request, response) {
@Override
protected String getDefaultTemplate() {
return null;
}
};
}
public void testTemplateDir_ognlExpression() {
bean.setTemplateDir("%{testDir}");
stack.push(new Object() {
public String getTestDir() {
return "testValue";
}
});
assertEquals("testValue", bean.getTemplateDir());
}
public void testTemplateDir_attrMapFallback() {
ActionContext.of(context).getApplication().put(TEMPLATE_DIR, "applicationValue");
assertEquals("applicationValue", bean.getTemplateDir());
ActionContext.of(context).getSession().put(TEMPLATE_DIR, "sessionValue");
assertEquals("sessionValue", bean.getTemplateDir());
((Map<String, Object>) context.get(DispatcherConstants.REQUEST)).put(TEMPLATE_DIR, "requestValue");
assertEquals("requestValue", bean.getTemplateDir());
}
public void testTheme_ognlExpression() {
bean.setTheme("%{testTheme}");
stack.push(new Object() {
public String getTestTheme() {
return "testValue";
}
});
assertEquals("testValue", bean.getTheme());
}
public void testTheme_attrMapFallback() {
ActionContext.of(context).getApplication().put(THEME, "applicationValue");
assertEquals("applicationValue", bean.getTheme());
ActionContext.of(context).getSession().put(THEME, "sessionValue");
assertEquals("sessionValue", bean.getTheme());
((Map<String, Object>) context.get(DispatcherConstants.REQUEST)).put(THEME, "requestValue");
assertEquals("requestValue", bean.getTheme());
}
}
@@ -40,6 +40,11 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class AttributeMapTest {
@@ -360,4 +365,21 @@ public class AttributeMapTest {
assertEquals("value", value);
}
}
@Test
public void get_whenPageContextHasNoRequest() {
PageContext pageContext = mock(PageContext.class);
when(pageContext.getRequest()).thenReturn(null);
HttpServletRequest req = new MockHttpServletRequest();
req.setAttribute("attr", "reqValue");
AttributeMap attributeMap = new AttributeMap(new HashMap<String, Object>() {{
put(StrutsStatics.PAGE_CONTEXT, pageContext);
put(DispatcherConstants.REQUEST, new RequestMap(req));
}});
assertEquals("reqValue", attributeMap.get("attr"));
verify(pageContext, never()).findAttribute(anyString());
}
}
@@ -343,6 +343,9 @@ public class DispatcherTest extends StrutsJUnit4InternalTestCase {
req.setContentType("Multipart/Form-Data ; boundary=---------------------------207103069210263;charset=UTF-16LE");
assertTrue(dispatcher.isMultipartRequest(req));
req.setContentType("multipart/form-data; boundary=\"----=_Part_38_1092302434.1734807780737\"");
assertTrue(dispatcher.isMultipartRequest(req));
}
@Test
@@ -24,6 +24,9 @@ import com.opensymphony.xwork2.DefaultLocaleProvider;
import com.opensymphony.xwork2.ValidationAwareSupport;
import com.opensymphony.xwork2.mock.MockActionInvocation;
import com.opensymphony.xwork2.mock.MockActionProxy;
import com.opensymphony.xwork2.security.DefaultAcceptedPatternsChecker;
import com.opensymphony.xwork2.security.DefaultExcludedPatternsChecker;
import com.opensymphony.xwork2.security.DefaultNotExcludedAcceptedPatternsChecker;
import com.opensymphony.xwork2.util.ClassLoaderUtil;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.struts2.ServletActionContext;
@@ -663,6 +666,68 @@ public class ActionFileUploadInterceptorTest extends StrutsInternalTestCase {
assertTrue(msg.startsWith("Der Request übertraf die maximal erlaubte Größe"));
}
public void testUnacceptedFieldName() throws Exception {
MockHttpServletRequest req = new MockHttpServletRequest();
req.setCharacterEncoding(StandardCharsets.UTF_8.name());
req.setMethod("post");
req.addHeader("Content-type", "multipart/form-data; boundary=---1234");
// inspired by the unit tests for jakarta commons fileupload
String content = ("-----1234\r\n" +
"Content-Disposition: form-data; name=\"top.file\"; filename=\"deleteme.txt\"\r\n" +
"Content-Type: text/html\r\n" +
"\r\n" +
"Unit test of ActionFileUploadInterceptor" +
"\r\n" +
"-----1234--\r\n");
req.setContent(content.getBytes(StandardCharsets.US_ASCII));
MyFileUploadAction action = container.inject(MyFileUploadAction.class);
MockActionInvocation mai = new MockActionInvocation();
mai.setAction(action);
mai.setResultCode("success");
mai.setInvocationContext(ActionContext.getContext());
ActionContext.getContext()
.withServletRequest(createMultipartRequestMaxSize(req, 2000));
interceptor.intercept(mai);
assertFalse(action.hasActionErrors());
assertNull(action.getUploadFiles());
}
public void testUnacceptedFileName() throws Exception {
MockHttpServletRequest req = new MockHttpServletRequest();
req.setCharacterEncoding(StandardCharsets.UTF_8.name());
req.setMethod("post");
req.addHeader("Content-type", "multipart/form-data; boundary=---1234");
// inspired by the unit tests for jakarta commons fileupload
String content = ("-----1234\r\n" +
"Content-Disposition: form-data; name=\"file\"; filename=\"../deleteme.txt\"\r\n" +
"Content-Type: text/html\r\n" +
"\r\n" +
"Unit test of ActionFileUploadInterceptor" +
"\r\n" +
"-----1234--\r\n");
req.setContent(content.getBytes(StandardCharsets.US_ASCII));
MyFileUploadAction action = container.inject(MyFileUploadAction.class);
MockActionInvocation mai = new MockActionInvocation();
mai.setAction(action);
mai.setResultCode("success");
mai.setInvocationContext(ActionContext.getContext());
ActionContext.getContext()
.withServletRequest(createMultipartRequestMaxSize(req, 2000));
interceptor.intercept(mai);
assertFalse(action.hasActionErrors());
assertNull(action.getUploadFiles());
}
private String encodeTextFile(String filename, String contentType, String content) {
return "\r\n" +
"--" +
@@ -672,7 +737,7 @@ public class ActionFileUploadInterceptorTest extends StrutsInternalTestCase {
"file" +
"\"; filename=\"" +
filename +
"\r\n" +
"\"\r\n" +
"Content-Type: " +
contentType +
"\r\n" +
@@ -697,7 +762,6 @@ public class ActionFileUploadInterceptorTest extends StrutsInternalTestCase {
}
private MultiPartRequestWrapper createMultipartRequest(HttpServletRequest req, int maxsize, int maxfilesize, int maxfiles, int maxStringLength) {
JakartaMultiPartRequest jak = new JakartaMultiPartRequest();
jak.setMaxSize(String.valueOf(maxsize));
jak.setMaxFileSize(String.valueOf(maxfilesize));
@@ -707,7 +771,6 @@ public class ActionFileUploadInterceptorTest extends StrutsInternalTestCase {
}
private MultiPartRequestWrapper createMultipartRequestNoMaxParamsSet(HttpServletRequest req) {
JakartaMultiPartRequest jak = new JakartaMultiPartRequest();
return new MultiPartRequestWrapper(jak, req, tempDir.getAbsolutePath(), new DefaultLocaleProvider());
}
@@ -23,6 +23,7 @@ import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.DefaultLocaleProvider;
import com.opensymphony.xwork2.ValidationAwareSupport;
import com.opensymphony.xwork2.mock.MockActionInvocation;
import com.opensymphony.xwork2.security.DefaultNotExcludedAcceptedPatternsChecker;
import com.opensymphony.xwork2.util.ClassLoaderUtil;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.struts2.ServletActionContext;
@@ -727,6 +728,68 @@ public class FileUploadInterceptorTest extends StrutsInternalTestCase {
assertTrue(msg.startsWith("Der Request übertraf die maximal erlaubte Größe"));
}
public void testUnacceptedFieldName() throws Exception {
MockHttpServletRequest req = new MockHttpServletRequest();
req.setCharacterEncoding(StandardCharsets.UTF_8.name());
req.setMethod("post");
req.addHeader("Content-type", "multipart/form-data; boundary=---1234");
// inspired by the unit tests for jakarta commons fileupload
String content = ("-----1234\r\n" +
"Content-Disposition: form-data; name=\"top.file\"; filename=\"deleteme.txt\"\r\n" +
"Content-Type: text/html\r\n" +
"\r\n" +
"Unit test of ActionFileUploadInterceptor" +
"\r\n" +
"-----1234--\r\n");
req.setContent(content.getBytes(StandardCharsets.US_ASCII));
ActionFileUploadInterceptorTest.MyFileUploadAction action = container.inject(ActionFileUploadInterceptorTest.MyFileUploadAction.class);
MockActionInvocation mai = new MockActionInvocation();
mai.setAction(action);
mai.setResultCode("success");
mai.setInvocationContext(ActionContext.getContext());
ActionContext.getContext()
.withServletRequest(createMultipartRequestMaxSize(req, 2000));
interceptor.intercept(mai);
assertFalse(action.hasActionErrors());
assertNull(action.getUploadFiles());
}
public void testUnacceptedFileName() throws Exception {
MockHttpServletRequest req = new MockHttpServletRequest();
req.setCharacterEncoding(StandardCharsets.UTF_8.name());
req.setMethod("post");
req.addHeader("Content-type", "multipart/form-data; boundary=---1234");
// inspired by the unit tests for jakarta commons fileupload
String content = ("-----1234\r\n" +
"Content-Disposition: form-data; name=\"file\"; filename=\"../deleteme.txt\"\r\n" +
"Content-Type: text/html\r\n" +
"\r\n" +
"Unit test of ActionFileUploadInterceptor" +
"\r\n" +
"-----1234--\r\n");
req.setContent(content.getBytes(StandardCharsets.US_ASCII));
ActionFileUploadInterceptorTest.MyFileUploadAction action = container.inject(ActionFileUploadInterceptorTest.MyFileUploadAction.class);
MockActionInvocation mai = new MockActionInvocation();
mai.setAction(action);
mai.setResultCode("success");
mai.setInvocationContext(ActionContext.getContext());
ActionContext.getContext()
.withServletRequest(createMultipartRequestMaxSize(req, 2000));
interceptor.intercept(mai);
assertFalse(action.hasActionErrors());
assertNull(action.getUploadFiles());
}
private String encodeTextFile(String filename, String contentType, String content) {
return "\r\n" +
"--" +
@@ -736,7 +799,7 @@ public class FileUploadInterceptorTest extends StrutsInternalTestCase {
"file" +
"\"; filename=\"" +
filename +
"\r\n" +
"\"\r\n" +
"Content-Type: " +
contentType +
"\r\n" +
@@ -761,18 +824,18 @@ public class FileUploadInterceptorTest extends StrutsInternalTestCase {
}
private MultiPartRequestWrapper createMultipartRequest(HttpServletRequest req, int maxsize, int maxfilesize, int maxfiles, int maxStringLength) {
JakartaMultiPartRequest jak = new JakartaMultiPartRequest();
jak.setMaxSize(String.valueOf(maxsize));
jak.setMaxFileSize(String.valueOf(maxfilesize));
jak.setMaxFiles(String.valueOf(maxfiles));
jak.setMaxStringLength(String.valueOf(maxStringLength));
return new MultiPartRequestWrapper(jak, req, tempDir.getAbsolutePath(), new DefaultLocaleProvider());
}
private MultiPartRequestWrapper createMultipartRequestNoMaxParamsSet(HttpServletRequest req) {
JakartaMultiPartRequest jak = new JakartaMultiPartRequest();
return new MultiPartRequestWrapper(jak, req, tempDir.getAbsolutePath(), new DefaultLocaleProvider());
}
@@ -116,9 +116,9 @@ public class ParametersInterceptorTest extends XWorkTestCase {
pi.setParameters(action, vs, HttpParameters.create(params).build());
// then
assertEquals(3, action.getActionErrors().size());
assertEquals(3, action.getActionMessages().size());
List<String> actionErrors = new ArrayList<>(action.getActionErrors());
List<String> actionErrors = new ArrayList<>(action.getActionMessages());
String msg1 = actionErrors.get(0);
String msg2 = actionErrors.get(1);
@@ -203,9 +203,9 @@ public class ParametersInterceptorTest extends XWorkTestCase {
pi.setParameters(action, vs, HttpParameters.create(params).build());
// then
assertEquals(3, action.getActionErrors().size());
assertEquals(3, action.getActionMessages().size());
List<String> actionErrors = new ArrayList<>(action.getActionErrors());
List<String> actionErrors = new ArrayList<>(action.getActionMessages());
String msg1 = actionErrors.get(0);
String msg2 = actionErrors.get(1);
String msg3 = actionErrors.get(2);
@@ -585,7 +585,7 @@ public class ParametersInterceptorTest extends XWorkTestCase {
container.inject(config.getInterceptors().get(0).getInterceptor());
ActionProxy proxy = actionProxyFactory.createActionProxy("", MockConfigurationProvider.PARAM_INTERCEPTOR_ACTION_NAME, null, extraContext.getContextMap());
proxy.execute();
final String actionError = "" + ((SimpleAction) proxy.getAction()).getActionErrors().toArray()[0];
final String actionError = "" + ((SimpleAction) proxy.getAction()).getActionMessages().toArray()[0];
assertTrue(actionError.contains("Error setting expression 'not_a_property' with value 'There is no action property named like this'"));
}
@@ -19,6 +19,7 @@
package org.apache.struts2.util;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.test.StrutsUtilBean;
import com.opensymphony.xwork2.util.ValueStack;
import org.apache.struts2.StrutsInternalTestCase;
import org.apache.struts2.TestAction;
@@ -37,7 +38,6 @@ import static org.assertj.core.api.Assertions.assertThat;
/**
* Test case for StrutsUtil.
*
*/
public class StrutsUtilTest extends StrutsInternalTestCase {
@@ -53,14 +53,7 @@ public class StrutsUtilTest extends StrutsInternalTestCase {
}
public void testIsTrueMethod() {
stack.push(new Object() {
public String getMyString() {
return "myString";
}
public boolean getMyBoolean(boolean bool) {
return bool;
}
});
stack.push(new StrutsUtilBean());
assertTrue(strutsUtil.isTrue("myString == 'myString'"));
assertFalse(strutsUtil.isTrue("myString == 'myOtherString'"));
assertTrue(strutsUtil.isTrue("getMyBoolean(true)"));
@@ -68,14 +61,7 @@ public class StrutsUtilTest extends StrutsInternalTestCase {
}
public void testFindStringMethod() {
stack.push(new Object() {
public String getMyString() {
return "myString";
}
public boolean getMyBoolean(boolean bool) {
return bool;
}
});
stack.push(new StrutsUtilBean());
assertEquals(strutsUtil.findString("myString"), "myString");
assertNull(strutsUtil.findString("myOtherString"));
@@ -105,14 +91,7 @@ public class StrutsUtilTest extends StrutsInternalTestCase {
public void testFindValueMethod() throws Exception {
stack.push(new Object() {
public String getMyString() {
return "myString";
}
public boolean getMyBoolean(boolean bool) {
return bool;
}
});
stack.push(new StrutsUtilBean());
Object obj1 = strutsUtil.findValue("myString", "java.lang.String");
Object obj2 = strutsUtil.findValue("getMyBoolean(true)", "java.lang.Boolean");
@@ -125,7 +104,6 @@ public class StrutsUtilTest extends StrutsInternalTestCase {
}
public void testGetTextMethod() {
// this should be in xwork-messages.properties (included by default
// by LocalizedTextUtil
@@ -233,11 +211,7 @@ public class StrutsUtilTest extends StrutsInternalTestCase {
}
public void testTranslateVariables() {
stack.push(new Object() {
public String getFoo() {
return "bar";
}
});
stack.push(new StrutsUtilBean());
String obj1 = strutsUtil.translateVariables("try: %{foo}");
assertNotNull(obj1);
@@ -245,15 +219,8 @@ public class StrutsUtilTest extends StrutsInternalTestCase {
}
public void testTranslateVariablesRecursion() {
stack.push(new Object() {
public String getFoo() {
return "%{bar}";
}
public String getBar() {
return "bar";
}
});
String obj1 = strutsUtil.translateVariables("try: %{foo}");
stack.push(new StrutsUtilBean());
String obj1 = strutsUtil.translateVariables("try: %{barExpression}");
assertNotNull(obj1);
assertEquals("try: %{bar}", obj1);
@@ -277,12 +244,10 @@ public class StrutsUtilTest extends StrutsInternalTestCase {
super.tearDown();
}
// === internal class to assist in testing
protected static class InternalMockHttpServletRequest extends MockHttpServletRequest {
InternalMockRequestDispatcher dispatcher = null;
public RequestDispatcher getRequestDispatcher(String path) {
dispatcher = new InternalMockRequestDispatcher(path);
return dispatcher;
@@ -296,10 +261,12 @@ public class StrutsUtilTest extends StrutsInternalTestCase {
protected static class InternalMockRequestDispatcher extends MockRequestDispatcher {
private final String url;
boolean included = false;
public InternalMockRequestDispatcher(String url) {
super(url);
this.url = url;
}
public void include(ServletRequest servletRequest, ServletResponse servletResponse) {
if (servletResponse instanceof MockHttpServletResponse) {
((MockHttpServletResponse) servletResponse).setIncludedUrl(this.url);
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.7.0-SNAPSHOT</version>
<version>6.7.2</version>
</parent>
<artifactId>struts2-async-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.7.0-SNAPSHOT</version>
<version>6.7.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -25,7 +25,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.7.0-SNAPSHOT</version>
<version>6.7.2</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.7.0-SNAPSHOT</version>
<version>6.7.2</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.7.0-SNAPSHOT</version>
<version>6.7.2</version>
</parent>
<artifactId>struts2-convention-plugin</artifactId>
+4
View File
@@ -1,4 +1,8 @@
# Struts 2 Direct Web Remoting (DWR) plugin
**DEPRECATED**: The plugin has been marked as deprecated since Struts 6.7.0 as there is no active development
nor DWR doesn't support JakartaEE!
This plugin allows to use Struts validation via DWR as remote beans.
You will find more details in [documentation](https://struts.apache.org/plugins/dwr/).
+2 -2
View File
@@ -24,12 +24,12 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.7.0-SNAPSHOT</version>
<version>6.7.2</version>
</parent>
<artifactId>struts2-dwr-plugin</artifactId>
<packaging>jar</packaging>
<name>Struts 2 DWR Plugin</name>
<name>DEPRECATED: Struts 2 DWR Plugin - since 6.7.0</name>
<dependencies>
<dependency>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.7.0-SNAPSHOT</version>
<version>6.7.2</version>
</parent>
<artifactId>struts2-embeddedjsp-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.7.0-SNAPSHOT</version>
<version>6.7.2</version>
</parent>
<artifactId>struts2-gxp-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.7.0-SNAPSHOT</version>
<version>6.7.2</version>
</parent>
<artifactId>struts2-jasperreports-plugin</artifactId>
+1 -1
View File
@@ -25,7 +25,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.7.0-SNAPSHOT</version>
<version>6.7.2</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.7.0-SNAPSHOT</version>
<version>6.7.2</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.7.0-SNAPSHOT</version>
<version>6.7.2</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.7.0-SNAPSHOT</version>
<version>6.7.2</version>
</parent>
<artifactId>struts2-junit-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.7.0-SNAPSHOT</version>
<version>6.7.2</version>
</parent>
<artifactId>struts2-osgi-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.7.0-SNAPSHOT</version>
<version>6.7.2</version>
</parent>
<artifactId>struts2-oval-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.7.0-SNAPSHOT</version>
<version>6.7.2</version>
</parent>
<artifactId>struts2-pell-multipart-plugin</artifactId>
@@ -18,9 +18,11 @@
*/
package org.apache.struts2.dispatcher.multipart;
import com.opensymphony.xwork2.inject.Inject;
import http.utils.multipartrequest.ServletMultipartRequest;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.StrutsConstants;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
@@ -38,6 +40,11 @@ public class PellMultiPartRequest extends AbstractMultiPartRequest {
private ServletMultipartRequest multi;
@Inject(value = StrutsConstants.STRUTS_ENABLE_DYNAMIC_METHOD_INVOCATION, required = false)
public PellMultiPartRequest(String dmiValue) {
super(dmiValue);
}
/**
* Creates a new request wrapper to handle multi-part data using methods adapted from Jason Pell's
* multipart classes (see class description).
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.7.0-SNAPSHOT</version>
<version>6.7.2</version>
</parent>
<artifactId>struts2-plexus-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>6.7.0-SNAPSHOT</version>
<version>6.7.2</version>
</parent>
<artifactId>struts2-plugins</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.7.0-SNAPSHOT</version>
<version>6.7.2</version>
</parent>
<artifactId>struts2-portlet-junit-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.7.0-SNAPSHOT</version>
<version>6.7.2</version>
</parent>
<artifactId>struts2-portlet-mocks-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.7.0-SNAPSHOT</version>
<version>6.7.2</version>
</parent>
<artifactId>struts2-portlet-tiles-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.7.0-SNAPSHOT</version>
<version>6.7.2</version>
</parent>
<artifactId>struts2-portlet-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.7.0-SNAPSHOT</version>
<version>6.7.2</version>
</parent>
<artifactId>struts2-rest-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.7.0-SNAPSHOT</version>
<version>6.7.2</version>
</parent>
<artifactId>struts2-sitemesh-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.7.0-SNAPSHOT</version>
<version>6.7.2</version>
</parent>
<artifactId>struts2-spring-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.7.0-SNAPSHOT</version>
<version>6.7.2</version>
</parent>
<artifactId>struts2-testng-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.7.0-SNAPSHOT</version>
<version>6.7.2</version>
</parent>
<artifactId>struts2-tiles-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.7.0-SNAPSHOT</version>
<version>6.7.2</version>
</parent>
<artifactId>struts2-velocity-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.7.0-SNAPSHOT</version>
<version>6.7.2</version>
</parent>
<artifactId>struts2-xslt-plugin</artifactId>
+3 -3
View File
@@ -29,7 +29,7 @@
<modelVersion>4.0.0</modelVersion>
<artifactId>struts2-parent</artifactId>
<version>6.7.0-SNAPSHOT</version>
<version>6.7.2</version>
<packaging>pom</packaging>
<name>Struts 2</name>
<url>https://struts.apache.org/</url>
@@ -51,7 +51,7 @@
<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>
<tag>STRUTS_6_3_0_1</tag>
<tag>STRUTS_6_7_2</tag>
</scm>
<issueManagement>
@@ -104,7 +104,7 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.build.outputTimestamp>2024-10-05T14:08:38Z</project.build.outputTimestamp>
<project.build.outputTimestamp>2025-02-04T06:34:33Z</project.build.outputTimestamp>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>