Merge branch 'master' of https://github.com/apache/struts into issue/WW-5196

This commit is contained in:
Stefaan Dutry
2023-03-12 19:47:43 +01:00
594 changed files with 24058 additions and 25005 deletions
+4
View File
@@ -12,3 +12,7 @@ notifications:
github:
del_branch_on_merge: true
protected_branches:
master: { }
autolink_jira:
- WW
+55
View File
@@ -0,0 +1,55 @@
# 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.
name: "CodeQL"
on:
push:
branches: [ "master" ]
pull_request:
permissions:
# Needed to upload the results to code-scanning dashboard.
security-events: write
actions: read
contents: read
# Needed to access OIDC token.
id-token: write
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'java' ]
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
- name: Autobuild
uses: github/codeql-action/autobuild@v2
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
category: "/language:${{matrix.language}}"
+59
View File
@@ -0,0 +1,59 @@
# 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.
name: Java Build
on:
pull_request:
push:
branches:
- master
permissions: read-all
env:
MAVEN_OPTS: -Xmx2048m -Xms1024m
LANG: en_US.utf8
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
java: [ '8', '11', '17' ]
steps:
- name: Checkout code
uses: actions/checkout@v3.0.2
- name: Set up cache
uses: actions/cache@v3.0.8
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-
- name: Set up JDK ${{ matrix.java }}
uses: actions/setup-java@v3
with:
distribution: adopt
java-version: ${{ matrix.java }}
- name: Build with Maven on Java ${{ matrix.java }}
if: matrix.java != '11'
run: mvn -B -V -DskipAssembly test --no-transfer-progress
- name: Code coverage on Java ${{ matrix.java }}
if: matrix.java == '11'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONARCLOUD_TOKEN }}
run: mvn -B -V -Pcoverage -DskipAssembly verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar --no-transfer-progress
@@ -0,0 +1,69 @@
# 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.
name: "Scorecards supply-chain security"
on:
branch_protection_rule:
schedule:
- cron: "30 1 * * 6" # Weekly on Saturdays
push:
branches: [ "master" ]
permissions: read-all
jobs:
analysis:
name: "Scorecards analysis"
runs-on: ubuntu-latest
permissions:
# Needed to upload the results to the code-scanning dashboard.
security-events: write
actions: read
id-token: write # This is required for requesting the JWT
contents: read # This is required for actions/checkout
steps:
- name: "Checkout code"
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # 3.1.0
with:
persist-credentials: false
- name: "Run analysis"
uses: ossf/scorecard-action@99c53751e09b9529366343771cc321ec74e9bd3d # 2.0.6
with:
results_file: results.sarif
results_format: sarif
# A read-only PAT token, which is sufficient for the action to function.
# The relevant discussion: https://github.com/ossf/scorecard-action/issues/188
repo_token: ${{ secrets.GITHUB_TOKEN }}
# Publish the results for public repositories to enable scorecard badges.
# For more details: https://github.com/ossf/scorecard-action#publishing-results
publish_results: true
- name: "Upload artifact"
uses: actions/upload-artifact@3cea5372237819ed00197afe530f5a7ea3e805c8 # 3.1.0
with:
name: SARIF file
path: results.sarif
retention-days: 5
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@b398f525a5587552e573b247ac661067fafa920b # 2.1.22
with:
sarif_file: results.sarif
-29
View File
@@ -1,29 +0,0 @@
dist: trusty
language: java
sudo: false
jdk:
- oraclejdk9
- oraclejdk11
install: true
env:
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
script:
- if [ "$STRUTS_IT" == "true" ]; then
./mvnw clean install -DskipTests -DskipAssembly -B;
./mvnw test org.jacoco:jacoco-maven-plugin:report org.jacoco:jacoco-maven-plugin:report-integration org.eluder.coveralls:coveralls-maven-plugin:report -Ptravis-coveralls -DskipAssembly -B;
else
./mvnw clean package test -DskipAssembly -B;
fi;
cache:
directories:
- $HOME/.m2
Vendored
+5 -3
View File
@@ -79,7 +79,7 @@ pipeline {
}
stage('Test') {
steps {
sh './mvnw -B test'
sh './mvnw -B verify -Pcoverage -DskipAssembly'
}
post {
always {
@@ -94,7 +94,7 @@ pipeline {
}
steps {
withCredentials([string(credentialsId: 'asf-struts-sonarcloud', variable: 'SONARCLOUD_TOKEN')]) {
sh './mvnw sonar:sonar -DskipAssembly -Dsonar.login=${SONARCLOUD_TOKEN}'
sh './mvnw -B -Pcoverage -DskipAssembly -Dsonar.login=${SONARCLOUD_TOKEN} verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar'
}
}
}
@@ -160,6 +160,7 @@ pipeline {
branch 'master'
}
steps {
sh './mvnw -B package -DskipTests'
sshPublisher(publishers: [
sshPublisherDesc(
configName: 'Nightlies',
@@ -167,7 +168,8 @@ pipeline {
sshTransfer(
remoteDirectory: '/struts/snapshot',
removePrefix: 'assembly/target/assembly/out',
sourceFiles: 'assembly/target/assembly/out/struts-*.zip'
sourceFiles: 'assembly/target/assembly/out/struts-*.zip',
cleanRemote: true
)
],
verbose: true
+5 -3
View File
@@ -17,11 +17,13 @@
The Apache Struts web framework
-------------------------------
[![Build Status @ Jenkins](https://builds.apache.org/buildStatus/icon?job=Struts%2FStruts+Core%2Fmaster)](https://ci-builds.apache.org/job/Struts/job/Struts%20Core/job/master/)
[![Build Status @ Travis](https://travis-ci.com/apache/struts.svg?branch=master)](https://app.travis-ci.com/apache/struts)
[![Jenkins Build](https://builds.apache.org/buildStatus/icon?job=Struts%2FStruts+Core%2Fmaster)](https://ci-builds.apache.org/job/Struts/job/Struts%20Core/job/master/)
[![Java Build](https://github.com/apache/struts/actions/workflows/maven.yml/badge.svg)](https://github.com/apache/struts/actions/workflows/maven.yml)
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.apache.struts/struts2-core/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.apache.struts/struts2-core/)
[![Javadocs](https://javadoc.io/badge/org.apache.struts/struts2-core.svg)](https://javadoc.io/doc/org.apache.struts/struts2-core)
[![Coverage Status](https://coveralls.io/repos/github/apache/struts/badge.svg)](https://coveralls.io/github/apache/struts)
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=apache_struts&metric=coverage)](https://sonarcloud.io/summary/new_code?id=apache_struts)
[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/apache/struts/badge)](https://deps.dev/maven/org.apache.struts%3Astruts2-core)
[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/6618/badge)](https://bestpractices.coreinfrastructure.org/projects/6618)
[![License](http://img.shields.io/:license-apache-blue.svg)](http://www.apache.org/licenses/LICENSE-2.0.html)
The Apache Struts web framework is a free open-source solution for creating Java web applications.
+2 -2
View File
@@ -7,8 +7,8 @@ and what potential vulnerability it can have:
| Version | Supported |
| ------- | ------------------ |
| 2.5.20 | :white_check_mark: |
| 2.3.37 | :white_check_mark: |
| 6.0.0 | :white_check_mark: |
| 2.5.30 | :white_check_mark: |
## Reporting New Security Issues with thr Apache Struts
+12
View File
@@ -0,0 +1,12 @@
# Struts 2 Apps
These module consists of two example applications, which were built using the Apache Struts project.
One is an old-fashioned Web application and another is a modern REST based single page app.
## Installation
Enter a given folder, either `showcase/` or `rest-showcase/` and start the app using Maven:
```
mvn jetty:run
```
then open your browser at http://localhost:8080 and navigate to a proper context.
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>6.1.0-SNAPSHOT</version>
<version>6.2.0-SNAPSHOT</version>
</parent>
<artifactId>struts2-apps</artifactId>
<packaging>pom</packaging>
+3 -2
View File
@@ -24,12 +24,12 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-apps</artifactId>
<version>6.1.0-SNAPSHOT</version>
<version>6.2.0-SNAPSHOT</version>
</parent>
<artifactId>struts2-rest-showcase</artifactId>
<packaging>war</packaging>
<version>6.1.0-SNAPSHOT</version>
<version>6.2.0-SNAPSHOT</version>
<name>Struts 2 Rest Showcase Webapp</name>
<description>Struts 2 Rest Showcase Example</description>
@@ -125,6 +125,7 @@
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>@{argLine}</argLine>
<excludes>
<exclude>it/**</exclude>
<exclude>**/*$*</exclude>
@@ -21,7 +21,7 @@
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
@@ -41,4 +41,4 @@
<package name="rest-showcase" extends="rest-default">
<global-allowed-methods>index,show,create,update,destroy,deleteConfirm,edit,editNew</global-allowed-methods>
</package>
</struts>
</struts>
+7 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-apps</artifactId>
<version>6.1.0-SNAPSHOT</version>
<version>6.2.0-SNAPSHOT</version>
</parent>
<artifactId>struts2-showcase</artifactId>
@@ -99,6 +99,11 @@
<artifactId>struts2-velocity-plugin</artifactId>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-xslt-plugin</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
@@ -237,6 +242,7 @@
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>@{argLine}</argLine>
<excludes>
<exclude>it/**</exclude>
<exclude>**/*$*</exclude>
@@ -0,0 +1,41 @@
/*
* $Id$
*
* 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.showcase.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class TestServlet extends HttpServlet {
@Override
public void service(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
switch (request.getPathInfo()) {
case "/forward":
getServletContext().getRequestDispatcher("/dispatcher/dispatch.action").forward(request, response);
break;
default:
response.sendError(404);
break;
}
}
}
@@ -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 org.apache.struts2.showcase.wait;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.interceptor.exec.ExecutorProvider;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPoolExecutorProvider implements ExecutorProvider {
private static final Logger LOG = LogManager.getLogger(ThreadPoolExecutorProvider.class);
private final ExecutorService executor;
public ThreadPoolExecutorProvider() {
this.executor = new ThreadPoolExecutor(1, 2, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingDeque<>());
}
@Override
public void execute(Runnable task) {
LOG.info("Executing task: {}", task);
executor.execute(task);
}
@Override
public boolean isShutdown() {
return executor.isShutdown();
}
@Override
public void shutdown() {
LOG.info("Shutting down executor");
executor.shutdown();
}
}
@@ -21,31 +21,31 @@
-->
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator 1.0.2//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
"https://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
<validators>
<field name="description1">
<field-validator type="requiredstring">
<message>Description1 Is Required !!!</message>
</field-validator>
</field>
<field name="description2">
<field-validator type="requiredstring">
<message>Description2 Is Required !!!</message>
</field-validator>
</field>
<field name="description3">
<field-validator type="requiredstring">
<message>Description3 Is Required !!!</message>
</field-validator>
</field>
<field name="description4">
<field-validator type="requiredstring">
<message>Description4 Is Required !!!</message>
</field-validator>
</field>
</validators>
</validators>
@@ -19,7 +19,7 @@
* under the License.
*/
-->
<!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator 1.0//EN" "http://struts.apache.org/dtds/xwork-validator-1.0.dtd">
<!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator 1.0//EN" "https://struts.apache.org/dtds/xwork-validator-1.0.dtd">
<validators>
<!-- Field-Validator Syntax -->
<field name="currentEmployee.empId">
@@ -19,7 +19,7 @@
* under the License.
*/
-->
<!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator 1.0//EN" "http://struts.apache.org/dtds/xwork-validator-1.0.dtd">
<!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator 1.0//EN" "https://struts.apache.org/dtds/xwork-validator-1.0.dtd">
<validators>
<!-- Field-Validator Syntax -->
<field name="currentSkill.name">
@@ -21,7 +21,7 @@
-->
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator 1.0.2//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
"https://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
<validators>
<field name="upload">
@@ -35,5 +35,4 @@
<message>Caption cannot be empty</message>
</field-validator>
</field>
</validators>
</validators>
@@ -19,7 +19,7 @@
* under the License.
*/
-->
<!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator 1.0//EN" "http://struts.apache.org/dtds/xwork-validator-1.0.dtd">
<!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator 1.0//EN" "https://struts.apache.org/dtds/xwork-validator-1.0.dtd">
<validators>
<field name="person">
<field-validator type="visitor">
@@ -19,7 +19,7 @@
* under the License.
*/
-->
<!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator 1.0//EN" "http://struts.apache.org/dtds/xwork-validator-1.0.dtd">
<!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator 1.0//EN" "https://struts.apache.org/dtds/xwork-validator-1.0.dtd">
<validators>
<field name="name">
<field-validator type="requiredstring">
@@ -21,8 +21,8 @@
-->
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator 1.0.2//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
"https://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
<validators>
<field name="value">
<field-validator type="requiredstring">
@@ -30,13 +30,13 @@
<message>Value must not be empty</message>
</field-validator>
</field>
<field name="count">
<field-validator type="int">
<message>Count must be an integer</message>
</field-validator>
</field-validator>
</field>
</validators>
</validators>
@@ -21,10 +21,10 @@
-->
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator 1.0//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
<!-- START SNIPPET: fieldValidatorsExample -->
"https://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
<!-- START SNIPPET: fieldValidatorsExample -->
<validators>
<field name="requiredValidatorField">
<field-validator type="required">
@@ -19,12 +19,12 @@
* under the License.
*/
-->
<!DOCTYPE validators PUBLIC
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator 1.0//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
<!-- START SNIPPET: fieldValidatorsExample -->
"https://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
<!-- START SNIPPET: fieldValidatorsExample -->
<validators>
<field name="requiredValidatorField">
<field-validator type="required">
@@ -19,13 +19,13 @@
* under the License.
*/
-->
<!DOCTYPE validators PUBLIC
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator 1.0//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
<!-- START SNIPPET: nonFieldValidatorsExample -->
"https://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
<!-- START SNIPPET: nonFieldValidatorsExample -->
<validators>
<validator type="expression">
<param name="expression"><![CDATA[ ( (someText == someTextRetype) && (someTextRetype == someTextRetypeAgain) ) ]]></param>
@@ -19,11 +19,11 @@
* under the License.
*/
-->
<!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator 1.0.2//EN" "http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
<!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator 1.0.2//EN" "https://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
<!-- START SNIPPET: quizValidators -->
<!--
Add the following DOCTYPE declaration as first line of your XXX-validation.xml file:
<!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator 1.0.2//EN" "http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
<!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator 1.0.2//EN" "https://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
-->
<validators>
<field name="name">
@@ -21,7 +21,7 @@
-->
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator 1.0//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
"https://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
<validators>
<field name="name">
@@ -19,17 +19,17 @@
* under the License.
*/
-->
<!DOCTYPE validators PUBLIC
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator 1.0//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
"https://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
<validators>
<field name="name">
<field-validator type="requiredstring">
<message>Name Required</message>
</field-validator>
</field>
<field name="age">
<field name="age">
<field-validator type="int">
<param name="min">1</param>
<param name="max">100</param>
@@ -41,6 +41,6 @@
<message>Birthday Required</message>
</field-validator>
</field>
</validators>
</validators>
@@ -19,10 +19,10 @@
* under the License.
*/
-->
<!DOCTYPE validators PUBLIC
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator 1.0//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
"https://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
<!-- START SNIPPET: visitorValidatorsExample -->
<validators>
@@ -21,12 +21,12 @@
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="actionchaining" extends="struts-default" namespace="/actionchaining">
<action name="actionChain1" class="org.apache.struts2.showcase.actionchaining.ActionChain1">
<result type="chain">actionChain2</result>
<result type="chain">actionChain2</result>
</action>
<action name="actionChain2" class="org.apache.struts2.showcase.actionchaining.ActionChain2">
<result type="chain">actionChain3</result>
@@ -21,7 +21,7 @@
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="async" extends="json-default" namespace="/async">
@@ -21,15 +21,15 @@
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="conversion" namespace="/conversion" extends="struts-default">
<action name="index">
<result>/WEB-INF/conversion/index.jsp</result>
</action>
<!-- Example populating a List inside an Action -->
<action name="enterPersonsInfo" method="input"
class="org.apache.struts2.showcase.conversion.PersonAction">
@@ -49,8 +49,8 @@
<action name="showPersonJavaCode">
<result type="plainText">/WEB-INF/conversion/Person.java.txt</result>
</action>
<!-- Example populating a Set inside an Action -->
<action name="enterAddressesInfo" class="org.apache.struts2.showcase.conversion.AddressAction" method="input">
<result>/WEB-INF/conversion/enterAddressInfo.jsp</result>
@@ -68,9 +68,9 @@
<action name="showAddressJavaCode">
<result type="plainText">/WEB-INF/conversion/Address.java.txt</result>
</action>
<!-- Example populating a List with Tiger 5 Enum -->
<!-- Example populating a List with Tiger 5 Enum -->
<action name="enterOperationEnumInfo" class="org.apache.struts2.showcase.conversion.OperationsEnumAction" method="input">
<result>/WEB-INF/conversion/enterOperations.jsp</result>
</action>
@@ -93,6 +93,6 @@
<action name="showOperationsEnumActionConversionProperties">
<result type="plainText">/WEB-INF/conversion/OperationsEnumActionConversion.txt</result>
</action>
</package>
</struts>
</struts>
@@ -21,7 +21,7 @@
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="dispatcher" extends="struts-default" namespace="/dispatcher">
@@ -21,7 +21,7 @@
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="filedownload" extends="struts-default" namespace="/filedownload">
@@ -21,11 +21,11 @@
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="fileupload" extends="struts-default" namespace="/fileupload">
<action name="upload" class="org.apache.struts2.showcase.fileupload.FileUploadAction" method="input">
<result>/WEB-INF/fileupload/upload.jsp</result>
</action>
@@ -34,11 +34,11 @@
<result name="input">/WEB-INF/fileupload/upload.jsp</result>
<result>/WEB-INF/fileupload/upload-success.jsp</result>
</action>
<action name="multipleUploadUsingList">
<result>/WEB-INF/fileupload/multipleUploadUsingList.jsp</result>
</action>
<action name="doMultipleUploadUsingList" class="org.apache.struts2.showcase.fileupload.MultipleFileUploadUsingListAction" method="upload">
<result name="input">/WEB-INF/fileupload/multipleUploadUsingList.jsp</result>
<result>/WEB-INF/fileupload/multiple-success.jsp</result>
@@ -21,16 +21,16 @@
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="freemarker" namespace="/freemarker" extends="struts-default">
<action name="customFreemarkerManagerDemo">
<result type="freemarker">/WEB-INF/freemarker/customFreemarkerManagerUsage.ftl</result>
</action>
<action name="standardTags" class="org.apache.struts2.showcase.freemarker.StandardTagsAction">
<result type="freemarker">/WEB-INF/freemarker/standardTags.ftl</result>
</action>
</package>
</struts>
</struts>
@@ -21,7 +21,7 @@
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="hangman" extends="struts-default" namespace="/hangman">
@@ -41,9 +41,9 @@
<result type="freemarker">/WEB-INF/hangman/hangmanNonAjax.ftl</result>
</action>
</package>
<package name="hangman-ajax" extends="struts-default" namespace="/hangman/ajax">
<action name="blank">
<result type="freemarker">/WEB-INF/hangman/blank.ftl</result>
@@ -61,4 +61,4 @@
<result type="freemarker">/WEB-INF/hangman/updateGuessLeft.ftl</result>
</action>
</package>
</struts>
</struts>
@@ -21,7 +21,7 @@
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="interactive" namespace="/interactive" extends="struts-default">
@@ -38,4 +38,4 @@
</action>
<action name="example" class="org.apache.struts2.showcase.action.ExampleAction" />
</package>
</struts>
</struts>
@@ -21,7 +21,7 @@
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="modelDriven" extends="struts-default" namespace="/modelDriven">
@@ -29,11 +29,10 @@
<action name="modelDriven" class="org.apache.struts2.showcase.modelDriven.ModelDrivenAction" method="input">
<result>/WEB-INF/modelDriven/modelDriven.jsp</result>
</action>
<!-- Display the result page whose content is populated using the Model-Driven technique -->
<action name="modelDrivenResult" class="org.apache.struts2.showcase.modelDriven.ModelDrivenAction">
<result>/WEB-INF/modelDriven/modelDrivenResult.jsp</result>
</action>
</package>
</struts>
</struts>
@@ -21,7 +21,7 @@
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
"https://struts.apache.org/dtds/struts-2.5.dtd">
<!-- START SNIPPET: xworkSample -->
@@ -21,14 +21,14 @@
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<!-- ================================== -->
<!-- === Package for ActionTag Demo === -->
<!-- ================================== -->
<package name="actionTag" extends="struts-default" namespace="/tags/non-ui/actionTag">
<action name="showActionTagDemo" class="org.apache.struts2.showcase.tag.nonui.actiontag.ActionTagDemo" method="show">
<result>/WEB-INF/tags/non-ui/actionTag/showActionTagDemo.jsp</result>
@@ -46,11 +46,11 @@
<result type="plainText">/WEB-INF/tags/non-ui/actionTag/showActionTagDemo.jsp</result>
</action>
</package>
<!-- ==================================== -->
<!-- === Package for IteratorGeneratorTag Demo === -->
<!-- ==================================== -->
<package name="iteratorGeneratorTag" extends="struts-default" namespace="/tags/non-ui/iteratorGeneratorTag">
<action name="showGeneratorTagDemo" class="org.apache.struts2.showcase.tag.nonui.iteratortag.IteratorGeneratorTagDemo" method="input">
<result>/WEB-INF/tags/non-ui/iteratorTag/showIteratorGeneratorTagDemo.jsp</result>
@@ -60,8 +60,8 @@
<result>/WEB-INF/tags/non-ui/iteratorTag/iteratorGeneratorTagDemoResult.jsp</result>
</action>
</package>
<!-- ===================================== -->
<!-- === Package for AppendIteratorTag Demo === -->
<!-- ========================================== -->
@@ -74,8 +74,8 @@
<result>/WEB-INF/tags/non-ui/iteratorTag/appendIteratorTagDemoResult.jsp</result>
</action>
</package>
<!-- ====================================== -->
<!-- === Package for MergeIteratorTag Demo === -->
<!-- ========================================= -->
@@ -88,7 +88,7 @@
<result>/WEB-INF/tags/non-ui/iteratorTag/mergeIteratorTagDemoResult.jsp</result>
</action>
</package>
<!-- ============================================= -->
<!-- === Package for SubsetIteratorTag Demo === -->
<!-- =========================================== -->
@@ -101,14 +101,14 @@
<result>/WEB-INF/tags/non-ui/iteratorTag/subsetIteratorTagDemoResult.jsp</result>
</action>
</package>
<!-- ========================================= -->
<!-- === Package for Action Prefix Example === -->
<!-- ========================================= -->
<package name="actionPrefix" extends="struts-default" namespace="/tags/non-ui/actionPrefix">
<action name="actionPrefixExampleUsingFreemarker">
<result type="freemarker">/WEB-INF/tags/non-ui/actionPrefix/actionPrefixExample.ftl</result>
</action>
</action>
<action name="submit" class="org.apache.struts2.showcase.tag.nonui.actionPrefix.SubmitAction">
<result type="freemarker">/WEB-INF/tags/non-ui/actionPrefix/normalSubmit.ftl</result>
</action>
@@ -131,7 +131,7 @@
<result type="plainText">/WEB-INF/tags/non-ui/actionPrefix/actionPrefixExample.ftl</result>
</action>
</package>
<!-- ========================================== -->
<!-- === Package for If Tag Test / Examples === -->
<!-- ========================================== -->
@@ -21,7 +21,7 @@
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="ui-tags" extends="velocity-default" namespace="/tags/ui">
@@ -21,7 +21,7 @@
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<include file="struts-tags-ui.xml"/>
@@ -21,7 +21,7 @@
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="tiles" extends="tiles-default" namespace="/tiles">
@@ -21,7 +21,7 @@
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="token" extends="struts-default" namespace="/token">
@@ -21,8 +21,8 @@
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<constant name="struts.beanValidation.providerClass" value="org.hibernate.validator.HibernateValidator"/>
@@ -74,7 +74,7 @@
<action name="ajaxFormSubmitSuccess" class="org.apache.struts2.showcase.validation.AjaxFormSubmitSuccessAction">
<result>/WEB-INF/validation/ajaxFormSubmitSuccess.jsp</result>
</action>
</package>
@@ -83,46 +83,46 @@
<action name="list">
<result>index.jsp</result>
</action>
<!-- ======================== -->
<!-- === Field Validators === -->
<!-- ======================== -->
<action name="showFieldValidatorsExamples" class="org.apache.struts2.showcase.validation.FieldValidatorsExampleAction" method="input">
<result name="input" type="dispatcher">/WEB-INF/validation/fieldValidatorsExample.jsp</result>
</action>
<action name="submitFieldValidatorsExamples" class="org.apache.struts2.showcase.validation.FieldValidatorsExampleAction" method="submit">
<result name="input" type="dispatcher">/WEB-INF/validation/fieldValidatorsExample.jsp</result>
<result type="dispatcher">/WEB-INF/validation/successFieldValidatorsExample.jsp</result>
</action>
<!-- ============================ -->
<!-- === Non Field Validators === -->
<!-- ============================ -->
<action name="showNonFieldValidatorsExamples" class="org.apache.struts2.showcase.validation.NonFieldValidatorsExampleAction" method="input">
<result name="input">/WEB-INF/validation/nonFieldValidatorsExample.jsp</result>
</action>
<action name="submitNonFieldValidatorsExamples" class="org.apache.struts2.showcase.validation.NonFieldValidatorsExampleAction" method="submit">
<result name="input">/WEB-INF/validation/nonFieldValidatorsExample.jsp</result>
<result>/WEB-INF/validation/successNonFieldValidatorsExample.jsp</result>
</action>
<!-- ========================== -->
<!-- === Visitor Validators === -->
<!-- ========================== -->
<action name="showVisitorValidatorsExamples" class="org.apache.struts2.showcase.validation.VisitorValidatorsExampleAction" method="input">
<result name="input">/WEB-INF/validation/visitorValidatorsExample.jsp</result>
</action>
<action name="submitVisitorValidatorsExamples" class="org.apache.struts2.showcase.validation.VisitorValidatorsExampleAction" method="submit">
<result name="input">/WEB-INF/validation/visitorValidatorsExample.jsp</result>
<result>/WEB-INF/validation/successVisitorValidatorsExample.jsp</result>
@@ -146,7 +146,7 @@
<!-- =========================================== -->
<!-- === Store Error Messages Across Request === -->
<!-- =========================================== -->
<action name="submitApplication" class="org.apache.struts2.showcase.validation.SubmitApplication" method="submitApplication">
<interceptor-ref name="store">
<param name="operationMode">STORE</param>
@@ -173,7 +173,7 @@
<action name="cancelApplication" class="org.apache.struts2.showcase.validation.SubmitApplication" method="cancelApplication">
<result>/WEB-INF/validation/storeErrorsAcrossRequestCancel.jsp</result>
</action>
</package>
</struts>
@@ -21,10 +21,17 @@
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<bean type="org.apache.struts2.interceptor.exec.ExecutorProvider" class="org.apache.struts2.showcase.wait.ThreadPoolExecutorProvider"/>
<package name="wait" extends="struts-default" namespace="/wait">
<default-action-ref name="index"/>
<action name="index">
<result>/WEB-INF/wait/index.jsp</result>
</action>
<action name="example1">
<result>/WEB-INF/wait/example1.jsp</result>
@@ -20,11 +20,11 @@
*/
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
"-//Apache Software Foundation//DTD Struts Configuration 6.0//EN"
"https://struts.apache.org/dtds/struts-6.0.dtd">
<struts>
<package name="xslt" extends="struts-default" namespace="/xslt">
<package name="xslt" extends="xslt-default" namespace="/xslt">
<default-action-ref name="index"/>
<action name="index">
@@ -36,7 +36,7 @@
<param name="exposedValue">info.classpath</param>
</result>
</action>
<action name="jvmInfoRaw" class="org.apache.struts2.showcase.xslt.JVMAction">
<result type="xslt">
<param name="exposedValue">info</param>
+2 -2
View File
@@ -21,7 +21,7 @@
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
"https://struts.apache.org/dtds/struts-2.5.dtd">
<!-- START SNIPPET: xworkSample -->
<struts>
@@ -44,7 +44,7 @@
<constant name="struts.serve.static" value="true" />
<constant name="struts.serve.static.browserCache" value="false" />
<constant name="struts.action.excludePattern" value=".*/images/.*\.gif,.*/img/.*\.gif,.*/styles/.*\.css,.*/js/.*\.js"/>
<constant name="struts.action.excludePattern" value=".*/images/.*\.gif,.*/img/.*\.gif,.*/styles/.*\.css,.*/js/.*\.js,/testServlet/.*"/>
<include file="struts-interactive.xml" />
@@ -64,14 +64,14 @@
<title><decorator:title default="Struts2 Showcase"/></title>
<s:url var="bootstrapCss" value='/styles/bootstrap.css' encode='false' includeParams='none'/>
<s:link href="%{bootstrapCss}" rel="stylesheet" type="text/css" media="all"></s:link>
<s:link href="%{bootstrapCss}" rel="stylesheet" type="text/css" media="all"/>
<s:url var="mainCss" value='/styles/main.css' encode='false' includeParams='none'/>
<s:link href="%{mainCss}" rel="stylesheet" type="text/css" media="all"></s:link>
<s:link href="%{mainCss}" rel="stylesheet" type="text/css" media="all"/>
<s:url var="jqueryJs" value='/js/jquery-2.1.4.min.js' encode='false' includeParams='none'/>
<s:script src="%{jqueryJs}"></s:script>
<s:script src="%{jqueryJs}"/>
<s:url var="bootstrapJs" value='/js/bootstrap.min.js' encode='false' includeParams='none'/>
<s:script src="%{bootstrapJs}"></s:script>
<s:script src="%{bootstrapJs}"/>
<s:script type="text/javascript">
$(function () {
var alerts = $('ul.alert').wrap('<div />');
@@ -82,19 +82,22 @@
<!-- Prettify -->
<s:url var="prettifyCss" value='/styles/prettify.css' encode='false' includeParams='none'/>
<s:link href="%{prettifyCss}" rel="stylesheet"></s:link>
<s:link href="%{prettifyCss}" rel="stylesheet"/>
<s:url var="prettifyJs" value='/js/prettify.js' encode='false' includeParams='none'/>
<s:script src="%{prettifyJs}"></s:script>
<s:script src="%{prettifyJs}"/>
<!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
<!--[if lt IE 9]>
<s:script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></s:script>
<s:script src="http://html5shim.googlecode.com/svn/trunk/html5.js"/>
<![endif]-->
<s:script>
jQuery(document).ready(function() { prettyPrint(); } );
</s:script>
<decorator:head/>
</head>
<body id="page-home" onload="prettyPrint();">
<body id="page-home">
<nav class="navbar navbar-default navbar-fixed-top">
<div class="container-fluid">
@@ -239,7 +242,7 @@
</li>
<li><s:a value="/person/index.html">Person Manager</s:a></li>
<li><s:a value="/skill/index.html">CRUD</s:a></li>
<li><s:a value="/wait/index.html">Execute &amp; Wait</s:a></li>
<li><s:a value="/wait/index">Execute &amp; Wait</s:a></li>
<li><s:a value="/token/index.html">Token</s:a></li>
<li><s:url var="url" namespace="/modelDriven" action="modelDriven"/><s:a
href="%{url}">Model Driven</s:a></li>
@@ -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.
*/
@@ -21,25 +21,26 @@
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Struts2 Showcase - Execute and Wait Examples - Complete</title>
<title>Struts2 Showcase - Execute and Wait Examples - Complete</title>
</head>
<body>
<div class="page-header">
<h1>The process is complete</h1>
<h1>The process is complete</h1>
</div>
<div class="container-fluid">
<div class="row">
<div class="col-md-12" style="text-align: center;">
<div class="row">
<div class="col-md-12" style="text-align: center;">
<b>We have processed your request.</b>
<p/>
Click here to <s:url var="back" value="/wait/index.html"/><s:a href="%{back}" cssClass="btn btn-link">return</s:a>.
<div class="panel">We have processed your request.</div>
</div>
</div>
<s:url var="back" value="/wait/index"/>
Click here to <s:a href="%{back}" cssClass="btn btn-primary">return</s:a>
</div>
</div>
</div>
</body>
</html>
@@ -0,0 +1,53 @@
<!--
/*
* 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.
*/
-->
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Struts2 Showcase - Execute and Wait Examples</title>
</head>
<body>
<div class="page-header">
<h1>Execute and Wait Examples</h1>
</div>
<div class="container-fluid">
<div class="row">
<div class="col-md-12" style="text-align: center;">
<p>
These examples illustrate Struts build in support for execute and wait.
<p/>
<p>
When you have a process that takes a long time your users can be impatient and starts to submit/click
again.
<br/> A good solution is to show the user a progress page (wait page) while the process takes it time.
<p/>
<br/>
<br/><a href='<s:url value="example1"/>'>Example 1 (no delay)</a>
<br/><a href='<s:url value="example2"/>'>Example 2 (with delay)</a>
<br/><a href='<s:url value="example3"/>'>Example 3 (with longer check delay)</a>
</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.
*/
@@ -21,53 +21,33 @@
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Struts2 Showcase - Execute and Wait Examples - Wait</title>
</head>
<body>
<div class="page-header">
<h1>Execute and Wait Examples - Wait</h1>
</div>
<div class="container-fluid">
<div class="row">
<div class="col-md-12" style="text-align: center;">
<p class="well">
We are processing your request. Please wait.
</p>
<div class="progress progress-striped active">
<div class="bar" style="width: 50%;"></div>
</div>
<p/>
You can click this link to <a href="<s:url includeParams="all"/>">refresh</a>.
<b>We have processed your request.</b>
<p/>
Click here to <s:url var="back" value="/wait/index.html"/><s:a href="%{back}" cssClass="btn btn-link">return</s:a>.
</div>
</div>
</div>
</body>
</html>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Struts2 Showcase - Execute and Wait Examples - Wait</title>
<meta http-equiv="refresh" content="5;url=<s:url includeParams="all"/>"/>
</head>
<body>
<p style="border: 1px solid silver; padding: 5px; background: #ffd; text-align: center;">
We are processing your request. Please wait.
</p>
<div class="page-header">
<h1>Execute and Wait Examples - Wait</h1>
</div>
<p/>
You can click this link to <a href="<s:url includeParams="all"/>">refresh</a>.
<div class="container-fluid">
<div class="row">
<div class="col-md-12" style="text-align: center;">
<p class="well">
We are processing your request. Please wait.
</p>
<div class="progress progress-striped active">
<div class="bar progress-bar progress-bar-success" style="width: 50%;"></div>
</div>
<div class="panel">
You can click this link to <a href="<s:url includeParams="all"/>">refresh</a>.
</div>
</div>
</div>
</div>
</body>
</html>
@@ -142,6 +142,11 @@
<load-on-startup>4</load-on-startup>
</servlet>
<servlet>
<servlet-name>testServlet</servlet-name>
<servlet-class>org.apache.struts2.showcase.servlet.TestServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>dwr</servlet-name>
<url-pattern>/dwr/*</url-pattern>
@@ -162,6 +167,11 @@
<url-pattern>/async/receiveNewMessages</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>testServlet</servlet-name>
<url-pattern>/testServlet/*</url-pattern>
</servlet-mapping>
<!-- END SNIPPET: dwr -->
<!-- SNIPPET START: example.velocity.filter.chain
@@ -1,53 +0,0 @@
<!--
/*
* 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.
*/
-->
<html>
<head>
<title>Struts2 Showcase - Execute and Wait Examples</title>
</head>
<body>
<div class="page-header">
<h1>Execute and Wait Examples</h1>
</div>
<div class="container-fluid">
<div class="row">
<div class="col-md-12" style="text-align: center;">
<p>
These examples illustrate Struts build in support for execute and wait.
<p/>
<p>
When you have a process that takes a long time your users can be impatient and starts to submit/click again.
<br/> A good solution is to show the user a progress page (wait page) while the process takes it time.
<p/>
<br/>
<br/><a href="example1.action">Example 1 (no delay)</a>
<br/><a href="example2.action">Example 2 (with delay)</a>
<br/><a href="example3.action">Example 3 (with longer check delay)</a>
</div>
</div>
</div>
</body>
</html>
@@ -32,7 +32,6 @@ public class DispatcherResultTest {
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());
}
}
@@ -40,16 +39,10 @@ public class DispatcherResultTest {
@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());
DomElement div = page.getElementById("dispatcher-result");
Assert.assertEquals("This page is a result of \"dispatching\" to it from an action", div.asNormalizedText());
}
}
@@ -1,4 +1,6 @@
/*
* $Id$
*
* 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,41 +18,24 @@
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.struts2.dispatcher;
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;
import org.apache.struts2.TestAction;
public class ForwardTest {
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.Interceptor;
@Test
public void testServletForwardingToAction() throws Exception {
try (final WebClient webClient = new WebClient()) {
// Struts excluded URL, as defined by struts.action.excludePattern
final HtmlPage page = webClient.getPage(ParameterUtils.getBaseUrl() + "/testServlet/forward");
/**
*/
public class ServletDispatchedTestAssertInterceptor implements Interceptor {
private static final long serialVersionUID = 1980347231443329805L;
public ServletDispatchedTestAssertInterceptor() {
super();
}
public void destroy() {
}
public void init() {
}
public String intercept(ActionInvocation invocation) throws Exception {
Assert.assertTrue(invocation.getAction() instanceof TestAction);
TestAction testAction = (TestAction) invocation.getAction();
Assert.assertEquals("bar", testAction.getFoo());
String result = invocation.invoke();
return result;
DomElement div = page.getElementById("dispatcher-result");
Assert.assertEquals("This page is a result of \"dispatching\" to it from an action", div.asNormalizedText());
}
}
}
+3
View File
@@ -0,0 +1,3 @@
# Struts 2 Assemblies
This module is used to prepare ZIP archives with different set of JARs, like code source, Javadocs, etc.
It's a part of the release process, it shouldn't be used directly by users.
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>6.1.0-SNAPSHOT</version>
<version>6.2.0-SNAPSHOT</version>
</parent>
<artifactId>struts2-assembly</artifactId>
+1
View File
@@ -36,6 +36,7 @@
<include>org.apache.struts:struts2-core</include>
<include>org.freemarker:freemarker</include>
<include>org.apache.commons:commons-lang3</include>
<include>org.apache.commons:commons-text</include>
<include>org.apache.logging.log4j:log4j-api</include>
<include>ognl:ognl</include>
<include>commons-fileupload:commons-fileupload</include>
+20
View File
@@ -0,0 +1,20 @@
# Struts 2 BOM
This is a Bill-Of-Materials to be used with Maven based project. It allows to import all the Struts 2
dependencies at once and used them in your project where needed.
## Installation
You must add a proper import statement into your `pom.xml` as presented below:
```xml
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-bom</artifactId>
<version>${struts2.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
```
+7 -2
View File
@@ -29,7 +29,7 @@
</parent>
<artifactId>struts2-bom</artifactId>
<version>6.1.0-SNAPSHOT</version>
<version>6.2.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Struts 2 Bill of Materials</name>
@@ -44,7 +44,7 @@
</licenses>
<properties>
<struts-version.version>6.1.0-SNAPSHOT</struts-version.version>
<struts-version.version>6.2.0-SNAPSHOT</struts-version.version>
<maven.site.skip>true</maven.site.skip>
<maven.site.deploy.skip>true</maven.site.deploy.skip>
</properties>
@@ -181,6 +181,11 @@
<artifactId>struts2-velocity-plugin</artifactId>
<version>${struts-version.version}</version>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-xslt-plugin</artifactId>
<version>${struts-version.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-osgi-bundles</artifactId>
<version>6.1.0-SNAPSHOT</version>
<version>6.2.0-SNAPSHOT</version>
</parent>
<artifactId>struts2-osgi-admin-bundle</artifactId>
+2 -2
View File
@@ -22,13 +22,13 @@
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<constant name="struts.enable.DynamicMethodInvocation" value="false" />
<package name="bundle-admin" namespace="/osgi/admin" extends="osgi-default" strict-method-invocation="true">
<default-action-ref name="bundles" />
<action name="bundle_*!*" class="org.apache.struts2.osgi.admin.actions.BundlesAction" method="{2}">
<param name="id">{1}</param>
<result type="freemarker">viewBundle.ftl</result>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-osgi-bundles</artifactId>
<version>6.1.0-SNAPSHOT</version>
<version>6.2.0-SNAPSHOT</version>
</parent>
<artifactId>struts2-osgi-demo-bundle</artifactId>
+4 -4
View File
@@ -21,11 +21,11 @@
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<!-- Set some Struts 2 constants relevant to the OSGi Plugin.
Note: The constant definitions specified here (within the demo bundle) may also need to be
<!-- Set some Struts 2 constants relevant to the OSGi Plugin.
Note: The constant definitions specified here (within the demo bundle) may also need to be
duplicated in the struts.xml configuration of the outer project that packages the demo
bundle JAR file within it.
-->
@@ -52,4 +52,4 @@
</action>
</package>
</struts>
</struts>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>6.1.0-SNAPSHOT</version>
<version>6.2.0-SNAPSHOT</version>
</parent>
<artifactId>struts2-osgi-bundles</artifactId>
+6
View File
@@ -0,0 +1,6 @@
# Struts 2 Core
This is a core of the Apache Struts framework and all other modules depend on it.
It requires Java 8 at minimum and a Servlet container supporting Java Servlet API 3.1 at least.
## Installation
Just drop this plugin into `WEB-INF/lib` folder or add it as Maven dependency
+3 -6
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>6.1.0-SNAPSHOT</version>
<version>6.2.0-SNAPSHOT</version>
</parent>
<artifactId>struts2-core</artifactId>
<packaging>jar</packaging>
@@ -41,6 +41,7 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>@{argLine}</argLine>
<properties>
<property>
<name>maven.testng.output.dir</name>
@@ -54,9 +55,6 @@
<element>${project.build.testOutputDirectory}/xwork - jar.jar</element>
<element>${project.build.testOutputDirectory}/xwork - zip.zip</element>
</additionalClasspathElements>
<includes>
<include>**/*Test.java</include>
</includes>
<excludes>
<exclude>**/XWorkTestCase.java</exclude>
<exclude>**/TestBean.java</exclude>
@@ -339,8 +337,7 @@
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<scope>compile</scope>
<optional>true</optional>
<scope>test</scope>
</dependency>
<!-- SLF4J support -->
@@ -24,6 +24,7 @@ import com.opensymphony.xwork2.config.entities.InterceptorMapping;
import com.opensymphony.xwork2.config.entities.ResultConfig;
import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.interceptor.ConditionalInterceptor;
import com.opensymphony.xwork2.interceptor.Interceptor;
import com.opensymphony.xwork2.interceptor.PreResultListener;
import com.opensymphony.xwork2.interceptor.WithLazyParams;
@@ -73,7 +74,7 @@ public class DefaultActionInvocation implements ActionInvocation {
protected UnknownHandlerManager unknownHandlerManager;
protected OgnlUtil ognlUtil;
protected AsyncManager asyncManager;
protected Callable asyncAction;
protected Callable<?> asyncAction;
protected WithLazyParams.LazyParamInjector lazyParamInjector;
public DefaultActionInvocation(final Map<String, Object> extraContext, final boolean pushAction) {
@@ -101,7 +102,7 @@ public class DefaultActionInvocation implements ActionInvocation {
this.container = cont;
}
@Inject(required=false)
@Inject(required = false)
public void setActionEventListener(ActionEventListener listener) {
this.actionEventListener = listener;
}
@@ -111,7 +112,7 @@ public class DefaultActionInvocation implements ActionInvocation {
this.ognlUtil = ognlUtil;
}
@Inject(required=false)
@Inject(required = false)
public void setAsyncManager(AsyncManager asyncManager) {
this.asyncManager = asyncManager;
}
@@ -214,7 +215,7 @@ public class DefaultActionInvocation implements ActionInvocation {
} catch (NullPointerException e) {
LOG.debug("Got NPE trying to read result configuration for resultCode [{}]", resultCode);
}
if (resultConfig == null) {
// If no result is found for the given resultCode, try to get a wildcard '*' match.
resultConfig = results.get("*");
@@ -248,7 +249,12 @@ public class DefaultActionInvocation implements ActionInvocation {
if (interceptor instanceof WithLazyParams) {
interceptor = lazyParamInjector.injectParams(interceptor, interceptorMapping.getParams(), invocationContext);
}
resultCode = interceptor.intercept(DefaultActionInvocation.this);
if (interceptor instanceof ConditionalInterceptor) {
resultCode = executeConditional((ConditionalInterceptor) interceptor);
} else {
LOG.debug("Executing normal interceptor: {}", interceptorMapping.getName());
resultCode = interceptor.intercept(this);
}
} else {
resultCode = invokeActionOnly();
}
@@ -268,9 +274,7 @@ public class DefaultActionInvocation implements ActionInvocation {
if (preResultListeners != null) {
LOG.trace("Executing PreResultListeners for result [{}]", result);
for (Object preResultListener : preResultListeners) {
PreResultListener listener = (PreResultListener) preResultListener;
for (PreResultListener listener : preResultListeners) {
listener.beforeResult(this, resultCode);
}
}
@@ -289,6 +293,16 @@ public class DefaultActionInvocation implements ActionInvocation {
return resultCode;
}
protected String executeConditional(ConditionalInterceptor conditionalInterceptor) throws Exception {
if (conditionalInterceptor.shouldIntercept(this)) {
LOG.debug("Executing conditional interceptor: {}", conditionalInterceptor.getClass().getSimpleName());
return conditionalInterceptor.intercept(this);
} else {
LOG.debug("Interceptor: {} is disabled, skipping to next", conditionalInterceptor.getClass().getSimpleName());
return this.invoke();
}
}
public String invokeActionOnly() throws Exception {
return invokeAction(getAction(), proxy.getConfig());
}
@@ -314,7 +328,7 @@ public class DefaultActionInvocation implements ActionInvocation {
gripe = "Unable to instantiate Action, " + proxy.getConfig().getClassName() + ", defined for '" + proxy.getActionName() + "' in namespace '" + proxy.getNamespace() + "'";
}
gripe += (((" -- " + e.getMessage()) != null) ? e.getMessage() : " [no message in exception]");
gripe += e.getMessage();
throw new StrutsException(gripe, e, proxy.getConfig());
}
@@ -363,7 +377,7 @@ public class DefaultActionInvocation implements ActionInvocation {
result.execute(this);
} else if (resultCode != null && !Action.NONE.equals(resultCode)) {
throw new ConfigurationException("No result defined for action " + getAction().getClass().getName()
+ " and result " + getResultCode(), proxy.getConfig());
+ " and result " + getResultCode(), proxy.getConfig());
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("No result returned for action {} at {}", getAction().getClass().getName(), proxy.getConfig().getLocation());
@@ -464,6 +478,7 @@ public class DefaultActionInvocation implements ActionInvocation {
/**
* Save the result to be used later.
*
* @param actionConfig current ActionConfig
* @param methodResult the result of the action.
* @return the result code to process.
@@ -476,7 +491,7 @@ public class DefaultActionInvocation implements ActionInvocation {
container.inject(explicitResult);
return null;
} else if (methodResult instanceof Callable) {
asyncAction = (Callable) methodResult;
asyncAction = (Callable<?>) methodResult;
return null;
} else {
return (String) methodResult;
@@ -34,22 +34,22 @@ import java.util.Locale;
import java.util.Map;
/**
* Base JUnit TestCase to extend for XWork specific JUnit tests. Uses
* Base JUnit TestCase to extend for XWork specific JUnit tests. Uses
* the generic test setup for logic.
*
* @author plightbo
*/
public abstract class XWorkTestCase extends TestCase {
protected ConfigurationManager configurationManager;
protected Configuration configuration;
protected Container container;
protected ActionProxyFactory actionProxyFactory;
public XWorkTestCase() {
super();
}
@Override
protected void setUp() throws Exception {
configurationManager = XWorkTestCaseHelper.setUp();
@@ -57,7 +57,7 @@ public abstract class XWorkTestCase extends TestCase {
container = configuration.getContainer();
actionProxyFactory = container.getInstance(ActionProxyFactory.class);
}
@Override
protected void tearDown() throws Exception {
XWorkTestCaseHelper.tearDown(configurationManager);
@@ -66,34 +66,33 @@ public abstract class XWorkTestCase extends TestCase {
container = null;
actionProxyFactory = null;
}
protected void loadConfigurationProviders(ConfigurationProvider... providers) {
configurationManager = XWorkTestCaseHelper.loadConfigurationProviders(configurationManager, providers);
configuration = configurationManager.getConfiguration();
container = configuration.getContainer();
actionProxyFactory = container.getInstance(ActionProxyFactory.class);
}
protected void loadButAdd(final Class<?> type, final Object impl) {
protected <T> void loadButAdd(final Class<T> type, final T impl) {
loadButAdd(type, Container.DEFAULT_NAME, impl);
}
protected void loadButAdd(final Class<?> type, final String name, final Object impl) {
protected <T> void loadButAdd(final Class<T> type, final String name, final T impl) {
loadConfigurationProviders(new StubConfigurationProvider() {
@Override
public void register(ContainerBuilder builder,
LocatableProperties props) throws ConfigurationException {
public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException {
if (impl instanceof String || ClassUtils.isPrimitiveOrWrapper(impl.getClass())) {
props.setProperty(name, "" + impl);
} else {
builder.factory(type, name, new Factory() {
public Object create(Context context) throws Exception {
builder.factory(type, name, new Factory<T>() {
public T create(Context context) throws Exception {
return impl;
}
@Override
public Class type() {
return impl.getClass();
public Class<T> type() {
return (Class<T>) impl.getClass();
}
}, Scope.SINGLETON);
}
@@ -22,12 +22,12 @@ import com.opensymphony.xwork2.config.impl.DefaultConfiguration;
import com.opensymphony.xwork2.config.providers.StrutsDefaultConfigurationProvider;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.StrutsConstants;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.Optional;
import static org.apache.struts2.StrutsConstants.STRUTS_CONFIGURATION_XML_RELOAD;
/**
@@ -42,12 +42,11 @@ public class ConfigurationManager {
protected static final Logger LOG = LogManager.getLogger(ConfigurationManager.class);
protected Configuration configuration;
protected Lock providerLock = new ReentrantLock();
private List<ContainerProvider> containerProviders = new CopyOnWriteArrayList<>();
private List<PackageProvider> packageProviders = new CopyOnWriteArrayList<>();
private List<ContainerProvider> containerProviders = new ArrayList<>();
private List<PackageProvider> packageProviders = new ArrayList<>();
protected String defaultFrameworkBeanName;
private boolean providersChanged = false;
private boolean reloadConfigs = true; // for the first time
private boolean providersChanged = true;
private boolean alwaysReloadConfigs = false;
public ConfigurationManager(String name) {
this.defaultFrameworkBeanName = name;
@@ -59,54 +58,69 @@ public class ConfigurationManager {
* @see com.opensymphony.xwork2.config.impl.DefaultConfiguration
*/
public synchronized Configuration getConfiguration() {
if (configuration == null) {
setConfiguration(createConfiguration(defaultFrameworkBeanName));
try {
configuration.reloadContainer(getContainerProviders());
} catch (ConfigurationException e) {
setConfiguration(null);
throw new ConfigurationException("Unable to load configuration.", e);
}
} else {
if (wasConfigInitialised()) {
conditionalReload();
}
return configuration;
}
/**
* @return whether configuration was initialised (was null)
*/
private boolean wasConfigInitialised() {
if (configuration == null) {
initialiseConfiguration();
return false;
}
return true;
}
protected void initialiseConfiguration() {
if (containerProviders.isEmpty()) {
addDefaultContainerProviders();
}
configuration = createConfiguration(defaultFrameworkBeanName);
try {
reload();
} catch (ConfigurationException e) {
configuration.destroy();
configuration = null;
providersChanged = true;
throw new ConfigurationException("Unable to load configuration.", e);
}
}
protected void addDefaultContainerProviders() {
containerProviders.add(new StrutsDefaultConfigurationProvider());
}
protected Configuration createConfiguration(String beanName) {
return new DefaultConfiguration(beanName);
}
/**
* Clear all container providers and destroy managing Configuration instance
*/
public synchronized void destroyConfiguration() {
clearContainerProviders();
if (configuration != null) {
configuration.destroy();
configuration = null;
}
}
public synchronized void setConfiguration(Configuration configuration) {
this.configuration = configuration;
}
/**
* <p>
* Get the current list of ConfigurationProviders. If no custom ConfigurationProviders have been added, this method
* will return a list containing only a default ConfigurationProvider, {@link StrutsDefaultConfigurationProvider}.
* If a custom ConfigurationProvider has been added, then the StrutsDefaultConfigurationProvider must be added by hand.
* </p>
*
* <p>
* TODO: The lazy instantiation of XmlConfigurationProvider should be refactored to be elsewhere. The behavior described above seems unintuitive.
* </p>
* Get the current list of ConfigurationProviders.
*
* @return the list of registered ConfigurationProvider objects
* @see ConfigurationProvider
*/
public List<ContainerProvider> getContainerProviders() {
providerLock.lock();
try {
if (containerProviders.size() == 0) {
containerProviders.add(new StrutsDefaultConfigurationProvider());
}
return containerProviders;
} finally {
providerLock.unlock();
}
public synchronized List<ContainerProvider> getContainerProviders() {
return new ArrayList<>(containerProviders);
}
/**
@@ -114,14 +128,9 @@ public class ConfigurationManager {
*
* @param containerProviders list of {@link ConfigurationProvider} to be set
*/
public void setContainerProviders(List<ContainerProvider> containerProviders) {
providerLock.lock();
try {
this.containerProviders = new CopyOnWriteArrayList<>(containerProviders);
providersChanged = true;
} finally {
providerLock.unlock();
}
public synchronized void setContainerProviders(List<ContainerProvider> containerProviders) {
this.containerProviders = new ArrayList<>(containerProviders);
providersChanged = true;
}
/**
@@ -130,22 +139,32 @@ public class ConfigurationManager {
*
* @param provider the ConfigurationProvider to register
*/
public void addContainerProvider(ContainerProvider provider) {
public synchronized void addContainerProvider(ContainerProvider provider) {
if (!containerProviders.contains(provider)) {
containerProviders.add(provider);
providersChanged = true;
}
}
public void clearContainerProviders() {
for (ContainerProvider containerProvider : containerProviders) {
clearContainerProvider(containerProvider);
public synchronized void removeContainerProvider(ContainerProvider provider) {
if (containerProviders.remove(provider)) {
destroyContainerProvider(provider);
providersChanged = true;
}
}
public synchronized void clearContainerProviders() {
destroyContainerProviders();
containerProviders.clear();
providersChanged = true;
}
private void clearContainerProvider(ContainerProvider containerProvider) {
private void destroyContainerProviders() {
LOG.debug("Destroying all providers.");
containerProviders.forEach(this::destroyContainerProvider);
}
private void destroyContainerProvider(ContainerProvider containerProvider) {
try {
containerProvider.destroy();
} catch (Exception e) {
@@ -153,80 +172,61 @@ public class ConfigurationManager {
}
}
/**
* Destroy its managing Configuration instance
*/
public synchronized void destroyConfiguration() {
clearContainerProviders(); // let's destroy the ConfigurationProvider first
containerProviders = new CopyOnWriteArrayList<>();
if (configuration != null)
configuration.destroy(); // let's destroy it first, before nulling it.
configuration = null;
}
/**
* Reloads the Configuration files if the configuration files indicate that they need to be reloaded.
*/
public synchronized void conditionalReload() {
if (reloadConfigs || providersChanged) {
if (alwaysReloadConfigs || providersChanged) {
LOG.debug("Checking ConfigurationProviders for reload.");
List<ContainerProvider> providers = getContainerProviders();
boolean reload = needReloadContainerProviders(providers);
if (!reload) {
reload = needReloadPackageProviders();
if (needReloadContainerProviders() || needReloadPackageProviders()) {
destroyAndReload();
}
if (reload) {
reloadProviders(providers);
}
updateReloadConfigsFlag();
providersChanged = false;
}
}
private void updateReloadConfigsFlag() {
reloadConfigs = Boolean.parseBoolean(configuration.getContainer().getInstance(String.class, StrutsConstants.STRUTS_CONFIGURATION_XML_RELOAD));
if (LOG.isDebugEnabled()) {
LOG.debug("Updating [{}], current value is [{}], new value [{}]",
StrutsConstants.STRUTS_CONFIGURATION_XML_RELOAD, String.valueOf(reloadConfigs), String.valueOf(reloadConfigs));
private void updateAlwaysReloadFlag() {
boolean newValue = Boolean.parseBoolean(configuration.getContainer()
.getInstance(String.class, STRUTS_CONFIGURATION_XML_RELOAD));
if (alwaysReloadConfigs != newValue) {
LOG.debug(
"Updating [{}], current value is [{}], new value [{}]",
STRUTS_CONFIGURATION_XML_RELOAD,
String.valueOf(alwaysReloadConfigs),
String.valueOf(newValue));
alwaysReloadConfigs = newValue;
}
}
private boolean needReloadPackageProviders() {
if (packageProviders != null) {
for (PackageProvider provider : packageProviders) {
if (provider.needsReload()) {
LOG.info("Detected package provider [{}] needs to be reloaded. Reloading all providers.", provider);
return true;
}
}
Optional<PackageProvider> provider = packageProviders.stream().filter(PackageProvider::needsReload).findAny();
if (provider.isPresent()) {
LOG.info("Detected package provider [{}] needs to be reloaded.", provider.get());
return true;
}
return false;
}
private boolean needReloadContainerProviders(List<ContainerProvider> providers) {
for (ContainerProvider provider : providers) {
if (provider.needsReload()) {
LOG.info("Detected container provider [{}] needs to be reloaded. Reloading all providers.", provider);
return true;
}
private boolean needReloadContainerProviders() {
Optional<ContainerProvider> provider = containerProviders.stream().filter(ContainerProvider::needsReload).findAny();
if (provider.isPresent()) {
LOG.info("Detected container provider [{}] needs to be reloaded.", provider.get());
return true;
}
return false;
}
private void reloadProviders(List<ContainerProvider> providers) {
for (ContainerProvider containerProvider : containerProviders) {
try {
containerProvider.destroy();
} catch (Exception e) {
LOG.warn("error while destroying configuration provider [{}]", containerProvider, e);
}
}
packageProviders = this.configuration.reloadContainer(providers);
public synchronized void destroyAndReload() {
destroyContainerProviders();
reload();
}
public synchronized void reload() {
packageProviders = getConfiguration().reloadContainer(getContainerProviders());
if (wasConfigInitialised()) {
LOG.debug("Reloading all providers.");
packageProviders = configuration.reloadContainer(containerProviders);
providersChanged = false;
updateAlwaysReloadFlag();
}
}
}
@@ -24,7 +24,7 @@ import com.opensymphony.xwork2.util.location.LocatableProperties;
/**
* Provides beans and constants/properties for the Container
*
*
* @since 2.1
*/
public interface ContainerProvider {
@@ -32,29 +32,29 @@ public interface ContainerProvider {
/**
* Called before removed from the configuration manager
*/
public void destroy();
void destroy();
/**
* Initializes with the configuration
* @param configuration The configuration
* @throws ConfigurationException If anything goes wrong
*/
public void init(Configuration configuration) throws ConfigurationException;
void init(Configuration configuration) throws ConfigurationException;
/**
* Tells whether the ContainerProvider should reload its configuration
*
* @return <tt>true</tt>, whether the ContainerProvider should reload its configuration, <tt>false</tt>otherwise.
*/
public boolean needsReload();
boolean needsReload();
/**
* Registers beans and properties for the Container
*
*
* @param builder The builder to register beans with
* @param props The properties to register constants with
* @throws ConfigurationException If anything goes wrong
*/
public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException;
void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException;
}
@@ -20,26 +20,18 @@ package com.opensymphony.xwork2.config.entities;
import com.opensymphony.xwork2.util.location.Located;
import com.opensymphony.xwork2.util.location.Location;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.Serializable;
import java.util.*;
/**
* Configuration for Package.
*
* <p>
* In the xml configuration file this is defined as the <code>package</code> tag.
* </p>
*
* @author Rainer Hermanns
* @version $Revision$
*/
public class PackageConfig extends Located implements Comparable, Serializable, InterceptorLocator {
private static final Logger LOG = LogManager.getLogger(PackageConfig.class);
public class PackageConfig extends Located implements Comparable<PackageConfig>, Serializable, InterceptorLocator {
protected Map<String, ActionConfig> actionConfigs;
protected Map<String, ResultConfig> globalResultConfigs;
@@ -422,8 +414,7 @@ public class PackageConfig extends Located implements Comparable, Serializable,
return "PackageConfig: [" + name + "] for namespace [" + namespace + "] with parents [" + parents + "]";
}
public int compareTo(Object o) {
PackageConfig other = (PackageConfig) o;
public int compareTo(PackageConfig other) {
String full = namespace + "!" + name;
String otherFull = other.namespace + "!" + other.name;
@@ -443,7 +434,6 @@ public class PackageConfig extends Located implements Comparable, Serializable,
public static class Builder implements InterceptorLocator {
protected PackageConfig target;
private boolean strictDMI = true;
public Builder(String name) {
target = new PackageConfig(name);
@@ -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);
@@ -21,26 +21,16 @@ package com.opensymphony.xwork2.config.providers;
import com.opensymphony.xwork2.ActionProxyFactory;
import com.opensymphony.xwork2.DefaultActionProxyFactory;
import com.opensymphony.xwork2.DefaultLocaleProviderFactory;
import com.opensymphony.xwork2.LocaleProviderFactory;
import com.opensymphony.xwork2.StrutsTextProviderFactory;
import com.opensymphony.xwork2.TextProviderFactory;
import com.opensymphony.xwork2.factory.DefaultUnknownHandlerFactory;
import com.opensymphony.xwork2.factory.UnknownHandlerFactory;
import com.opensymphony.xwork2.ognl.BeanInfoCacheFactory;
import com.opensymphony.xwork2.ognl.ExpressionCacheFactory;
import com.opensymphony.xwork2.ognl.accessor.HttpParametersPropertyAccessor;
import com.opensymphony.xwork2.ognl.accessor.ParameterPropertyAccessor;
import com.opensymphony.xwork2.security.AcceptedPatternsChecker;
import com.opensymphony.xwork2.security.DefaultAcceptedPatternsChecker;
import com.opensymphony.xwork2.security.DefaultExcludedPatternsChecker;
import com.opensymphony.xwork2.DefaultTextProvider;
import com.opensymphony.xwork2.DefaultUnknownHandlerManager;
import com.opensymphony.xwork2.security.DefaultNotExcludedAcceptedPatternsChecker;
import com.opensymphony.xwork2.security.ExcludedPatternsChecker;
import com.opensymphony.xwork2.FileManager;
import com.opensymphony.xwork2.FileManagerFactory;
import com.opensymphony.xwork2.LocaleProviderFactory;
import com.opensymphony.xwork2.LocalizedTextProvider;
import com.opensymphony.xwork2.ObjectFactory;
import com.opensymphony.xwork2.StrutsTextProviderFactory;
import com.opensymphony.xwork2.TextProvider;
import com.opensymphony.xwork2.TextProviderFactory;
import com.opensymphony.xwork2.UnknownHandlerManager;
import com.opensymphony.xwork2.config.Configuration;
import com.opensymphony.xwork2.config.ConfigurationException;
@@ -57,14 +47,7 @@ import com.opensymphony.xwork2.conversion.impl.CollectionConverter;
import com.opensymphony.xwork2.conversion.impl.DateConverter;
import com.opensymphony.xwork2.conversion.impl.DefaultConversionAnnotationProcessor;
import com.opensymphony.xwork2.conversion.impl.DefaultConversionFileProcessor;
import com.opensymphony.xwork2.security.NotExcludedAcceptedPatternsChecker;
import org.apache.struts2.components.date.DateFormatter;
import org.apache.struts2.components.date.DateTimeFormatterAdapter;
import org.apache.struts2.components.date.SimpleDateFormatAdapter;
import org.apache.struts2.conversion.StrutsConversionPropertiesProcessor;
import com.opensymphony.xwork2.conversion.impl.DefaultObjectTypeDeterminer;
import org.apache.struts2.conversion.StrutsTypeConverterCreator;
import org.apache.struts2.conversion.StrutsTypeConverterHolder;
import com.opensymphony.xwork2.conversion.impl.InstantiatingNullHandler;
import com.opensymphony.xwork2.conversion.impl.NumberConverter;
import com.opensymphony.xwork2.conversion.impl.StringConverter;
@@ -73,34 +56,45 @@ import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
import com.opensymphony.xwork2.factory.ActionFactory;
import com.opensymphony.xwork2.factory.ConverterFactory;
import com.opensymphony.xwork2.factory.DefaultActionFactory;
import com.opensymphony.xwork2.factory.StrutsConverterFactory;
import com.opensymphony.xwork2.factory.DefaultInterceptorFactory;
import com.opensymphony.xwork2.factory.DefaultResultFactory;
import com.opensymphony.xwork2.factory.DefaultUnknownHandlerFactory;
import com.opensymphony.xwork2.factory.InterceptorFactory;
import com.opensymphony.xwork2.factory.ResultFactory;
import com.opensymphony.xwork2.factory.StrutsConverterFactory;
import com.opensymphony.xwork2.factory.UnknownHandlerFactory;
import com.opensymphony.xwork2.inject.ContainerBuilder;
import com.opensymphony.xwork2.inject.Scope;
import com.opensymphony.xwork2.ognl.BeanInfoCacheFactory;
import com.opensymphony.xwork2.ognl.DefaultOgnlBeanInfoCacheFactory;
import com.opensymphony.xwork2.ognl.DefaultOgnlExpressionCacheFactory;
import com.opensymphony.xwork2.ognl.ExpressionCacheFactory;
import com.opensymphony.xwork2.ognl.ObjectProxy;
import com.opensymphony.xwork2.ognl.OgnlReflectionContextFactory;
import com.opensymphony.xwork2.ognl.OgnlReflectionProvider;
import com.opensymphony.xwork2.ognl.OgnlUtil;
import com.opensymphony.xwork2.ognl.OgnlValueStackFactory;
import com.opensymphony.xwork2.ognl.accessor.CompoundRootAccessor;
import com.opensymphony.xwork2.ognl.accessor.HttpParametersPropertyAccessor;
import com.opensymphony.xwork2.ognl.accessor.ObjectAccessor;
import com.opensymphony.xwork2.ognl.accessor.ObjectProxyPropertyAccessor;
import com.opensymphony.xwork2.ognl.accessor.ParameterPropertyAccessor;
import com.opensymphony.xwork2.ognl.accessor.XWorkCollectionPropertyAccessor;
import com.opensymphony.xwork2.ognl.accessor.XWorkEnumerationAccessor;
import com.opensymphony.xwork2.ognl.accessor.XWorkIteratorPropertyAccessor;
import com.opensymphony.xwork2.ognl.accessor.XWorkListPropertyAccessor;
import com.opensymphony.xwork2.ognl.accessor.XWorkMapPropertyAccessor;
import com.opensymphony.xwork2.ognl.accessor.XWorkMethodAccessor;
import com.opensymphony.xwork2.security.AcceptedPatternsChecker;
import com.opensymphony.xwork2.security.DefaultAcceptedPatternsChecker;
import com.opensymphony.xwork2.security.DefaultExcludedPatternsChecker;
import com.opensymphony.xwork2.security.DefaultNotExcludedAcceptedPatternsChecker;
import com.opensymphony.xwork2.security.ExcludedPatternsChecker;
import com.opensymphony.xwork2.security.NotExcludedAcceptedPatternsChecker;
import com.opensymphony.xwork2.util.CompoundRoot;
import com.opensymphony.xwork2.LocalizedTextProvider;
import com.opensymphony.xwork2.ognl.DefaultOgnlBeanInfoCacheFactory;
import com.opensymphony.xwork2.ognl.DefaultOgnlExpressionCacheFactory;
import com.opensymphony.xwork2.util.StrutsLocalizedTextProvider;
import com.opensymphony.xwork2.util.OgnlTextParser;
import com.opensymphony.xwork2.util.PatternMatcher;
import com.opensymphony.xwork2.util.StrutsLocalizedTextProvider;
import com.opensymphony.xwork2.util.TextParser;
import com.opensymphony.xwork2.util.ValueStackFactory;
import com.opensymphony.xwork2.util.WildcardHelper;
@@ -119,8 +113,19 @@ import com.opensymphony.xwork2.validator.ValidatorFileParser;
import ognl.MethodAccessor;
import ognl.PropertyAccessor;
import org.apache.struts2.StrutsConstants;
import org.apache.struts2.conversion.StrutsConversionPropertiesProcessor;
import org.apache.struts2.conversion.StrutsTypeConverterCreator;
import org.apache.struts2.conversion.StrutsTypeConverterHolder;
import org.apache.struts2.dispatcher.HttpParameters;
import org.apache.struts2.dispatcher.Parameter;
import org.apache.struts2.url.QueryStringBuilder;
import org.apache.struts2.url.QueryStringParser;
import org.apache.struts2.url.StrutsQueryStringBuilder;
import org.apache.struts2.url.StrutsQueryStringParser;
import org.apache.struts2.url.StrutsUrlDecoder;
import org.apache.struts2.url.StrutsUrlEncoder;
import org.apache.struts2.url.UrlDecoder;
import org.apache.struts2.url.UrlEncoder;
import java.util.ArrayList;
import java.util.Collection;
@@ -153,88 +158,90 @@ public class StrutsDefaultConfigurationProvider implements ConfigurationProvider
@Override
public void register(ContainerBuilder builder, LocatableProperties props)
throws ConfigurationException {
throws ConfigurationException {
builder
.factory(ObjectFactory.class)
.factory(ActionFactory.class, DefaultActionFactory.class)
.factory(ResultFactory.class, DefaultResultFactory.class)
.factory(InterceptorFactory.class, DefaultInterceptorFactory.class)
.factory(com.opensymphony.xwork2.factory.ValidatorFactory.class, com.opensymphony.xwork2.factory.DefaultValidatorFactory.class)
.factory(ConverterFactory.class, StrutsConverterFactory.class)
.factory(UnknownHandlerFactory.class, DefaultUnknownHandlerFactory.class)
.factory(ObjectFactory.class)
.factory(ActionFactory.class, DefaultActionFactory.class)
.factory(ResultFactory.class, DefaultResultFactory.class)
.factory(InterceptorFactory.class, DefaultInterceptorFactory.class)
.factory(com.opensymphony.xwork2.factory.ValidatorFactory.class, com.opensymphony.xwork2.factory.DefaultValidatorFactory.class)
.factory(ConverterFactory.class, StrutsConverterFactory.class)
.factory(UnknownHandlerFactory.class, DefaultUnknownHandlerFactory.class)
.factory(ActionProxyFactory.class, DefaultActionProxyFactory.class, Scope.SINGLETON)
.factory(ObjectTypeDeterminer.class, DefaultObjectTypeDeterminer.class, Scope.SINGLETON)
.factory(ActionProxyFactory.class, DefaultActionProxyFactory.class, Scope.SINGLETON)
.factory(ObjectTypeDeterminer.class, DefaultObjectTypeDeterminer.class, Scope.SINGLETON)
.factory(XWorkConverter.class, Scope.SINGLETON)
.factory(XWorkBasicConverter.class, Scope.SINGLETON)
.factory(ConversionPropertiesProcessor.class, StrutsConversionPropertiesProcessor.class, Scope.SINGLETON)
.factory(ConversionFileProcessor.class, DefaultConversionFileProcessor.class, Scope.SINGLETON)
.factory(ConversionAnnotationProcessor.class, DefaultConversionAnnotationProcessor.class, Scope.SINGLETON)
.factory(TypeConverterCreator.class, StrutsTypeConverterCreator.class, Scope.SINGLETON)
.factory(TypeConverterHolder.class, StrutsTypeConverterHolder.class, Scope.SINGLETON)
.factory(XWorkConverter.class, Scope.SINGLETON)
.factory(XWorkBasicConverter.class, Scope.SINGLETON)
.factory(ConversionPropertiesProcessor.class, StrutsConversionPropertiesProcessor.class, Scope.SINGLETON)
.factory(ConversionFileProcessor.class, DefaultConversionFileProcessor.class, Scope.SINGLETON)
.factory(ConversionAnnotationProcessor.class, DefaultConversionAnnotationProcessor.class, Scope.SINGLETON)
.factory(TypeConverterCreator.class, StrutsTypeConverterCreator.class, Scope.SINGLETON)
.factory(TypeConverterHolder.class, StrutsTypeConverterHolder.class, Scope.SINGLETON)
.factory(FileManager.class, "system", DefaultFileManager.class, Scope.SINGLETON)
.factory(FileManagerFactory.class, DefaultFileManagerFactory.class, Scope.SINGLETON)
.factory(ValueStackFactory.class, OgnlValueStackFactory.class, Scope.SINGLETON)
.factory(ValidatorFactory.class, DefaultValidatorFactory.class, Scope.SINGLETON)
.factory(ValidatorFileParser.class, DefaultValidatorFileParser.class, Scope.SINGLETON)
.factory(PatternMatcher.class, WildcardHelper.class, Scope.SINGLETON)
.factory(ReflectionProvider.class, OgnlReflectionProvider.class, Scope.SINGLETON)
.factory(ReflectionContextFactory.class, OgnlReflectionContextFactory.class, Scope.SINGLETON)
.factory(FileManager.class, "system", DefaultFileManager.class, Scope.SINGLETON)
.factory(FileManagerFactory.class, DefaultFileManagerFactory.class, Scope.SINGLETON)
.factory(ValueStackFactory.class, OgnlValueStackFactory.class, Scope.SINGLETON)
.factory(ValidatorFactory.class, DefaultValidatorFactory.class, Scope.SINGLETON)
.factory(ValidatorFileParser.class, DefaultValidatorFileParser.class, Scope.SINGLETON)
.factory(PatternMatcher.class, WildcardHelper.class, Scope.SINGLETON)
.factory(ReflectionProvider.class, OgnlReflectionProvider.class, Scope.SINGLETON)
.factory(ReflectionContextFactory.class, OgnlReflectionContextFactory.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, CompoundRoot.class.getName(), CompoundRootAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, Object.class.getName(), ObjectAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, Iterator.class.getName(), XWorkIteratorPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, Enumeration.class.getName(), XWorkEnumerationAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, CompoundRoot.class.getName(), CompoundRootAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, Object.class.getName(), ObjectAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, Iterator.class.getName(), XWorkIteratorPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, Enumeration.class.getName(), XWorkEnumerationAccessor.class, Scope.SINGLETON)
.factory(UnknownHandlerManager.class, DefaultUnknownHandlerManager.class, Scope.SINGLETON)
.factory(UnknownHandlerManager.class, DefaultUnknownHandlerManager.class, Scope.SINGLETON)
// silly workarounds for ognl since there is no way to flush its caches
.factory(PropertyAccessor.class, List.class.getName(), XWorkListPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, ArrayList.class.getName(), XWorkListPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, HashSet.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, Set.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, HashMap.class.getName(), XWorkMapPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, Map.class.getName(), XWorkMapPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, Collection.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, ObjectProxy.class.getName(), ObjectProxyPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, HttpParameters.class.getName(), HttpParametersPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, Parameter.class.getName(), ParameterPropertyAccessor.class, Scope.SINGLETON)
// silly workarounds for ognl since there is no way to flush its caches
.factory(PropertyAccessor.class, List.class.getName(), XWorkListPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, ArrayList.class.getName(), XWorkListPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, HashSet.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, Set.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, HashMap.class.getName(), XWorkMapPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, Map.class.getName(), XWorkMapPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, Collection.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, ObjectProxy.class.getName(), ObjectProxyPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, HttpParameters.class.getName(), HttpParametersPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, Parameter.class.getName(), ParameterPropertyAccessor.class, Scope.SINGLETON)
.factory(MethodAccessor.class, Object.class.getName(), XWorkMethodAccessor.class, Scope.SINGLETON)
.factory(MethodAccessor.class, CompoundRoot.class.getName(), CompoundRootAccessor.class, Scope.SINGLETON)
.factory(MethodAccessor.class, Object.class.getName(), XWorkMethodAccessor.class, Scope.SINGLETON)
.factory(MethodAccessor.class, CompoundRoot.class.getName(), CompoundRootAccessor.class, Scope.SINGLETON)
.factory(TextParser.class, OgnlTextParser.class, Scope.SINGLETON)
.factory(TextParser.class, OgnlTextParser.class, Scope.SINGLETON)
.factory(NullHandler.class, Object.class.getName(), InstantiatingNullHandler.class, Scope.SINGLETON)
.factory(ActionValidatorManager.class, AnnotationActionValidatorManager.class, Scope.SINGLETON)
.factory(ActionValidatorManager.class, "no-annotations", DefaultActionValidatorManager.class, Scope.SINGLETON)
.factory(NullHandler.class, Object.class.getName(), InstantiatingNullHandler.class, Scope.SINGLETON)
.factory(ActionValidatorManager.class, AnnotationActionValidatorManager.class, Scope.SINGLETON)
.factory(ActionValidatorManager.class, "no-annotations", DefaultActionValidatorManager.class, Scope.SINGLETON)
.factory(TextProvider.class, "system", DefaultTextProvider.class, Scope.SINGLETON)
.factory(LocalizedTextProvider.class, StrutsLocalizedTextProvider.class, Scope.SINGLETON)
.factory(TextProviderFactory.class, StrutsTextProviderFactory.class, Scope.SINGLETON)
.factory(LocaleProviderFactory.class, DefaultLocaleProviderFactory.class, Scope.SINGLETON)
.factory(TextProvider.class, "system", DefaultTextProvider.class, Scope.SINGLETON)
.factory(LocalizedTextProvider.class, StrutsLocalizedTextProvider.class, Scope.SINGLETON)
.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(OgnlUtil.class, Scope.SINGLETON)
.factory(CollectionConverter.class, Scope.SINGLETON)
.factory(ArrayConverter.class, Scope.SINGLETON)
.factory(DateConverter.class, Scope.SINGLETON)
.factory(NumberConverter.class, Scope.SINGLETON)
.factory(StringConverter.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)
.factory(DateConverter.class, Scope.SINGLETON)
.factory(NumberConverter.class, Scope.SINGLETON)
.factory(StringConverter.class, Scope.SINGLETON)
.factory(ExcludedPatternsChecker.class, DefaultExcludedPatternsChecker.class, Scope.PROTOTYPE)
.factory(AcceptedPatternsChecker.class, DefaultAcceptedPatternsChecker.class, Scope.PROTOTYPE)
.factory(NotExcludedAcceptedPatternsChecker.class, DefaultNotExcludedAcceptedPatternsChecker.class
, Scope.SINGLETON)
.factory(ExcludedPatternsChecker.class, DefaultExcludedPatternsChecker.class, Scope.PROTOTYPE)
.factory(AcceptedPatternsChecker.class, DefaultAcceptedPatternsChecker.class, Scope.PROTOTYPE)
.factory(NotExcludedAcceptedPatternsChecker.class, DefaultNotExcludedAcceptedPatternsChecker.class
, Scope.SINGLETON)
.factory(ValueSubstitutor.class, EnvsValueSubstitutor.class, Scope.SINGLETON)
.factory(ValueSubstitutor.class, EnvsValueSubstitutor.class, Scope.SINGLETON)
.factory(DateFormatter.class, "simpleDateFormatter", SimpleDateFormatAdapter.class, Scope.SINGLETON)
.factory(DateFormatter.class, "dateTimeFormatter", DateTimeFormatterAdapter.class, Scope.SINGLETON)
.factory(QueryStringBuilder.class, StrutsQueryStringBuilder.class, Scope.SINGLETON)
.factory(QueryStringParser.class, StrutsQueryStringParser.class, Scope.SINGLETON)
.factory(UrlEncoder.class, StrutsUrlEncoder.class, Scope.SINGLETON)
.factory(UrlDecoder.class, StrutsUrlDecoder.class, Scope.SINGLETON)
;
props.setProperty(StrutsConstants.STRUTS_ENABLE_DYNAMIC_METHOD_INVOCATION, Boolean.FALSE.toString());
@@ -19,10 +19,26 @@ import com.opensymphony.xwork2.inject.util.ReferenceCache;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ReflectPermission;
import java.security.AccessControlException;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/**
* Default {@link Container} implementation.
@@ -39,11 +55,7 @@ class ContainerImpl implements Container {
this.factories = factories;
final Map<Class<?>, Set<String>> map = new HashMap<>();
for (Key<?> key : factories.keySet()) {
Set<String> names = map.get(key.getType());
if (names == null) {
names = new HashSet<>();
map.put(key.getType(), names);
}
Set<String> names = map.computeIfAbsent(key.getType(), k -> new HashSet<>());
names.add(key.getName());
}
@@ -63,20 +75,20 @@ class ContainerImpl implements Container {
* Field and method injectors.
*/
final Map<Class<?>, List<Injector>> injectors =
new ReferenceCache<Class<?>, List<Injector>>() {
@Override
protected List<Injector> create(Class<?> key) {
List<Injector> injectors = new ArrayList<>();
addInjectors(key, injectors);
return injectors;
}
};
new ReferenceCache<Class<?>, List<Injector>>() {
@Override
protected List<Injector> create(Class<?> key) {
List<Injector> injectors = new ArrayList<>();
addInjectors(key, injectors);
return injectors;
}
};
/**
* Recursively adds injectors for fields and methods from the given class to the given list. Injects parent classes
* before sub classes.
*/
void addInjectors(Class clazz, List<Injector> injectors) {
void addInjectors(Class<?> clazz, List<Injector> injectors) {
if (clazz == Object.class) {
return;
}
@@ -97,38 +109,24 @@ class ContainerImpl implements Container {
addInjectorsForMethods(clazz.getDeclaredMethods(), true, injectors);
}
callInContext(new ContextualCallable<Void>() {
public Void call(InternalContext context) {
for (Injector injector : injectors) {
injector.inject(context, null);
}
return null;
callInContext((ContextualCallable<Void>) context -> {
for (Injector injector : injectors) {
injector.inject(context, null);
}
return null;
});
}
void addInjectorsForMethods(Method[] methods, boolean statics, List<Injector> injectors) {
addInjectorsForMembers(Arrays.asList(methods), statics, injectors,
new InjectorFactory<Method>() {
public Injector create(ContainerImpl container, Method method,
String name) throws MissingDependencyException {
return new MethodInjector(container, method, name);
}
});
addInjectorsForMembers(Arrays.asList(methods), statics, injectors, MethodInjector::new);
}
void addInjectorsForFields(Field[] fields, boolean statics, List<Injector> injectors) {
addInjectorsForMembers(Arrays.asList(fields), statics, injectors,
new InjectorFactory<Field>() {
public Injector create(ContainerImpl container, Field field,
String name) throws MissingDependencyException {
return new FieldInjector(container, field, name);
}
});
addInjectorsForMembers(Arrays.asList(fields), statics, injectors, FieldInjector::new);
}
<M extends Member & AnnotatedElement> void addInjectorsForMembers(
List<M> members, boolean statics, List<Injector> injectors, InjectorFactory<M> injectorFactory) {
List<M> members, boolean statics, List<Injector> injectors, InjectorFactory<M> injectorFactory) {
for (M member : members) {
if (isStatic(member) == statics) {
Inject inject = member.getAnnotation(Inject.class);
@@ -148,12 +146,12 @@ class ContainerImpl implements Container {
interface InjectorFactory<M extends Member & AnnotatedElement> {
Injector create(ContainerImpl container, M member, String name)
throws MissingDependencyException;
throws MissingDependencyException;
}
/**
* Determines if a given {@link Member} is static or not.
*
*
* @param member checked for the static modifier.
* @return true if member is static, false otherwise.
*/
@@ -163,13 +161,13 @@ class ContainerImpl implements Container {
/**
* Determines if a given {@link Member} is considered to be public for reflection usage or not.
*
*
* @param member checked to see if it is public for reflection usage.
* @return true if member is public for reflection usage, false otherwise.
*/
private static boolean isPublicForReflection(Member member) {
return Modifier.isPublic(member.getModifiers()) &&
Modifier.isPublic(member.getDeclaringClass().getModifiers());
Modifier.isPublic(member.getDeclaringClass().getModifiers());
}
static class FieldInjector implements Injector {
@@ -179,7 +177,7 @@ class ContainerImpl implements Container {
final ExternalContext<?> externalContext;
public FieldInjector(ContainerImpl container, Field field, String name)
throws MissingDependencyException {
throws MissingDependencyException {
this.field = field;
if (!isPublicForReflection(field) && !field.isAccessible()) {
SecurityManager sm = System.getSecurityManager();
@@ -190,7 +188,7 @@ class ContainerImpl implements Container {
field.setAccessible(true);
} catch (AccessControlException e) {
throw new DependencyException("Security manager in use, could not access field: "
+ field.getDeclaringClass().getName() + "(" + field.getName() + ")", e);
+ field.getDeclaringClass().getName() + "(" + field.getName() + ")", e);
}
}
@@ -225,8 +223,12 @@ class ContainerImpl implements Container {
* @param parameterTypes parameter types
* @return injections
*/
<M extends AccessibleObject & Member> ParameterInjector<?>[]
getParametersInjectors(M member, Annotation[][] annotations, Class[] parameterTypes, String defaultName) throws MissingDependencyException {
<M extends AccessibleObject & Member> ParameterInjector<?>[] getParametersInjectors(
M member,
Annotation[][] annotations,
Class<?>[] parameterTypes,
String defaultName
) throws MissingDependencyException {
final List<ParameterInjector<?>> parameterInjectors = new ArrayList<>();
final Iterator<Annotation[]> annotationsIterator = Arrays.asList(annotations).iterator();
@@ -247,12 +249,11 @@ class ContainerImpl implements Container {
}
final ExternalContext<T> externalContext = ExternalContext.newInstance(member, key, this);
return new ParameterInjector<T>(externalContext, factory);
return new ParameterInjector<>(externalContext, factory);
}
@SuppressWarnings("unchecked")
private ParameterInjector<?>[] toArray(List<ParameterInjector<?>> parameterInjections) {
return parameterInjections.toArray(new ParameterInjector[parameterInjections.size()]);
return parameterInjections.toArray(new ParameterInjector[0]);
}
/**
@@ -261,7 +262,7 @@ class ContainerImpl implements Container {
Inject findInject(Annotation[] annotations) {
for (Annotation annotation : annotations) {
if (annotation.annotationType() == Inject.class) {
return Inject.class.cast(annotation);
return (Inject) annotation;
}
}
return null;
@@ -283,7 +284,7 @@ class ContainerImpl implements Container {
method.setAccessible(true);
} catch (AccessControlException e) {
throw new DependencyException("Security manager in use, could not access method: "
+ name + "(" + method.getName() + ")", e);
+ name + "(" + method.getName() + ")", e);
}
}
@@ -292,7 +293,7 @@ class ContainerImpl implements Container {
throw new DependencyException(method + " has no parameters to inject.");
}
parameterInjectors = container.getParametersInjectors(
method, method.getParameterAnnotations(), parameterTypes, name);
method, method.getParameterAnnotations(), parameterTypes, name);
}
@Override
@@ -305,14 +306,12 @@ class ContainerImpl implements Container {
}
}
Map<Class<?>, ConstructorInjector> constructors =
new ReferenceCache<Class<?>, ConstructorInjector>() {
@Override
@SuppressWarnings("unchecked")
protected ConstructorInjector<?> create(Class<?> implementation) {
return new ConstructorInjector(ContainerImpl.this, implementation);
}
};
Map<Class<?>, ConstructorInjector<?>> constructors = new ReferenceCache<Class<?>, ConstructorInjector<?>>() {
@Override
protected ConstructorInjector<?> create(Class<?> implementation) {
return new ConstructorInjector<>(ContainerImpl.this, implementation);
}
};
static class ConstructorInjector<T> {
@@ -334,7 +333,7 @@ class ContainerImpl implements Container {
constructor.setAccessible(true);
} catch (AccessControlException e) {
throw new DependencyException("Security manager in use, could not access constructor: "
+ implementation.getName() + "(" + constructor.getName() + ")", e);
+ implementation.getName() + "(" + constructor.getName() + ")", e);
}
}
@@ -359,14 +358,14 @@ class ContainerImpl implements Container {
}
ParameterInjector<?>[] constructParameterInjector(
Inject inject, ContainerImpl container, Constructor<T> constructor) throws MissingDependencyException {
Inject inject, ContainerImpl container, Constructor<T> constructor) throws MissingDependencyException {
return constructor.getParameterTypes().length == 0
? null // default constructor.
: container.getParametersInjectors(
constructor,
constructor.getParameterAnnotations(),
constructor.getParameterTypes(),
inject.value()
? null // default constructor.
: container.getParametersInjectors(
constructor,
constructor.getParameterAnnotations(),
constructor.getParameterTypes(),
inject.value()
);
}
@@ -378,7 +377,7 @@ class ContainerImpl implements Container {
if (constructor.getAnnotation(Inject.class) != null) {
if (found != null) {
throw new DependencyException("More than one constructor annotated"
+ " with @Inject found in " + implementation + ".");
+ " with @Inject found in " + implementation + ".");
}
found = constructor;
}
@@ -466,7 +465,7 @@ class ContainerImpl implements Container {
}
}
private static Object[] getParameters(Member member, InternalContext context, ParameterInjector[] parameterInjectors) {
private static Object[] getParameters(Member member, InternalContext context, ParameterInjector<?>[] parameterInjectors) {
if (parameterInjectors == null) {
return null;
}
@@ -494,13 +493,12 @@ class ContainerImpl implements Container {
}
}
@SuppressWarnings("unchecked")
<T> T getInstance(Class<T> type, String name, InternalContext context) {
final ExternalContext<?> previous = context.getExternalContext();
final Key<T> key = Key.newInstance(type, name);
context.setExternalContext(ExternalContext.newInstance(null, key, this));
try {
final InternalFactory o = getFactory(key);
final InternalFactory<? extends T> o = getFactory(key);
if (o != null) {
return getFactory(key).create(context);
} else {
@@ -517,39 +515,25 @@ class ContainerImpl implements Container {
@Override
public void inject(final Object o) {
callInContext(new ContextualCallable<Void>() {
public Void call(InternalContext context) {
inject(o, context);
return null;
}
callInContext((ContextualCallable<Void>) context -> {
inject(o, context);
return null;
});
}
@Override
public <T> T inject(final Class<T> implementation) {
return callInContext(new ContextualCallable<T>() {
public T call(InternalContext context) {
return inject(implementation, context);
}
});
return callInContext(context -> inject(implementation, context));
}
@Override
public <T> T getInstance(final Class<T> type, final String name) {
return callInContext(new ContextualCallable<T>() {
public T call(InternalContext context) {
return getInstance(type, name, context);
}
});
return callInContext(context -> getInstance(type, name, context));
}
@Override
public <T> T getInstance(final Class<T> type) {
return callInContext(new ContextualCallable<T>() {
public T call(InternalContext context) {
return getInstance(type, context);
}
});
return callInContext(context -> getInstance(type, context));
}
@Override
@@ -561,12 +545,7 @@ class ContainerImpl implements Container {
return names;
}
ThreadLocal<Object[]> localContext = new ThreadLocal<Object[]>() {
@Override
protected Object[] initialValue() {
return new Object[1];
}
};
ThreadLocal<Object[]> localContext = ThreadLocal.withInitial(() -> new Object[1]);
/**
* Looks up thread local context. Creates (and removes) a new context if necessary.
@@ -598,7 +577,7 @@ class ContainerImpl implements Container {
*/
@SuppressWarnings("unchecked")
<T> ConstructorInjector<T> getConstructor(Class<T> implementation) {
return constructors.get(implementation);
return (ConstructorInjector<T>) constructors.get(implementation);
}
final ThreadLocal<Object> localScopeStrategy = new ThreadLocal<>();
@@ -201,11 +201,25 @@ public enum Scope {
<T> Callable<? extends T> toCallable(final InternalContext context,
final InternalFactory<? extends T> factory) {
return new Callable<T>() {
public T call() throws Exception {
return InitializableFactory.wrapIfNeeded(factory).create(context);
}
};
return (Callable<T>) () -> InitializableFactory.wrapIfNeeded(factory).create(context);
}
public static Scope fromString(String scopeStr) {
switch (scopeStr) {
case "prototype":
return Scope.PROTOTYPE;
case "request":
return Scope.REQUEST;
case "session":
return Scope.SESSION;
case "thread":
return Scope.THREAD;
case "wizard":
return Scope.WIZARD;
case "singleton":
default:
return Scope.SINGLETON;
}
}
/**
@@ -23,23 +23,39 @@ import com.opensymphony.xwork2.ActionInvocation;
/**
* Provides default implementations of optional lifecycle methods
*/
public abstract class AbstractInterceptor implements Interceptor {
public abstract class AbstractInterceptor implements ConditionalInterceptor {
private boolean disabled;
/**
* Does nothing
*/
public void init() {
}
/**
* Does nothing
*/
public void destroy() {
}
/**
* Override to handle interception
*/
public abstract String intercept(ActionInvocation invocation) throws Exception;
/**
* Allows to skip executing a given interceptor, just define {@code <param name="disabled">true</param>}
* or use other way to override interceptor's parameters, see
* <a href="https://struts.apache.org/core-developers/interceptors#interceptor-parameter-overriding">docs</a>.
* @param disable if set to true, execution of a given interceptor will be skipped.
*/
public void setDisabled(String disable) {
this.disabled = Boolean.parseBoolean(disable);
}
@Override
public boolean shouldIntercept(ActionInvocation invocation) {
return !this.disabled;
}
}
@@ -0,0 +1,39 @@
/*
* 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.interceptor;
import com.opensymphony.xwork2.ActionInvocation;
/**
* A marking interface, when implemented allows to conditionally execute a given interceptor
* within the current action invocation.
*
* @since Struts 6.1.1
*/
public interface ConditionalInterceptor extends Interceptor {
/**
* Determines if a given interceptor should be executed in the current processing of action invocation.
*
* @param invocation current {@link ActionInvocation} to determine if the interceptor should be executed
* @return true if the given interceptor should be included in the current action invocation
* @since 6.1.1
*/
boolean shouldIntercept(ActionInvocation invocation);
}
@@ -0,0 +1,37 @@
/*
* 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.interceptor;
/**
* This interface is implemented by actions that want to declare acceptable parameter value. Works in conjunction with {@link
* ParametersInterceptor}. For example, actions may want to create a white list of parameter values they will accept or a
* blacklist of parameter values they will reject to prevent clients from setting other unexpected (and possibly dangerous)
* parameter values.
*/
public interface ParameterValueAware {
/**
* Tests if the the action will accept the parameter with the given value.
*
* @param parameterValue the parameter value
* @return <tt>true</tt> if accepted, <tt>false</tt> otherwise
*/
boolean acceptableParameterValue(String parameterValue);
}
@@ -26,6 +26,7 @@ import com.opensymphony.xwork2.security.AcceptedPatternsChecker;
import com.opensymphony.xwork2.security.ExcludedPatternsChecker;
import com.opensymphony.xwork2.util.ClearableValueStack;
import com.opensymphony.xwork2.util.MemberAccessValueStack;
import com.opensymphony.xwork2.util.TextParseUtil;
import com.opensymphony.xwork2.util.ValueStack;
import com.opensymphony.xwork2.util.ValueStackFactory;
import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
@@ -33,12 +34,15 @@ import org.apache.commons.lang3.BooleanUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.StrutsConstants;
import org.apache.struts2.dispatcher.Parameter;
import org.apache.struts2.dispatcher.HttpParameters;
import org.apache.struts2.dispatcher.Parameter;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.regex.Pattern;
@@ -62,6 +66,8 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
private ValueStackFactory valueStackFactory;
private ExcludedPatternsChecker excludedPatterns;
private AcceptedPatternsChecker acceptedPatterns;
private Set<Pattern> excludedValuePatterns = null;
private Set<Pattern> acceptedValuePatterns = null;
@Inject
public void setValueStackFactory(ValueStackFactory valueStackFactory) {
@@ -183,8 +189,10 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
for (Map.Entry<String, Parameter> entry : params.entrySet()) {
String parameterName = entry.getKey();
boolean isAcceptableParameter = isAcceptableParameter(parameterName, action);
isAcceptableParameter &= isAcceptableParameterValue(entry.getValue(), action);
if (isAcceptableParameter(parameterName, action)) {
if (isAcceptableParameter) {
acceptableParameters.put(parameterName, entry.getValue());
}
}
@@ -237,8 +245,8 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
if (action instanceof TextProvider) {
TextProvider tp = (TextProvider) action;
developerNotification = tp.getText("devmode.notification",
"Developer Notification:\n{0}",
new String[]{developerNotification}
"Developer Notification:\n{0}",
new String[]{developerNotification}
);
}
@@ -264,6 +272,23 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
return acceptableName(name) && (parameterNameAware == null || parameterNameAware.acceptableParameterName(name));
}
/**
* Checks if parameter value can be accepted or thrown away
*
* @param param the parameter
* @param action current action
* @return true if parameter is accepted
*/
protected boolean isAcceptableParameterValue(Parameter param, Object action) {
ParameterValueAware parameterValueAware = (action instanceof ParameterValueAware) ? (ParameterValueAware) action : null;
boolean acceptableParamValue = (parameterValueAware == null || parameterValueAware.acceptableParameterValue(param.getValue()));
if (hasParamValuesToExclude() || hasParamValuesToAccept()) {
// Additional validations to process
acceptableParamValue &= acceptableValue(param.getName(), param.getValue());
}
return acceptableParamValue;
}
/**
* Gets an instance of the comparator to use for the ordered sorting. Override this
* method to customize the ordering of the parameters as they are set to the
@@ -291,6 +316,15 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
return logEntry.toString();
}
/**
* Validates the name passed is:
* * Within the max length of a parameter name
* * Is not excluded
* * Is accepted
*
* @param name - Name to check
* @return true if accepted
*/
protected boolean acceptableName(String name) {
if (isIgnoredDMI(name)) {
LOG.trace("DMI is enabled, ignoring DMI method: {}", name);
@@ -311,14 +345,37 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
}
}
/**
* Validates:
* * Value is null/blank
* * Value is not excluded
* * Value is accepted
*
* @param name - Param name (for logging)
* @param value - value to check
* @return true if accepted
*/
protected boolean acceptableValue(String name, String value) {
boolean accepted = (value == null || value.isEmpty() || (!isParamValueExcluded(value) && isParamValueAccepted(value)));
if (!accepted) {
String message = "Value [{}] of parameter [{}] was not accepted and will be dropped!";
if (devMode) {
LOG.warn(message, value, name);
} else {
LOG.debug(message, value, name);
}
}
return accepted;
}
protected boolean isWithinLengthLimit(String name) {
boolean matchLength = name.length() <= paramNameMaxLength;
if (!matchLength) {
if (devMode) { // warn only when in devMode
LOG.warn("Parameter [{}] is too long, allowed length is [{}]. Use Interceptor Parameter Overriding " +
"to override the limit, see more at\n" +
"https://struts.apache.org/core-developers/interceptors.html#interceptor-parameter-overriding",
name, paramNameMaxLength);
"to override the limit, see more at\n" +
"https://struts.apache.org/core-developers/interceptors.html#interceptor-parameter-overriding",
name, paramNameMaxLength);
} else {
LOG.warn("Parameter [{}] is too long, allowed length is [{}]", name, paramNameMaxLength);
}
@@ -332,8 +389,8 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
return true;
} else if (devMode) { // warn only when in devMode
LOG.warn("Parameter [{}] didn't match accepted pattern [{}]! See Accepted / Excluded patterns at\n" +
"https://struts.apache.org/security/#accepted--excluded-patterns",
paramName, result.getAcceptedPattern());
"https://struts.apache.org/security/#accepted--excluded-patterns",
paramName, result.getAcceptedPattern());
} else {
LOG.debug("Parameter [{}] didn't match accepted pattern [{}]!", paramName, result.getAcceptedPattern());
}
@@ -345,8 +402,8 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
if (result.isExcluded()) {
if (devMode) { // warn only when in devMode
LOG.warn("Parameter [{}] matches excluded pattern [{}]! See Accepted / Excluded patterns at\n" +
"https://struts.apache.org/security/#accepted--excluded-patterns",
paramName, result.getExcludedPattern());
"https://struts.apache.org/security/#accepted--excluded-patterns",
paramName, result.getExcludedPattern());
} else {
LOG.debug("Parameter [{}] matches excluded pattern [{}]!", paramName, result.getExcludedPattern());
}
@@ -355,6 +412,54 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
return false;
}
protected boolean isParamValueExcluded(String value) {
if (!hasParamValuesToExclude()) {
LOG.debug("'excludedValuePatterns' not defined so anything is allowed");
return false;
}
for (Pattern excludedValuePattern : excludedValuePatterns) {
if (excludedValuePattern.matcher(value).matches()) {
if (devMode) {
LOG.warn("Parameter value [{}] matches excluded pattern [{}]! See Accepting/Excluding parameter values at\n" +
"https://struts.apache.org/core-developers/parameters-interceptor#excluding-parameter-values",
value, excludedValuePatterns);
} else {
LOG.debug("Parameter value [{}] matches excluded pattern [{}]", value, excludedValuePattern);
}
return true;
}
}
return false;
}
protected boolean isParamValueAccepted(String value) {
if (!hasParamValuesToAccept()) {
LOG.debug("'acceptedValuePatterns' not defined so anything is allowed");
return true;
}
for (Pattern acceptedValuePattern : acceptedValuePatterns) {
if (acceptedValuePattern.matcher(value).matches()) {
return true;
}
}
if (devMode) {
LOG.warn("Parameter value [{}] didn't match accepted pattern [{}]! See Accepting/Excluding parameter values at\n" +
"https://struts.apache.org/core-developers/parameters-interceptor#excluding-parameter-values",
value, acceptedValuePatterns);
} else {
LOG.debug("Parameter value [{}] was not accepted!", value);
}
return false;
}
private boolean hasParamValuesToExclude() {
return excludedValuePatterns != null && excludedValuePatterns.size() > 0;
}
private boolean hasParamValuesToAccept() {
return acceptedValuePatterns != null && acceptedValuePatterns.size() > 0;
}
/**
* Whether to order the parameters or not
*
@@ -397,4 +502,53 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
excludedPatterns.setExcludedPatterns(commaDelim);
}
/**
* Sets a comma-delimited list of regular expressions to match
* values of parameters that should be accepted and included in the parameter map.
*
* @param commaDelimitedPatterns A comma-delimited set of regular expressions
*/
public void setAcceptedValuePatterns(String commaDelimitedPatterns) {
Set<String> patterns = TextParseUtil.commaDelimitedStringToSet(commaDelimitedPatterns);
if (acceptedValuePatterns == null) {
// Limit unwanted log entries (for 1st call, acceptedValuePatterns null)
LOG.debug("Sets accepted value patterns to [{}], note this may impact the safety of your application!", patterns);
} else {
LOG.warn("Replacing accepted patterns [{}] with [{}], be aware that this may impact safety of your application!",
acceptedValuePatterns, patterns);
}
acceptedValuePatterns = new HashSet<>(patterns.size());
try {
for (String pattern : patterns) {
acceptedValuePatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
}
} finally {
acceptedValuePatterns = Collections.unmodifiableSet(acceptedValuePatterns);
}
}
/**
* Sets a comma-delimited list of regular expressions to match
* values of parameters that should be removed from the parameter map.
*
* @param commaDelimitedPatterns A comma-delimited set of regular expressions
*/
public void setExcludedValuePatterns(String commaDelimitedPatterns) {
Set<String> patterns = TextParseUtil.commaDelimitedStringToSet(commaDelimitedPatterns);
if (excludedValuePatterns == null) {
// Limit unwanted log entries (for 1st call, excludedValuePatterns null)
LOG.debug("Setting excluded value patterns to [{}]", patterns);
} else {
LOG.warn("Replacing excluded value patterns [{}] with [{}], be aware that this may impact safety of your application!",
excludedValuePatterns, patterns);
}
excludedValuePatterns = new HashSet<>(patterns.size());
try {
for (String pattern : patterns) {
excludedValuePatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
}
} finally {
excludedValuePatterns = Collections.unmodifiableSet(excludedValuePatterns);
}
}
}
@@ -26,7 +26,13 @@ import com.opensymphony.xwork2.ognl.accessor.CompoundRootAccessor;
import com.opensymphony.xwork2.util.CompoundRoot;
import com.opensymphony.xwork2.util.TextParseUtil;
import com.opensymphony.xwork2.util.reflection.ReflectionException;
import ognl.*;
import ognl.ClassResolver;
import ognl.Ognl;
import ognl.OgnlContext;
import ognl.OgnlException;
import ognl.OgnlRuntime;
import ognl.SimpleNode;
import ognl.TypeConverter;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -37,7 +43,12 @@ import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.*;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Pattern;
@@ -66,10 +77,12 @@ public class OgnlUtil {
private Set<Class<?>> excludedClasses;
private Set<Pattern> excludedPackageNamePatterns;
private Set<String> excludedPackageNames;
private Set<Class<?>> excludedPackageExemptClasses;
private Set<Class<?>> devModeExcludedClasses;
private Set<Pattern> devModeExcludedPackageNamePatterns;
private Set<String> devModeExcludedPackageNames;
private Set<Class<?>> devModeExcludedPackageExemptClasses;
private Container container;
private boolean allowStaticFieldAccess = true;
@@ -82,7 +95,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,23 +111,29 @@ 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<>());
excludedPackageExemptClasses = Collections.unmodifiableSet(new HashSet<>());
devModeExcludedClasses = Collections.unmodifiableSet(new HashSet<>());
devModeExcludedPackageNamePatterns = Collections.unmodifiableSet(new HashSet<>());
devModeExcludedPackageNames = Collections.unmodifiableSet(new HashSet<>());
devModeExcludedPackageExemptClasses = 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
@@ -153,7 +174,7 @@ public class OgnlUtil {
protected void setExcludedClasses(String commaDelimitedClasses) {
Set<Class<?>> excludedClasses = new HashSet<>();
excludedClasses.addAll(this.excludedClasses);
excludedClasses.addAll(parseExcludedClasses(commaDelimitedClasses));
excludedClasses.addAll(parseClasses(commaDelimitedClasses));
this.excludedClasses = Collections.unmodifiableSet(excludedClasses);
}
@@ -161,11 +182,11 @@ public class OgnlUtil {
protected void setDevModeExcludedClasses(String commaDelimitedClasses) {
Set<Class<?>> excludedClasses = new HashSet<>();
excludedClasses.addAll(this.devModeExcludedClasses);
excludedClasses.addAll(parseExcludedClasses(commaDelimitedClasses));
excludedClasses.addAll(parseClasses(commaDelimitedClasses));
this.devModeExcludedClasses = Collections.unmodifiableSet(excludedClasses);
}
private Set<Class<?>> parseExcludedClasses(String commaDelimitedClasses) {
private Set<Class<?>> parseClasses(String commaDelimitedClasses) {
Set<String> classNames = TextParseUtil.commaDelimitedStringToSet(commaDelimitedClasses);
Set<Class<?>> classes = new HashSet<>();
@@ -173,7 +194,7 @@ public class OgnlUtil {
try {
classes.add(Class.forName(className));
} catch (ClassNotFoundException e) {
throw new ConfigurationException("Cannot load excluded class: " + className, e);
throw new ConfigurationException("Cannot load class for exclusion/exemption configuration: " + className, e);
}
}
@@ -223,6 +244,22 @@ public class OgnlUtil {
this.devModeExcludedPackageNames = Collections.unmodifiableSet(excludedPackageNames);
}
@Inject(value = StrutsConstants.STRUTS_EXCLUDED_PACKAGE_EXEMPT_CLASSES, required = false)
public void setExcludedPackageExemptClasses(String commaDelimitedClasses) {
Set<Class<?>> excludedPackageExemptClasses = new HashSet<>();
excludedPackageExemptClasses.addAll(this.excludedPackageExemptClasses);
excludedPackageExemptClasses.addAll(parseClasses(commaDelimitedClasses));
this.excludedPackageExemptClasses = Collections.unmodifiableSet(excludedPackageExemptClasses);
}
@Inject(value = StrutsConstants.STRUTS_DEV_MODE_EXCLUDED_PACKAGE_EXEMPT_CLASSES, required = false)
public void setDevModeExcludedPackageExemptClasses(String commaDelimitedClasses) {
Set<Class<?>> excludedPackageExemptClasses = new HashSet<>();
excludedPackageExemptClasses.addAll(this.devModeExcludedPackageExemptClasses);
excludedPackageExemptClasses.addAll(parseClasses(commaDelimitedClasses));
this.devModeExcludedPackageExemptClasses = Collections.unmodifiableSet(excludedPackageExemptClasses);
}
private Set<String> parseExcludedPackageNames(String commaDelimitedPackageNames) {
return TextParseUtil.commaDelimitedStringToSet(commaDelimitedPackageNames);
}
@@ -239,6 +276,10 @@ public class OgnlUtil {
return excludedPackageNames;
}
public Set<Class<?>> getExcludedPackageExemptClasses() {
return excludedPackageExemptClasses;
}
@Inject
protected void setContainer(Container container) {
this.container = container;
@@ -262,10 +303,10 @@ public class OgnlUtil {
try {
if (maxLength == null || maxLength.isEmpty()) {
Ognl.applyExpressionMaxLength(null);
LOG.info("OGNL Expression Max Length disabled.");
LOG.warn("OGNL Expression Max Length disabled.");
} else {
Ognl.applyExpressionMaxLength(Integer.parseInt(maxLength));
LOG.info("OGNL Expression Max Length enabled with {}.", maxLength);
LOG.debug("OGNL Expression Max Length enabled with {}.", maxLength);
}
} catch (Exception ex) {
LOG.error("Unable to set OGNL Expression Max Length {}.", maxLength); // Help configuration debugging.
@@ -841,10 +882,12 @@ public class OgnlUtil {
memberAccess.setExcludedClasses(devModeExcludedClasses);
memberAccess.setExcludedPackageNamePatterns(devModeExcludedPackageNamePatterns);
memberAccess.setExcludedPackageNames(devModeExcludedPackageNames);
memberAccess.setExcludedPackageExemptClasses(devModeExcludedPackageExemptClasses);
} else {
memberAccess.setExcludedClasses(excludedClasses);
memberAccess.setExcludedPackageNamePatterns(excludedPackageNamePatterns);
memberAccess.setExcludedPackageNames(excludedPackageNames);
memberAccess.setExcludedPackageExemptClasses(excludedPackageExemptClasses);
}
return Ognl.createDefaultContext(root, memberAccess, resolver, defaultConverter);
@@ -29,7 +29,12 @@ import com.opensymphony.xwork2.util.CompoundRoot;
import com.opensymphony.xwork2.util.MemberAccessValueStack;
import com.opensymphony.xwork2.util.ValueStack;
import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
import ognl.*;
import ognl.MethodFailedException;
import ognl.NoSuchPropertyException;
import ognl.Ognl;
import ognl.OgnlContext;
import ognl.OgnlException;
import ognl.PropertyAccessor;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -87,6 +92,7 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
securityMemberAccess.setExcludedClasses(ognlUtil.getExcludedClasses());
securityMemberAccess.setExcludedPackageNamePatterns(ognlUtil.getExcludedPackageNamePatterns());
securityMemberAccess.setExcludedPackageNames(ognlUtil.getExcludedPackageNames());
securityMemberAccess.setExcludedPackageExemptClasses(ognlUtil.getExcludedPackageExemptClasses());
securityMemberAccess.setDisallowProxyMemberAccess(ognlUtil.isDisallowProxyMemberAccess());
}
@@ -47,6 +47,7 @@ public class SecurityMemberAccess implements MemberAccess {
private Set<Class<?>> excludedClasses = Collections.emptySet();
private Set<Pattern> excludedPackageNamePatterns = Collections.emptySet();
private Set<String> excludedPackageNames = Collections.emptySet();
private Set<Class<?>> excludedPackageExemptClasses = Collections.emptySet();
private boolean disallowProxyMemberAccess;
/**
@@ -117,23 +118,23 @@ public class SecurityMemberAccess implements MemberAccess {
}
final Class<?> memberClass = member.getDeclaringClass();
// target can be null in case of accessing static fields, since OGNL 3.2.8
final Class<?> targetClass = Modifier.isStatic(memberModifiers) ? memberClass : target.getClass();
if (isClassExcluded(memberClass)) {
LOG.warn("Declaring class of member type [{}] is excluded!", member);
return false;
}
// target can be null in case of accessing static fields, since OGNL 3.2.8
final Class<?> targetClass = Modifier.isStatic(memberModifiers) ? memberClass : target.getClass();
if (isPackageExcluded(targetClass.getPackage(), memberClass.getPackage())) {
LOG.warn("Package [{}] of target class [{}] of target [{}] or package [{}] of member [{}] are excluded!", targetClass.getPackage(), targetClass,
target, memberClass.getPackage(), member);
if (isClassExcluded(targetClass)) {
LOG.warn("Target class [{}] of target [{}] is excluded!", targetClass, target);
return false;
}
if (isClassExcluded(targetClass)) {
LOG.warn("Target class [{}] of target [{}] is excluded!", targetClass, target);
if (!isClassExcludedPackageExempt(targetClass) && !isClassExcludedPackageExempt(memberClass)
&& isPackageExcluded(targetClass.getPackage(), memberClass.getPackage())) {
LOG.warn("Package [{}] of target class [{}] of target [{}] or package [{}] of member [{}] are excluded!",
targetClass.getPackage(), targetClass, target, memberClass.getPackage(), member);
return false;
}
@@ -234,6 +235,15 @@ public class SecurityMemberAccess implements MemberAccess {
return false;
}
protected boolean isClassExcludedPackageExempt(Class<?> clazz) {
for (Class<?> excludedPackageExemptClass : excludedPackageExemptClasses) {
if (clazz.isAssignableFrom(excludedPackageExemptClass)) {
return true;
}
}
return false;
}
protected boolean isAcceptableProperty(String name) {
return name == null || ((!isExcluded(name)) && isAccepted(name));
}
@@ -287,6 +297,10 @@ public class SecurityMemberAccess implements MemberAccess {
this.excludedPackageNames = excludedPackageNames;
}
public void setExcludedPackageExemptClasses(Set<Class<?>> excludedPackageExemptClasses) {
this.excludedPackageExemptClasses = excludedPackageExemptClasses;
}
public void setDisallowProxyMemberAccess(boolean disallowProxyMemberAccess) {
this.disallowProxyMemberAccess = disallowProxyMemberAccess;
}
@@ -18,9 +18,11 @@
*/
package com.opensymphony.xwork2.util;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.ClassUtils;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
@@ -52,7 +54,7 @@ public class AnnotationUtils {
* @param clazz The {@link Class} to inspect
* @param allFields list of all fields
*/
public static void addAllFields(Class<? extends Annotation> annotationClass, Class clazz, List<Field> allFields) {
public static void addAllFields(Class<? extends Annotation> annotationClass, Class<?> clazz, List<Field> allFields) {
if (clazz == null) {
return;
@@ -76,7 +78,7 @@ public class AnnotationUtils {
* @param clazz The {@link Class} to inspect
* @param allMethods list of all methods
*/
public static void addAllMethods(Class<? extends Annotation> annotationClass, Class clazz, List<Method> allMethods) {
public static void addAllMethods(Class<? extends Annotation> annotationClass, Class<?> clazz, List<Method> allMethods) {
if (clazz == null) {
return;
@@ -97,12 +99,12 @@ public class AnnotationUtils {
* @param clazz The {@link Class} to inspect
* @param allInterfaces list of all interfaces
*/
public static void addAllInterfaces(Class clazz, List<Class> allInterfaces) {
public static void addAllInterfaces(Class<?> clazz, List<Class<?>> allInterfaces) {
if (clazz == null) {
return;
}
Class[] interfaces = clazz.getInterfaces();
Class<?>[] interfaces = clazz.getInterfaces();
allInterfaces.addAll(Arrays.asList(interfaces));
addAllInterfaces(clazz.getSuperclass(), allInterfaces);
}
@@ -189,4 +191,21 @@ public class AnnotationUtils {
return anns;
}
/**
* Varargs version of <code>AnnotatedElement.isAnnotationPresent()</code>
*
* @see AnnotatedElement
*/
@SafeVarargs
public static boolean isAnnotatedBy(AnnotatedElement annotatedElement, Class<? extends Annotation>... annotation) {
if (ArrayUtils.isEmpty(annotation)) return false;
for (Class<? extends Annotation> c : annotation) {
if (annotatedElement.isAnnotationPresent(c)) return true;
}
return false;
}
}
@@ -28,9 +28,17 @@ import org.apache.struts2.StrutsException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.*;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.TransformerFactory;
@@ -48,28 +56,24 @@ import java.util.Map;
public class DomHelper {
private static final Logger LOG = LogManager.getLogger(DomHelper.class);
public static final String XMLNS_URI = "http://www.w3.org/2000/xmlns/";
public static Location getLocationObject(Element element) {
return LocationAttributes.getLocation(element);
}
/**
* Creates a W3C Document that remembers the location of each element in
* the source file. The location of element nodes can then be retrieved
* using the {@link #getLocationObject(Element)} method.
*
* @param inputSource the inputSource to read the document from
*
* @return the W3C Document
*/
public static Document parse(InputSource inputSource) {
return parse(inputSource, null);
}
/**
* Creates a W3C Document that remembers the location of each element in
* the source file. The location of element nodes can then be retrieved
@@ -77,17 +81,16 @@ public class DomHelper {
*
* @param inputSource the inputSource to read the document from
* @param dtdMappings a map of DTD names and public ids
*
* @return the W3C Document
*/
public static Document parse(InputSource inputSource, Map<String, String> dtdMappings) {
SAXParserFactory factory = null;
String parserProp = System.getProperty("xwork.saxParserFactory");
if (parserProp != null) {
try {
ObjectFactory objectFactory = ActionContext.getContext().getContainer().getInstance(ObjectFactory.class);
Class clazz = objectFactory.getClassInstance(parserProp);
Class<?> clazz = objectFactory.getClassInstance(parserProp);
factory = (SAXParserFactory) clazz.newInstance();
} catch (Exception e) {
LOG.error("Unable to load saxParserFactory set by system property 'xwork.saxParserFactory': {}", parserProp, e);
@@ -98,6 +101,13 @@ public class DomHelper {
factory = SAXParserFactory.newInstance();
}
try {
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
} catch (ParserConfigurationException | SAXNotRecognizedException | SAXNotSupportedException e) {
throw new StrutsException("Unable to disable resolving external entities!", e);
}
factory.setValidating((dtdMappings != null));
factory.setNamespaceAware(true);
@@ -107,22 +117,22 @@ public class DomHelper {
} catch (Exception ex) {
throw new StrutsException("Unable to create SAX parser", ex);
}
DOMBuilder builder = new DOMBuilder();
// Enhance the sax stream with location information
ContentHandler locationHandler = new LocationAttributes.Pipe(builder);
try {
parser.parse(inputSource, new StartHandler(locationHandler, dtdMappings));
} catch (Exception ex) {
throw new StrutsException(ex);
}
return builder.getDocument();
}
/**
* The <code>DOMBuilder</code> is a utility class that will generate a W3C
* DOM Document from SAX events.
@@ -130,27 +140,35 @@ public class DomHelper {
* @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
*/
static public class DOMBuilder implements ContentHandler {
/** The default transformer factory shared by all instances */
/**
* The default transformer factory shared by all instances
*/
protected static SAXTransformerFactory FACTORY;
/** The transformer factory */
/**
* The transformer factory
*/
protected SAXTransformerFactory factory;
/** The result */
/**
* The result
*/
protected DOMResult result;
/** The parentNode */
/**
* The parentNode
*/
protected Node parentNode;
protected ContentHandler nextHandler;
static {
String parserProp = System.getProperty("xwork.saxTransformerFactory");
if (parserProp != null) {
try {
ObjectFactory objectFactory = ActionContext.getContext().getContainer().getInstance(ObjectFactory.class);
Class clazz = objectFactory.getClassInstance(parserProp);
Class<?> clazz = objectFactory.getClassInstance(parserProp);
FACTORY = (SAXTransformerFactory) clazz.newInstance();
} catch (Exception e) {
LOG.error("Unable to load SAXTransformerFactory set by system property 'xwork.saxTransformerFactory': {}", parserProp, e);
@@ -158,7 +176,7 @@ public class DomHelper {
}
if (FACTORY == null) {
FACTORY = (SAXTransformerFactory) TransformerFactory.newInstance();
FACTORY = (SAXTransformerFactory) TransformerFactory.newInstance();
}
}
@@ -168,15 +186,16 @@ public class DomHelper {
public DOMBuilder() {
this((Node) null);
}
/**
* Construct a new instance of this DOMBuilder.
*
* @param factory the SAX transformer factory
*/
public DOMBuilder(SAXTransformerFactory factory) {
this(factory, null);
}
/**
* Constructs a new instance that appends nodes to the given parent node.
*
@@ -185,19 +204,19 @@ public class DomHelper {
public DOMBuilder(Node parentNode) {
this(null, parentNode);
}
/**
* Construct a new instance of this DOMBuilder.
*
* @param factory the SAX transformer factory
* @param factory the SAX transformer factory
* @param parentNode the parent node
*/
public DOMBuilder(SAXTransformerFactory factory, Node parentNode) {
this.factory = factory == null? FACTORY: factory;
this.factory = factory == null ? FACTORY : factory;
this.parentNode = parentNode;
setup();
}
/**
* Setup this instance transformer and result objects.
*/
@@ -215,7 +234,7 @@ public class DomHelper {
throw new StrutsException("Fatal-Error: Unable to get transformer handler", local);
}
}
/**
* Return the newly built Document.
*
@@ -230,60 +249,61 @@ public class DomHelper {
return this.result.getNode().getOwnerDocument();
}
}
public void setDocumentLocator(Locator locator) {
nextHandler.setDocumentLocator(locator);
}
public void startDocument() throws SAXException {
nextHandler.startDocument();
}
public void endDocument() throws SAXException {
nextHandler.endDocument();
}
public void startElement(String uri, String loc, String raw, Attributes attrs) throws SAXException {
nextHandler.startElement(uri, loc, raw, attrs);
}
public void endElement(String arg0, String arg1, String arg2) throws SAXException {
nextHandler.endElement(arg0, arg1, arg2);
}
public void startPrefixMapping(String arg0, String arg1) throws SAXException {
nextHandler.startPrefixMapping(arg0, arg1);
}
public void endPrefixMapping(String arg0) throws SAXException {
nextHandler.endPrefixMapping(arg0);
}
public void characters(char[] arg0, int arg1, int arg2) throws SAXException {
nextHandler.characters(arg0, arg1, arg2);
}
public void ignorableWhitespace(char[] arg0, int arg1, int arg2) throws SAXException {
nextHandler.ignorableWhitespace(arg0, arg1, arg2);
}
public void processingInstruction(String arg0, String arg1) throws SAXException {
nextHandler.processingInstruction(arg0, arg1);
}
public void skippedEntity(String arg0) throws SAXException {
nextHandler.skippedEntity(arg0);
}
}
public static class StartHandler extends DefaultHandler {
private ContentHandler nextHandler;
private Map<String, String> dtdMappings;
private final ContentHandler nextHandler;
private final Map<String, String> dtdMappings;
/**
* Create a filter that is chained to another handler.
* @param next the next handler in the chain.
*
* @param next the next handler in the chain.
* @param dtdMappings map of DTD mappings
*/
public StartHandler(ContentHandler next, Map<String, String> dtdMappings) {
@@ -295,12 +315,12 @@ public class DomHelper {
public void setDocumentLocator(Locator locator) {
nextHandler.setDocumentLocator(locator);
}
@Override
public void startDocument() throws SAXException {
nextHandler.startDocument();
}
@Override
public void endDocument() throws SAXException {
nextHandler.endDocument();
@@ -345,7 +365,7 @@ public class DomHelper {
public void skippedEntity(String arg0) throws SAXException {
nextHandler.skippedEntity(arg0);
}
@Override
public InputSource resolveEntity(String publicId, String systemId) {
if (dtdMappings != null && dtdMappings.containsKey(publicId)) {
@@ -356,7 +376,7 @@ public class DomHelper {
}
return null;
}
@Override
public void warning(SAXParseException exception) {
}
@@ -18,6 +18,8 @@
*/
package com.opensymphony.xwork2.util;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -57,7 +59,7 @@ import java.util.regex.Pattern;
* </table>
*
* <p>
* Excaping hasn't been implemented since the intended use of these patterns will be in matching URLs.
* Escaping hasn't been implemented since the intended use of these patterns will be in matching URLs.
* </p>
*
* @since 2.1
@@ -75,38 +77,47 @@ public class NamedVariablePatternMatcher implements PatternMatcher<NamedVariable
* @return The compiled pattern, null if the pattern was null or empty
*/
public CompiledPattern compilePattern(String data) {
StringBuilder regex = new StringBuilder();
if (data != null && data.length() > 0) {
List<String> varNames = new ArrayList<>();
StringBuilder varName = null;
for (int x=0; x<data.length(); x++) {
char c = data.charAt(x);
switch (c) {
case '{' : varName = new StringBuilder(); break;
case '}' : if (varName == null) {
throw new IllegalArgumentException("Mismatched braces in pattern");
}
varNames.add(varName.toString());
regex.append("([^/]+)");
varName = null;
break;
default : if (varName == null) {
regex.append(c);
} else {
varName.append(c);
}
}
}
return new CompiledPattern(Pattern.compile(regex.toString()), varNames);
if (StringUtils.isEmpty(data)) {
return null;
}
return null;
int len = data.length();
StringBuilder regex = new StringBuilder();
List<String> varNames = new ArrayList<>();
int s = 0;
while (s < len) {
int e = data.indexOf('{', s);
if (e < 0 && data.indexOf('}') > -1) {
throw new IllegalArgumentException("Missing openning '{' in [" + data + "]!");
}
if (e < 0) {
regex.append(Pattern.quote(data.substring(s)));
break;
}
if (e > s) {
regex.append(Pattern.quote(data.substring(s, e)));
}
s = e + 1;
e = data.indexOf('}', s);
if (e < 0) {
return null;
}
String varName = data.substring(s, e);
if (StringUtils.isEmpty(varName)) {
throw new IllegalArgumentException("Missing variable name in [" + data + "]!");
}
varNames.add(varName);
regex.append("([^/]+)");
s = e + 1;
}
return new CompiledPattern(Pattern.compile(regex.toString()), varNames);
}
/**
* Tries to process the data against the compiled expression. If successful, the map will contain
* the matched data, using the specified variable names in the original pattern.
*
* @param map The map of variables
* @param map The map of variables
* @param data The data to match
* @param expr The compiled pattern
* @return True if matched, false if not matched, the data was null, or the data was an empty string
@@ -116,8 +127,8 @@ public class NamedVariablePatternMatcher implements PatternMatcher<NamedVariable
if (data != null && data.length() > 0) {
Matcher matcher = expr.getPattern().matcher(data);
if (matcher.matches()) {
for (int x=0; x<expr.getVariableNames().size(); x++) {
map.put(expr.getVariableNames().get(x), matcher.group(x+1));
for (int x = 0; x < expr.getVariableNames().size(); x++) {
map.put(expr.getVariableNames().get(x), matcher.group(x + 1));
}
return true;
}
@@ -129,8 +140,8 @@ public class NamedVariablePatternMatcher implements PatternMatcher<NamedVariable
* Stores the compiled pattern and the variable names matches will correspond to.
*/
public static class CompiledPattern {
private Pattern pattern;
private List<String> variableNames;
private final Pattern pattern;
private final List<String> variableNames;
public CompiledPattern(Pattern pattern, List<String> variableNames) {
@@ -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();
@@ -18,20 +18,14 @@
*/
package com.opensymphony.xwork2.validator;
import com.opensymphony.xwork2.*;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.ActionProxy;
import com.opensymphony.xwork2.config.entities.ActionConfig;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.ClassLoaderUtil;
import com.opensymphony.xwork2.util.ValueStack;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.StrutsConstants;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.*;
import java.util.ArrayList;
import java.util.List;
/**
* AnnotationActionValidatorManager is the entry point into XWork's annotations-based validator framework.
@@ -40,185 +34,9 @@ import java.util.*;
* @author Rainer Hermanns
* @author jepjep
*/
public class AnnotationActionValidatorManager implements ActionValidatorManager {
public class AnnotationActionValidatorManager extends DefaultActionValidatorManager {
/**
* The file suffix for any validation file.
*/
protected static final String VALIDATION_CONFIG_SUFFIX = "-validation.xml";
private final Map<String, List<ValidatorConfig>> validatorCache = Collections.synchronizedMap(new HashMap<String, List<ValidatorConfig>>());
private final Map<String, List<ValidatorConfig>> validatorFileCache = Collections.synchronizedMap(new HashMap<String, List<ValidatorConfig>>());
private static final Logger LOG = LogManager.getLogger(AnnotationActionValidatorManager.class);
private ValidatorFactory validatorFactory;
private ValidatorFileParser validatorFileParser;
private FileManager fileManager;
private boolean reloadingConfigs;
private TextProviderFactory textProviderFactory;
@Inject
public void setValidatorFactory(ValidatorFactory fac) {
this.validatorFactory = fac;
}
@Inject
public void setValidatorFileParser(ValidatorFileParser parser) {
this.validatorFileParser = parser;
}
@Inject
public void setFileManagerFactory(FileManagerFactory fileManagerFactory) {
this.fileManager = fileManagerFactory.getFileManager();
}
@Inject(value = StrutsConstants.STRUTS_CONFIGURATION_XML_RELOAD, required = false)
public void setReloadingConfigs(String reloadingConfigs) {
this.reloadingConfigs = Boolean.parseBoolean(reloadingConfigs);
}
@Inject
public void setTextProviderFactory(TextProviderFactory textProviderFactory) {
this.textProviderFactory = textProviderFactory;
}
public List<Validator> getValidators(Class clazz, String context) {
return getValidators(clazz, context, null);
}
public List<Validator> getValidators(Class clazz, String context, String method) {
final String validatorKey = buildValidatorKey(clazz, context);
final List<ValidatorConfig> cfgs;
if (validatorCache.containsKey(validatorKey)) {
if (reloadingConfigs) {
validatorCache.put(validatorKey, buildValidatorConfigs(clazz, context, true, null));
}
} else {
validatorCache.put(validatorKey, buildValidatorConfigs(clazz, context, false, null));
}
// get the set of validator configs
cfgs = new ArrayList<ValidatorConfig>(validatorCache.get(validatorKey));
ValueStack stack = ActionContext.getContext().getValueStack();
// create clean instances of the validators for the caller's use
ArrayList<Validator> validators = new ArrayList<>(cfgs.size());
for (ValidatorConfig cfg : cfgs) {
if (method == null || method.equals(cfg.getParams().get("methodName"))) {
Validator validator = validatorFactory.getValidator(
new ValidatorConfig.Builder(cfg)
.removeParam("methodName")
.build());
validator.setValidatorType(cfg.getType());
validator.setValueStack(stack);
validators.add(validator);
}
}
return validators;
}
public void validate(Object object, String context) throws ValidationException {
validate(object, context, (String) null);
}
public void validate(Object object, String context, String method) throws ValidationException {
ValidatorContext validatorContext = new DelegatingValidatorContext(object, textProviderFactory);
validate(object, context, validatorContext, method);
}
public void validate(Object object, String context, ValidatorContext validatorContext) throws ValidationException {
validate(object, context, validatorContext, null);
}
public void validate(Object object, String context, ValidatorContext validatorContext, String method) throws ValidationException {
List<Validator> validators = getValidators(object.getClass(), context, method);
Set<String> shortcircuitedFields = null;
for (final Validator validator : validators) {
try {
validator.setValidatorContext(validatorContext);
LOG.debug("Running validator: {} for object {} and method {}", validator, object, method);
FieldValidator fValidator = null;
String fullFieldName = null;
if (validator instanceof FieldValidator) {
fValidator = (FieldValidator) validator;
fullFieldName = fValidator.getValidatorContext().getFullFieldName(fValidator.getFieldName());
if ((shortcircuitedFields != null) && shortcircuitedFields.contains(fullFieldName)) {
LOG.debug("Short-circuited, skipping");
continue;
}
}
if (validator instanceof ShortCircuitableValidator && ((ShortCircuitableValidator) validator).isShortCircuit()) {
// get number of existing errors
List<String> errs = null;
if (fValidator != null) {
if (validatorContext.hasFieldErrors()) {
Collection<String> fieldErrors = validatorContext.getFieldErrors().get(fullFieldName);
if (fieldErrors != null) {
errs = new ArrayList<>(fieldErrors);
}
}
} else if (validatorContext.hasActionErrors()) {
Collection<String> actionErrors = validatorContext.getActionErrors();
if (actionErrors != null) {
errs = new ArrayList<>(actionErrors);
}
}
validator.validate(object);
if (fValidator != null) {
if (validatorContext.hasFieldErrors()) {
Collection<String> errCol = validatorContext.getFieldErrors().get(fullFieldName);
if ((errCol != null) && !errCol.equals(errs)) {
LOG.debug("Short-circuiting on field validation");
if (shortcircuitedFields == null) {
shortcircuitedFields = new TreeSet<String>();
}
shortcircuitedFields.add(fullFieldName);
}
}
} else if (validatorContext.hasActionErrors()) {
Collection<String> errCol = validatorContext.getActionErrors();
if ((errCol != null) && !errCol.equals(errs)) {
LOG.debug("Short-circuiting");
break;
}
}
continue;
}
validator.validate(object);
} finally {
validator.setValidatorContext(null);
}
}
}
/**
* Builds a key for validators - used when caching validators.
*
* @param clazz the action.
* @param context context
* @return a validator key which is the class name plus context.
*/
@Override
protected String buildValidatorKey(Class clazz, String context) {
ActionInvocation invocation = ActionContext.getContext().getActionInvocation();
ActionProxy proxy = invocation.getProxy();
@@ -230,20 +48,11 @@ public class AnnotationActionValidatorManager implements ActionValidatorManager
sb.append(config.getPackageName());
sb.append("/");
}
// the key needs to use the name of the action from the config file,
// instead of the url, so wild card actions will have the same validator
// see WW-2996
// UPDATE:
// WW-3753 Using the config name instead of the context only for
// wild card actions to keep the flexibility provided
// by the original design (such as mapping different contexts
// to the same action and method if desired)
// UPDATE:
// WW-4536 Using NameVariablePatternMatcher allows defines actions
// with patterns enclosed with '{}', it's similar case to WW-3753
// WW-2996: key needs to use the name of the action from the config file, instead of the url,
// so wildcard actions will have the same validator
// WW-3753: Using the config name instead of the context only for wildcard actions to keep the flexibility
// provided by the original design (such as mapping different contexts to the same action and method if desired)
// WW-4536: Using NamedVariablePatternMatcher allows defines actions with patterns enclosed with '{}'
String configName = config.getName();
if (configName.contains(ActionConfig.WILDCARD) || (configName.contains("{") && configName.contains("}"))) {
sb.append(configName);
@@ -252,143 +61,22 @@ public class AnnotationActionValidatorManager implements ActionValidatorManager
} else {
sb.append(context);
}
return sb.toString();
}
private List<ValidatorConfig> buildAliasValidatorConfigs(Class aClass, String context, boolean checkFile) {
@Override
protected List<ValidatorConfig> buildAliasValidatorConfigs(Class aClass, String context, boolean checkFile) {
String fileName = aClass.getName().replace('.', '/') + "-" + context.replace('/', '-') + VALIDATION_CONFIG_SUFFIX;
return loadFile(fileName, aClass, checkFile);
}
@Override
protected List<ValidatorConfig> buildClassValidatorConfigs(Class aClass, boolean checkFile) {
String fileName = aClass.getName().replace('.', '/') + VALIDATION_CONFIG_SUFFIX;
List<ValidatorConfig> result = new ArrayList<>(loadFile(fileName, aClass, checkFile));
AnnotationValidationConfigurationBuilder builder = new AnnotationValidationConfigurationBuilder(validatorFactory);
List<ValidatorConfig> annotationResult = new ArrayList<>(builder.buildAnnotationClassValidatorConfigs(aClass));
result.addAll(annotationResult);
return result;
}
/**
* <p>This method 'collects' all the validator configurations for a given
* action invocation.</p>
*
* <p>It will traverse up the class hierarchy looking for validators for every super class
* and directly implemented interface of the current action, as well as adding validators for
* any alias of this invocation. Nifty!</p>
*
* <p>Given the following class structure:</p>
* <pre>
* interface Thing;
* interface Animal extends Thing;
* interface Quadraped extends Animal;
* class AnimalImpl implements Animal;
* class QuadrapedImpl extends AnimalImpl implements Quadraped;
* class Dog extends QuadrapedImpl;
* </pre>
*
* <p>This method will look for the following config files for Dog:</p>
* <pre>
* Animal
* Animal-context
* AnimalImpl
* AnimalImpl-context
* Quadraped
* Quadraped-context
* QuadrapedImpl
* QuadrapedImpl-context
* Dog
* Dog-context
* </pre>
*
* <p>Note that the validation rules for Thing is never looked for because no class in the
* hierarchy directly implements Thing.</p>
*
* @param clazz the Class to look up validators for.
* @param context the context to use when looking up validators.
* @param checkFile true if the validation config file should be checked to see if it has been
* updated.
* @param checked the set of previously checked class-contexts, null if none have been checked
* @return a list of validator configs for the given class and context.
*/
private List<ValidatorConfig> buildValidatorConfigs(Class clazz, String context, boolean checkFile, Set<String> checked) {
List<ValidatorConfig> validatorConfigs = new ArrayList<>();
if (checked == null) {
checked = new TreeSet<>();
} else if (checked.contains(clazz.getName())) {
return validatorConfigs;
}
if (clazz.isInterface()) {
Class[] interfaces = clazz.getInterfaces();
for (Class anInterface : interfaces) {
validatorConfigs.addAll(buildValidatorConfigs(anInterface, context, checkFile, checked));
}
} else {
if (!clazz.equals(Object.class)) {
validatorConfigs.addAll(buildValidatorConfigs(clazz.getSuperclass(), context, checkFile, checked));
}
}
// look for validators for implemented interfaces
Class[] interfaces = clazz.getInterfaces();
for (Class anInterface1 : interfaces) {
if (checked.contains(anInterface1.getName())) {
continue;
}
validatorConfigs.addAll(buildClassValidatorConfigs(anInterface1, checkFile));
if (context != null) {
validatorConfigs.addAll(buildAliasValidatorConfigs(anInterface1, context, checkFile));
}
checked.add(anInterface1.getName());
}
validatorConfigs.addAll(buildClassValidatorConfigs(clazz, checkFile));
if (context != null) {
validatorConfigs.addAll(buildAliasValidatorConfigs(clazz, context, checkFile));
}
checked.add(clazz.getName());
return validatorConfigs;
}
private List<ValidatorConfig> loadFile(String fileName, Class clazz, boolean checkFile) {
List<ValidatorConfig> retList = Collections.emptyList();
URL fileUrl = ClassLoaderUtil.getResource(fileName, clazz);
if ((checkFile && fileManager.fileNeedsReloading(fileUrl)) || !validatorFileCache.containsKey(fileName)) {
try (InputStream is = fileManager.loadFile(fileUrl)) {
if (is != null) {
retList = new ArrayList<>(validatorFileParser.parseActionValidatorConfigs(validatorFactory, is, fileName));
}
} catch (IOException e) {
LOG.error("Caught exception while loading file {}", fileName, e);
}
validatorFileCache.put(fileName, retList);
} else {
retList = validatorFileCache.get(fileName);
}
return retList;
}
}
@@ -32,7 +32,16 @@ import org.apache.struts2.StrutsConstants;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import static java.util.Collections.synchronizedMap;
/**
* <p>
@@ -53,29 +62,30 @@ import java.util.*;
*/
public class DefaultActionValidatorManager implements ActionValidatorManager {
private final static Logger LOG = LogManager.getLogger(DefaultActionValidatorManager.class);
/** The file suffix for any validation file. */
/**
* The file suffix for any validation file.
*/
protected static final String VALIDATION_CONFIG_SUFFIX = "-validation.xml";
private final Map<String, List<ValidatorConfig>> validatorCache = Collections.synchronizedMap(new HashMap<String, List<ValidatorConfig>>());
private final Map<String, List<ValidatorConfig>> validatorFileCache = Collections.synchronizedMap(new HashMap<String, List<ValidatorConfig>>());
protected final Map<String, List<ValidatorConfig>> validatorCache = synchronizedMap(new HashMap<>());
protected final Map<String, List<ValidatorConfig>> validatorFileCache = synchronizedMap(new HashMap<>());
private static final Logger LOG = LogManager.getLogger(DefaultActionValidatorManager.class);
private ValidatorFactory validatorFactory;
private ValidatorFileParser validatorFileParser;
private FileManager fileManager;
private boolean reloadingConfigs;
private TextProviderFactory textProviderFactory;
protected ValidatorFactory validatorFactory;
protected ValidatorFileParser validatorFileParser;
protected FileManager fileManager;
protected boolean reloadingConfigs;
protected TextProviderFactory textProviderFactory;
@Inject
public void setValidatorFactory(ValidatorFactory fac) {
this.validatorFactory = fac;
}
@Inject
public void setValidatorFileParser(ValidatorFileParser parser) {
this.validatorFileParser = parser;
}
@Inject
public void setValidatorFactory(ValidatorFactory fac) {
this.validatorFactory = fac;
}
@Inject
public void setFileManagerFactory(FileManagerFactory fileManagerFactory) {
@@ -92,153 +102,137 @@ public class DefaultActionValidatorManager implements ActionValidatorManager {
this.textProviderFactory = textProviderFactory;
}
public synchronized List<Validator> getValidators(Class clazz, String context) {
return getValidators(clazz, context, null);
}
public synchronized List<Validator> getValidators(Class clazz, String context, String method) {
final String validatorKey = buildValidatorKey(clazz, context);
if (validatorCache.containsKey(validatorKey)) {
if (reloadingConfigs) {
validatorCache.put(validatorKey, buildValidatorConfigs(clazz, context, true, null));
}
} else {
validatorCache.put(validatorKey, buildValidatorConfigs(clazz, context, false, null));
}
ValueStack stack = ActionContext.getContext().getValueStack();
// get the set of validator configs
List<ValidatorConfig> cfgs = validatorCache.get(validatorKey);
// create clean instances of the validators for the caller's use
ArrayList<Validator> validators = new ArrayList<>(cfgs.size());
for (ValidatorConfig cfg : cfgs) {
if (method == null || method.equals(cfg.getParams().get("methodName"))) {
Validator validator = validatorFactory.getValidator(cfg);
validator.setValidatorType(cfg.getType());
validator.setValueStack(stack);
validators.add(validator);
}
}
return validators;
}
@Override
public void validate(Object object, String context) throws ValidationException {
validate(object, context, (String) null);
}
@Override
public void validate(Object object, String context, String method) throws ValidationException {
ValidatorContext validatorContext = new DelegatingValidatorContext(object, textProviderFactory);
validate(object, context, validatorContext, method);
}
@Override
public void validate(Object object, String context, ValidatorContext validatorContext) throws ValidationException {
validate(object, context, validatorContext, null);
}
public void validate(Object object, String context, ValidatorContext validatorContext, String method) throws ValidationException {
List<Validator> validators = getValidators(object.getClass(), context, method);
Set<String> shortcircuitedFields = null;
for (final Validator validator : validators) {
try {
validator.setValidatorContext(validatorContext);
LOG.debug("Running validator: {} for object {} and method {}", validator, object, method);
FieldValidator fValidator = null;
String fullFieldName = null;
if (validator instanceof FieldValidator) {
fValidator = (FieldValidator) validator;
fullFieldName = fValidator.getValidatorContext().getFullFieldName(fValidator.getFieldName());
if ((shortcircuitedFields != null) && shortcircuitedFields.contains(fullFieldName)) {
LOG.debug("Short-circuited, skipping");
continue;
}
}
if (validator instanceof ShortCircuitableValidator && ((ShortCircuitableValidator) validator).isShortCircuit()) {
// get number of existing errors
List<String> errs = null;
if (fValidator != null) {
if (validatorContext.hasFieldErrors()) {
Collection<String> fieldErrors = validatorContext.getFieldErrors().get(fullFieldName);
if (fieldErrors != null) {
errs = new ArrayList<>(fieldErrors);
}
}
} else if (validatorContext.hasActionErrors()) {
Collection<String> actionErrors = validatorContext.getActionErrors();
if (actionErrors != null) {
errs = new ArrayList<String>(actionErrors);
}
}
validator.validate(object);
if (fValidator != null) {
if (validatorContext.hasFieldErrors()) {
Collection<String> errCol = validatorContext.getFieldErrors().get(fullFieldName);
if ((errCol != null) && !errCol.equals(errs)) {
LOG.debug("Short-circuiting on field validation");
if (shortcircuitedFields == null) {
shortcircuitedFields = new TreeSet<>();
}
shortcircuitedFields.add(fullFieldName);
}
}
} else if (validatorContext.hasActionErrors()) {
Collection<String> errCol = validatorContext.getActionErrors();
if ((errCol != null) && !errCol.equals(errs)) {
LOG.debug("Short-circuiting");
break;
}
}
continue;
}
validator.validate(object);
}
finally {
validator.setValidatorContext(null);
}
}
}
/**
* Builds a key for validators - used when caching validators.
*
* @param clazz the action.
* @param context the action's context.
* @param context context
* @return a validator key which is the class name plus context.
*/
protected static String buildValidatorKey(Class clazz, String context) {
StringBuilder sb = new StringBuilder(clazz.getName());
sb.append("/");
sb.append(context);
return sb.toString();
protected String buildValidatorKey(Class clazz, String context) {
return clazz.getName() + "/" + context;
}
private List<ValidatorConfig> buildAliasValidatorConfigs(Class aClass, String context, boolean checkFile) {
String fileName = aClass.getName().replace('.', '/') + "-" + context + VALIDATION_CONFIG_SUFFIX;
return loadFile(fileName, aClass, checkFile);
protected Validator getValidatorFromValidatorConfig(ValidatorConfig config, ValueStack stack) {
Validator validator = validatorFactory.getValidator(config);
validator.setValidatorType(config.getType());
validator.setValueStack(stack);
return validator;
}
private List<ValidatorConfig> buildClassValidatorConfigs(Class aClass, boolean checkFile) {
String fileName = aClass.getName().replace('.', '/') + VALIDATION_CONFIG_SUFFIX;
@Override
public synchronized List<Validator> getValidators(Class clazz, String context, String method) {
String validatorKey = buildValidatorKey(clazz, context);
return loadFile(fileName, aClass, checkFile);
if (!validatorCache.containsKey(validatorKey)) {
validatorCache.put(validatorKey, buildValidatorConfigs(clazz, context, false, null));
} else if (reloadingConfigs) {
validatorCache.put(validatorKey, buildValidatorConfigs(clazz, context, true, null));
}
ValueStack stack = ActionContext.getContext().getValueStack();
List<ValidatorConfig> configs = validatorCache.get(validatorKey);
List<Validator> validators = new ArrayList<>();
for (ValidatorConfig config : configs) {
if (method == null || method.equals(config.getParams().get("methodName"))) {
validators.add(getValidatorFromValidatorConfig(config, stack));
}
}
return validators;
}
@Override
public synchronized List<Validator> getValidators(Class clazz, String context) {
return getValidators(clazz, context, null);
}
@Override
public void validate(Object object, String context, ValidatorContext validatorContext, String method) throws ValidationException {
List<Validator> validators = getValidators(object.getClass(), context, method);
Set<String> shortcircuitedFields = null;
for (Validator validator : validators) {
validator.setValidatorContext(validatorContext);
LOG.debug("Running validator: {} for object {} and method {}", validator, object, method);
FieldValidator fValidator = null;
String fullFieldName = null;
if (validator instanceof FieldValidator) {
fValidator = (FieldValidator) validator;
fullFieldName = validatorContext.getFullFieldName(fValidator.getFieldName());
if ((shortcircuitedFields != null) && shortcircuitedFields.contains(fullFieldName)) {
LOG.debug("Short-circuited, skipping");
continue;
}
}
if (validator instanceof ShortCircuitableValidator && ((ShortCircuitableValidator) validator).isShortCircuit()) {
// get number of existing errors
List<String> errs = null;
if (fValidator != null) {
if (validatorContext.hasFieldErrors()) {
Collection<String> fieldErrors = validatorContext.getFieldErrors().get(fullFieldName);
if (fieldErrors != null) {
errs = new ArrayList<>(fieldErrors);
}
}
} else if (validatorContext.hasActionErrors()) {
Collection<String> actionErrors = validatorContext.getActionErrors();
if (actionErrors != null) {
errs = new ArrayList<>(actionErrors);
}
}
validator.validate(object);
if (fValidator != null) {
if (validatorContext.hasFieldErrors()) {
Collection<String> errCol = validatorContext.getFieldErrors().get(fullFieldName);
if ((errCol != null) && !errCol.equals(errs)) {
LOG.debug("Short-circuiting on field validation");
if (shortcircuitedFields == null) {
shortcircuitedFields = new TreeSet<>();
}
shortcircuitedFields.add(fullFieldName);
}
}
} else if (validatorContext.hasActionErrors()) {
Collection<String> errCol = validatorContext.getActionErrors();
if ((errCol != null) && !errCol.equals(errs)) {
LOG.debug("Short-circuiting");
break;
}
}
continue;
}
validator.validate(object);
}
}
/**
@@ -276,18 +270,18 @@ public class DefaultActionValidatorManager implements ActionValidatorManager {
* <p>Note that the validation rules for Thing is never looked for because no class in the
* hierarchy directly implements Thing.</p>
*
* @param clazz the Class to look up validators for.
* @param context the context to use when looking up validators.
* @param clazz the Class to look up validators for.
* @param context the context to use when looking up validators.
* @param checkFile true if the validation config file should be checked to see if it has been
* updated.
* @param checked the set of previously checked class-contexts, null if none have been checked
* updated.
* @param checked the set of previously checked class-contexts, null if none have been checked
* @return a list of validator configs for the given class and context.
*/
private List<ValidatorConfig> buildValidatorConfigs(Class clazz, String context, boolean checkFile, Set<String> checked) {
protected List<ValidatorConfig> buildValidatorConfigs(Class clazz, String context, boolean checkFile, Set<String> checked) {
List<ValidatorConfig> validatorConfigs = new ArrayList<>();
if (checked == null) {
checked = new TreeSet<String>();
checked = new TreeSet<>();
} else if (checked.contains(clazz.getName())) {
return validatorConfigs;
}
@@ -295,7 +289,7 @@ public class DefaultActionValidatorManager implements ActionValidatorManager {
if (clazz.isInterface()) {
for (Class anInterface : clazz.getInterfaces()) {
validatorConfigs.addAll(buildValidatorConfigs(anInterface, context, checkFile, checked));
}
}
} else {
if (!clazz.equals(Object.class)) {
validatorConfigs.addAll(buildValidatorConfigs(clazz.getSuperclass(), context, checkFile, checked));
@@ -307,37 +301,44 @@ public class DefaultActionValidatorManager implements ActionValidatorManager {
if (checked.contains(anInterface1.getName())) {
continue;
}
validatorConfigs.addAll(buildClassValidatorConfigs(anInterface1, checkFile));
if (context != null) {
validatorConfigs.addAll(buildAliasValidatorConfigs(anInterface1, context, checkFile));
}
checked.add(anInterface1.getName());
}
validatorConfigs.addAll(buildClassValidatorConfigs(clazz, checkFile));
if (context != null) {
validatorConfigs.addAll(buildAliasValidatorConfigs(clazz, context, checkFile));
}
checked.add(clazz.getName());
return validatorConfigs;
}
private List<ValidatorConfig> loadFile(String fileName, Class clazz, boolean checkFile) {
protected List<ValidatorConfig> buildAliasValidatorConfigs(Class aClass, String context, boolean checkFile) {
String fileName = aClass.getName().replace('.', '/') + "-" + context + VALIDATION_CONFIG_SUFFIX;
return loadFile(fileName, aClass, checkFile);
}
protected List<ValidatorConfig> buildClassValidatorConfigs(Class aClass, boolean checkFile) {
String fileName = aClass.getName().replace('.', '/') + VALIDATION_CONFIG_SUFFIX;
return loadFile(fileName, aClass, checkFile);
}
protected List<ValidatorConfig> loadFile(String fileName, Class clazz, boolean checkFile) {
List<ValidatorConfig> retList = Collections.emptyList();
URL fileUrl = ClassLoaderUtil.getResource(fileName, clazz);
if ((checkFile && fileManager.fileNeedsReloading(fileUrl)) || !validatorFileCache.containsKey(fileName)) {
try (InputStream is = fileManager.loadFile(fileUrl)) {
if (is != null) {
retList = new ArrayList<>(validatorFileParser.parseActionValidatorConfigs(validatorFactory, is, fileName));
}
} catch (IOException e) {
LOG.error("Caught exception while loading file {}", fileName, e);
LOG.error("Caught exception while closing file {}", fileName, e);
}
validatorFileCache.put(fileName, retList);
@@ -222,7 +222,7 @@ import com.opensymphony.xwork2.util.ValueStack;
* &lt;!-- START SNIPPET: exShortCircuitingValidators --&gt;
* &lt;!DOCTYPE validators PUBLIC
* &quot;-//Apache Struts//XWork Validator 1.0.3//EN&quot;
* &quot;http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd&quot;&gt;
* &quot;https://struts.apache.org/dtds/xwork-validator-1.0.3.dtd&quot;&gt;
* &lt;validators&gt;
* &lt;!-- Field Validators for email field --&gt;
* &lt;field name=&quot;email&quot;&gt;
@@ -115,7 +115,7 @@ package com.opensymphony.xwork2.validator;
* <pre>
* <!-- START SNIPPET: exValidationRules1 -->
* &lt;!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator 1.0.3//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd"&gt;
"https://struts.apache.org/dtds/xwork-validator-1.0.3.dtd"&gt;
* &lt;validators&gt;
* &lt;field name="bar"&gt;
* &lt;field-validator type="required"&gt;
@@ -56,6 +56,9 @@ public final class StrutsConstants {
/** Comma separated list of patterns (java.util.regex.Pattern) to be excluded from Struts2-processing */
public static final String STRUTS_ACTION_EXCLUDE_PATTERN = "struts.action.excludePattern";
/** A custom separator used to split list of patterns (java.util.regex.Pattern) to be excluded from Struts2-processing */
public static final String STRUTS_ACTION_EXCLUDE_PATTERN_SEPARATOR = "struts.action.excludePattern.separator";
/** Whether to use the response encoding (JSP page encoding) for s:include tag processing (false - use STRUTS_I18N_ENCODING - by default) */
public static final String STRUTS_TAG_INCLUDETAG_USERESPONSEENCODING = "struts.tag.includetag.useResponseEncoding";
@@ -139,6 +142,9 @@ public final class StrutsConstants {
/** The maximize size of a multipart request (file upload) */
public static final String STRUTS_MULTIPART_MAXSIZE = "struts.multipart.maxSize";
/** The maximized number of files allowed to upload */
public static final String STRUTS_MULTIPART_MAXFILES = "struts.multipart.maxFiles";
/** The directory to use for storing uploaded files */
public static final String STRUTS_MULTIPART_SAVEDIR = "struts.multipart.saveDir";
@@ -174,9 +180,6 @@ public final class StrutsConstants {
@Deprecated
public static final String STRUTS_OBJECTFACTORY_SPRING_ENABLE_AOP_SUPPORT = "struts.objectFactory.spring.enableAopSupport";
/** Whether or not XSLT templates should not be cached */
public static final String STRUTS_XSLT_NOCACHE = "struts.xslt.nocache";
/** Location of additional configuration properties files to load */
public static final String STRUTS_CUSTOM_PROPERTIES = "struts.custom.properties";
@@ -376,7 +379,7 @@ public final class StrutsConstants {
public static final String STRUTS_CONVERTER_FILE_PROCESSOR = "struts.converter.file.processor";
public static final String STRUTS_CONVERTER_ANNOTATION_PROCESSOR = "struts.converter.annotation.processor";
public static final String STRUTS_CONVERTER_CREATOR = "struts.converter.creator";
public static final String STRUTS_CONVERTER_HOLDER = "struts..converter.holder";
public static final String STRUTS_CONVERTER_HOLDER = "struts.converter.holder";
public static final String STRUTS_EXPRESSION_PARSER = "struts.expression.parser";
@@ -398,7 +401,11 @@ public final class StrutsConstants {
/** Enables action: prefix */
public static final String STRUTS_MAPPER_ACTION_PREFIX_ENABLED = "struts.mapper.action.prefix.enabled";
/** Enables access to actions in other namespaces than current with action: prefix */
/**
* Enables access to actions in other namespaces than current with action: prefix
* @deprecated it will be removed soon, please refactor your application
*/
@Deprecated
public static final String STRUTS_MAPPER_ACTION_PREFIX_CROSSNAMESPACES = "struts.mapper.action.prefix.crossNamespaces";
public static final String DEFAULT_TEMPLATE_TYPE_CONFIG_KEY = "struts.ui.templateSuffix";
@@ -410,11 +417,14 @@ public final class StrutsConstants {
public static final String STRUTS_EXCLUDED_CLASSES = "struts.excludedClasses";
public static final String STRUTS_EXCLUDED_PACKAGE_NAME_PATTERNS = "struts.excludedPackageNamePatterns";
public static final String STRUTS_EXCLUDED_PACKAGE_NAMES = "struts.excludedPackageNames";
public static final String STRUTS_EXCLUDED_PACKAGE_EXEMPT_CLASSES = "struts.excludedPackageExemptClasses";
/** Comma delimited set of excluded classes and package names which cannot be accessed via expressions in devMode */
public static final String STRUTS_DEV_MODE_EXCLUDED_CLASSES = "struts.devMode.excludedClasses";
public static final String STRUTS_DEV_MODE_EXCLUDED_PACKAGE_NAME_PATTERNS = "struts.devMode.excludedPackageNamePatterns";
public static final String STRUTS_DEV_MODE_EXCLUDED_PACKAGE_NAMES = "struts.devMode.excludedPackageNames";
public static final String STRUTS_DEV_MODE_EXCLUDED_PACKAGE_EXEMPT_CLASSES = "struts.devMode.excludedPackageExemptClasses";
/** Dedicated services to check if passed string is excluded/accepted */
public static final String STRUTS_EXCLUDED_PATTERNS_CHECKER = "struts.excludedPatterns.checker";
@@ -450,4 +460,12 @@ public final class StrutsConstants {
/** See {@link org.apache.struts2.components.Date#setDateFormatter(DateFormatter)} */
public static final String STRUTS_DATE_FORMATTER = "struts.date.formatter";
public static final String STRUTS_URL_QUERY_STRING_BUILDER = "struts.url.queryStringBuilder";
public static final String STRUTS_URL_QUERY_STRING_PARSER = "struts.url.queryStringParser";
public static final String STRUTS_URL_ENCODER = "struts.url.encoder";
public static final String STRUTS_URL_DECODER = "struts.url.decoder";
/** A global flag to set property {@link org.apache.struts2.components.Checkbox#setSubmitUnchecked(String)} */
public static final String STRUTS_UI_CHECKBOX_SUBMIT_UNCHECKED = "struts.ui.checkbox.submitUnchecked";
}
@@ -0,0 +1,33 @@
/*
* 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.action;
import org.apache.struts2.interceptor.csp.CspSettings;
/**
* Implement this interface by an action to provide a custom {@link CspSettings},
* see {@link org.apache.struts2.interceptor.csp.CspInterceptor} for more details
*
* @since Struts 6.2.0
*/
public interface CspSettingsAware {
CspSettings getCspSettings();
}
@@ -18,13 +18,14 @@
*/
package org.apache.struts2.components;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.ValueStack;
import org.apache.struts2.StrutsConstants;
import org.apache.struts2.views.annotations.StrutsTag;
import org.apache.struts2.views.annotations.StrutsTagAttribute;
import com.opensymphony.xwork2.util.ValueStack;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* <!-- START SNIPPET: javadoc -->
@@ -46,15 +47,19 @@ import com.opensymphony.xwork2.util.ValueStack;
*
* <!-- END SNIPPET: example -->
* </pre>
*
*/
@StrutsTag(
name="checkbox",
tldTagClass="org.apache.struts2.views.jsp.ui.CheckboxTag",
description="Render a checkbox input field",
allowDynamicAttributes=true)
name = "checkbox",
tldTagClass = "org.apache.struts2.views.jsp.ui.CheckboxTag",
description = "Render a checkbox input field",
allowDynamicAttributes = true)
public class Checkbox extends UIBean {
final public static String TEMPLATE = "checkbox";
private static final String ATTR_SUBMIT_UNCHECKED = "submitUnchecked";
public static final String TEMPLATE = "checkbox";
private String submitUncheckedGlobal;
protected String fieldValue;
protected String submitUnchecked;
@@ -69,46 +74,45 @@ public class Checkbox extends UIBean {
protected void evaluateExtraParams() {
if (fieldValue != null) {
addParameter("fieldValue", findString(fieldValue));
addParameter(ATTR_FIELD_VALUE, findString(fieldValue));
} else {
addParameter("fieldValue", "true");
addParameter(ATTR_FIELD_VALUE, "true");
}
if (submitUnchecked != null) {
Object parsedValue = findValue(submitUnchecked, Boolean.class);
addParameter("submitUnchecked", parsedValue == null ? Boolean.valueOf(submitUnchecked) : parsedValue);
addParameter(ATTR_SUBMIT_UNCHECKED, parsedValue == null ? Boolean.valueOf(submitUnchecked) : parsedValue);
} else if (submitUncheckedGlobal != null) {
addParameter(ATTR_SUBMIT_UNCHECKED, Boolean.parseBoolean(submitUncheckedGlobal));
} else {
addParameter("submitUnchecked", false);
addParameter(ATTR_SUBMIT_UNCHECKED, false);
}
}
protected Class getValueClassType() {
@Override
protected Class<?> getValueClassType() {
return Boolean.class; // for checkboxes, everything needs to end up as a Boolean
}
@StrutsTagAttribute(description="The actual HTML value attribute of the checkbox.", defaultValue="true")
@Inject(value = StrutsConstants.STRUTS_UI_CHECKBOX_SUBMIT_UNCHECKED, required = false)
public void setSubmitUncheckedGlobal(String submitUncheckedGlobal) {
this.submitUncheckedGlobal = submitUncheckedGlobal;
}
@StrutsTagAttribute(description = "The actual HTML value attribute of the checkbox.", defaultValue = "true")
public void setFieldValue(String fieldValue) {
this.fieldValue = fieldValue;
}
@StrutsTagAttribute(description="If set to true, unchecked elements will be submitted with the form.", type="Boolean", defaultValue="false")
@StrutsTagAttribute(description = "If set to true, unchecked elements will be submitted with the form. " +
"Since Struts 6.1.1 you can use a constant \"" + StrutsConstants.STRUTS_UI_CHECKBOX_SUBMIT_UNCHECKED + "\" to set this attribute globally",
type = "Boolean", defaultValue = "false")
public void setSubmitUnchecked(String submitUnchecked) {
this.submitUnchecked = submitUnchecked;
}
/**
* Deprecated since 2.5.27
* @deprecated use {@link #setLabelPosition(String)} instead
*/
@Deprecated
@Override
@StrutsTagAttribute(description="(Deprecated) Define label position of form element (top/left), also 'right' is supported when using 'xhtml' theme")
public void setLabelposition(String labelPosition) {
super.setLabelPosition(labelPosition);
}
@Override
@StrutsTagAttribute(description="Define label position of form element (top/left), also 'right' is supported when using 'xhtml' theme")
@StrutsTagAttribute(description = "Define label position of form element (top/left), also 'right' is supported when using 'xhtml' theme")
public void setLabelPosition(String labelPosition) {
super.setLabelPosition(labelPosition);
}
@@ -32,6 +32,7 @@ import java.io.Writer;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.ArrayList;
@@ -294,7 +295,9 @@ public class Date extends ContextBean {
// find the name on the valueStack
Object dateObject = findValue(name);
if (dateObject instanceof java.sql.Date) {
date = ((java.sql.Date) dateObject).toLocalDate().atStartOfDay(tz);
date = ((java.sql.Date) dateObject).toLocalDate().atTime(LocalTime.now(tz)).atZone(tz);
} else if (dateObject instanceof java.sql.Time) {
date = ((java.sql.Time) dateObject).toLocalTime().atDate(ZonedDateTime.now(tz).toLocalDate()).atZone(tz);
} else if (dateObject instanceof java.util.Date) {
date = ((java.util.Date) dateObject).toInstant().atZone(tz);
} else if (dateObject instanceof Calendar) {
@@ -21,5 +21,7 @@ package org.apache.struts2.components;
import java.util.Map;
public interface ExtraParameterProvider {
public Map getExtraParameters();
Map<String, Object> getExtraParameters();
}
@@ -29,6 +29,7 @@ import org.apache.logging.log4j.Logger;
import org.apache.struts2.StrutsException;
import org.apache.struts2.dispatcher.mapper.ActionMapper;
import org.apache.struts2.dispatcher.mapper.ActionMapping;
import org.apache.struts2.url.QueryStringParser;
import org.apache.struts2.views.util.UrlHelper;
import java.io.IOException;
@@ -48,6 +49,7 @@ public class ServletUrlRenderer implements UrlRenderer {
private ActionMapper actionMapper;
private UrlHelper urlHelper;
private QueryStringParser queryStringParser;
@Override
@Inject
@@ -60,6 +62,11 @@ public class ServletUrlRenderer implements UrlRenderer {
this.urlHelper = urlHelper;
}
@Inject
public void setQueryStringParser(QueryStringParser queryStringParser) {
this.queryStringParser = queryStringParser;
}
/**
* {@inheritDoc}
*/
@@ -152,10 +159,10 @@ public class ServletUrlRenderer implements UrlRenderer {
}
}
Map actionParams = null;
Map<String, Object> actionParams = null;
if (action != null && action.indexOf('?') > 0) {
String queryString = action.substring(action.indexOf('?') + 1);
actionParams = urlHelper.parseQueryString(queryString, false);
actionParams = queryStringParser.parse(queryString, false);
action = action.substring(0, action.indexOf('?'));
}
@@ -164,19 +171,19 @@ public class ServletUrlRenderer implements UrlRenderer {
String actionMethod = nameMapping.getMethod();
final ActionConfig actionConfig = formComponent.configuration.getRuntimeConfiguration().getActionConfig(
namespace, actionName);
namespace, actionName);
if (actionConfig != null) {
ActionMapping mapping = new ActionMapping(actionName, namespace, actionMethod, formComponent.parameters);
String result = urlHelper.buildUrl(formComponent.actionMapper.getUriFromActionMapping(mapping),
formComponent.request, formComponent.response, actionParams, scheme, formComponent.includeContext, true, false, false);
formComponent.request, formComponent.response, actionParams, scheme, formComponent.includeContext, true, false, false);
formComponent.addParameter("action", result);
// let's try to get the actual action class and name
// this can be used for getting the list of validators
formComponent.addParameter("actionName", actionName);
try {
Class clazz = formComponent.objectFactory.getClassInstance(actionConfig.getClassName());
Class<?> clazz = formComponent.objectFactory.getClassInstance(actionConfig.getClassName());
formComponent.addParameter("actionClass", clazz);
} catch (ClassNotFoundException e) {
// this is OK, we'll just move on
@@ -258,7 +265,7 @@ public class ServletUrlRenderer implements UrlRenderer {
}
if (UrlProvider.NONE.equalsIgnoreCase(includeParams)) {
mergeRequestParameters(urlComponent.getValue(), urlComponent.getParameters(), Collections.<String, Object>emptyMap());
mergeRequestParameters(urlComponent.getValue(), urlComponent.getParameters(), Collections.emptyMap());
} else if (UrlProvider.ALL.equalsIgnoreCase(includeParams)) {
mergeRequestParameters(urlComponent.getValue(), urlComponent.getParameters(), urlComponent.getHttpServletRequest().getParameterMap());
@@ -284,7 +291,7 @@ public class ServletUrlRenderer implements UrlRenderer {
private void includeGetParameters(UrlProvider urlComponent) {
String query = extractQueryString(urlComponent);
mergeRequestParameters(urlComponent.getValue(), urlComponent.getParameters(), urlHelper.parseQueryString(query, false));
mergeRequestParameters(urlComponent.getValue(), urlComponent.getParameters(), queryStringParser.parse(query, false));
}
private String extractQueryString(UrlProvider urlComponent) {
@@ -309,7 +316,7 @@ public class ServletUrlRenderer implements UrlRenderer {
* Merge request parameters into current parameters. If a parameter is
* already present, than the request parameter in the current request and value attribute
* will not override its value.
*
* <p>
* The priority is as follows:-
* <ul>
* <li>parameter from the current request (least priority)</li>
@@ -317,8 +324,8 @@ public class ServletUrlRenderer implements UrlRenderer {
* <li>parameter from the param tag (most priority)</li>
* </ul>
*
* @param value the value attribute (URL to be generated by this component)
* @param parameters component parameters
* @param value the value attribute (URL to be generated by this component)
* @param parameters component parameters
* @param contextParameters request parameters
*/
protected void mergeRequestParameters(String value, Map<String, Object> parameters, Map<String, ?> contextParameters) {
@@ -332,7 +339,7 @@ public class ServletUrlRenderer implements UrlRenderer {
if (StringUtils.contains(value, "?")) {
String queryString = value.substring(value.indexOf('?') + 1);
mergedParams = urlHelper.parseQueryString(queryString, false);
mergedParams = queryStringParser.parse(queryString, false);
for (Map.Entry<String, ?> entry : contextParameters.entrySet()) {
if (!mergedParams.containsKey(entry.getKey())) {
mergedParams.put(entry.getKey(), entry.getValue());

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