Compare commits

..

10 Commits

Author SHA1 Message Date
Lukasz Lenart 6d8cf56977 [maven-release-plugin] prepare release STRUTS_6_1_2_2 2023-09-11 08:47:08 +02:00
Lukasz Lenart 7b7d3ca0e1 Sets proper Maven version 2023-09-11 08:42:18 +02:00
Lukasz Lenart 3292152f8c Always delete uploaded file 2023-09-03 08:57:26 +02:00
Lukasz Lenart 28498a807d [maven-release-plugin] prepare release STRUTS_6_1_2_1 2023-06-13 09:31:55 +02:00
Yasser Zamani 2d6f1bc0a6 add some improvements 2023-06-05 13:37:59 +04:30
Lukasz Lenart 36e4e7410e [maven-release-plugin] prepare for next development iteration 2023-03-08 17:28:00 +01:00
Lukasz Lenart e903739624 [maven-release-plugin] prepare release STRUTS_6_1_2 2023-03-08 17:27:52 +01:00
Lukasz Lenart a85328e74e Sets proper SNAPSHOT version to start release process 2023-03-08 17:23:24 +01:00
Lukasz Lenart c3deb39237 WW-5285 Uses Long and null to check if option has been defined 2023-03-08 17:15:40 +01:00
Lukasz Lenart 483a0c8d01 WW-5285 Limits max number of files to upload at once
Upgrades commons-fileupload to ver. 1.5 and sets default limit to 256 files
2023-03-08 17:15:29 +01:00
1078 changed files with 12048 additions and 68054 deletions
+3 -7
View File
@@ -3,10 +3,10 @@ notifications:
commits: commits@struts.apache.org
# Send all issue emails (new, closed, comments) to issues@
issues: issues@struts.apache.org
# Send new/closed PR notifications to commits@
pullrequests_status: notifications@struts.apache.org
# Send new/closed PR notifications to dev@
pullrequests_status: dev@struts.apache.org
# Send individual PR comments/reviews to issues@
pullrequests_comment: notifications@struts.apache.org
pullrequests_comment: issues@struts.apache.org
# Link opened PRs with JIRA
jira_options: link label worklog
@@ -14,7 +14,3 @@ github:
del_branch_on_merge: true
protected_branches:
master: { }
autolink_jira:
- WW
dependabot_alerts: true
dependabot_updates: true
-10
View File
@@ -1,10 +0,0 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
- package-ecosystem: "maven"
directory: "/"
schedule:
interval: "weekly"
+5 -13
View File
@@ -20,14 +20,6 @@ on:
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
@@ -42,14 +34,14 @@ jobs:
language: [ 'java' ]
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
languages: ${{ matrix.language }}
- name: Autobuild
uses: github/codeql-action/autobuild@v3
uses: github/codeql-action/autobuild@v2
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
uses: github/codeql-action/analyze@v2
with:
category: "/language:${{matrix.language}}"
+21 -10
View File
@@ -13,7 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
name: Java Maven
name: Java Build
on:
pull_request:
@@ -21,26 +21,37 @@ on:
branches:
- master
permissions: read-all
env:
MAVEN_OPTS: -Xmx2048m -Xms1024m
LANG: en_US.utf8
jobs:
build:
name: Build and Test
runs-on: ubuntu-latest
strategy:
matrix:
java: [ '8', '11', '17', '21' ]
java: [ '8', '11', '17' ]
steps:
- name: Checkout code
uses: actions/checkout@v4
- uses: actions/setup-java@v4
uses: actions/checkout@v3.0.2
- name: Set up cache
uses: actions/cache@v3.0.8
with:
distribution: temurin
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 }}
cache: 'maven'
- name: Build with Maven on Java ${{ matrix.java }}
run: mvn -B -V -DskipAssembly verify --no-transfer-progress
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
+4 -4
View File
@@ -40,12 +40,12 @@ jobs:
steps:
- name: "Checkout code"
uses: actions/checkout@v4 # 3.1.0
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # 3.1.0
with:
persist-credentials: false
- name: "Run analysis"
uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # 2.3.1
uses: ossf/scorecard-action@99c53751e09b9529366343771cc321ec74e9bd3d # 2.0.6
with:
results_file: results.sarif
results_format: sarif
@@ -57,13 +57,13 @@ jobs:
publish_results: true
- name: "Upload artifact"
uses: actions/upload-artifact@c7d193f32edcb7bfad88892161225aeda64e9392 # 4.0.0
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@03e7845b7bfcd5e7fb63d1ae8c61b0e791134fab # 2.22.11
uses: github/codeql-action/upload-sarif@b398f525a5587552e573b247ac661067fafa920b # 2.1.22
with:
sarif_file: results.sarif
-46
View File
@@ -1,46 +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.
name: SonarCloud
on:
pull_request:
push:
branches:
- master
permissions: read-all
env:
MAVEN_OPTS: -Xmx2048m -Xms1024m
LANG: en_US.utf8
jobs:
sonarcloud:
name: Scan
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 17
cache: 'maven'
- 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
-2
View File
@@ -1,2 +0,0 @@
# Request PR review from any Apache Struts committer
* @apache/struts-committers
Vendored
+20 -93
View File
@@ -25,36 +25,6 @@ pipeline {
}
}
}
stage('JDK 21') {
agent {
label 'ubuntu'
}
tools {
jdk 'jdk_21_latest'
maven 'maven_3_latest'
}
environment {
MAVEN_OPTS = "-Xmx1024m"
}
stages {
stage('Test') {
steps {
sh './mvnw -B -DskipAssembly verify --no-transfer-progress'
}
post {
always {
junit(testResults: '**/surefire-reports/*.xml', allowEmptyResults: true)
junit(testResults: '**/failsafe-reports/*.xml', allowEmptyResults: true)
}
}
}
}
post {
always {
cleanWs deleteDirs: true, patterns: [[pattern: '**/target/**', type: 'INCLUDE']]
}
}
}
stage('JDK 17') {
agent {
label 'ubuntu'
@@ -69,12 +39,12 @@ pipeline {
stages {
stage('Build') {
steps {
sh './mvnw -B -DskipAssembly verify --no-transfer-progress'
sh './mvnw -B clean install -DskipTests -DskipAssembly'
}
}
stage('Test') {
steps {
sh './mvnw -B verify -Pcoverage -DskipAssembly'
sh './mvnw -B test'
}
post {
always {
@@ -83,61 +53,6 @@ pipeline {
}
}
}
stage('Code Quality') {
when {
anyOf {
branch 'master'; branch 'release/struts-7-0-x'
}
}
steps {
withCredentials([string(credentialsId: 'asf-struts-sonarcloud', variable: 'SONARCLOUD_TOKEN')]) {
sh './mvnw -B -Pcoverage -DskipAssembly -Dsonar.login=${SONARCLOUD_TOKEN} verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar'
}
}
}
stage('Build Source & JavaDoc') {
when {
branch 'release/struts-7-0-x'
}
steps {
dir("local-snapshots-dir/") {
deleteDir()
}
sh './mvnw -B source:jar javadoc:jar -DskipTests -DskipAssembly'
}
}
stage('Deploy Snapshot') {
when {
branch 'release/struts-7-0-x'
}
steps {
withCredentials([file(credentialsId: 'lukaszlenart-repository-access-token', variable: 'CUSTOM_SETTINGS')]) {
sh './mvnw -s \${CUSTOM_SETTINGS} deploy -DskipTests -DskipAssembly'
}
}
}
stage('Upload nightlies') {
when {
branch 'release/struts-7-0-x'
}
steps {
sh './mvnw -B package -DskipTests'
sshPublisher(publishers: [
sshPublisherDesc(
configName: 'Nightlies',
transfers: [
sshTransfer(
remoteDirectory: '/struts/snapshot',
removePrefix: 'assembly/target/assembly/out',
sourceFiles: 'assembly/target/assembly/out/struts-*.zip',
cleanRemote: true
)
],
verbose: true
)
])
}
}
}
post {
always {
@@ -159,12 +74,12 @@ pipeline {
stages {
stage('Build') {
steps {
sh './mvnw -B -DskipAssembly verify --no-transfer-progress'
sh './mvnw -B clean install -DskipTests -DskipAssembly'
}
}
stage('Test') {
steps {
sh './mvnw -B test'
sh './mvnw -B verify -Pcoverage -DskipAssembly'
}
post {
always {
@@ -173,6 +88,16 @@ pipeline {
}
}
}
stage('Code Quality') {
when {
branch 'master'
}
steps {
withCredentials([string(credentialsId: 'asf-struts-sonarcloud', variable: 'SONARCLOUD_TOKEN')]) {
sh './mvnw -B -Pcoverage -DskipAssembly -Dsonar.login=${SONARCLOUD_TOKEN} verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar'
}
}
}
}
post {
always {
@@ -200,6 +125,7 @@ pipeline {
stage('Test') {
steps {
sh './mvnw -B test'
// step([$class: 'JiraIssueUpdater', issueSelector: [$class: 'DefaultIssueSelector'], scm: scm])
}
post {
always {
@@ -242,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
@@ -263,7 +190,7 @@ pipeline {
failure {
script {
emailext(
to: "notifications@struts.apache.org",
to: "dev@struts.apache.org",
recipientProviders: [[$class: 'DevelopersRecipientProvider']],
from: "Mr. Jenkins <jenkins@builds.apache.org>",
subject: "Jenkins job ${env.JOB_NAME}#${env.BUILD_NUMBER} failed",
@@ -286,7 +213,7 @@ Director of Continuous Integration
unstable {
script {
emailext(
to: "notifications@struts.apache.org",
to: "dev@struts.apache.org",
recipientProviders: [[$class: 'DevelopersRecipientProvider']],
from: "Mr. Jenkins <jenkins@builds.apache.org>",
subject: "Jenkins job ${env.JOB_NAME}#${env.BUILD_NUMBER} unstable",
@@ -309,7 +236,7 @@ Director of Continuous Integration
fixed {
script {
emailext(
to: "notifications@struts.apache.org",
to: "dev@struts.apache.org",
recipientProviders: [[$class: 'DevelopersRecipientProvider']],
from: 'Mr. Jenkins <jenkins@builds.apache.org>',
subject: "Jenkins job ${env.JOB_NAME}#${env.BUILD_NUMBER} back to normal",
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>6.4.0-SNAPSHOT</version>
<version>6.1.2.2</version>
</parent>
<artifactId>struts2-apps</artifactId>
<packaging>pom</packaging>
+5 -2
View File
@@ -24,12 +24,12 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-apps</artifactId>
<version>6.4.0-SNAPSHOT</version>
<version>6.1.2.2</version>
</parent>
<artifactId>struts2-rest-showcase</artifactId>
<packaging>war</packaging>
<version>6.4.0-SNAPSHOT</version>
<version>6.1.2.2</version>
<name>Struts 2 Rest Showcase Webapp</name>
<description>Struts 2 Rest Showcase Example</description>
@@ -56,10 +56,12 @@
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>${log4j2.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j2.version}</version>
</dependency>
<dependency>
@@ -78,6 +80,7 @@
<dependency>
<groupId>net.sourceforge.htmlunit</groupId>
<artifactId>htmlunit</artifactId>
<version>2.39.0</version>
<scope>test</scope>
</dependency>
@@ -20,8 +20,8 @@
*/
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 6.0//EN"
"https://struts.apache.org/dtds/struts-6.0.dtd">
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
+16 -6
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-apps</artifactId>
<version>6.4.0-SNAPSHOT</version>
<version>6.1.2.2</version>
</parent>
<artifactId>struts2-showcase</artifactId>
@@ -68,6 +68,12 @@
<artifactId>struts2-json-plugin</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tiles</groupId>
<artifactId>tiles-jsp</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-convention-plugin</artifactId>
@@ -93,11 +99,6 @@
<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>
@@ -108,18 +109,22 @@
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>${log4j2.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j2.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-jcl</artifactId>
<version>${log4j2.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>${log4j2.version}</version>
</dependency>
<dependency>
@@ -130,6 +135,10 @@
<groupId>org.directwebremoting</groupId>
<artifactId>dwr</artifactId>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
@@ -140,6 +149,7 @@
<dependency>
<groupId>net.sourceforge.htmlunit</groupId>
<artifactId>htmlunit</artifactId>
<version>2.61.0</version>
<scope>test</scope>
</dependency>
@@ -21,17 +21,16 @@
package org.apache.struts2.showcase.chat;
import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.interceptor.SessionAware;
import java.util.Map;
import org.apache.struts2.action.SessionAware;
public class ChatLoginAction extends ActionSupport implements SessionAware {
private static final long serialVersionUID = 1L;
private ChatService chatService;
private Map<String, Object> session;
private Map session;
private String name;
@@ -61,8 +60,8 @@ public class ChatLoginAction extends ActionSupport implements SessionAware {
}
@Override
public void withSession(Map<String, Object> session) {
this.session = session;
}
// === SessionAware ===
public void setSession(Map session) {
this.session = session;
}
}
@@ -21,18 +21,17 @@
package org.apache.struts2.showcase.chat;
import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.interceptor.SessionAware;
import java.util.Map;
import org.apache.struts2.action.SessionAware;
public class ChatLogoutAction extends ActionSupport implements SessionAware {
private static final long serialVersionUID = 1L;
private ChatService chatService;
private Map<String, Object> session;
private Map session;
public ChatLogoutAction(ChatService chatService) {
@@ -51,8 +50,8 @@ public class ChatLogoutAction extends ActionSupport implements SessionAware {
}
@Override
public void withSession(Map<String, Object> session) {
this.session = session;
}
// === SessionAware ===
public void setSession(Map session) {
this.session = session;
}
}
@@ -21,17 +21,16 @@
package org.apache.struts2.showcase.chat;
import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.interceptor.SessionAware;
import java.util.Map;
import org.apache.struts2.action.SessionAware;
public class EnterRoomAction extends ActionSupport implements SessionAware {
private static final long serialVersionUID = 1L;
private ChatService chatService;
private Map<String, Object> session;
private Map session;
private String roomName;
public String getRoomName() {
@@ -57,9 +56,10 @@ public class EnterRoomAction extends ActionSupport implements SessionAware {
return SUCCESS;
}
@Override
public void withSession(Map<String, Object> session) {
this.session = session;
}
// === SessionAware ===
public void setSession(Map session) {
this.session = session;
}
}
@@ -21,18 +21,17 @@
package org.apache.struts2.showcase.chat;
import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.interceptor.SessionAware;
import java.util.Map;
import org.apache.struts2.action.SessionAware;
public class ExitRoomAction extends ActionSupport implements SessionAware {
private static final long serialVersionUID = 1L;
private String roomName;
private Map<String, Object> session;
private Map session;
public String getRoomName() {
return roomName;
@@ -55,9 +54,9 @@ public class ExitRoomAction extends ActionSupport implements SessionAware {
return SUCCESS;
}
@Override
public void withSession(Map<String, Object> session) {
this.session = session;
}
// === SessionAware ===
public void setSession(Map session) {
this.session = session;
}
}
@@ -21,11 +21,10 @@
package org.apache.struts2.showcase.chat;
import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.interceptor.SessionAware;
import java.util.Map;
import org.apache.struts2.action.SessionAware;
public class SendMessageToRoomAction extends ActionSupport implements SessionAware {
private static final long serialVersionUID = 1L;
@@ -34,7 +33,7 @@ public class SendMessageToRoomAction extends ActionSupport implements SessionAwa
private String roomName;
private String message;
private Map<String, Object> session;
private Map session;
public SendMessageToRoomAction(ChatService chatService) {
@@ -68,10 +67,9 @@ public class SendMessageToRoomAction extends ActionSupport implements SessionAwa
return SUCCESS;
}
@Override
public void withSession(Map<String, Object> session) {
this.session = session;
}
public void setSession(Map session) {
this.session = session;
}
}
@@ -21,67 +21,75 @@
package org.apache.struts2.showcase.fileupload;
import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.action.UploadedFilesAware;
import org.apache.struts2.dispatcher.multipart.UploadedFile;
import java.util.List;
import java.io.File;
/**
* Show case File Upload example's action. <code>FileUploadAction</code>
*/
public class FileUploadAction extends ActionSupport implements UploadedFilesAware {
public class FileUploadAction extends ActionSupport {
private String contentType;
private UploadedFile uploadedFile;
private String fileName;
private String caption;
private String originalName;
private static final long serialVersionUID = 5156288255337069381L;
public String input() throws Exception {
return SUCCESS;
}
private String contentType;
private File upload;
private String fileName;
private String caption;
public String upload() throws Exception {
return SUCCESS;
}
public String input() throws Exception {
return SUCCESS;
}
public String getContentType() {
return contentType;
}
public String upload() throws Exception {
return SUCCESS;
}
public String getFileName() {
return fileName;
}
// since we are using <s:file name="upload" .../> the file name will be
// obtained through getter/setter of <file-tag-name>FileName
public String getUploadFileName() {
return fileName;
}
public String getOriginalName() {
return originalName;
}
public void setUploadFileName(String fileName) {
this.fileName = fileName;
}
public Object getUploadedFile() {
return uploadedFile.getContent();
}
public String getCaption() {
return caption;
}
// since we are using <s:file name="upload" ... /> the content type will be
// obtained through getter/setter of <file-tag-name>ContentType
public String getUploadContentType() {
return contentType;
}
public void setCaption(String caption) {
this.caption = caption;
}
public void setUploadContentType(String contentType) {
this.contentType = contentType;
}
public long getUploadSize() {
if (uploadedFile != null) {
return uploadedFile.length();
} else {
return 0;
// since we are using <s:file name="upload" ... /> the File itself will be
// obtained through getter/setter of <file-tag-name>
public File getUpload() {
return upload;
}
public void setUpload(File upload) {
this.upload = upload;
}
public String getCaption() {
return caption;
}
public void setCaption(String caption) {
this.caption = caption;
}
public long getUploadSize() {
if (upload != null) {
return upload.length();
} else {
return 0;
}
}
}
@Override
public void withUploadedFiles(List<UploadedFile> uploadedFiles) {
this.uploadedFile = uploadedFiles.get(0);
this.fileName = uploadedFile.getName();
this.contentType = uploadedFile.getContentType();
this.originalName = uploadedFile.getOriginalName();
}
}
@@ -21,16 +21,15 @@
package org.apache.struts2.showcase.hangman;
import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.interceptor.SessionAware;
import java.util.Map;
import org.apache.struts2.action.SessionAware;
public class GetUpdatedHangmanAction extends ActionSupport implements SessionAware {
private static final long serialVersionUID = 5506025785406043027L;
private Map<String, Object> session;
private Map session;
private Hangman hangman;
@@ -46,6 +45,10 @@ public class GetUpdatedHangmanAction extends ActionSupport implements SessionAwa
return SUCCESS;
}
public void setSession(Map session) {
this.session = session;
}
public Hangman getHangman() {
return hangman;
}
@@ -53,9 +56,4 @@ public class GetUpdatedHangmanAction extends ActionSupport implements SessionAwa
public void setHangman(Hangman hangman) {
this.hangman = hangman;
}
@Override
public void withSession(Map<String, Object> session) {
this.session = session;
}
}
@@ -21,16 +21,15 @@
package org.apache.struts2.showcase.hangman;
import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.interceptor.SessionAware;
import java.util.Map;
import org.apache.struts2.action.SessionAware;
public class GuessCharacterAction extends ActionSupport implements SessionAware {
private static final long serialVersionUID = 9050915577007590674L;
private Map<String, Object> session;
private Map session;
private Character character;
private Hangman hangman;
@@ -45,6 +44,10 @@ public class GuessCharacterAction extends ActionSupport implements SessionAware
return hangman;
}
public void setSession(Map session) {
this.session = session;
}
public void setCharacter(Character character) {
this.character = character;
}
@@ -52,9 +55,4 @@ public class GuessCharacterAction extends ActionSupport implements SessionAware
public Character getCharacter() {
return this.character;
}
@Override
public void withSession(Map<String, Object> session) {
this.session = session;
}
}
@@ -21,11 +21,10 @@
package org.apache.struts2.showcase.hangman;
import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.interceptor.SessionAware;
import java.util.Map;
import org.apache.struts2.action.SessionAware;
import static org.apache.struts2.showcase.hangman.HangmanConstants.HANGMAN_SESSION_KEY;
public class StartHangmanAction extends ActionSupport implements SessionAware {
@@ -34,7 +33,7 @@ public class StartHangmanAction extends ActionSupport implements SessionAware {
private HangmanService service;
private Hangman hangman;
private Map<String, Object> session;
private Map session;
public StartHangmanAction(HangmanService service) {
@@ -54,8 +53,8 @@ public class StartHangmanAction extends ActionSupport implements SessionAware {
}
@Override
public void withSession(Map<String, Object> session) {
this.session = session;
}
// === SessionAware ===
public void setSession(Map session) {
this.session = session;
}
}
@@ -1,41 +0,0 @@
/*
* $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;
}
}
}
@@ -21,11 +21,9 @@
package org.apache.struts2.showcase.xslt;
import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.interceptor.ServletRequestAware;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts2.action.ServletRequestAware;
import java.util.Map;
import java.util.Properties;
@@ -55,6 +53,10 @@ public class JVMAction implements ServletRequestAware {
return servletRequest;
}
public void setServletRequest(HttpServletRequest servletRequest) {
this.servletRequest = servletRequest;
}
public Map<String, String> getEnvironment() {
return environment;
}
@@ -98,9 +100,4 @@ public class JVMAction implements ServletRequestAware {
this.systemProperties = systemProperties;
}
}
@Override
public void withServletRequest(HttpServletRequest request) {
this.servletRequest = request;
}
}
@@ -20,8 +20,8 @@
*/
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 6.0//EN"
"https://struts.apache.org/dtds/struts-6.0.dtd">
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="actionchaining" extends="struts-default" namespace="/actionchaining">
@@ -20,8 +20,8 @@
*/
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 6.0//EN"
"https://struts.apache.org/dtds/struts-6.0.dtd">
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="async" extends="json-default" namespace="/async">
@@ -20,8 +20,8 @@
*/
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 6.0//EN"
"https://struts.apache.org/dtds/struts-6.0.dtd">
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="conversion" namespace="/conversion" extends="struts-default">
@@ -20,8 +20,8 @@
*/
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 6.0//EN"
"https://struts.apache.org/dtds/struts-6.0.dtd">
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="dispatcher" extends="struts-default" namespace="/dispatcher">
@@ -20,8 +20,8 @@
*/
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 6.0//EN"
"https://struts.apache.org/dtds/struts-6.0.dtd">
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="filedownload" extends="struts-default" namespace="/filedownload">
@@ -20,8 +20,8 @@
*/
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 6.0//EN"
"https://struts.apache.org/dtds/struts-6.0.dtd">
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="fileupload" extends="struts-default" namespace="/fileupload">
@@ -20,8 +20,8 @@
*/
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 6.0//EN"
"https://struts.apache.org/dtds/struts-6.0.dtd">
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="freemarker" namespace="/freemarker" extends="struts-default">
@@ -20,8 +20,8 @@
*/
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 6.0//EN"
"https://struts.apache.org/dtds/struts-6.0.dtd">
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="hangman" extends="struts-default" namespace="/hangman">
@@ -20,8 +20,8 @@
*/
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 6.0//EN"
"https://struts.apache.org/dtds/struts-6.0.dtd">
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="interactive" namespace="/interactive" extends="struts-default">
@@ -20,8 +20,8 @@
*/
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 6.0//EN"
"https://struts.apache.org/dtds/struts-6.0.dtd">
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="modelDriven" extends="struts-default" namespace="/modelDriven">
@@ -20,8 +20,8 @@
*/
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 6.0//EN"
"https://struts.apache.org/dtds/struts-6.0.dtd">
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"https://struts.apache.org/dtds/struts-2.5.dtd">
<!-- START SNIPPET: xworkSample -->
@@ -20,8 +20,8 @@
*/
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 6.0//EN"
"https://struts.apache.org/dtds/struts-6.0.dtd">
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
@@ -20,8 +20,8 @@
*/
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 6.0//EN"
"https://struts.apache.org/dtds/struts-6.0.dtd">
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="ui-tags" extends="velocity-default" namespace="/tags/ui">
@@ -20,8 +20,8 @@
*/
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 6.0//EN"
"https://struts.apache.org/dtds/struts-6.0.dtd">
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<include file="struts-tags-ui.xml"/>
@@ -20,8 +20,8 @@
*/
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 6.0//EN"
"https://struts.apache.org/dtds/struts-6.0.dtd">
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="tiles" extends="tiles-default" namespace="/tiles">
@@ -20,8 +20,8 @@
*/
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 6.0//EN"
"https://struts.apache.org/dtds/struts-6.0.dtd">
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="token" extends="struts-default" namespace="/token">
@@ -20,8 +20,8 @@
*/
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 6.0//EN"
"https://struts.apache.org/dtds/struts-6.0.dtd">
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
@@ -20,15 +20,12 @@
*/
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 6.0//EN"
"https://struts.apache.org/dtds/struts-6.0.dtd">
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<bean type="org.apache.struts2.interceptor.exec.ExecutorProvider" name="threadPool"
class="org.apache.struts2.showcase.wait.ThreadPoolExecutorProvider"/>
<constant name="struts.executor.provider" value="threadPool"/>
<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"/>
@@ -20,11 +20,11 @@
*/
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 6.0//EN"
"https://struts.apache.org/dtds/struts-6.0.dtd">
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="xslt" extends="xslt-default" namespace="/xslt">
<package name="xslt" extends="struts-default" namespace="/xslt">
<default-action-ref name="index"/>
<action name="index">
+3 -16
View File
@@ -20,8 +20,8 @@
*/
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 6.0//EN"
"https://struts.apache.org/dtds/struts-6.0.dtd">
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"https://struts.apache.org/dtds/struts-2.5.dtd">
<!-- START SNIPPET: xworkSample -->
<struts>
@@ -34,19 +34,6 @@
<constant name="struts.custom.i18n.resources" value="globalMessages" />
<constant name="struts.action.extension" value="action,," />
<constant name="struts.allowlist.enable" value="true" />
<constant name="struts.allowlist.packageNames"
value="
org.apache.struts2.showcase.model,
org.apache.struts2.showcase.conversion
"/>
<constant name="struts.allowlist.classes"
value="
org.apache.struts2.showcase.UITagExample$Language,
org.apache.struts2.showcase.UITagExample$VehicalType,
org.apache.struts2.showcase.UITagExample$VehicalSpecific
"/>
<constant name="struts.convention.package.locators.basePackage" value="org.apache.struts2.showcase" />
<constant name="struts.convention.result.path" value="/WEB-INF" />
@@ -57,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,/testServlet/.*"/>
<constant name="struts.action.excludePattern" value=".*/images/.*\.gif,.*/img/.*\.gif,.*/styles/.*\.css,.*/js/.*\.js"/>
<include file="struts-interactive.xml" />
@@ -72,7 +72,7 @@
<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 type="text/javascript">
$(function () {
var alerts = $('ul.alert').wrap('<div />');
alerts.prepend('<a class="close" data-dismiss="alert" href="#">&times;</a>');
@@ -19,7 +19,7 @@
*/
-->
<%@ page
language="java"
language="java"
contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
@@ -37,10 +37,9 @@
<div class="row">
<div class="col-md-12">
<ul>
<li>ContentType: <s:property value="contentType" /></li>
<li>FileName: <s:property value="fileName" /></li>
<li>Original FileName: <s:property value="originalName" /></li>
<li>File: <s:property value="uploadedFile" /></li>
<li>ContentType: <s:property value="uploadContentType" /></li>
<li>FileName: <s:property value="uploadFileName" /></li>
<li>File: <s:property value="upload" /></li>
<li>Caption:<s:property value="caption" /></li>
</ul>
</div>
@@ -72,7 +72,7 @@
</div>
</div>
<s:script>
<s:script type="text/javascript">
/********************************************************************
* JS just used on this page.
* Usually this would be placed in a JS file
@@ -142,11 +142,6 @@
<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>
@@ -167,11 +162,6 @@
<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
@@ -32,6 +32,7 @@ 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());
}
}
@@ -39,10 +40,16 @@ 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());
//DomElement div = page.getElementById("dispatcher-result");
//Assert.assertEquals("This page is a result of \"dispatching\" to it from an action", div.asNormalizedText());
// support for forwarding to another action is broken on StrutsPrepareFilter/StrutsExecuteFilter
// it only works in StrutsPrepareAndExecuteFilter
// this will be fixed in Struts 6.1.x
Assert.assertEquals(404, page.getWebResponse().getStatusCode());
}
}
@@ -1,41 +0,0 @@
/*
* $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 it.org.apache.struts2.showcase;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.DomElement;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import org.junit.Assert;
import org.junit.Test;
public class ForwardTest {
@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");
DomElement div = page.getElementById("dispatcher-result");
Assert.assertEquals("This page is a result of \"dispatching\" to it from an action", div.asNormalizedText());
}
}
}
+4 -3
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>6.4.0-SNAPSHOT</version>
<version>6.1.2.2</version>
</parent>
<artifactId>struts2-assembly</artifactId>
@@ -41,8 +41,9 @@
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<groupId>org.codehaus.mojo</groupId>
<artifactId>dependency-maven-plugin</artifactId>
<version>1.0</version>
<executions>
<execution>
<id>copy-war</id>
+4 -4
View File
@@ -1,6 +1,6 @@
#set ($stylebase = "https://struts.apache.org/css")
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<!--
<!--
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,7 +16,7 @@ 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.
under the License.
-->
<html>
<head>
@@ -43,12 +43,12 @@ under the License.
<script src='http://struts.apache.org/highlighter/js/$hscript' type='text/javascript'></script>
#end
<script>
<script type="text/javascript">
SyntaxHighlighter.defaults['toolbar'] = false;
SyntaxHighlighter.all();
</script>
#end
<script>
<script type="text/javascript" language="javascript">
var hide = null;
var show = null;
var children = null;
+5 -10
View File
@@ -24,12 +24,12 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>6.4.0-SNAPSHOT</version>
<artifactId>struts-master</artifactId>
<version>14</version>
</parent>
<artifactId>struts2-bom</artifactId>
<version>6.4.0-SNAPSHOT</version>
<version>6.1.2.2</version>
<packaging>pom</packaging>
<name>Struts 2 Bill of Materials</name>
@@ -44,7 +44,7 @@
</licenses>
<properties>
<struts-version.version>6.4.0-SNAPSHOT</struts-version.version>
<struts-version.version>6.1.2.2</struts-version.version>
<maven.site.skip>true</maven.site.skip>
<maven.site.deploy.skip>true</maven.site.deploy.skip>
</properties>
@@ -181,16 +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>
<scm>
<tag>STRUTS_6_3_0_1</tag>
<tag>STRUTS_6_1_2_2</tag>
<connection>scm:git:https://gitbox.apache.org/repos/asf/struts.git</connection>
<developerConnection>scm:git:https://gitbox.apache.org/repos/asf/struts.git</developerConnection>
<url>https://github.com/apache/struts/</url>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-osgi-bundles</artifactId>
<version>6.4.0-SNAPSHOT</version>
<version>6.1.2.2</version>
</parent>
<artifactId>struts2-osgi-admin-bundle</artifactId>
@@ -22,13 +22,13 @@
<head>
<title>OSGi Console</title>
<@s.link rel="stylesheet" type="text/css" href="${base}${parameters.staticContentPath}/css/shell.css" />
<@s.link rel="stylesheet" type="text/css" href="${base}${parameters.staticContentPath}/css/main.css" />
<@s.link rel="stylesheet" type="text/css" href="${base}${parameters.staticContentPath}/css/redmond/jquery-ui-1.12.1.redmond.css" />
<link rel="stylesheet" type="text/css" href="<@s.url value="${parameters.staticContentPath}/css/shell.css" />" />
<link rel="stylesheet" type="text/css" href="<@s.url value="${parameters.staticContentPath}/css/main.css" />" />
<link rel="stylesheet" type="text/css" href="<@s.url value="${parameters.staticContentPath}/css/redmond/jquery-ui-1.12.1.redmond.css" />" />
<@s.script src="${base}${parameters.staticContentPath}/js/shell.js" />
<@s.script src="${base}${parameters.staticContentPath}/js/jquery-1.12.4.min.js" />
<@s.script src="${base}${parameters.staticContentPath}/js/jquery-ui-1.12.1.min.js" />
<script src="<@s.url value="${parameters.staticContentPath}/js/shell.js" />"></script>
<script src="<@s.url value="${parameters.staticContentPath}/js/jquery-1.12.4.min.js" />"></script>
<script src="<@s.url value="${parameters.staticContentPath}/js/jquery-ui-1.12.1.min.js" />"></script>
</head>
<body>
<div class="menu">
@@ -22,17 +22,17 @@
<head>
<title>${bundle.symbolicName!}</title>
<@s.link rel="stylesheet" type="text/css" href="${base}${parameters.staticContentPath}/css/main.css" />
<@s.link rel="stylesheet" type="text/css" href="${base}${parameters.staticContentPath}/css/redmond/jquery-ui-1.12.1.redmond.css" />
<link rel="stylesheet" type="text/css" href="<@s.url value="${parameters.staticContentPath}/css/main.css" />" />
<link rel="stylesheet" type="text/css" href="<@s.url value="${parameters.staticContentPath}/css/redmond/jquery-ui-1.12.1.redmond.css" />" />
<@s.script src="${base}${parameters.staticContentPath}/js/jquery-1.12.4.min.js"/>
<@s.script src="${base}${parameters.staticContentPath}/js/jquery-ui-1.12.1.min.js" />
<script src="<@s.url value="${parameters.staticContentPath}/js/jquery-1.12.4.min.js" />"></script>
<script src="<@s.url value="${parameters.staticContentPath}/js/jquery-ui-1.12.1.min.js" />"></script>
<@s.script>
<script type="text/javascript">
$(function() {
$("#tabs").tabs();
});
</@s.script>
</script>
</head>
<body>
@@ -72,7 +72,7 @@
<span class="ui-icon ui-icon-transferthick-e-w"></span>
OSGi Shell
</a>
</div>
</div>
</div>
<@s.actionerror />
@@ -22,11 +22,11 @@
<head>
<title>OSGi Bundles</title>
<@s.link rel="stylesheet" type="text/css" href="${base}${parameters.staticContentPath}/css/main.css" />
<@s.link rel="stylesheet" type="text/css" href="${base}${parameters.staticContentPath}/css/redmond/jquery-ui-1.12.1.redmond.css" />
<link rel="stylesheet" type="text/css" href="<@s.url value="${parameters.staticContentPath}/css/main.css" />" />
<link rel="stylesheet" type="text/css" href="<@s.url value="${parameters.staticContentPath}/css/redmond/jquery-ui-1.12.1.redmond.css" />" />
<@s.script src="${base}${parameters.staticContentPath}/js/jquery-1.12.4.min.js" />
<@s.script src="${base}${parameters.staticContentPath}/js/jquery-ui-1.12.1.min.js" />
<script src="<@s.url value="${parameters.staticContentPath}/js/jquery-1.12.4.min.js" />"></script>
<script src="<@s.url value="${parameters.staticContentPath}/js/jquery-ui-1.12.1.min.js" />"></script>
</head>
<body>
@@ -42,7 +42,7 @@
<span class="ui-icon ui-icon-transferthick-e-w"></span>
OSGi Shell
</a>
</div>
</div>
</div>
<@s.actionerror />
+2 -2
View File
@@ -21,8 +21,8 @@
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 6.0//EN"
"https://struts.apache.org/dtds/struts-6.0.dtd">
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<constant name="struts.enable.DynamicMethodInvocation" value="false" />
+3 -3
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-osgi-bundles</artifactId>
<version>6.4.0-SNAPSHOT</version>
<version>6.1.2.2</version>
</parent>
<artifactId>struts2-osgi-demo-bundle</artifactId>
@@ -39,8 +39,8 @@
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-digester3</artifactId>
<groupId>commons-digester</groupId>
<artifactId>commons-digester</artifactId>
</dependency>
<dependency>
+2 -2
View File
@@ -20,8 +20,8 @@
*/
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 6.0//EN"
"https://struts.apache.org/dtds/struts-6.0.dtd">
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<!-- Set some Struts 2 constants relevant to the OSGi Plugin.
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>6.4.0-SNAPSHOT</version>
<version>6.1.2.2</version>
</parent>
<artifactId>struts2-osgi-bundles</artifactId>
+7 -6
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>6.4.0-SNAPSHOT</version>
<version>6.1.2.2</version>
</parent>
<artifactId>struts2-core</artifactId>
<packaging>jar</packaging>
@@ -184,11 +184,6 @@
<artifactId>freemarker</artifactId>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
@@ -222,6 +217,11 @@
<artifactId>commons-io</artifactId>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
@@ -260,6 +260,7 @@
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j2.version}</version>
<scope>test</scope>
</dependency>
@@ -20,17 +20,12 @@ package com.opensymphony.xwork2;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.TextParseUtil;
import com.opensymphony.xwork2.util.ValueStack;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.StrutsException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.*;
/**
* <!-- START SNIPPET: description -->
@@ -101,9 +96,15 @@ public class ActionChainResult implements Result {
*/
private static final String CHAIN_HISTORY = "CHAIN_HISTORY";
/**
* The result parameter name to set the name of the action to chain to.
*/
public static final String SKIP_ACTIONS_PARAM = "skipActions";
private ActionProxy proxy;
private String actionName;
private String namespace;
private String methodName;
@@ -132,6 +133,7 @@ public class ActionChainResult implements Result {
this.skipActions = skipActions;
}
/**
* @param actionProxyFactory the actionProxyFactory to set
*/
@@ -170,6 +172,7 @@ public class ActionChainResult implements Result {
this.skipActions = actions;
}
public void setMethod(String method) {
this.methodName = method;
}
@@ -203,14 +206,18 @@ public class ActionChainResult implements Result {
throw new IllegalArgumentException("Invocation cannot be null!");
}
String finalNamespace = namespace != null ? translateVariables(namespace) : invocation.getProxy()
.getNamespace();
String finalActionName = translateVariables(actionName);
String finalMethodName = methodName != null ? translateVariables(methodName) : null;
ValueStack stack = invocation.getInvocationContext().getValueStack();
String finalNamespace = this.namespace != null
? TextParseUtil.translateVariables(namespace, stack)
: invocation.getProxy().getNamespace();
String finalActionName = TextParseUtil.translateVariables(actionName, stack);
String finalMethodName = this.methodName != null
? TextParseUtil.translateVariables(this.methodName, stack)
: null;
if (isInChainHistory(finalNamespace, finalActionName, finalMethodName)) {
addToHistory(finalNamespace, finalActionName, finalMethodName);
throw new StrutsException("Infinite recursion detected: " + ActionChainResult.getChainHistory());
throw new StrutsException("Infinite recursion detected: " + ActionChainResult.getChainHistory().toString());
}
if (ActionChainResult.getChainHistory().isEmpty() && invocation.getProxy() != null) {
@@ -218,7 +225,7 @@ public class ActionChainResult implements Result {
}
addToHistory(finalNamespace, finalActionName, finalMethodName);
Map<String, Object> extraContext = ActionContext.of()
Map<String, Object> extraContext = ActionContext.of(new HashMap<>())
.withValueStack(invocation.getInvocationContext().getValueStack())
.withParameters(invocation.getInvocationContext().getParameters())
.with(CHAIN_HISTORY, ActionChainResult.getChainHistory())
@@ -230,25 +237,20 @@ public class ActionChainResult implements Result {
proxy.execute();
}
protected String translateVariables(String text) {
return TextParseUtil.translateVariables(text, ActionContext.getContext().getValueStack());
@Override public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final ActionChainResult that = (ActionChainResult) o;
if (actionName != null ? !actionName.equals(that.actionName) : that.actionName != null) return false;
if (methodName != null ? !methodName.equals(that.methodName) : that.methodName != null) return false;
if (namespace != null ? !namespace.equals(that.namespace) : that.namespace != null) return false;
return true;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ActionChainResult that = (ActionChainResult) o;
return Objects.equals(actionName, that.actionName) && Objects.equals(methodName,
that.methodName) && Objects.equals(namespace, that.namespace);
}
@Override
public int hashCode() {
@Override public int hashCode() {
int result;
result = (actionName != null ? actionName.hashCode() : 0);
result = 31 * result + (namespace != null ? namespace.hashCode() : 0);
@@ -258,15 +260,24 @@ public class ActionChainResult implements Result {
private boolean isInChainHistory(String namespace, String actionName, String methodName) {
LinkedList<? extends String> chainHistory = ActionChainResult.getChainHistory();
Set<String> skipActionsList = new HashSet<>();
if (skipActions != null && skipActions.length() > 0) {
String finalSkipActions = translateVariables(skipActions);
skipActionsList.addAll(TextParseUtil.commaDelimitedStringToSet(finalSkipActions));
if (chainHistory == null) {
return false;
} else {
// Actions to skip
Set<String> skipActionsList = new HashSet<>();
if (skipActions != null && skipActions.length() > 0) {
ValueStack stack = ActionContext.getContext().getValueStack();
String finalSkipActions = TextParseUtil.translateVariables(this.skipActions, stack);
skipActionsList.addAll(TextParseUtil.commaDelimitedStringToSet(finalSkipActions));
}
if (!skipActionsList.contains(actionName)) {
// Get if key is in the chain history
return chainHistory.contains(makeKey(namespace, actionName, methodName));
}
return false;
}
if (!skipActionsList.contains(actionName)) {
return chainHistory.contains(makeKey(namespace, actionName, methodName));
}
return false;
}
private void addToHistory(String namespace, String actionName, String methodName) {
@@ -275,6 +286,10 @@ public class ActionChainResult implements Result {
}
private String makeKey(String namespace, String actionName, String methodName) {
return namespace + "/" + actionName + (methodName != null ? "!" + methodName : "");
if (null == methodName) {
return namespace + "/" + actionName;
}
return namespace + "/" + actionName + "!" + methodName;
}
}
@@ -58,52 +58,79 @@ import java.util.Map;
*/
public class ActionContext implements Serializable {
private static final ThreadLocal<ActionContext> actionContext = new ThreadLocal<>();
static ThreadLocal<ActionContext> actionContext = new ThreadLocal<>();
/**
* Constant for the name of the action being executed.
*
* @deprecated scope will be narrowed to "private", use helper methods instead
*/
private static final String ACTION_NAME = "org.apache.struts2.ActionContext.name";
@Deprecated
public static final String ACTION_NAME = "com.opensymphony.xwork2.ActionContext.name";
/**
* Constant for the {@link com.opensymphony.xwork2.util.ValueStack OGNL value stack}.
*
* @deprecated scope will be narrowed to "private", use helper methods instead
*/
private static final String VALUE_STACK = ValueStack.VALUE_STACK;
@Deprecated
public static final String VALUE_STACK = ValueStack.VALUE_STACK;
/**
* Constant for the action's session.
*
* @deprecated scope will be narrowed to "private", use helper methods instead
*/
private static final String SESSION = "org.apache.struts2.ActionContext.session";
@Deprecated
public static final String SESSION = "com.opensymphony.xwork2.ActionContext.session";
/**
* Constant for the action's application context.
*
* @deprecated scope will be narrowed to "private", use helper methods instead
*/
private static final String APPLICATION = "org.apache.struts2.ActionContext.application";
@Deprecated
public static final String APPLICATION = "com.opensymphony.xwork2.ActionContext.application";
/**
* Constant for the action's parameters.
*
* @deprecated scope will be narrowed to "private", use helper methods instead
*/
private static final String PARAMETERS = "org.apache.struts2.ActionContext.parameters";
@Deprecated
public static final String PARAMETERS = "com.opensymphony.xwork2.ActionContext.parameters";
/**
* Constant for the action's locale.
*
* @deprecated scope will be narrowed to "private", use helper methods instead
*/
private static final String LOCALE = "org.apache.struts2.ActionContext.locale";
@Deprecated
public static final String LOCALE = "com.opensymphony.xwork2.ActionContext.locale";
/**
* Constant for the action's {@link com.opensymphony.xwork2.ActionInvocation invocation} context.
*
* @deprecated scope will be narrowed to "private", use helper methods instead
*/
private static final String ACTION_INVOCATION = "org.apache.struts2.ActionContext.actionInvocation";
@Deprecated
public static final String ACTION_INVOCATION = "com.opensymphony.xwork2.ActionContext.actionInvocation";
/**
* Constant for the map of type conversion errors.
*
* @deprecated scope will be narrowed to "private", use helper methods instead
*/
private static final String CONVERSION_ERRORS = "org.apache.struts2.ActionContext.conversionErrors";
@Deprecated
public static final String CONVERSION_ERRORS = "com.opensymphony.xwork2.ActionContext.conversionErrors";
/**
* Constant for the container
*
* @deprecated scope will be narrowed to "private", use helper methods instead
*/
private static final String CONTAINER = "org.apache.struts2.ActionContext.container";
@Deprecated
public static final String CONTAINER = "com.opensymphony.xwork2.ActionContext.container";
private final Map<String, Object> context;
@@ -118,6 +145,7 @@ public class ActionContext implements Serializable {
/**
* Creates a new ActionContext based on passed in Map
* and assign this instance to the current thread
*
* @param context a map with context values
* @return new ActionContext
@@ -129,15 +157,6 @@ public class ActionContext implements Serializable {
return new ActionContext(context);
}
/**
* Creates a new ActionContext based on empty Map
*
* @return new ActionContext
*/
public static ActionContext of() {
return of(new HashMap<>());
}
/**
* Binds the provided context with the current thread
*
@@ -192,7 +211,13 @@ public class ActionContext implements Serializable {
* Sets the action invocation (the execution state).
*
* @param actionInvocation the action execution state.
* @deprecated use {@link #withActionInvocation(ActionInvocation)} instead
*/
@Deprecated
public void setActionInvocation(ActionInvocation actionInvocation) {
put(ACTION_INVOCATION, actionInvocation);
}
public ActionContext withActionInvocation(ActionInvocation actionInvocation) {
put(ACTION_INVOCATION, actionInvocation);
return this;
@@ -211,7 +236,13 @@ public class ActionContext implements Serializable {
* Sets the action's application context.
*
* @param application the action's application context.
* @deprecated use {@link #withApplication(Map)} instead
*/
@Deprecated
public void setApplication(Map<String, Object> application) {
put(APPLICATION, application);
}
public ActionContext withApplication(Map<String, Object> application) {
put(APPLICATION, application);
return this;
@@ -240,7 +271,13 @@ public class ActionContext implements Serializable {
* Sets conversion errors which occurred when executing the action.
*
* @param conversionErrors a Map of errors which occurred when executing the action.
* @deprecated use {@link #withConversionErrors(Map)} instead
*/
@Deprecated
public void setConversionErrors(Map<String, ConversionData> conversionErrors) {
put(CONVERSION_ERRORS, conversionErrors);
}
public ActionContext withConversionErrors(Map<String, ConversionData> conversionErrors) {
put(CONVERSION_ERRORS, conversionErrors);
return this;
@@ -267,7 +304,13 @@ public class ActionContext implements Serializable {
* Sets the Locale for the current action.
*
* @param locale the Locale for the current action.
* @deprecated use {@link #withLocale(Locale)} instead
*/
@Deprecated
public void setLocale(Locale locale) {
put(LOCALE, locale);
}
public ActionContext withLocale(Locale locale) {
put(LOCALE, locale);
return this;
@@ -284,7 +327,7 @@ public class ActionContext implements Serializable {
if (locale == null) {
locale = Locale.getDefault();
withLocale(locale);
setLocale(locale);
}
return locale;
@@ -293,13 +336,28 @@ public class ActionContext implements Serializable {
/**
* Sets the name of the current Action in the ActionContext.
*
* @param actionName the name of the current action.
* @param name the name of the current action.
* @deprecated use {@link #withActionName(String)} instead
*/
@Deprecated
public void setName(String name) {
put(ACTION_NAME, name);
}
public ActionContext withActionName(String actionName) {
put(ACTION_NAME, actionName);
return this;
}
/**
* Gets the name of the current Action.
*
* @return the name of the current action.
*/
public String getName() {
return (String) get(ACTION_NAME);
}
/**
* Gets the name of the current Action.
*
@@ -314,6 +372,10 @@ public class ActionContext implements Serializable {
*
* @param parameters the parameters for the current action.
*/
public void setParameters(HttpParameters parameters) {
put(PARAMETERS, parameters);
}
public ActionContext withParameters(HttpParameters parameters) {
put(PARAMETERS, parameters);
return this;
@@ -334,7 +396,13 @@ public class ActionContext implements Serializable {
* Sets a map of action session values.
*
* @param session the session values.
* @deprecated use {@link #withSession(Map)} instead
*/
@Deprecated
public void setSession(Map<String, Object> session) {
put(SESSION, session);
}
public ActionContext withSession(Map<String, Object> session) {
put(SESSION, session);
return this;
@@ -353,8 +421,14 @@ public class ActionContext implements Serializable {
/**
* Sets the OGNL value stack.
*
* @param valueStack the OGNL value stack.
* @param stack the OGNL value stack.
* @deprecated Use {@link #withValueStack(ValueStack)} instead
*/
@Deprecated
public void setValueStack(ValueStack stack) {
put(VALUE_STACK, stack);
}
public ActionContext withValueStack(ValueStack valueStack) {
put(VALUE_STACK, valueStack);
return this;
@@ -372,8 +446,14 @@ public class ActionContext implements Serializable {
/**
* Gets the container for this request
*
* @param container The container
* @param cont The container
* @deprecated use {@link #withContainer(Container)} instead
*/
@Deprecated
public void setContainer(Container cont) {
put(CONTAINER, cont);
}
public ActionContext withContainer(Container container) {
put(CONTAINER, container);
return this;
@@ -1,86 +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.
*/
package com.opensymphony.xwork2;
import com.opensymphony.xwork2.config.Configuration;
import com.opensymphony.xwork2.config.ConfigurationException;
import com.opensymphony.xwork2.config.ConfigurationManager;
import com.opensymphony.xwork2.config.ConfigurationProvider;
import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.inject.ContainerBuilder;
import com.opensymphony.xwork2.inject.Context;
import com.opensymphony.xwork2.inject.Factory;
import com.opensymphony.xwork2.inject.Scope;
import com.opensymphony.xwork2.test.StubConfigurationProvider;
import com.opensymphony.xwork2.util.XWorkTestCaseHelper;
import com.opensymphony.xwork2.util.location.LocatableProperties;
import org.junit.After;
import org.junit.Before;
public abstract class XWorkJUnit4TestCase {
protected ConfigurationManager configurationManager;
protected Configuration configuration;
protected Container container;
protected ActionProxyFactory actionProxyFactory;
@Before
public void setUp() throws Exception {
configurationManager = XWorkTestCaseHelper.setUp();
configuration = configurationManager.getConfiguration();
container = configuration.getContainer();
actionProxyFactory = container.getInstance(ActionProxyFactory.class);
}
@After
public void tearDown() throws Exception {
XWorkTestCaseHelper.tearDown(configurationManager);
}
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) {
loadButAdd(type, Container.DEFAULT_NAME, impl);
}
protected void loadButAdd(final Class<?> type, final String name, final Object impl) {
loadConfigurationProviders(new StubConfigurationProvider() {
@Override
public void register(ContainerBuilder builder,
LocatableProperties props) throws ConfigurationException {
builder.factory(type, name, new Factory() {
public Object create(Context context) throws Exception {
return impl;
}
@Override
public Class type() {
return impl.getClass();
}
}, Scope.SINGLETON);
}
});
}
}
@@ -22,17 +22,14 @@ import com.opensymphony.xwork2.config.Configuration;
import com.opensymphony.xwork2.config.ConfigurationException;
import com.opensymphony.xwork2.config.ConfigurationManager;
import com.opensymphony.xwork2.config.ConfigurationProvider;
import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.inject.ContainerBuilder;
import com.opensymphony.xwork2.inject.Context;
import com.opensymphony.xwork2.inject.Factory;
import com.opensymphony.xwork2.inject.Scope;
import com.opensymphony.xwork2.inject.*;
import com.opensymphony.xwork2.test.StubConfigurationProvider;
import com.opensymphony.xwork2.util.XWorkTestCaseHelper;
import com.opensymphony.xwork2.util.location.LocatableProperties;
import junit.framework.TestCase;
import org.apache.commons.lang3.ClassUtils;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
@@ -64,6 +61,10 @@ public abstract class XWorkTestCase extends TestCase {
@Override
protected void tearDown() throws Exception {
XWorkTestCaseHelper.tearDown(configurationManager);
configurationManager = null;
configuration = null;
container = null;
actionProxyFactory = null;
}
protected void loadConfigurationProviders(ConfigurationProvider... providers) {
@@ -100,7 +101,7 @@ public abstract class XWorkTestCase extends TestCase {
}
protected Map<String, Object> createContextWithLocale(Locale locale) {
return ActionContext.of()
return ActionContext.of(new HashMap<>())
.withLocale(locale)
.getContextMap();
}
@@ -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.Optional;
import static org.apache.struts2.StrutsConstants.STRUTS_CONFIGURATION_XML_RELOAD;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
@@ -42,11 +42,12 @@ public class ConfigurationManager {
protected static final Logger LOG = LogManager.getLogger(ConfigurationManager.class);
protected Configuration configuration;
private List<ContainerProvider> containerProviders = new ArrayList<>();
private List<PackageProvider> packageProviders = new ArrayList<>();
protected Lock providerLock = new ReentrantLock();
private List<ContainerProvider> containerProviders = new CopyOnWriteArrayList<>();
private List<PackageProvider> packageProviders = new CopyOnWriteArrayList<>();
protected String defaultFrameworkBeanName;
private boolean providersChanged = true;
private boolean alwaysReloadConfigs = false;
private boolean providersChanged = false;
private boolean reloadConfigs = true; // for the first time
public ConfigurationManager(String name) {
this.defaultFrameworkBeanName = name;
@@ -58,69 +59,54 @@ public class ConfigurationManager {
* @see com.opensymphony.xwork2.config.impl.DefaultConfiguration
*/
public synchronized Configuration getConfiguration() {
if (wasConfigInitialised()) {
if (configuration == null) {
setConfiguration(createConfiguration(defaultFrameworkBeanName));
try {
configuration.reloadContainer(getContainerProviders());
} catch (ConfigurationException e) {
setConfiguration(null);
throw new ConfigurationException("Unable to load configuration.", e);
}
} else {
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;
}
/**
* Get the current list of ConfigurationProviders.
* <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>
*
* @return the list of registered ConfigurationProvider objects
* @see ConfigurationProvider
*/
public synchronized List<ContainerProvider> getContainerProviders() {
return new ArrayList<>(containerProviders);
public List<ContainerProvider> getContainerProviders() {
providerLock.lock();
try {
if (containerProviders.size() == 0) {
containerProviders.add(new StrutsDefaultConfigurationProvider());
}
return containerProviders;
} finally {
providerLock.unlock();
}
}
/**
@@ -128,9 +114,14 @@ public class ConfigurationManager {
*
* @param containerProviders list of {@link ConfigurationProvider} to be set
*/
public synchronized void setContainerProviders(List<ContainerProvider> containerProviders) {
this.containerProviders = new ArrayList<>(containerProviders);
providersChanged = true;
public void setContainerProviders(List<ContainerProvider> containerProviders) {
providerLock.lock();
try {
this.containerProviders = new CopyOnWriteArrayList<>(containerProviders);
providersChanged = true;
} finally {
providerLock.unlock();
}
}
/**
@@ -139,32 +130,22 @@ public class ConfigurationManager {
*
* @param provider the ConfigurationProvider to register
*/
public synchronized void addContainerProvider(ContainerProvider provider) {
public void addContainerProvider(ContainerProvider provider) {
if (!containerProviders.contains(provider)) {
containerProviders.add(provider);
providersChanged = true;
}
}
public synchronized void removeContainerProvider(ContainerProvider provider) {
if (containerProviders.remove(provider)) {
destroyContainerProvider(provider);
providersChanged = true;
public void clearContainerProviders() {
for (ContainerProvider containerProvider : containerProviders) {
clearContainerProvider(containerProvider);
}
}
public synchronized void clearContainerProviders() {
destroyContainerProviders();
containerProviders.clear();
providersChanged = true;
}
private void destroyContainerProviders() {
LOG.debug("Destroying all providers.");
containerProviders.forEach(this::destroyContainerProvider);
}
private void destroyContainerProvider(ContainerProvider containerProvider) {
private void clearContainerProvider(ContainerProvider containerProvider) {
try {
containerProvider.destroy();
} catch (Exception e) {
@@ -172,61 +153,80 @@ 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 (alwaysReloadConfigs || providersChanged) {
if (reloadConfigs || providersChanged) {
LOG.debug("Checking ConfigurationProviders for reload.");
if (needReloadContainerProviders() || needReloadPackageProviders()) {
destroyAndReload();
List<ContainerProvider> providers = getContainerProviders();
boolean reload = needReloadContainerProviders(providers);
if (!reload) {
reload = needReloadPackageProviders();
}
if (reload) {
reloadProviders(providers);
}
updateReloadConfigsFlag();
providersChanged = false;
}
}
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 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 boolean needReloadPackageProviders() {
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;
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;
}
}
}
return false;
}
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;
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;
}
}
return false;
}
public synchronized void destroyAndReload() {
destroyContainerProviders();
reload();
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 reload() {
if (wasConfigInitialised()) {
LOG.debug("Reloading all providers.");
packageProviders = configuration.reloadContainer(containerProviders);
providersChanged = false;
updateAlwaysReloadFlag();
}
packageProviders = getConfiguration().reloadContainer(getContainerProviders());
}
}
@@ -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
*/
void destroy();
public void destroy();
/**
* Initializes with the configuration
* @param configuration The configuration
* @throws ConfigurationException If anything goes wrong
*/
void init(Configuration configuration) throws ConfigurationException;
public 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.
*/
boolean needsReload();
public 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
*/
void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException;
public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException;
}
@@ -18,110 +18,39 @@
*/
package com.opensymphony.xwork2.config.impl;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.DefaultLocaleProviderFactory;
import com.opensymphony.xwork2.DefaultTextProvider;
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.config.Configuration;
import com.opensymphony.xwork2.config.ConfigurationException;
import com.opensymphony.xwork2.config.ContainerProvider;
import com.opensymphony.xwork2.config.FileManagerFactoryProvider;
import com.opensymphony.xwork2.config.FileManagerProvider;
import com.opensymphony.xwork2.config.PackageProvider;
import com.opensymphony.xwork2.config.RuntimeConfiguration;
import com.opensymphony.xwork2.config.entities.ActionConfig;
import com.opensymphony.xwork2.config.entities.InterceptorMapping;
import com.opensymphony.xwork2.config.entities.PackageConfig;
import com.opensymphony.xwork2.config.entities.ResultConfig;
import com.opensymphony.xwork2.config.entities.ResultTypeConfig;
import com.opensymphony.xwork2.config.entities.UnknownHandlerConfig;
import com.opensymphony.xwork2.*;
import com.opensymphony.xwork2.config.*;
import com.opensymphony.xwork2.config.entities.*;
import com.opensymphony.xwork2.config.providers.EnvsValueSubstitutor;
import com.opensymphony.xwork2.config.providers.InterceptorBuilder;
import com.opensymphony.xwork2.config.providers.ValueSubstitutor;
import com.opensymphony.xwork2.conversion.ConversionAnnotationProcessor;
import com.opensymphony.xwork2.conversion.ConversionFileProcessor;
import com.opensymphony.xwork2.conversion.ConversionPropertiesProcessor;
import com.opensymphony.xwork2.conversion.ObjectTypeDeterminer;
import com.opensymphony.xwork2.conversion.TypeConverter;
import com.opensymphony.xwork2.conversion.TypeConverterCreator;
import com.opensymphony.xwork2.conversion.TypeConverterHolder;
import com.opensymphony.xwork2.conversion.impl.ArrayConverter;
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.conversion.impl.DefaultObjectTypeDeterminer;
import com.opensymphony.xwork2.conversion.impl.NumberConverter;
import com.opensymphony.xwork2.conversion.impl.StringConverter;
import com.opensymphony.xwork2.conversion.impl.XWorkBasicConverter;
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.DefaultInterceptorFactory;
import com.opensymphony.xwork2.factory.DefaultResultFactory;
import com.opensymphony.xwork2.factory.DefaultUnknownHandlerFactory;
import com.opensymphony.xwork2.factory.DefaultValidatorFactory;
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.factory.ValidatorFactory;
import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.inject.ContainerBuilder;
import com.opensymphony.xwork2.inject.Context;
import com.opensymphony.xwork2.inject.Factory;
import com.opensymphony.xwork2.inject.Scope;
import com.opensymphony.xwork2.conversion.*;
import com.opensymphony.xwork2.conversion.impl.*;
import com.opensymphony.xwork2.factory.*;
import com.opensymphony.xwork2.inject.*;
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.OgnlCacheFactory;
import com.opensymphony.xwork2.ognl.OgnlReflectionProvider;
import com.opensymphony.xwork2.ognl.OgnlUtil;
import com.opensymphony.xwork2.ognl.OgnlValueStackFactory;
import com.opensymphony.xwork2.ognl.SecurityMemberAccess;
import com.opensymphony.xwork2.ognl.accessor.CompoundRootAccessor;
import com.opensymphony.xwork2.ognl.accessor.RootAccessor;
import com.opensymphony.xwork2.ognl.accessor.XWorkMethodAccessor;
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.ValueStack;
import com.opensymphony.xwork2.util.ValueStackFactory;
import com.opensymphony.xwork2.util.*;
import com.opensymphony.xwork2.util.fs.DefaultFileManager;
import com.opensymphony.xwork2.util.fs.DefaultFileManagerFactory;
import com.opensymphony.xwork2.util.location.LocatableProperties;
import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
import ognl.MethodAccessor;
import ognl.PropertyAccessor;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.StrutsConstants;
import org.apache.struts2.conversion.StrutsConversionPropertiesProcessor;
import org.apache.struts2.conversion.StrutsTypeConverterCreator;
import org.apache.struts2.conversion.StrutsTypeConverterHolder;
import org.apache.struts2.ognl.OgnlGuard;
import org.apache.struts2.ognl.ProviderAllowlist;
import org.apache.struts2.ognl.StrutsOgnlGuard;
import org.apache.struts2.conversion.StrutsTypeConverterCreator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.*;
/**
@@ -132,21 +61,6 @@ import java.util.TreeSet;
*/
public class DefaultConfiguration implements Configuration {
public static final Map<String, Object> BOOTSTRAP_CONSTANTS;
static {
Map<String, Object> constants = new HashMap<>();
constants.put(StrutsConstants.STRUTS_DEVMODE, Boolean.FALSE);
constants.put(StrutsConstants.STRUTS_CONFIGURATION_XML_RELOAD, Boolean.FALSE);
constants.put(StrutsConstants.STRUTS_MATCHER_APPEND_NAMED_PARAMETERS, Boolean.TRUE);
constants.put(StrutsConstants.STRUTS_OGNL_EXPRESSION_CACHE_TYPE, OgnlCacheFactory.CacheType.BASIC);
constants.put(StrutsConstants.STRUTS_OGNL_EXPRESSION_CACHE_MAXSIZE, 10000);
constants.put(StrutsConstants.STRUTS_OGNL_BEANINFO_CACHE_TYPE, OgnlCacheFactory.CacheType.BASIC);
constants.put(StrutsConstants.STRUTS_OGNL_BEANINFO_CACHE_MAXSIZE, 10000);
constants.put(StrutsConstants.STRUTS_ENABLE_DYNAMIC_METHOD_INVOCATION, Boolean.FALSE);
BOOTSTRAP_CONSTANTS = Collections.unmodifiableMap(constants);
}
protected static final Logger LOG = LogManager.getLogger(DefaultConfiguration.class);
// Programmatic Action Configurations
@@ -325,8 +239,12 @@ public class DefaultConfiguration implements Configuration {
}
protected ActionContext setContext(Container cont) {
ValueStack vs = cont.getInstance(ValueStackFactory.class).createValueStack();
return ActionContext.of(vs.getContext()).bind();
ActionContext context = ActionContext.getContext();
if (context == null) {
ValueStack vs = cont.getInstance(ValueStackFactory.class).createValueStack();
context = ActionContext.of(vs.getContext()).bind();
}
return context;
}
protected Container createBootstrapContainer(List<ContainerProvider> providers) {
@@ -341,73 +259,63 @@ public class DefaultConfiguration implements Configuration {
fmFactoryRegistered = true;
}
}
builder.factory(ObjectFactory.class, Scope.SINGLETON);
builder.factory(ActionFactory.class, DefaultActionFactory.class, Scope.SINGLETON);
builder.factory(ResultFactory.class, DefaultResultFactory.class, Scope.SINGLETON);
builder.factory(InterceptorFactory.class, DefaultInterceptorFactory.class, Scope.SINGLETON);
builder.factory(com.opensymphony.xwork2.factory.ValidatorFactory.class, com.opensymphony.xwork2.factory.DefaultValidatorFactory.class, Scope.SINGLETON);
builder.factory(ConverterFactory.class, StrutsConverterFactory.class, Scope.SINGLETON);
builder.factory(UnknownHandlerFactory.class, DefaultUnknownHandlerFactory.class, Scope.SINGLETON);
bootstrapFactories(builder);
bootstrapTypeConverters(builder);
builder.factory(FileManager.class, "system", DefaultFileManager.class, Scope.SINGLETON);
if (!fmFactoryRegistered) {
builder.factory(FileManagerFactory.class, DefaultFileManagerFactory.class, Scope.SINGLETON);
}
builder.factory(ReflectionProvider.class, OgnlReflectionProvider.class, Scope.SINGLETON);
builder.factory(ValueStackFactory.class, OgnlValueStackFactory.class, Scope.SINGLETON);
for (Map.Entry<String, Object> entry : BOOTSTRAP_CONSTANTS.entrySet()) {
builder.constant(entry.getKey(), String.valueOf(entry.getValue()));
}
builder.factory(XWorkConverter.class, Scope.SINGLETON);
builder.factory(ConversionPropertiesProcessor.class, StrutsConversionPropertiesProcessor.class, Scope.SINGLETON);
builder.factory(ConversionFileProcessor.class, DefaultConversionFileProcessor.class, Scope.SINGLETON);
builder.factory(ConversionAnnotationProcessor.class, DefaultConversionAnnotationProcessor.class, Scope.SINGLETON);
builder.factory(TypeConverterCreator.class, StrutsTypeConverterCreator.class, Scope.SINGLETON);
builder.factory(TypeConverterHolder.class, StrutsTypeConverterHolder.class, Scope.SINGLETON);
builder.factory(XWorkBasicConverter.class, Scope.SINGLETON);
builder.factory(TypeConverter.class, StrutsConstants.STRUTS_CONVERTER_COLLECTION, CollectionConverter.class, Scope.SINGLETON);
builder.factory(TypeConverter.class, StrutsConstants.STRUTS_CONVERTER_ARRAY, ArrayConverter.class, Scope.SINGLETON);
builder.factory(TypeConverter.class, StrutsConstants.STRUTS_CONVERTER_DATE, DateConverter.class, Scope.SINGLETON);
builder.factory(TypeConverter.class, StrutsConstants.STRUTS_CONVERTER_NUMBER, NumberConverter.class, Scope.SINGLETON);
builder.factory(TypeConverter.class, StrutsConstants.STRUTS_CONVERTER_STRING, StringConverter.class, Scope.SINGLETON);
builder.factory(TextProvider.class, "system", DefaultTextProvider.class, Scope.SINGLETON);
builder.factory(LocalizedTextProvider.class, StrutsLocalizedTextProvider.class, Scope.SINGLETON);
builder.factory(TextProviderFactory.class, StrutsTextProviderFactory.class, Scope.SINGLETON);
builder.factory(LocaleProviderFactory.class, DefaultLocaleProviderFactory.class, Scope.SINGLETON);
builder.factory(TextParser.class, OgnlTextParser.class, Scope.SINGLETON);
builder.factory(ObjectTypeDeterminer.class, DefaultObjectTypeDeterminer.class, Scope.SINGLETON);
builder.factory(PropertyAccessor.class, CompoundRoot.class.getName(), CompoundRootAccessor.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);
builder.constant(StrutsConstants.STRUTS_DEVMODE, "false");
builder.constant(StrutsConstants.STRUTS_OGNL_LOG_MISSING_PROPERTIES, "false");
builder.constant(StrutsConstants.STRUTS_OGNL_ENABLE_EVAL_EXPRESSION, "false");
builder.constant(StrutsConstants.STRUTS_OGNL_ENABLE_EXPRESSION_CACHE, "true");
builder.constant(StrutsConstants.STRUTS_CONFIGURATION_XML_RELOAD, "false");
builder.constant(StrutsConstants.STRUTS_I18N_RELOAD, "false");
builder.constant(StrutsConstants.STRUTS_MATCHER_APPEND_NAMED_PARAMETERS, "true");
return builder.create(true);
}
public static ContainerBuilder bootstrapFactories(ContainerBuilder builder) {
return builder
// TODO: SpringObjectFactoryTest fails when these are SINGLETON
.factory(ObjectFactory.class, Scope.PROTOTYPE)
.factory(ActionFactory.class, DefaultActionFactory.class, Scope.PROTOTYPE)
.factory(ResultFactory.class, DefaultResultFactory.class, Scope.PROTOTYPE)
.factory(InterceptorFactory.class, DefaultInterceptorFactory.class, Scope.PROTOTYPE)
.factory(ValidatorFactory.class, DefaultValidatorFactory.class, Scope.PROTOTYPE)
.factory(ConverterFactory.class, StrutsConverterFactory.class, Scope.PROTOTYPE)
.factory(UnknownHandlerFactory.class, DefaultUnknownHandlerFactory.class, Scope.PROTOTYPE)
.factory(FileManager.class, "system", DefaultFileManager.class, Scope.SINGLETON)
.factory(ReflectionProvider.class, OgnlReflectionProvider.class, Scope.SINGLETON)
.factory(ValueStackFactory.class, OgnlValueStackFactory.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(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(TextParser.class, OgnlTextParser.class, Scope.SINGLETON)
.factory(ObjectTypeDeterminer.class, DefaultObjectTypeDeterminer.class, Scope.SINGLETON)
.factory(RootAccessor.class, CompoundRootAccessor.class, Scope.SINGLETON)
.factory(MethodAccessor.class, XWorkMethodAccessor.class, Scope.SINGLETON)
.factory(ExpressionCacheFactory.class, DefaultOgnlExpressionCacheFactory.class, Scope.SINGLETON)
.factory(BeanInfoCacheFactory.class, DefaultOgnlBeanInfoCacheFactory.class, Scope.SINGLETON)
.factory(OgnlUtil.class, Scope.SINGLETON)
.factory(SecurityMemberAccess.class, Scope.PROTOTYPE)
.factory(OgnlGuard.class, StrutsOgnlGuard.class, Scope.SINGLETON)
.factory(ProviderAllowlist.class, Scope.SINGLETON)
.factory(ValueSubstitutor.class, EnvsValueSubstitutor.class, Scope.SINGLETON);
}
public static ContainerBuilder bootstrapTypeConverters(ContainerBuilder builder) {
return builder
.factory(TypeConverter.class, StrutsConstants.STRUTS_CONVERTER_COLLECTION, CollectionConverter.class, Scope.SINGLETON)
.factory(TypeConverter.class, StrutsConstants.STRUTS_CONVERTER_ARRAY, ArrayConverter.class, Scope.SINGLETON)
.factory(TypeConverter.class, StrutsConstants.STRUTS_CONVERTER_DATE, DateConverter.class, Scope.SINGLETON)
.factory(TypeConverter.class, StrutsConstants.STRUTS_CONVERTER_NUMBER, NumberConverter.class, Scope.SINGLETON)
.factory(TypeConverter.class, StrutsConstants.STRUTS_CONVERTER_STRING, StringConverter.class, Scope.SINGLETON);
}
/**
* <p>
* This builds the internal runtime configuration used by Xwork for finding and configuring Actions from the
@@ -18,11 +18,7 @@
*/
package com.opensymphony.xwork2.config.impl;
import com.opensymphony.xwork2.config.Configuration;
import com.opensymphony.xwork2.config.ConfigurationException;
import com.opensymphony.xwork2.config.ContainerProvider;
import com.opensymphony.xwork2.config.PackageProvider;
import com.opensymphony.xwork2.config.RuntimeConfiguration;
import com.opensymphony.xwork2.config.*;
import com.opensymphony.xwork2.config.entities.PackageConfig;
import com.opensymphony.xwork2.config.entities.UnknownHandlerConfig;
import com.opensymphony.xwork2.config.providers.StrutsDefaultConfigurationProvider;
@@ -30,12 +26,9 @@ import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.inject.ContainerBuilder;
import com.opensymphony.xwork2.inject.Scope;
import com.opensymphony.xwork2.util.location.LocatableProperties;
import org.apache.struts2.StrutsConstants;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
/**
@@ -58,9 +51,10 @@ public class MockConfiguration implements Configuration {
builder.factory(Configuration.class, MockConfiguration.class, Scope.SINGLETON);
LocatableProperties props = new LocatableProperties();
new StrutsDefaultConfigurationProvider().register(builder, props);
for (Map.Entry<String, Object> entry : DefaultConfiguration.BOOTSTRAP_CONSTANTS.entrySet()) {
builder.constant(entry.getKey(), String.valueOf(entry.getValue()));
}
builder.constant(StrutsConstants.STRUTS_DEVMODE, "false");
builder.constant(StrutsConstants.STRUTS_CONFIGURATION_XML_RELOAD, "true");
builder.constant(StrutsConstants.STRUTS_OGNL_ENABLE_EXPRESSION_CACHE, "true");
builder.constant(StrutsConstants.STRUTS_ENABLE_DYNAMIC_METHOD_INVOCATION, "false");
container = builder.create(true);
}
@@ -20,24 +20,61 @@ 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.DefaultTextProvider;
import com.opensymphony.xwork2.DefaultUnknownHandlerManager;
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;
import com.opensymphony.xwork2.config.ConfigurationProvider;
import com.opensymphony.xwork2.config.impl.DefaultConfiguration;
import com.opensymphony.xwork2.conversion.ConversionAnnotationProcessor;
import com.opensymphony.xwork2.conversion.ConversionFileProcessor;
import com.opensymphony.xwork2.conversion.ConversionPropertiesProcessor;
import com.opensymphony.xwork2.conversion.NullHandler;
import com.opensymphony.xwork2.conversion.ObjectTypeDeterminer;
import com.opensymphony.xwork2.conversion.TypeConverterCreator;
import com.opensymphony.xwork2.conversion.TypeConverterHolder;
import com.opensymphony.xwork2.conversion.impl.ArrayConverter;
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.conversion.impl.DefaultObjectTypeDeterminer;
import com.opensymphony.xwork2.conversion.impl.InstantiatingNullHandler;
import com.opensymphony.xwork2.conversion.impl.NumberConverter;
import com.opensymphony.xwork2.conversion.impl.StringConverter;
import com.opensymphony.xwork2.conversion.impl.XWorkBasicConverter;
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.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;
@@ -47,17 +84,25 @@ 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.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;
import com.opensymphony.xwork2.util.fs.DefaultFileManager;
import com.opensymphony.xwork2.util.fs.DefaultFileManagerFactory;
import com.opensymphony.xwork2.util.location.LocatableProperties;
import com.opensymphony.xwork2.util.reflection.ReflectionContextFactory;
import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
import com.opensymphony.xwork2.validator.ActionValidatorManager;
import com.opensymphony.xwork2.validator.AnnotationActionValidatorManager;
import com.opensymphony.xwork2.validator.DefaultActionValidatorManager;
@@ -65,11 +110,14 @@ import com.opensymphony.xwork2.validator.DefaultValidatorFactory;
import com.opensymphony.xwork2.validator.DefaultValidatorFileParser;
import com.opensymphony.xwork2.validator.ValidatorFactory;
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.interceptor.exec.ExecutorProvider;
import org.apache.struts2.interceptor.exec.StrutsExecutorProvider;
import org.apache.struts2.url.QueryStringBuilder;
import org.apache.struts2.url.QueryStringParser;
import org.apache.struts2.url.StrutsQueryStringBuilder;
@@ -109,60 +157,102 @@ public class StrutsDefaultConfigurationProvider implements ConfigurationProvider
}
@Override
public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException {
public void register(ContainerBuilder builder, LocatableProperties props)
throws ConfigurationException {
DefaultConfiguration.bootstrapFactories(builder)
.factory(FileManagerFactory.class, DefaultFileManagerFactory.class, Scope.SINGLETON)
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(ActionProxyFactory.class, DefaultActionProxyFactory.class, Scope.SINGLETON)
.factory(ActionProxyFactory.class, DefaultActionProxyFactory.class, Scope.SINGLETON)
.factory(ObjectTypeDeterminer.class, DefaultObjectTypeDeterminer.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(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(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, 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(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(MethodAccessor.class, Object.class.getName(), XWorkMethodAccessor.class, Scope.SINGLETON)
.factory(MethodAccessor.class, CompoundRoot.class.getName(), CompoundRootAccessor.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(TextParser.class, OgnlTextParser.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(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(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)
.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(ExecutorProvider.class, StrutsExecutorProvider.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)
for (Map.Entry<String, Object> entry : DefaultConfiguration.BOOTSTRAP_CONSTANTS.entrySet()) {
props.setProperty(entry.getKey(), String.valueOf(entry.getValue()));
}
.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(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());
props.setProperty(StrutsConstants.STRUTS_I18N_RELOAD, Boolean.FALSE.toString());
props.setProperty(StrutsConstants.STRUTS_DEVMODE, Boolean.FALSE.toString());
props.setProperty(StrutsConstants.STRUTS_OGNL_LOG_MISSING_PROPERTIES, Boolean.FALSE.toString());
props.setProperty(StrutsConstants.STRUTS_OGNL_ENABLE_EXPRESSION_CACHE, Boolean.TRUE.toString());
props.setProperty(StrutsConstants.STRUTS_OGNL_ENABLE_EVAL_EXPRESSION, Boolean.FALSE.toString());
props.setProperty(StrutsConstants.STRUTS_CONFIGURATION_XML_RELOAD, Boolean.FALSE.toString());
props.setProperty(StrutsConstants.STRUTS_ALLOW_STATIC_FIELD_ACCESS, Boolean.TRUE.toString());
props.setProperty(StrutsConstants.STRUTS_MATCHER_APPEND_NAMED_PARAMETERS, Boolean.TRUE.toString());
}
}
@@ -201,25 +201,11 @@ public enum Scope {
<T> Callable<? extends T> toCallable(final InternalContext context,
final InternalFactory<? extends T> factory) {
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;
}
return new Callable<T>() {
public T call() throws Exception {
return InitializableFactory.wrapIfNeeded(factory).create(context);
}
};
}
/**
@@ -52,9 +52,9 @@ import java.util.Map;
* <ul>
*
* <li>logEnabled (optional) - Should exceptions also be logged? (boolean true|false)</li>
*
*
* <li>logLevel (optional) - what log level should we use (<code>trace, debug, info, warn, error, fatal</code>)? - defaut is <code>debug</code></li>
*
*
* <li>logCategory (optional) - If provided we would use this category (eg. <code>com.mycompany.app</code>).
* Default is to use <code>com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor</code>.</li>
*
@@ -102,7 +102,7 @@ import java.util.Map;
* &lt;/xwork&gt;
* <!-- END SNIPPET: example -->
* </pre>
*
*
* <p>
* This second example will also log the exceptions using our own category
* <code>com.mycompany.app.unhandled</code> at WARN level.
@@ -117,8 +117,8 @@ import java.util.Map;
* &lt;interceptor-ref name="exception"&gt;
* &lt;param name="logEnabled"&gt;true&lt;/param&gt;
* &lt;param name="logCategory"&gt;com.mycompany.app.unhandled&lt;/param&gt;
* &lt;param name="logLevel"&gt;WARN&lt;/param&gt;
* &lt;/interceptor-ref&gt;
* &lt;param name="logLevel"&gt;WARN&lt;/param&gt;
* &lt;/interceptor-ref&gt;
* &lt;interceptor-ref name="i18n"/&gt;
* &lt;interceptor-ref name="staticParams"/&gt;
* &lt;interceptor-ref name="params"/&gt;
@@ -129,7 +129,7 @@ import java.util.Map;
* &lt;/interceptors&gt;
*
* &lt;default-interceptor-ref name="exceptionmappingStack"/&gt;
*
*
* &lt;global-results&gt;
* &lt;result name="unhandledException"&gt;/unhandled-exception.jsp&lt;/result&gt;
* &lt;/global-results&gt;
@@ -137,12 +137,12 @@ import java.util.Map;
* &lt;global-exception-mappings&gt;
* &lt;exception-mapping exception="java.lang.Exception" result="unhandledException"/&gt;
* &lt;/global-exception-mappings&gt;
*
*
* &lt;action name="exceptionDemo" class="org.apache.struts2.showcase.exceptionmapping.ExceptionMappingAction"&gt;
* &lt;exception-mapping exception="org.apache.struts2.showcase.exceptionmapping.ExceptionMappingException"
* result="damm"/&gt;
* &lt;result name="input"&gt;index.jsp&lt;/result&gt;
* &lt;result name="success"&gt;success.jsp&lt;/result&gt;
* &lt;result name="success"&gt;success.jsp&lt;/result&gt;
* &lt;result name="damm"&gt;damm.jsp&lt;/result&gt;
* &lt;/action&gt;
*
@@ -151,18 +151,18 @@ import java.util.Map;
* <!-- END SNIPPET: example2 -->
* </pre>
*
* @author Matthew E. Porter (matthew dot porter at metissian dot com)
* @author Matthew E. Porter (matthew dot porter at metissian dot com)
* @author Claus Ibsen
*/
public class ExceptionMappingInterceptor extends AbstractInterceptor {
private static final Logger LOG = LogManager.getLogger(ExceptionMappingInterceptor.class);
protected Logger categoryLogger;
protected boolean logEnabled = false;
protected String logCategory;
protected String logLevel;
public boolean isLogEnabled() {
return logEnabled;
@@ -204,7 +204,7 @@ public class ExceptionMappingInterceptor extends AbstractInterceptor {
Map<String, String> mappingParams = mappingConfig.getParams();
// create a mutable HashMap since some interceptors will remove parameters, and parameterMap is immutable
HttpParameters parameters = HttpParameters.create(mappingParams).build();
invocation.getInvocationContext().withParameters(parameters);
invocation.getInvocationContext().setParameters(parameters);
result = mappingConfig.getResult();
publishException(invocation, new ExceptionHolder(e));
} else {
@@ -217,7 +217,7 @@ public class ExceptionMappingInterceptor extends AbstractInterceptor {
/**
* Handles the logging of the exception.
*
*
* @param e the exception to log.
*/
protected void handleLogging(Exception e) {
@@ -231,10 +231,10 @@ public class ExceptionMappingInterceptor extends AbstractInterceptor {
doLog(LOG, e);
}
}
/**
* Performs the actual logging.
*
*
* @param logger the provided logger to use.
* @param e the exception to log.
*/
@@ -243,7 +243,7 @@ public class ExceptionMappingInterceptor extends AbstractInterceptor {
logger.debug(e.getMessage(), e);
return;
}
if ("trace".equalsIgnoreCase(logLevel)) {
logger.trace(e.getMessage(), e);
} else if ("debug".equalsIgnoreCase(logLevel)) {
@@ -30,8 +30,7 @@ package com.opensymphony.xwork2.interceptor;
* parameters cannot be set by malicious users.
* </p>
*
* @deprecated since Struts 6.2.0, use {@link org.apache.struts2.action.NoParameters}
* @author Dick Zetterberg (dick@transitor.se)
*/
@Deprecated
public interface NoParameters extends org.apache.struts2.action.NoParameters {
public interface NoParameters {
}
@@ -26,6 +26,7 @@ import org.apache.logging.log4j.Logger;
import org.apache.struts2.dispatcher.HttpParameters;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.TreeMap;
@@ -33,33 +34,33 @@ import java.util.TreeMap;
* <!-- START SNIPPET: description -->
*
* The Parameter Filter Interceptor blocks parameters from getting
* to the rest of the stack or your action. You can use multiple
* to the rest of the stack or your action. You can use multiple
* parameter filter interceptors for a given action, so, for example,
* you could use one in your default stack that filtered parameters
* you wanted blocked from every action and those you wanted blocked
* you wanted blocked from every action and those you wanted blocked
* from an individual action you could add an additional interceptor
* for each action.
*
*
* <!-- END SNIPPET: description -->
*
*
* <!-- START SNIPPET: parameters -->
*
* <ul>
* <li>allowed - a comma delimited list of parameter prefixes
* that are allowed to pass to the action</li>
* <li>blocked - a comma delimited list of parameter prefixes
* <li>blocked - a comma delimited list of parameter prefixes
* that are not allowed to pass to the action</li>
* <li>defaultBlock - boolean (default to false) whether by
* default a given parameter is blocked. If true, then a parameter
* must have a prefix in the allowed list in order to be able
* must have a prefix in the allowed list in order to be able
* to pass to the action
* </ul>
*
*
* <p>The way parameters are filtered for the least configuration is that
* if a string is in the allowed or blocked lists, then any parameter
* that is a member of the object represented by the parameter is allowed
* or blocked respectively.</p>
*
*
* <p>For example, if the parameters are:
* <ul>
* <li>blocked: person,person.address.createDate,personDao</li>
@@ -68,16 +69,16 @@ import java.util.TreeMap;
* </ul>
*
* <p>
* The parameters person.name, person.phoneNum etc would be blocked
* The parameters person.name, person.phoneNum etc would be blocked
* because 'person' is in the blocked list. However, person.address.street
* and person.address.city would be allowed because person.address is
* in the allowed list (the longer string determines permissions).</p>
* in the allowed list (the longer string determines permissions).</p>
* <!-- END SNIPPET: parameters -->
*
* <!-- START SNIPPET: extending -->
* There are no known extension points to this interceptor.
* <!-- END SNIPPET: extending -->
*
*
* <pre>
* <!-- START SNIPPET: example -->
* &lt;interceptors&gt;
@@ -85,7 +86,7 @@ import java.util.TreeMap;
* &lt;interceptor name="parameterFilter" class="com.opensymphony.xwork2.interceptor.ParameterFilterInterceptor"/&gt;
* ...
* &lt;/interceptors&gt;
*
*
* &lt;action ....&gt;
* ...
* &lt;interceptor-ref name="parameterFilter"&gt;
@@ -95,12 +96,9 @@ import java.util.TreeMap;
* &lt;/action&gt;
* <!-- END SNIPPET: example -->
* </pre>
*
*
* @author Gabe
*
* @deprecated since 6.4.0, use {@link org.apache.struts2.interceptor.parameter.ParametersInterceptor}.
*/
@Deprecated
public class ParameterFilterInterceptor extends AbstractInterceptor {
private static final Logger LOG = LogManager.getLogger(ParameterFilterInterceptor.class);
@@ -135,7 +133,7 @@ public class ParameterFilterInterceptor extends AbstractInterceptor {
}
}
invocation.getInvocationContext().withParameters(parameters);
invocation.getInvocationContext().setParameters(parameters);
return invocation.invoke();
}
@@ -19,8 +19,19 @@
package com.opensymphony.xwork2.interceptor;
/**
* @deprecated since 6.4.0, use {@link org.apache.struts2.action.ParameterNameAware}.
* This interface is implemented by actions that want to declare acceptable parameters. Works in conjunction with {@link
* ParametersInterceptor}. For example, actions may want to create a white list of parameters they will accept or a
* blacklist of parameters they will reject to prevent clients from setting other unexpected (and possibly dangerous)
* parameters.
*/
@Deprecated
public interface ParameterNameAware extends org.apache.struts2.action.ParameterNameAware {
public interface ParameterNameAware {
/**
* Tests if the the action will accept the parameter with the given name.
*
* @param parameterName the parameter name
* @return <tt>true</tt> if accepted, <tt>false</tt> otherwise
*/
boolean acceptableParameterName(String parameterName);
}
@@ -23,102 +23,120 @@ import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.util.TextParseUtil;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.action.NoParameters;
import org.apache.struts2.dispatcher.HttpParameters;
import org.apache.struts2.dispatcher.Parameter;
import org.apache.struts2.dispatcher.HttpParameters;
import java.util.Collections;
import java.util.Set;
/**
* <!-- START SNIPPET: description -->
* This is a simple XWork interceptor that allows parameters (matching
* one of the paramNames attribute csv value) to be
* one of the paramNames attribute csv value) to be
* removed from the parameter map if they match a certain value
* (matching one of the paramValues attribute csv value), before they
* are set on the action. A typical usage would be to want a dropdown/select
* to map onto a boolean value on an action. The select had the options
* none, yes and no with values -1, true and false. The true and false would
* map across correctly. However the -1 would be set to false.
* This was not desired as one might needed the value on the action to stay null.
* This interceptor fixes this by preventing the parameter from ever reaching
* (matching one of the paramValues attribute csv value), before they
* are set on the action. A typical usage would be to want a dropdown/select
* to map onto a boolean value on an action. The select had the options
* none, yes and no with values -1, true and false. The true and false would
* map across correctly. However the -1 would be set to false.
* This was not desired as one might needed the value on the action to stay null.
* This interceptor fixes this by preventing the parameter from ever reaching
* the action.
*
* <!-- END SNIPPET: description -->
*
*
* <!-- START SNIPPET: parameters -->
* <ul>
* <li>paramNames - A comma separated value (csv) indicating the parameter name
* whose param value should be considered that if they match any of the
* comma separated value (csv) from paramValues attribute, shall be
* removed from the parameter map such that they will not be applied
* to the action</li>
* <li>paramValues - A comma separated value (csv) indicating the parameter value that if
* matched shall have its parameter be removed from the parameter map
* such that they will not be applied to the action</li>
* <li>paramNames - A comma separated value (csv) indicating the parameter name
* whose param value should be considered that if they match any of the
* comma separated value (csv) from paramValues attribute, shall be
* removed from the parameter map such that they will not be applied
* to the action</li>
* <li>paramValues - A comma separated value (csv) indicating the parameter value that if
* matched shall have its parameter be removed from the parameter map
* such that they will not be applied to the action</li>
* </ul>
* <p>
* <!-- END SNIPPET: parameters -->
*
*
* <!-- START SNIPPET: extending -->
* No intended extension point
*
* <!-- END SNIPPET: extending -->
*
* <pre>
* <!-- START SNIPPET: example -->
*
* &lt;action name="sample" class="org.martingilday.Sample"&gt;
* &lt;interceptor-ref name="paramRemover"&gt;
* &lt;param name="paramNames"&gt;aParam,anotherParam&lt;/param&gt;
* &lt;param name="paramValues"&gt;--,-1&lt;/param&gt;
* &lt;param name="paramNames"&gt;aParam,anotherParam&lt;/param&gt;
* &lt;param name="paramValues"&gt;--,-1&lt;/param&gt;
* &lt;/interceptor-ref&gt;
* &lt;interceptor-ref name="defaultStack" /&gt;
* ...
* &lt;/action&gt;
*
* <!-- END SNIPPET: example -->
* </pre>
*
*
* @author martin.gilday
*/
public class ParameterRemoverInterceptor extends AbstractInterceptor {
private static final Logger LOG = LogManager.getLogger(ParameterRemoverInterceptor.class);
private static final Logger LOG = LogManager.getLogger(ParameterRemoverInterceptor.class);
private Set<String> paramNames = Collections.emptySet();
private Set<String> paramValues = Collections.emptySet();
private static final long serialVersionUID = 1;
/**
* Decide if the parameter should be removed from the parameter map based on
* <code>paramNames</code> and <code>paramValues</code>.
*
* @see com.opensymphony.xwork2.interceptor.AbstractInterceptor
*/
@Override
public String intercept(ActionInvocation invocation) throws Exception {
if (!(invocation.getAction() instanceof NoParameters)
&& (null != this.paramNames)) {
ActionContext ac = invocation.getInvocationContext();
HttpParameters parameters = ac.getParameters();
private Set<String> paramNames = Collections.emptySet();
private Set<String> paramValues = Collections.emptySet();
if (parameters != null) {
/**
* Decide if the parameter should be removed from the parameter map based on
* <code>paramNames</code> and <code>paramValues</code>.
*
* @see com.opensymphony.xwork2.interceptor.AbstractInterceptor
*/
@Override
public String intercept(ActionInvocation invocation) throws Exception {
if (!(invocation.getAction() instanceof NoParameters)
&& (null != this.paramNames)) {
ActionContext ac = invocation.getInvocationContext();
HttpParameters parameters = ac.getParameters();
if (parameters != null) {
for (String removeName : paramNames) {
try {
Parameter parameter = parameters.get(removeName);
if (parameter.isDefined() && this.paramValues.contains(parameter.getValue())) {
parameters.remove(removeName);
}
} catch (Exception e) {
LOG.error("Failed to convert parameter to string", e);
}
try {
Parameter parameter = parameters.get(removeName);
if (parameter.isDefined() && this.paramValues.contains(parameter.getValue())) {
parameters.remove(removeName);
}
} catch (Exception e) {
LOG.error("Failed to convert parameter to string", e);
}
}
}
}
return invocation.invoke();
}
}
}
return invocation.invoke();
}
/**
* Allows <code>paramNames</code> attribute to be set as comma-separated-values (csv).
*
* @param paramNames the paramNames to set
*/
public void setParamNames(String paramNames) {
this.paramNames = TextParseUtil.commaDelimitedStringToSet(paramNames);
}
/**
* Allows <code>paramNames</code> attribute to be set as comma-separated-values (csv).
*
* @param paramNames the paramNames to set
*/
public void setParamNames(String paramNames) {
this.paramNames = TextParseUtil.commaDelimitedStringToSet(paramNames);
}
/**
* Allows <code>paramValues</code> attribute to be set as a comma-separated-values (csv).
*
* @param paramValues the paramValues to set
*/
public void setParamValues(String paramValues) {
this.paramValues = TextParseUtil.commaDelimitedStringToSet(paramValues);
}
/**
* Allows <code>paramValues</code> attribute to be set as a comma-separated-values (csv).
*
* @param paramValues the paramValues to set
*/
public void setParamValues(String paramValues) {
this.paramValues = TextParseUtil.commaDelimitedStringToSet(paramValues);
}
}
@@ -19,8 +19,19 @@
package com.opensymphony.xwork2.interceptor;
/**
* @deprecated since 6.4.0, use {@link org.apache.struts2.action.ParameterValueAware}.
* 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.
*/
@Deprecated
public interface ParameterValueAware extends org.apache.struts2.action.ParameterValueAware {
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);
}
@@ -18,8 +18,537 @@
*/
package com.opensymphony.xwork2.interceptor;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.TextProvider;
import com.opensymphony.xwork2.inject.Inject;
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;
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.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;
/**
* @deprecated since 6.4.0, use {@link org.apache.struts2.interceptor.parameter.ParametersInterceptor}.
* This interceptor sets all parameters on the value stack.
*/
public class ParametersInterceptor extends org.apache.struts2.interceptor.parameter.ParametersInterceptor {
public class ParametersInterceptor extends MethodFilterInterceptor {
private static final Logger LOG = LogManager.getLogger(ParametersInterceptor.class);
protected static final int PARAM_NAME_MAX_LENGTH = 100;
private static final Pattern DMI_IGNORED_PATTERN = Pattern.compile("^(action|method):.*", Pattern.CASE_INSENSITIVE);
private int paramNameMaxLength = PARAM_NAME_MAX_LENGTH;
private boolean devMode = false;
private boolean dmiEnabled = false;
protected boolean ordered = false;
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) {
this.valueStackFactory = valueStackFactory;
}
@Inject(StrutsConstants.STRUTS_DEVMODE)
public void setDevMode(String mode) {
this.devMode = BooleanUtils.toBoolean(mode);
}
@Inject
public void setExcludedPatterns(ExcludedPatternsChecker excludedPatterns) {
this.excludedPatterns = excludedPatterns;
}
@Inject
public void setAcceptedPatterns(AcceptedPatternsChecker acceptedPatterns) {
this.acceptedPatterns = acceptedPatterns;
}
@Inject(value = StrutsConstants.STRUTS_ENABLE_DYNAMIC_METHOD_INVOCATION, required = false)
protected void setDynamicMethodInvocation(String dmiEnabled) {
this.dmiEnabled = Boolean.parseBoolean(dmiEnabled);
}
/**
* If the param name exceeds the configured maximum length it will not be
* accepted.
*
* @param paramNameMaxLength Maximum length of param names
*/
public void setParamNameMaxLength(int paramNameMaxLength) {
this.paramNameMaxLength = paramNameMaxLength;
}
static private int countOGNLCharacters(String s) {
int count = 0;
for (int i = s.length() - 1; i >= 0; i--) {
char c = s.charAt(i);
if (c == '.' || c == '[') count++;
}
return count;
}
/**
* Compares based on number of '.' and '[' characters (fewer is higher)
*/
static final Comparator<String> rbCollator = (s1, s2) -> {
int l1 = countOGNLCharacters(s1);
int l2 = countOGNLCharacters(s2);
return l1 < l2 ? -1 : (l2 < l1 ? 1 : s1.compareTo(s2));
};
@Override
public String doIntercept(ActionInvocation invocation) throws Exception {
Object action = invocation.getAction();
if (!(action instanceof NoParameters)) {
ActionContext ac = invocation.getInvocationContext();
HttpParameters parameters = retrieveParameters(ac);
if (LOG.isDebugEnabled()) {
LOG.debug("Setting params {}", getParameterLogMap(parameters));
}
if (parameters != null) {
Map<String, Object> contextMap = ac.getContextMap();
try {
ReflectionContextState.setCreatingNullObjects(contextMap, true);
ReflectionContextState.setDenyMethodExecution(contextMap, true);
ReflectionContextState.setReportingConversionErrors(contextMap, true);
ValueStack stack = ac.getValueStack();
setParameters(action, stack, parameters);
} finally {
ReflectionContextState.setCreatingNullObjects(contextMap, false);
ReflectionContextState.setDenyMethodExecution(contextMap, false);
ReflectionContextState.setReportingConversionErrors(contextMap, false);
}
}
}
return invocation.invoke();
}
/**
* Gets the parameter map to apply from wherever appropriate
*
* @param ac The action context
* @return The parameter map to apply
*/
protected HttpParameters retrieveParameters(ActionContext ac) {
return ac.getParameters();
}
/**
* Adds the parameters into context's ParameterMap
*
* @param ac The action context
* @param newParams The parameter map to apply
* <p>
* In this class this is a no-op, since the parameters were fetched from the same location.
* In subclasses both retrieveParameters() and addParametersToContext() should be overridden.
* </p>
*/
protected void addParametersToContext(ActionContext ac, Map<String, ?> newParams) {
}
protected void setParameters(final Object action, ValueStack stack, HttpParameters parameters) {
HttpParameters params;
Map<String, Parameter> acceptableParameters;
if (ordered) {
params = HttpParameters.create().withComparator(getOrderedComparator()).withParent(parameters).build();
acceptableParameters = new TreeMap<>(getOrderedComparator());
} else {
params = HttpParameters.create().withParent(parameters).build();
acceptableParameters = new TreeMap<>();
}
for (Map.Entry<String, Parameter> entry : params.entrySet()) {
String parameterName = entry.getKey();
boolean isAcceptableParameter = isAcceptableParameter(parameterName, action);
isAcceptableParameter &= isAcceptableParameterValue(entry.getValue(), action);
if (isAcceptableParameter) {
acceptableParameters.put(parameterName, entry.getValue());
}
}
ValueStack newStack = valueStackFactory.createValueStack(stack);
boolean clearableStack = newStack instanceof ClearableValueStack;
if (clearableStack) {
//if the stack's context can be cleared, do that to prevent OGNL
//from having access to objects in the stack, see XW-641
((ClearableValueStack) newStack).clearContextValues();
Map<String, Object> context = newStack.getContext();
ReflectionContextState.setCreatingNullObjects(context, true);
ReflectionContextState.setDenyMethodExecution(context, true);
ReflectionContextState.setReportingConversionErrors(context, true);
//keep locale from original context
newStack.getActionContext().withLocale(stack.getActionContext().getLocale()).withValueStack(stack);
}
boolean memberAccessStack = newStack instanceof MemberAccessValueStack;
if (memberAccessStack) {
//block or allow access to properties
//see WW-2761 for more details
MemberAccessValueStack accessValueStack = (MemberAccessValueStack) newStack;
accessValueStack.setAcceptProperties(acceptedPatterns.getAcceptedPatterns());
accessValueStack.setExcludeProperties(excludedPatterns.getExcludedPatterns());
}
for (Map.Entry<String, Parameter> entry : acceptableParameters.entrySet()) {
String name = entry.getKey();
Parameter value = entry.getValue();
try {
newStack.setParameter(name, value.getObject());
} catch (RuntimeException e) {
if (devMode) {
notifyDeveloperParameterException(action, name, e.getMessage());
}
}
}
if (clearableStack) {
stack.getActionContext().withConversionErrors(newStack.getActionContext().getConversionErrors());
}
addParametersToContext(ActionContext.getContext(), acceptableParameters);
}
protected void notifyDeveloperParameterException(Object action, String property, String message) {
String developerNotification = "Unexpected Exception caught setting '" + property + "' on '" + action.getClass() + ": " + message;
if (action instanceof TextProvider) {
TextProvider tp = (TextProvider) action;
developerNotification = tp.getText("devmode.notification",
"Developer Notification:\n{0}",
new String[]{developerNotification}
);
}
LOG.error(developerNotification);
if (action instanceof ValidationAware) {
// see https://issues.apache.org/jira/browse/WW-4066
Collection<String> messages = ((ValidationAware) action).getActionMessages();
messages.add(message);
((ValidationAware) action).setActionMessages(messages);
}
}
/**
* Checks if name of parameter can be accepted or thrown away
*
* @param name parameter name
* @param action current action
* @return true if parameter is accepted
*/
protected boolean isAcceptableParameter(String name, Object action) {
ParameterNameAware parameterNameAware = (action instanceof ParameterNameAware) ? (ParameterNameAware) action : null;
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
* action.
*
* @return A comparator to sort the parameters
*/
protected Comparator<String> getOrderedComparator() {
return rbCollator;
}
protected String getParameterLogMap(HttpParameters parameters) {
if (parameters == null) {
return "NONE";
}
StringBuilder logEntry = new StringBuilder();
for (Map.Entry<String, Parameter> entry : parameters.entrySet()) {
logEntry.append(entry.getKey());
logEntry.append(" => ");
logEntry.append(entry.getValue().getValue());
logEntry.append(" ");
}
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);
return false;
}
boolean accepted = isWithinLengthLimit(name) && !isExcluded(name) && isAccepted(name);
if (devMode && accepted) { // notify only when in devMode
LOG.debug("Parameter [{}] was accepted and will be appended to action!", name);
}
return accepted;
}
private boolean isIgnoredDMI(String name) {
if (dmiEnabled) {
return DMI_IGNORED_PATTERN.matcher(name).matches();
} else {
return false;
}
}
/**
* 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);
} else {
LOG.warn("Parameter [{}] is too long, allowed length is [{}]", name, paramNameMaxLength);
}
}
return matchLength;
}
protected boolean isAccepted(String paramName) {
AcceptedPatternsChecker.IsAccepted result = acceptedPatterns.isAccepted(paramName);
if (result.isAccepted()) {
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());
} else {
LOG.debug("Parameter [{}] didn't match accepted pattern [{}]!", paramName, result.getAcceptedPattern());
}
return false;
}
protected boolean isExcluded(String paramName) {
ExcludedPatternsChecker.IsExcluded result = excludedPatterns.isExcluded(paramName);
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());
} else {
LOG.debug("Parameter [{}] matches excluded pattern [{}]!", paramName, result.getExcludedPattern());
}
return true;
}
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
*
* @return True to order
*/
public boolean isOrdered() {
return ordered;
}
/**
* Set whether to order the parameters by object depth or not
*
* @param ordered True to order them
*/
public void setOrdered(boolean ordered) {
this.ordered = ordered;
}
/**
* Sets a comma-delimited list of regular expressions to match
* parameters that are allowed in the parameter map (aka whitelist).
* <p>
* Don't change the default unless you know what you are doing in terms
* of security implications.
* </p>
*
* @param commaDelim A comma-delimited list of regular expressions
*/
public void setAcceptParamNames(String commaDelim) {
acceptedPatterns.setAcceptedPatterns(commaDelim);
}
/**
* Sets a comma-delimited list of regular expressions to match
* parameters that should be removed from the parameter map.
*
* @param commaDelim A comma-delimited list of regular expressions
*/
public void setExcludeParams(String commaDelim) {
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);
}
}
}
@@ -236,6 +236,6 @@ public class StaticParametersInterceptor extends AbstractInterceptor {
combinedParams = HttpParameters.create(newParams);
combinedParams = combinedParams.withExtraParams(previousParams);
}
ac.withParameters(combinedParams.build());
ac.setParameters(combinedParams.build());
}
}
@@ -104,7 +104,7 @@ public class AnnotationParameterFilterInterceptor extends AbstractInterceptor {
}
}
invocation.getInvocationContext().withParameters(parameters);
invocation.getInvocationContext().setParameters(parameters);
return invocation.invoke();
}
@@ -16,7 +16,6 @@
package com.opensymphony.xwork2.ognl;
import com.opensymphony.xwork2.inject.Inject;
import org.apache.commons.lang3.EnumUtils;
import org.apache.struts2.StrutsConstants;
/**
@@ -30,16 +29,16 @@ import org.apache.struts2.StrutsConstants;
public class DefaultOgnlBeanInfoCacheFactory<Key, Value> extends DefaultOgnlCacheFactory<Key, Value>
implements BeanInfoCacheFactory<Key, Value> {
/**
* @deprecated since 6.4.0, use {@link #DefaultOgnlBeanInfoCacheFactory(String, String)}
*/
@Deprecated
public DefaultOgnlBeanInfoCacheFactory() {
@Override
@Inject(value = StrutsConstants.STRUTS_OGNL_BEANINFO_CACHE_MAXSIZE, required = false)
protected void setCacheMaxSize(String maxSize) {
super.setCacheMaxSize(maxSize);
}
@Inject
public DefaultOgnlBeanInfoCacheFactory(@Inject(value = StrutsConstants.STRUTS_OGNL_BEANINFO_CACHE_MAXSIZE) String cacheMaxSize,
@Inject(value = StrutsConstants.STRUTS_OGNL_BEANINFO_CACHE_TYPE) String defaultCacheType) {
super(Integer.parseInt(cacheMaxSize), EnumUtils.getEnumIgnoreCase(CacheType.class, defaultCacheType));
@Override
@Inject(value = StrutsConstants.STRUTS_OGNL_BEANINFO_CACHE_LRU_MODE, required = false)
protected void setUseLRUCache(String useLRUMode) {
super.setUseLRUCache(useLRUMode);
}
}
@@ -15,86 +15,52 @@
*/
package com.opensymphony.xwork2.ognl;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.commons.lang3.BooleanUtils;
/**
* <p>Default OGNL Cache factory implementation.</p>
* Default OGNL Cache factory implementation.
*
* <p>Currently used for Expression cache and BeanInfo cache creation.</p>
* Currently used for Expression cache and BeanInfo cache creation.
*
* @param <Key> The type for the cache key entries
* @param <Key> The type for the cache key entries
* @param <Value> The type for the cache value entries
*/
public class DefaultOgnlCacheFactory<Key, Value> implements OgnlCacheFactory<Key, Value> {
private static final int DEFAULT_INIT_CAPACITY = 16;
private static final float DEFAULT_LOAD_FACTOR = 0.75f;
private CacheType defaultCacheType;
private int cacheMaxSize;
/**
* @deprecated since 6.4.0, use {@link #DefaultOgnlCacheFactory(int, CacheType)}
*/
@Deprecated
public DefaultOgnlCacheFactory() {
this(10000, CacheType.BASIC);
}
public DefaultOgnlCacheFactory(int cacheMaxSize, CacheType defaultCacheType) {
this.cacheMaxSize = cacheMaxSize;
this.defaultCacheType = defaultCacheType;
}
private final AtomicBoolean useLRUCache = new AtomicBoolean(false);
private final AtomicInteger cacheMaxSize = new AtomicInteger(25000);
@Override
public OgnlCache<Key, Value> buildOgnlCache() {
return buildOgnlCache(getCacheMaxSize(), DEFAULT_INIT_CAPACITY, DEFAULT_LOAD_FACTOR, defaultCacheType);
return buildOgnlCache(getCacheMaxSize(), 16, 0.75f, getUseLRUCache());
}
@Override
public OgnlCache<Key, Value> buildOgnlCache(int evictionLimit,
int initialCapacity,
float loadFactor,
CacheType cacheType) {
switch (cacheType) {
case BASIC:
return new OgnlDefaultCache<>(evictionLimit, initialCapacity, loadFactor);
case LRU:
return new OgnlLRUCache<>(evictionLimit, initialCapacity, loadFactor);
case WTLFU:
return new OgnlCaffeineCache<>(evictionLimit, initialCapacity);
default:
throw new IllegalArgumentException("Unknown cache type: " + cacheType);
public OgnlCache<Key, Value> buildOgnlCache(int evictionLimit, int initialCapacity, float loadFactor, boolean lruCache) {
if (lruCache) {
return new OgnlLRUCache<>(evictionLimit, initialCapacity, loadFactor);
} else {
return new OgnlDefaultCache<>(evictionLimit, initialCapacity, loadFactor);
}
}
@Override
public int getCacheMaxSize() {
return cacheMaxSize;
return cacheMaxSize.get();
}
/**
* @deprecated since 6.4.0
*/
@Deprecated
protected void setCacheMaxSize(String maxSize) {
cacheMaxSize = Integer.parseInt(maxSize);
cacheMaxSize.set(Integer.parseInt(maxSize));
}
@Override
public CacheType getDefaultCacheType() {
return defaultCacheType;
public boolean getUseLRUCache() {
return useLRUCache.get();
}
/**
* No effect when {@code useLRUMode} is {@code false}
*
* @deprecated since 6.4.0
*/
@Deprecated
protected void setUseLRUCache(String useLRUMode) {
if (BooleanUtils.toBoolean(useLRUMode)) {
defaultCacheType = CacheType.LRU;
}
useLRUCache.set(BooleanUtils.toBoolean(useLRUMode));
}
}
@@ -16,30 +16,29 @@
package com.opensymphony.xwork2.ognl;
import com.opensymphony.xwork2.inject.Inject;
import org.apache.commons.lang3.EnumUtils;
import org.apache.struts2.StrutsConstants;
/**
* Default OGNL Expression Cache factory implementation.
* <p>
*
* Currently used for Expression cache creation.
*
* @param <Key> The type for the cache key entries
* @param <Key> The type for the cache key entries
* @param <Value> The type for the cache value entries
*/
public class DefaultOgnlExpressionCacheFactory<Key, Value> extends DefaultOgnlCacheFactory<Key, Value>
implements ExpressionCacheFactory<Key, Value> {
implements ExpressionCacheFactory<Key, Value> {
/**
* @deprecated since 6.4.0, use {@link #DefaultOgnlExpressionCacheFactory(String, String)}
*/
@Deprecated
public DefaultOgnlExpressionCacheFactory() {
@Override
@Inject(value = StrutsConstants.STRUTS_OGNL_EXPRESSION_CACHE_MAXSIZE, required = false)
protected void setCacheMaxSize(String maxSize) {
super.setCacheMaxSize(maxSize);
}
@Inject
public DefaultOgnlExpressionCacheFactory(@Inject(value = StrutsConstants.STRUTS_OGNL_EXPRESSION_CACHE_MAXSIZE) String cacheMaxSize,
@Inject(value = StrutsConstants.STRUTS_OGNL_EXPRESSION_CACHE_TYPE) String defaultCacheType) {
super(Integer.parseInt(cacheMaxSize), EnumUtils.getEnumIgnoreCase(CacheType.class, defaultCacheType));
@Override
@Inject(value = StrutsConstants.STRUTS_OGNL_EXPRESSION_CACHE_LRU_MODE, required = false)
protected void setUseLRUCache(String useLRUMode) {
super.setUseLRUCache(useLRUMode);
}
}
@@ -19,23 +19,23 @@ package com.opensymphony.xwork2.ognl;
* A basic cache interface for use with OGNL processing (such as Expression, BeanInfo).
* All OGNL caches will have an eviction limit, but setting an extremely high value can
* simulate an "effectively unlimited" cache.
*
*
* @param <Key> The type for the cache key entries
* @param <Value> The type for the cache value entries
*/
public interface OgnlCache<Key, Value> {
Value get(Key key);
public Value get(Key key);
void put(Key key, Value value);
public void put(Key key, Value value);
void putIfAbsent(Key key, Value value);
public void putIfAbsent(Key key, Value value);
int size();
public int size();
void clear();
public void clear();
int getEvictionLimit();
public int getEvictionLimit();
void setEvictionLimit(int cacheEvictionLimit);
public void setEvictionLimit(int cacheEvictionLimit);
}
@@ -19,52 +19,12 @@ package com.opensymphony.xwork2.ognl;
* Used by {@link com.opensymphony.xwork2.ognl.OgnlUtil} to create appropriate OGNL
* caches based on configuration.
*
* @param <Key> The type for the cache key entries
* @param <Key> The type for the cache key entries
* @param <Value> The type for the cache value entries
*/
public interface OgnlCacheFactory<Key, Value> {
interface OgnlCacheFactory<Key, Value> {
OgnlCache<Key, Value> buildOgnlCache();
/**
* Note that if {@code lruCache} is {@code false}, the cache type could still be LRU if the default cache type is
* configured as such.
* @deprecated since 6.4.0, use {@link #buildOgnlCache(int, int, float, CacheType)}
*/
@Deprecated
default OgnlCache<Key, Value> buildOgnlCache(int evictionLimit,
int initialCapacity,
float loadFactor,
boolean lruCache) {
return buildOgnlCache(evictionLimit,
initialCapacity,
loadFactor,
lruCache ? CacheType.LRU : getDefaultCacheType());
}
/**
* @param evictionLimit maximum capacity of the cache where applicable for cache type chosen
* @param initialCapacity initial capacity of the cache where applicable for cache type chosen
* @param loadFactor load factor of the cache where applicable for cache type chosen
* @param cacheType type of cache to build
* @return a new cache instance
*/
OgnlCache<Key, Value> buildOgnlCache(int evictionLimit, int initialCapacity, float loadFactor, CacheType cacheType);
OgnlCache<Key, Value> buildOgnlCache(int evictionLimit, int initialCapacity, float loadFactor, boolean lruCache);
int getCacheMaxSize();
/**
* @deprecated since 6.4.0
*/
@Deprecated
default boolean getUseLRUCache() {
return CacheType.LRU.equals(getDefaultCacheType());
}
CacheType getDefaultCacheType();
enum CacheType {
BASIC,
LRU,
WTLFU
}
boolean getUseLRUCache();
}
@@ -1,78 +0,0 @@
/*
* Copyright 2022 Apache Software Foundation.
*
* Licensed 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.ognl;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
/**
* <p>This OGNL Cache implementation is backed by {@link Caffeine} which uses the Window TinyLfu algorithm.</p>
*
* <p>An appropriate eviction limit should be chosen for your specific application based on factors and requirements
* such as:</p>
* <ul>
* <li>Quantity and complexity of actions</li>
* <li>Volume of requests</li>
* <li>Rate limits and attack potential/patterns</li>
* <li>Memory constraints</li>
* </ul>
*
* @param <K> The type for the cache key entries
* @param <V> The type for the cache value entries
*/
public class OgnlCaffeineCache<K, V> implements OgnlCache<K, V> {
private final Cache<K, V> cache;
public OgnlCaffeineCache(int evictionLimit, int initialCapacity) {
this.cache = Caffeine.newBuilder().initialCapacity(initialCapacity).maximumSize(evictionLimit).build();
}
@Override
public V get(K key) {
return cache.getIfPresent(key);
}
@Override
public void put(K key, V value) {
cache.put(key, value);
}
@Override
public void putIfAbsent(K key, V value) {
cache.asMap().putIfAbsent(key, value);
}
@Override
public int size() {
return cache.asMap().size();
}
@Override
public void clear() {
cache.invalidateAll();
}
@Override
public int getEvictionLimit() {
return Math.toIntExact(cache.policy().eviction().orElseThrow(IllegalStateException::new).getMaximum());
}
@Override
public void setEvictionLimit(int cacheEvictionLimit) {
cache.policy().eviction().orElseThrow(IllegalStateException::new).setMaximum(cacheEvictionLimit);
}
}
@@ -19,40 +19,37 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
* <p>Basic OGNL cache implementation.</p>
*
* <p>This implementation is backed by a {@link ConcurrentHashMap} that is cleared whenever the eviction limit is
* surpassed.</p>
*
* <p>Setting a very high eviction limit simulates an unlimited cache.</p>
* <p>Setting too low an eviction limit will make the cache ineffective.</p>
*
* @param <K> The type for the cache key entries
* @param <V> The type for the cache value entries
* Default OGNL cache implementation.
*
* Setting a very high eviction limit simulates an unlimited cache.
* Setting too low an eviction limit will make the cache ineffective.
*
* @param <Key> The type for the cache key entries
* @param <Value> The type for the cache value entries
*/
public class OgnlDefaultCache<K, V> implements OgnlCache<K, V> {
public class OgnlDefaultCache<Key, Value> implements OgnlCache<Key, Value> {
private final ConcurrentHashMap<K, V> ognlCache;
private final AtomicInteger cacheEvictionLimit;
private final ConcurrentHashMap<Key, Value> ognlCache;
private final AtomicInteger cacheEvictionLimit = new AtomicInteger(25000);
public OgnlDefaultCache(int evictionLimit, int initialCapacity, float loadFactor) {
cacheEvictionLimit = new AtomicInteger(evictionLimit);
this.cacheEvictionLimit.set(evictionLimit);
ognlCache = new ConcurrentHashMap<>(initialCapacity, loadFactor);
}
@Override
public V get(K key) {
public Value get(Key key) {
return ognlCache.get(key);
}
@Override
public void put(K key, V value) {
public void put(Key key, Value value) {
ognlCache.put(key, value);
this.clearIfEvictionLimitExceeded();
}
@Override
public void putIfAbsent(K key, V value) {
public void putIfAbsent(Key key, Value value) {
ognlCache.putIfAbsent(key, value);
this.clearIfEvictionLimitExceeded();
}
@@ -21,46 +21,46 @@ import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
/**
* <p>A basic OGNL LRU cache implementation.</p>
*
* <p>The implementation utilizes a {@link Collections#synchronizedMap(java.util.Map)}
* backed by a {@link LinkedHashMap}. May be replaced by a more efficient implementation in the future.</p>
*
* <p>Setting too low an eviction limit will produce more overhead than value.</p>
* <p>Setting too high an eviction limit may also produce more overhead than value.</p>
* <p>An appropriate eviction limit will need to be determined on an individual application basis.</p>
*
* @param <K> The type for the cache key entries
* @param <V> The type for the cache value entries
* A basic OGNL LRU cache implementation.
*
* The implementation utilizes a {@link Collections#synchronizedMap(java.util.Map)}
* backed by a {@link LinkedHashMap}. May be replaced by a more efficient implementation in the future.
*
* Setting too low an eviction limit will produce more overhead than value.
* Setting too high an eviction limit may also produce more overhead than value.
* An appropriate eviction limit will need to be determined on an individual application basis.
*
* @param <Key> The type for the cache key entries
* @param <Value> The type for the cache value entries
*/
public class OgnlLRUCache<K, V> implements OgnlCache<K, V> {
public class OgnlLRUCache<Key, Value> implements OgnlCache<Key, Value> {
private final Map<K, V> ognlLRUCache;
private final AtomicInteger cacheEvictionLimit;
private final Map<Key, Value> ognlLRUCache;
private final AtomicInteger cacheEvictionLimit = new AtomicInteger(2500);
public OgnlLRUCache(int evictionLimit, int initialCapacity, float loadFactor) {
cacheEvictionLimit = new AtomicInteger(evictionLimit);
this.cacheEvictionLimit.set(evictionLimit);
// Access-order mode selected (order mode true in LinkedHashMap constructor).
ognlLRUCache = Collections.synchronizedMap(new LinkedHashMap<K, V>(initialCapacity, loadFactor, true) {
ognlLRUCache = Collections.synchronizedMap (new LinkedHashMap<Key, Value>(initialCapacity, loadFactor, true) {
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > cacheEvictionLimit.get();
protected boolean removeEldestEntry(Map.Entry<Key,Value> eldest) {
return (this.size() > cacheEvictionLimit.get());
}
});
}
@Override
public V get(K key) {
public Value get(Key key) {
return ognlLRUCache.get(key);
}
@Override
public void put(K key, V value) {
public void put(Key key, Value value) {
ognlLRUCache.put(key, value);
}
@Override
public void putIfAbsent(K key, V value) {
public void putIfAbsent(Key key, Value value) {
ognlLRUCache.putIfAbsent(key, value);
}
@@ -81,9 +81,7 @@ public class OgnlLRUCache<K, V> implements OgnlCache<K, V> {
@Override
public void setEvictionLimit(int cacheEvictionLimit) {
if (cacheEvictionLimit < size()) {
clear();
}
this.cacheEvictionLimit.set(cacheEvictionLimit);
}
}
@@ -21,6 +21,7 @@ package com.opensymphony.xwork2.ognl;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.reflection.ReflectionException;
import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
import ognl.Ognl;
import ognl.OgnlException;
import ognl.OgnlRuntime;
@@ -32,9 +33,9 @@ import java.util.Collection;
import java.util.Map;
public class OgnlReflectionProvider implements ReflectionProvider {
private OgnlUtil ognlUtil;
@Inject
public void setOgnlUtil(OgnlUtil ognlUtil) {
this.ognlUtil = ognlUtil;
@@ -68,6 +69,7 @@ public class OgnlReflectionProvider implements ReflectionProvider {
public void setProperties(Map<String, ?> props, Object o, Map<String, Object> context, boolean throwPropertyExceptions) throws ReflectionException{
ognlUtil.setProperties(props, o, context, throwPropertyExceptions);
}
public void setProperties(Map<String, ?> properties, Object o) {
@@ -132,7 +134,7 @@ public class OgnlReflectionProvider implements ReflectionProvider {
public void setValue(String expression, Map<String, Object> context, Object root,
Object value) throws ReflectionException {
try {
ognlUtil.setValue(expression, context, root, value);
Ognl.setValue(expression, context, root, value);
} catch (OgnlException e) {
throw new ReflectionException(e);
}
@@ -18,45 +18,29 @@
*/
package com.opensymphony.xwork2.ognl;
import com.opensymphony.xwork2.config.ConfigurationException;
import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.ognl.accessor.RootAccessor;
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.ClassResolver;
import ognl.Ognl;
import ognl.OgnlContext;
import ognl.OgnlException;
import ognl.OgnlRuntime;
import ognl.SimpleNode;
import ognl.TypeConverter;
import ognl.*;
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.ognl.OgnlGuard;
import org.apache.struts2.ognl.StrutsOgnlGuard;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Pattern;
import static com.opensymphony.xwork2.util.ConfigParseUtil.toClassesSet;
import static com.opensymphony.xwork2.util.ConfigParseUtil.toNewPatternsSet;
import static com.opensymphony.xwork2.util.ConfigParseUtil.toPackageNamesSet;
import static java.util.Collections.emptySet;
import static java.util.Objects.requireNonNull;
import static org.apache.struts2.ognl.OgnlGuard.EXPR_BLOCKED;
/**
* Utility class that provides common access to the Ognl APIs for
@@ -74,46 +58,67 @@ public class OgnlUtil {
private final OgnlCache<String, Object> expressionCache;
private final OgnlCache<Class<?>, BeanInfo> beanInfoCache;
private TypeConverter defaultConverter;
private final OgnlGuard ognlGuard;
private boolean devMode;
private boolean enableExpressionCache = true;
private boolean enableEvalExpression;
private String devModeExcludedClasses = "";
private String devModeExcludedPackageNamePatterns = "";
private String devModeExcludedPackageNames = "";
private String devModeExcludedPackageExemptClasses = "";
private Set<Class<?>> excludedClasses;
private Set<Pattern> excludedPackageNamePatterns;
private Set<String> excludedPackageNames;
private Set<Class<?>> devModeExcludedClasses;
private Set<Pattern> devModeExcludedPackageNamePatterns;
private Set<String> devModeExcludedPackageNames;
private Container container;
private boolean allowStaticFieldAccess = true;
private boolean disallowProxyMemberAccess;
/**
* Construct a new OgnlUtil instance for use with the framework
*
* @deprecated since 6.0.0. Use {@link #OgnlUtil(ExpressionCacheFactory, BeanInfoCacheFactory, OgnlGuard) instead.
* @deprecated It is recommended to utilize the {@link OgnlUtil#OgnlUtil(com.opensymphony.xwork2.ognl.ExpressionCacheFactory, com.opensymphony.xwork2.ognl.BeanInfoCacheFactory) method instead.
*/
@Deprecated
public OgnlUtil() {
this(new DefaultOgnlExpressionCacheFactory<>(),
new DefaultOgnlBeanInfoCacheFactory<>(),
new StrutsOgnlGuard());
// Instantiate default Expression and BeanInfo caches (factories must be non-null).
this(new DefaultOgnlExpressionCacheFactory<String, Object>(),
new DefaultOgnlBeanInfoCacheFactory<Class<?>, BeanInfo>());
}
/**
* Construct a new OgnlUtil instance for use with the framework, with optional cache factories for OGNL Expression
* and BeanInfo caches.
* Construct a new OgnlUtil instance for use with the framework, with optional
* cache factories for OGNL Expression and BeanInfo caches.
*
* @param ognlExpressionCacheFactory factory for Expression cache instance
* @param ognlBeanInfoCacheFactory factory for BeanInfo cache instance
* @param ognlGuard OGNL Guard instance
* NOTE: Although the extension points are defined for the optional cache factories, developer-defined overrides do
* do not appear to function at this time (it always appears to instantiate the default factories).
* Construction injectors do not allow the optional flag, so the definitions must be defined.
*
* @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 ExpressionCacheFactory<String, Object> ognlExpressionCacheFactory,
@Inject BeanInfoCacheFactory<Class<?>, BeanInfo> ognlBeanInfoCacheFactory,
@Inject OgnlGuard ognlGuard) {
this.expressionCache = requireNonNull(ognlExpressionCacheFactory).buildOgnlCache();
this.beanInfoCache = requireNonNull(ognlBeanInfoCacheFactory).buildOgnlCache();
this.ognlGuard = requireNonNull(ognlGuard);
public OgnlUtil(
@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<>());
devModeExcludedClasses = Collections.unmodifiableSet(new HashSet<>());
devModeExcludedPackageNamePatterns = Collections.unmodifiableSet(new HashSet<>());
devModeExcludedPackageNames = Collections.unmodifiableSet(new HashSet<>());
this.expressionCache = ognlExpressionCacheFactory.buildOgnlCache();
this.beanInfoCache = ognlBeanInfoCacheFactory.buildOgnlCache();
}
@Inject
@@ -126,23 +131,17 @@ public class OgnlUtil {
this.devMode = BooleanUtils.toBoolean(mode);
}
@Inject(value = StrutsConstants.STRUTS_OGNL_ENABLE_EXPRESSION_CACHE, required = false)
@Inject(StrutsConstants.STRUTS_OGNL_ENABLE_EXPRESSION_CACHE)
protected void setEnableExpressionCache(String cache) {
enableExpressionCache = BooleanUtils.toBoolean(cache);
}
/**
* @deprecated since 6.4.0, changing maximum cache size after initialisation is not necessary.
*/
@Deprecated
@Inject(value = StrutsConstants.STRUTS_OGNL_EXPRESSION_CACHE_MAXSIZE, required = false)
protected void setExpressionCacheMaxSize(String maxSize) {
expressionCache.setEvictionLimit(Integer.parseInt(maxSize));
}
/**
* @deprecated since 6.4.0, changing maximum cache size after initialisation is not necessary.
*/
@Deprecated
@Inject(value = StrutsConstants.STRUTS_OGNL_BEANINFO_CACHE_MAXSIZE, required = false)
protected void setBeanInfoCacheMaxSize(String maxSize) {
beanInfoCache.setEvictionLimit(Integer.parseInt(maxSize));
}
@@ -156,88 +155,94 @@ public class OgnlUtil {
}
}
/**
* @deprecated since 6.4.0, no replacement.
*/
@Deprecated
@Inject(value = StrutsConstants.STRUTS_EXCLUDED_CLASSES, required = false)
protected void setExcludedClasses(String commaDelimitedClasses) {
// Must be set directly on SecurityMemberAccess
Set<Class<?>> excludedClasses = new HashSet<>();
excludedClasses.addAll(this.excludedClasses);
excludedClasses.addAll(parseExcludedClasses(commaDelimitedClasses));
this.excludedClasses = Collections.unmodifiableSet(excludedClasses);
}
@Inject(value = StrutsConstants.STRUTS_DEV_MODE_EXCLUDED_CLASSES, required = false)
protected void setDevModeExcludedClasses(String commaDelimitedClasses) {
this.devModeExcludedClasses = commaDelimitedClasses;
Set<Class<?>> excludedClasses = new HashSet<>();
excludedClasses.addAll(this.devModeExcludedClasses);
excludedClasses.addAll(parseExcludedClasses(commaDelimitedClasses));
this.devModeExcludedClasses = Collections.unmodifiableSet(excludedClasses);
}
/**
* @deprecated since 6.4.0, no replacement.
*/
@Deprecated
private Set<Class<?>> parseExcludedClasses(String commaDelimitedClasses) {
Set<String> classNames = TextParseUtil.commaDelimitedStringToSet(commaDelimitedClasses);
Set<Class<?>> classes = new HashSet<>();
for (String className : classNames) {
try {
classes.add(Class.forName(className));
} catch (ClassNotFoundException e) {
throw new ConfigurationException("Cannot load excluded class: " + className, e);
}
}
return classes;
}
@Inject(value = StrutsConstants.STRUTS_EXCLUDED_PACKAGE_NAME_PATTERNS, required = false)
protected void setExcludedPackageNamePatterns(String commaDelimitedPackagePatterns) {
// Must be set directly on SecurityMemberAccess
Set<Pattern> excludedPackageNamePatterns = new HashSet<>();
excludedPackageNamePatterns.addAll(this.excludedPackageNamePatterns);
excludedPackageNamePatterns.addAll(parseExcludedPackageNamePatterns(commaDelimitedPackagePatterns));
this.excludedPackageNamePatterns = Collections.unmodifiableSet(excludedPackageNamePatterns);
}
@Inject(value = StrutsConstants.STRUTS_DEV_MODE_EXCLUDED_PACKAGE_NAME_PATTERNS, required = false)
protected void setDevModeExcludedPackageNamePatterns(String commaDelimitedPackagePatterns) {
this.devModeExcludedPackageNamePatterns = commaDelimitedPackagePatterns;
Set<Pattern> excludedPackageNamePatterns = new HashSet<>();
excludedPackageNamePatterns.addAll(this.devModeExcludedPackageNamePatterns);
excludedPackageNamePatterns.addAll(parseExcludedPackageNamePatterns(commaDelimitedPackagePatterns));
this.devModeExcludedPackageNamePatterns = Collections.unmodifiableSet(excludedPackageNamePatterns);
}
/**
* @deprecated since 6.4.0, no replacement.
*/
@Deprecated
private Set<Pattern> parseExcludedPackageNamePatterns(String commaDelimitedPackagePatterns) {
Set<String> packagePatterns = TextParseUtil.commaDelimitedStringToSet(commaDelimitedPackagePatterns);
Set<Pattern> packageNamePatterns = new HashSet<>();
for (String pattern : packagePatterns) {
packageNamePatterns.add(Pattern.compile(pattern));
}
return packageNamePatterns;
}
@Inject(value = StrutsConstants.STRUTS_EXCLUDED_PACKAGE_NAMES, required = false)
protected void setExcludedPackageNames(String commaDelimitedPackageNames) {
// Must be set directly on SecurityMemberAccess
Set<String> excludedPackageNames = new HashSet<>();
excludedPackageNames.addAll(this.excludedPackageNames);
excludedPackageNames.addAll(parseExcludedPackageNames(commaDelimitedPackageNames));
this.excludedPackageNames = Collections.unmodifiableSet(excludedPackageNames);
}
@Inject(value = StrutsConstants.STRUTS_DEV_MODE_EXCLUDED_PACKAGE_NAMES, required = false)
protected void setDevModeExcludedPackageNames(String commaDelimitedPackageNames) {
this.devModeExcludedPackageNames = commaDelimitedPackageNames;
Set<String> excludedPackageNames = new HashSet<>();
excludedPackageNames.addAll(this.devModeExcludedPackageNames);
excludedPackageNames.addAll(parseExcludedPackageNames(commaDelimitedPackageNames));
this.devModeExcludedPackageNames = Collections.unmodifiableSet(excludedPackageNames);
}
/**
* @deprecated since 6.4.0, no replacement.
*/
@Deprecated
public void setExcludedPackageExemptClasses(String commaDelimitedClasses) {
// Must be set directly on SecurityMemberAccess
private Set<String> parseExcludedPackageNames(String commaDelimitedPackageNames) {
return TextParseUtil.commaDelimitedStringToSet(commaDelimitedPackageNames);
}
@Inject(value = StrutsConstants.STRUTS_DEV_MODE_EXCLUDED_PACKAGE_EXEMPT_CLASSES, required = false)
public void setDevModeExcludedPackageExemptClasses(String commaDelimitedClasses) {
this.devModeExcludedPackageExemptClasses = commaDelimitedClasses;
public Set<Class<?>> getExcludedClasses() {
return excludedClasses;
}
/**
* @deprecated since 6.4.0, no replacement.
*/
@Deprecated
public Set<String> getExcludedClasses() {
return toClassesSet(container.getInstance(String.class, StrutsConstants.STRUTS_EXCLUDED_CLASSES));
}
/**
* @deprecated since 6.4.0, no replacement.
*/
@Deprecated
public Set<Pattern> getExcludedPackageNamePatterns() {
return toNewPatternsSet(emptySet(), container.getInstance(String.class, StrutsConstants.STRUTS_EXCLUDED_PACKAGE_NAME_PATTERNS));
return excludedPackageNamePatterns;
}
/**
* @deprecated since 6.4.0, no replacement.
*/
@Deprecated
public Set<String> getExcludedPackageNames() {
return toPackageNamesSet(container.getInstance(String.class, StrutsConstants.STRUTS_EXCLUDED_PACKAGE_NAMES));
}
/**
* @deprecated since 6.4.0, no replacement.
*/
@Deprecated
public Set<String> getExcludedPackageExemptClasses() {
return toClassesSet(container.getInstance(String.class, StrutsConstants.STRUTS_EXCLUDED_PACKAGE_EXEMPT_CLASSES));
return excludedPackageNames;
}
@Inject
@@ -245,28 +250,14 @@ public class OgnlUtil {
this.container = container;
}
/**
* @deprecated since 6.4.0, no replacement.
*/
@Deprecated
@Inject(value = StrutsConstants.STRUTS_ALLOW_STATIC_FIELD_ACCESS, required = false)
protected void setAllowStaticFieldAccess(String allowStaticFieldAccess) {
// Must be set directly on SecurityMemberAccess
this.allowStaticFieldAccess = BooleanUtils.toBoolean(allowStaticFieldAccess);
}
/**
* @deprecated since 6.4.0, no replacement.
*/
@Deprecated
@Inject(value = StrutsConstants.STRUTS_DISALLOW_PROXY_MEMBER_ACCESS, required = false)
protected void setDisallowProxyMemberAccess(String disallowProxyMemberAccess) {
// Must be set directly on SecurityMemberAccess
}
/**
* @deprecated since 6.4.0, no replacement.
*/
@Deprecated
protected void setDisallowDefaultPackageAccess(String disallowDefaultPackageAccess) {
// Must be set directly on SecurityMemberAccess
this.disallowProxyMemberAccess = BooleanUtils.toBoolean(disallowProxyMemberAccess);
}
/**
@@ -288,20 +279,8 @@ public class OgnlUtil {
}
}
/**
* @deprecated since 6.4.0, no replacement.
*/
@Deprecated
public boolean isDisallowProxyMemberAccess() {
return BooleanUtils.toBoolean(container.getInstance(String.class, StrutsConstants.STRUTS_DISALLOW_PROXY_MEMBER_ACCESS));
}
/**
* @deprecated since 6.4.0, no replacement.
*/
@Deprecated
public boolean isDisallowDefaultPackageAccess() {
return BooleanUtils.toBoolean(container.getInstance(String.class, StrutsConstants.STRUTS_DISALLOW_DEFAULT_PACKAGE_ACCESS));
return disallowProxyMemberAccess;
}
/**
@@ -509,7 +488,8 @@ public class OgnlUtil {
}
/**
* Wrapper around Ognl#setValue
* Wrapper around Ognl.setValue() to handle type conversion for collection elements.
* Ideally, this should be handled by OGNL directly.
*
* @param name the name
* @param context context map
@@ -519,7 +499,16 @@ public class OgnlUtil {
* @throws OgnlException in case of ognl errors
*/
public void setValue(final String name, final Map<String, Object> context, final Object root, final Object value) throws OgnlException {
ognlSet(name, context, root, value, context, this::checkEvalExpression, this::checkArithmeticExpression);
compileAndExecute(name, context, (OgnlTask<Void>) tree -> {
if (isEvalExpression(tree, context)) {
throw new OgnlException("Eval expression/chained expressions cannot be used as parameter name");
}
if (isArithmeticExpression(tree, context)) {
throw new OgnlException("Arithmetic expressions cannot be used as parameter name");
}
Ognl.setValue(tree, context, root, value);
return null;
});
}
private boolean isEvalExpression(Object tree, Map<String, Object> context) throws OgnlException {
@@ -562,58 +551,58 @@ public class OgnlUtil {
}
public Object getValue(final String name, final Map<String, Object> context, final Object root) throws OgnlException {
return getValue(name, context, root, null);
return compileAndExecute(name, context, tree -> Ognl.getValue(tree, context, root));
}
public Object callMethod(final String name, final Map<String, Object> context, final Object root) throws OgnlException {
return ognlGet(name, context, root, null, context, this::checkSimpleMethod);
return compileAndExecuteMethod(name, context, tree -> Ognl.getValue(tree, context, root));
}
public Object getValue(final String name, final Map<String, Object> context, final Object root, final Class<?> resultType) throws OgnlException {
return ognlGet(name, context, root, resultType, context, this::checkEnableEvalExpression);
return compileAndExecute(name, context, tree -> Ognl.getValue(tree, context, root, resultType));
}
public Object compile(String expression) throws OgnlException {
return compile(expression, null);
}
private void ognlSet(String expr, Map<String, Object> context, Object root, Object value, Map<String, Object> checkContext, TreeValidator... treeValidators) throws OgnlException {
Object tree = toTree(expr);
for (TreeValidator validator : treeValidators) {
validator.validate(tree, checkContext);
}
Ognl.setValue(tree, context, root, value);
}
private <T> T ognlGet(String expr, Map<String, Object> context, Object root, Class<T> resultType, Map<String, Object> checkContext, TreeValidator... treeValidators) throws OgnlException {
Object tree = toTree(expr);
for (TreeValidator validator : treeValidators) {
validator.validate(tree, checkContext);
}
return (T) Ognl.getValue(tree, context, root, resultType);
}
private Object toTree(String expr) throws OgnlException {
Object tree = null;
private <T> Object compileAndExecute(String expression, Map<String, Object> context, OgnlTask<T> task) throws OgnlException {
Object tree;
if (enableExpressionCache) {
tree = expressionCache.get(expr);
}
if (tree == null) {
tree = ognlGuard.parseExpression(expr);
if (enableExpressionCache) {
expressionCache.put(expr, tree);
tree = expressionCache.get(expression);
if (tree == null) {
tree = Ognl.parseExpression(expression);
checkEnableEvalExpression(tree, context);
expressionCache.putIfAbsent(expression, tree);
}
} else {
tree = Ognl.parseExpression(expression);
checkEnableEvalExpression(tree, context);
}
if (EXPR_BLOCKED.equals(tree)) {
throw new OgnlException("Expression blocked by OgnlGuard: " + expr);
return task.execute(tree);
}
private <T> Object compileAndExecuteMethod(String expression, Map<String, Object> context, OgnlTask<T> task) throws OgnlException {
Object tree;
if (enableExpressionCache) {
tree = expressionCache.get(expression);
if (tree == null) {
tree = Ognl.parseExpression(expression);
checkSimpleMethod(tree, context);
expressionCache.putIfAbsent(expression, tree);
}
} else {
tree = Ognl.parseExpression(expression);
checkSimpleMethod(tree, context);
}
return tree;
return task.execute(tree);
}
public Object compile(String expression, Map<String, Object> context) throws OgnlException {
Object tree = toTree(expression);
checkEnableEvalExpression(tree, context);
return tree;
return compileAndExecute(expression, context, tree -> tree);
}
private void checkEnableEvalExpression(Object tree, Map<String, Object> context) throws OgnlException {
@@ -628,18 +617,6 @@ public class OgnlUtil {
}
}
private void checkEvalExpression(Object tree, Map<String, Object> context) throws OgnlException {
if (isEvalExpression(tree, context)) {
throw new OgnlException("Eval expression/chained expressions cannot be used as parameter name");
}
}
private void checkArithmeticExpression(Object tree, Map<String, Object> context) throws OgnlException {
if (isArithmeticExpression(tree, context)) {
throw new OgnlException("Arithmetic expressions cannot be used as parameter name");
}
}
/**
* Copies the properties in the object "from" and sets them in the object "to"
* using specified type converter, or {@link com.opensymphony.xwork2.conversion.impl.XWorkConverter} if none
@@ -670,15 +647,9 @@ public class OgnlUtil {
* note if exclusions AND inclusions are supplied and not null nothing will get copied.
* @param editable the class (or interface) to restrict property setting to
*/
public void copy(final Object from,
final Object to,
final Map<String, Object> context,
Collection<String> exclusions,
Collection<String> inclusions,
Class<?> editable) {
public void copy(final Object from, final Object to, final Map<String, Object> context, Collection<String> exclusions, Collection<String> inclusions, Class<?> editable) {
if (from == null || to == null) {
LOG.warn(
"Skipping attempt to copy from, or to, a null source.", new RuntimeException());
LOG.warn("Attempting to copy from or to a null source. This is illegal and is bein skipped. This may be due to an error in an OGNL expression, action chaining, or some other event.");
return;
}
@@ -692,7 +663,8 @@ public class OgnlUtil {
fromPds = getPropertyDescriptors(from);
if (editable != null) {
toPds = getPropertyDescriptors(editable);
} else {
}
else {
toPds = getPropertyDescriptors(to);
}
} catch (IntrospectionException e) {
@@ -707,31 +679,33 @@ public class OgnlUtil {
}
for (PropertyDescriptor fromPd : fromPds) {
if (fromPd.getReadMethod() == null) {
continue;
if (fromPd.getReadMethod() != null) {
boolean copy = true;
if (exclusions != null && exclusions.contains(fromPd.getName())) {
copy = false;
} else if (inclusions != null && !inclusions.contains(fromPd.getName())) {
copy = false;
}
if (copy) {
PropertyDescriptor toPd = toPdHash.get(fromPd.getName());
if ((toPd != null) && (toPd.getWriteMethod() != null)) {
try {
compileAndExecute(fromPd.getName(), context, expr -> {
Object value = Ognl.getValue(expr, contextFrom, from);
Ognl.setValue(expr, contextTo, to, value);
return null;
});
} catch (OgnlException e) {
LOG.debug("Got OGNL exception", e);
}
}
}
}
if (exclusions != null && exclusions.contains(fromPd.getName()) ||
inclusions != null && !inclusions.contains(fromPd.getName())) {
continue;
}
PropertyDescriptor toPd = toPdHash.get(fromPd.getName());
if (toPd == null || toPd.getWriteMethod() == null) {
continue;
}
try {
Object value = ognlGet(fromPd.getName(),
contextFrom,
from,
null,
context,
this::checkEnableEvalExpression);
ognlSet(fromPd.getName(), contextTo, to, value, context);
} catch (OgnlException e) {
LOG.debug("Got OGNL exception", e);
}
}
}
@@ -750,7 +724,7 @@ public class OgnlUtil {
}
/**
* Gets the java beans property descriptors for the given source.
* Get's the java beans property descriptors for the given source.
*
* @param source the source object.
* @return property descriptors.
@@ -794,7 +768,7 @@ public class OgnlUtil {
final String propertyName = propertyDescriptor.getDisplayName();
Method readMethod = propertyDescriptor.getReadMethod();
if (readMethod != null) {
final Object value = ognlGet(propertyName, sourceMap, source, null, null, this::checkEnableEvalExpression);
final Object value = compileAndExecute(propertyName, null, expr -> Ognl.getValue(expr, sourceMap, source));
beanMap.put(propertyName, value);
} else {
beanMap.put(propertyName, "There is no read method for " + propertyName);
@@ -856,33 +830,34 @@ public class OgnlUtil {
return createDefaultContext(root, null);
}
protected Map<String, Object> createDefaultContext(Object root, ClassResolver resolver) {
protected Map<String, Object> createDefaultContext(Object root, ClassResolver classResolver) {
ClassResolver resolver = classResolver;
if (resolver == null) {
resolver = container.getInstance(RootAccessor.class);
if (resolver == null) {
throw new IllegalStateException("Cannot find ClassResolver");
}
resolver = container.getInstance(CompoundRootAccessor.class);
}
SecurityMemberAccess memberAccess = container.getInstance(SecurityMemberAccess.class);
memberAccess.useEnforceAllowlistEnabled(Boolean.FALSE.toString());
SecurityMemberAccess memberAccess = new SecurityMemberAccess(allowStaticFieldAccess);
memberAccess.setDisallowProxyMemberAccess(disallowProxyMemberAccess);
if (devMode) {
if (!warnReported.get()) {
warnReported.set(true);
LOG.warn("Working in devMode, using devMode excluded classes and packages!");
}
memberAccess.useExcludedClasses(devModeExcludedClasses);
memberAccess.useExcludedPackageNamePatterns(devModeExcludedPackageNamePatterns);
memberAccess.useExcludedPackageNames(devModeExcludedPackageNames);
memberAccess.useExcludedPackageExemptClasses(devModeExcludedPackageExemptClasses);
memberAccess.setExcludedClasses(devModeExcludedClasses);
memberAccess.setExcludedPackageNamePatterns(devModeExcludedPackageNamePatterns);
memberAccess.setExcludedPackageNames(devModeExcludedPackageNames);
} else {
memberAccess.setExcludedClasses(excludedClasses);
memberAccess.setExcludedPackageNamePatterns(excludedPackageNamePatterns);
memberAccess.setExcludedPackageNames(excludedPackageNames);
}
return Ognl.createDefaultContext(root, memberAccess, resolver, defaultConverter);
}
@FunctionalInterface
private interface TreeValidator {
void validate(Object tree, Map<String, Object> context) throws OgnlException;
private interface OgnlTask<T> {
T execute(Object tree) throws OgnlException;
}
}
@@ -24,17 +24,12 @@ import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.ognl.accessor.CompoundRootAccessor;
import com.opensymphony.xwork2.ognl.accessor.RootAccessor;
import com.opensymphony.xwork2.util.ClearableValueStack;
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.MethodFailedException;
import ognl.NoSuchPropertyException;
import ognl.Ognl;
import ognl.OgnlContext;
import ognl.OgnlException;
import ognl.*;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -76,93 +71,34 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
private transient XWorkConverter converter;
private boolean devMode;
private boolean logMissingProperties;
private boolean shouldFallbackToContext = true;
/**
* @since 6.4.0
*/
protected OgnlValueStack(ValueStack vs,
XWorkConverter xworkConverter,
RootAccessor accessor,
TextProvider prov,
SecurityMemberAccess securityMemberAccess) {
setRoot(xworkConverter,
accessor,
vs != null ? new CompoundRoot(vs.getRoot()) : new CompoundRoot(),
securityMemberAccess);
if (prov != null) {
push(prov);
}
}
/**
* @since 6.4.0
*/
protected OgnlValueStack(XWorkConverter xworkConverter, RootAccessor accessor, TextProvider prov, SecurityMemberAccess securityMemberAccess) {
this(null, xworkConverter, accessor, prov, securityMemberAccess);
}
/**
* @since 6.4.0
*/
protected OgnlValueStack(ValueStack vs, XWorkConverter xworkConverter, RootAccessor accessor, SecurityMemberAccess securityMemberAccess) {
this(vs, xworkConverter, accessor, null, securityMemberAccess);
}
/**
* @deprecated since 6.4.0, use {@link #OgnlValueStack(ValueStack, XWorkConverter, RootAccessor, TextProvider, SecurityMemberAccess)} instead.
*/
@Deprecated
protected OgnlValueStack(ValueStack vs,
XWorkConverter xworkConverter,
CompoundRootAccessor accessor,
TextProvider prov,
boolean allowStaticFieldAccess) {
this(vs, xworkConverter, accessor, prov, new SecurityMemberAccess(allowStaticFieldAccess));
}
/**
* @deprecated since 6.4.0, use {@link #OgnlValueStack(XWorkConverter, RootAccessor, TextProvider, SecurityMemberAccess)} instead.
*/
@Deprecated
protected OgnlValueStack(XWorkConverter xworkConverter, CompoundRootAccessor accessor, TextProvider prov, boolean allowStaticFieldAccess) {
this(xworkConverter, accessor, prov, new SecurityMemberAccess(allowStaticFieldAccess));
setRoot(xworkConverter, accessor, new CompoundRoot(), allowStaticFieldAccess);
push(prov);
}
/**
* @deprecated since 6.4.0, use {@link #OgnlValueStack(ValueStack, XWorkConverter, RootAccessor, SecurityMemberAccess)} instead.
*/
@Deprecated
protected OgnlValueStack(ValueStack vs, XWorkConverter xworkConverter, CompoundRootAccessor accessor, boolean allowStaticFieldAccess) {
this(vs, xworkConverter, accessor, new SecurityMemberAccess(allowStaticFieldAccess));
setRoot(xworkConverter, accessor, new CompoundRoot(vs.getRoot()), allowStaticFieldAccess);
}
@Inject
protected void setOgnlUtil(OgnlUtil ognlUtil) {
this.ognlUtil = ognlUtil;
securityMemberAccess.setExcludedClasses(ognlUtil.getExcludedClasses());
securityMemberAccess.setExcludedPackageNamePatterns(ognlUtil.getExcludedPackageNamePatterns());
securityMemberAccess.setExcludedPackageNames(ognlUtil.getExcludedPackageNames());
securityMemberAccess.setDisallowProxyMemberAccess(ognlUtil.isDisallowProxyMemberAccess());
}
/**
* @since 6.4.0
*/
protected void setRoot(XWorkConverter xworkConverter, RootAccessor accessor, CompoundRoot compoundRoot, SecurityMemberAccess securityMemberAccess) {
protected void setRoot(XWorkConverter xworkConverter, CompoundRootAccessor accessor, CompoundRoot compoundRoot, boolean allowStaticFieldAccess) {
this.root = compoundRoot;
this.securityMemberAccess = securityMemberAccess;
this.securityMemberAccess = new SecurityMemberAccess(allowStaticFieldAccess);
this.context = Ognl.createDefaultContext(this.root, securityMemberAccess, accessor, new OgnlTypeConverterWrapper(xworkConverter));
this.converter = xworkConverter;
context.put(VALUE_STACK, this);
((OgnlContext) context).setTraceEvaluations(false);
((OgnlContext) context).setKeepLastEvaluation(false);
}
/**
* @deprecated since 6.4.0, use {@link #setRoot(XWorkConverter, RootAccessor, CompoundRoot, SecurityMemberAccess)} instead.
*/
@Deprecated
protected void setRoot(XWorkConverter xworkConverter, CompoundRootAccessor accessor, CompoundRoot compoundRoot, boolean allowStaticFieldAccess) {
setRoot(xworkConverter, accessor, compoundRoot, new SecurityMemberAccess(allowStaticFieldAccess));
}
@Inject(StrutsConstants.STRUTS_DEVMODE)
protected void setDevMode(String mode) {
this.devMode = BooleanUtils.toBoolean(mode);
@@ -173,11 +109,6 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
this.logMissingProperties = BooleanUtils.toBoolean(logMissingProperties);
}
@Inject(value = StrutsConstants.STRUTS_OGNL_VALUE_STACK_FALLBACK_TO_CONTEXT, required = false)
protected void setShouldFallbackToContext(String shouldFallbackToContext) {
this.shouldFallbackToContext = BooleanUtils.toBoolean(shouldFallbackToContext);
}
/**
* @see com.opensymphony.xwork2.util.ValueStack#getContext()
*/
@@ -343,16 +274,34 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
}
private Object tryFindValue(String expr) throws OgnlException {
return tryFindValue(expr, defaultType);
Object value;
expr = lookupForOverrides(expr);
if (defaultType != null) {
value = findValue(expr, defaultType);
} else {
value = getValueUsingOgnl(expr);
if (value == null) {
value = findInContext(expr);
}
}
return value;
}
private String lookupForOverrides(String expr) {
if (overrides != null && overrides.containsKey(expr)) {
if ((overrides != null) && overrides.containsKey(expr)) {
expr = (String) overrides.get(expr);
}
return expr;
}
private Object getValueUsingOgnl(String expr) throws OgnlException {
try {
return ognlUtil.getValue(expr, context, root);
} finally {
context.remove(THROW_EXCEPTION_ON_FAILURE);
}
}
public Object findValue(String expr) {
return findValue(expr, false);
}
@@ -407,25 +356,25 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
}
private Object tryFindValue(String expr, Class asType) throws OgnlException {
Object value = null;
try {
expr = lookupForOverrides(expr);
Object value = ognlUtil.getValue(expr, context, root, asType);
value = getValue(expr, asType);
if (value == null) {
value = findInContext(expr);
if (value != null && asType != null) {
value = converter.convertValue(getContext(), value, asType);
}
return converter.convertValue(getContext(), value, asType);
}
return value;
} finally {
context.remove(THROW_EXCEPTION_ON_FAILURE);
}
return value;
}
private Object getValue(String expr, Class asType) throws OgnlException {
return ognlUtil.getValue(expr, context, root, asType);
}
protected Object findInContext(String name) {
if (!shouldFallbackToContext) {
return null;
}
return getContext().get(name);
}
@@ -505,42 +454,38 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
return root.size();
}
/**
* Retained for serializability - see {@link com.opensymphony.xwork2.ognl.OgnlValueStackTest#testSerializable}
*/
private Object readResolve() {
// TODO: this should be done better
ActionContext ac = ActionContext.getContext();
Container cont = ac.getContainer();
XWorkConverter xworkConverter = cont.getInstance(XWorkConverter.class);
RootAccessor accessor = cont.getInstance(RootAccessor.class);
CompoundRootAccessor accessor = (CompoundRootAccessor) cont.getInstance(PropertyAccessor.class, CompoundRoot.class.getName());
TextProvider prov = cont.getInstance(TextProvider.class, "system");
SecurityMemberAccess sma = cont.getInstance(SecurityMemberAccess.class);
OgnlValueStack aStack = new OgnlValueStack(xworkConverter, accessor, prov, sma);
final boolean allowStaticField = BooleanUtils.toBoolean(cont.getInstance(String.class, StrutsConstants.STRUTS_ALLOW_STATIC_FIELD_ACCESS));
OgnlValueStack aStack = new OgnlValueStack(xworkConverter, accessor, prov, allowStaticField);
aStack.setOgnlUtil(cont.getInstance(OgnlUtil.class));
aStack.setRoot(xworkConverter, accessor, this.root, sma);
aStack.setRoot(xworkConverter, accessor, this.root, allowStaticField);
return aStack;
}
public void clearContextValues() {
//this is an OGNL ValueStack so the context will be an OgnlContext
//it would be better to make context of type OgnlContext
((OgnlContext) context).getValues().clear();
}
public void useAcceptProperties(Set<Pattern> acceptedProperties) {
securityMemberAccess.useAcceptProperties(acceptedProperties);
public void setAcceptProperties(Set<Pattern> acceptedProperties) {
securityMemberAccess.setAcceptProperties(acceptedProperties);
}
public void useExcludeProperties(Set<Pattern> excludeProperties) {
securityMemberAccess.useExcludeProperties(excludeProperties);
public void setExcludeProperties(Set<Pattern> excludeProperties) {
securityMemberAccess.setExcludeProperties(excludeProperties);
}
/**
* @deprecated since 6.4.0, no replacement.
*/
@Deprecated
@Inject
protected void setXWorkConverter(final XWorkConverter converter) {
// no-op
this.converter = converter;
}
}
@@ -23,7 +23,7 @@ import com.opensymphony.xwork2.conversion.NullHandler;
import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.ognl.accessor.RootAccessor;
import com.opensymphony.xwork2.ognl.accessor.CompoundRootAccessor;
import com.opensymphony.xwork2.util.CompoundRoot;
import com.opensymphony.xwork2.util.ValueStack;
import com.opensymphony.xwork2.util.ValueStackFactory;
@@ -35,6 +35,7 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.StrutsConstants;
import java.util.Map;
import java.util.Set;
/**
@@ -42,10 +43,8 @@ import java.util.Set;
*/
public class OgnlValueStackFactory implements ValueStackFactory {
private static final Logger LOG = LogManager.getLogger(OgnlValueStackFactory.class);
protected XWorkConverter xworkConverter;
protected RootAccessor compoundRootAccessor;
protected CompoundRootAccessor compoundRootAccessor;
protected TextProvider textProvider;
protected Container container;
@@ -54,117 +53,62 @@ public class OgnlValueStackFactory implements ValueStackFactory {
this.xworkConverter = converter;
}
@Inject
protected void setCompoundRootAccessor(RootAccessor compoundRootAccessor) {
this.compoundRootAccessor = compoundRootAccessor;
OgnlRuntime.setPropertyAccessor(CompoundRoot.class, compoundRootAccessor);
OgnlRuntime.setMethodAccessor(CompoundRoot.class, compoundRootAccessor);
}
@Inject
protected void setMethodAccessor(MethodAccessor methodAccessor) {
OgnlRuntime.setMethodAccessor(Object.class, methodAccessor);
}
@Inject("system")
protected void setTextProvider(TextProvider textProvider) {
this.textProvider = textProvider;
}
@Override
public ValueStack createValueStack() {
return createValueStack(null, true);
ValueStack stack = new OgnlValueStack(xworkConverter, compoundRootAccessor, textProvider, containerAllowsStaticFieldAccess());
container.inject(stack);
return stack.getActionContext()
.withContainer(container)
.withValueStack(stack)
.getValueStack();
}
@Override
public ValueStack createValueStack(ValueStack stack) {
return createValueStack(stack, false);
ValueStack result = new OgnlValueStack(stack, xworkConverter, compoundRootAccessor, containerAllowsStaticFieldAccess());
container.inject(result);
return result.getActionContext()
.withContainer(container)
.withValueStack(result)
.getValueStack();
}
protected ValueStack createValueStack(ValueStack stack, boolean useTextProvider) {
ValueStack newStack = new OgnlValueStack(
stack, xworkConverter, compoundRootAccessor, useTextProvider ? textProvider : null, container.getInstance(SecurityMemberAccess.class));
container.inject(newStack);
return newStack.getActionContext().withContainer(container).withValueStack(newStack).getValueStack();
}
/**
* {@link PropertyAccessor}'s, {@link MethodAccessor}'s and {@link NullHandler}'s are registered on a per-class
* basis by defining a bean adhering to the corresponding interface with a name corresponding to the class it is
* intended to handle.
* <p>
* The only exception is the {@link MethodAccessor} for the {@link Object} type which has its own extension point.
*
* @see #setMethodAccessor(MethodAccessor)
* @see #registerAdditionalMethodAccessors()
*/
@Inject
protected void setContainer(Container container) throws ClassNotFoundException {
this.container = container;
registerPropertyAccessors();
registerNullHandlers();
registerAdditionalMethodAccessors();
}
/**
* Note that the default {@link MethodAccessor} for handling {@link Object} methods is registered in
* {@link #setMethodAccessor} and can be configured using the extension point
* {@link StrutsConstants#STRUTS_METHOD_ACCESSOR}.
*/
protected void registerAdditionalMethodAccessors() {
Set<String> names = container.getInstanceNames(MethodAccessor.class);
for (String name : names) {
Class<?> cls;
try {
cls = Class.forName(name);
if (cls.equals(Object.class)) {
// The Object method accessor can only be configured using the struts.methodAccessor extension point
continue;
}
if (cls.equals(CompoundRoot.class)) {
// TODO: This bean is deprecated, please remove this if statement when removing the struts-beans.xml entry
continue;
}
} catch (ClassNotFoundException e) {
// Since this interface is also used as an extension point for the Object MethodAccessor, we expect
// there to be beans with names that don't correspond to classes. We can safely ignore these.
continue;
}
MethodAccessor methodAccessor = container.getInstance(MethodAccessor.class, name);
OgnlRuntime.setMethodAccessor(cls, methodAccessor);
LOG.debug("Registered custom OGNL MethodAccessor [{}] for class [{}]", methodAccessor.getClass().getName(), cls.getName());
}
}
protected void registerNullHandlers() throws ClassNotFoundException {
Set<String> names = container.getInstanceNames(NullHandler.class);
for (String name : names) {
Class<?> cls = Class.forName(name);
NullHandler nullHandler = container.getInstance(NullHandler.class, name);
OgnlRuntime.setNullHandler(cls, new OgnlNullHandlerWrapper(nullHandler));
LOG.debug("Registered custom OGNL NullHandler [{}] for class [{}]", nullHandler.getClass().getName(), cls.getName());
}
}
protected void registerPropertyAccessors() throws ClassNotFoundException {
Set<String> names = container.getInstanceNames(PropertyAccessor.class);
for (String name : names) {
Class<?> cls = Class.forName(name);
if (cls.equals(CompoundRoot.class)) {
// TODO: This bean is deprecated, please remove this if statement when removing the struts-beans.xml entry
continue;
OgnlRuntime.setPropertyAccessor(cls, container.getInstance(PropertyAccessor.class, name));
if (compoundRootAccessor == null && CompoundRoot.class.isAssignableFrom(cls)) {
compoundRootAccessor = (CompoundRootAccessor) container.getInstance(PropertyAccessor.class, name);
}
PropertyAccessor propertyAccessor = container.getInstance(PropertyAccessor.class, name);
OgnlRuntime.setPropertyAccessor(cls, propertyAccessor);
LOG.debug("Registered custom OGNL PropertyAccessor [{}] for class [{}]", propertyAccessor.getClass().getName(), cls.getName());
}
names = container.getInstanceNames(MethodAccessor.class);
for (String name : names) {
Class<?> cls = Class.forName(name);
OgnlRuntime.setMethodAccessor(cls, container.getInstance(MethodAccessor.class, name));
}
names = container.getInstanceNames(NullHandler.class);
for (String name : names) {
Class<?> cls = Class.forName(name);
OgnlRuntime.setNullHandler(cls, new OgnlNullHandlerWrapper(container.getInstance(NullHandler.class, name)));
}
if (compoundRootAccessor == null) {
throw new IllegalStateException("Couldn't find the compound root accessor");
}
this.container = container;
}
/**
* @deprecated since 6.4.0, no replacement.
* Retrieve allowStaticFieldAccess state from the container (allows for lazy fetching)
*/
@Deprecated
protected boolean containerAllowsStaticFieldAccess() {
return BooleanUtils.toBoolean(container.getInstance(String.class, StrutsConstants.STRUTS_ALLOW_STATIC_FIELD_ACCESS));
}
}
@@ -18,39 +18,21 @@
*/
package com.opensymphony.xwork2.ognl;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.ProxyUtil;
import ognl.MemberAccess;
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.ognl.ProviderAllowlist;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static com.opensymphony.xwork2.util.ConfigParseUtil.toClassObjectsSet;
import static com.opensymphony.xwork2.util.ConfigParseUtil.toClassesSet;
import static com.opensymphony.xwork2.util.ConfigParseUtil.toNewClassesSet;
import static com.opensymphony.xwork2.util.ConfigParseUtil.toNewPackageNamesSet;
import static com.opensymphony.xwork2.util.ConfigParseUtil.toNewPatternsSet;
import static com.opensymphony.xwork2.util.ConfigParseUtil.toPackageNamesSet;
import static java.text.MessageFormat.format;
import static java.util.Collections.emptySet;
import static java.util.Collections.singletonList;
import static java.util.Collections.unmodifiableSet;
/**
* Allows access decisions to be made on the basis of whether a member is static or not.
* Also blocks or allows access to properties.
@@ -59,39 +41,13 @@ public class SecurityMemberAccess implements MemberAccess {
private static final Logger LOG = LogManager.getLogger(SecurityMemberAccess.class);
private static final Set<String> ALLOWLIST_REQUIRED_PACKAGES = unmodifiableSet(new HashSet<>(Arrays.asList(
"com.opensymphony.xwork2.validator.validators",
"org.apache.struts2.components",
"org.apache.struts2.views.jsp"
)));
private static final Set<Class<?>> ALLOWLIST_REQUIRED_CLASSES = unmodifiableSet(new HashSet<>(Arrays.asList(
java.lang.Enum.class,
java.lang.String.class,
java.util.Date.class,
java.util.HashMap.class,
java.util.Map.class,
java.util.Map.Entry.class
)));
private final ProviderAllowlist providerAllowlist;
private boolean allowStaticFieldAccess = true;
private Set<Pattern> excludeProperties = emptySet();
private Set<Pattern> acceptProperties = emptySet();
private Set<String> excludedClasses = unmodifiableSet(new HashSet<>(singletonList(Object.class.getName())));
private Set<Pattern> excludedPackageNamePatterns = emptySet();
private Set<String> excludedPackageNames = emptySet();
private Set<String> excludedPackageExemptClasses = emptySet();
private boolean enforceAllowlistEnabled = false;
private Set<Class<?>> allowlistClasses = emptySet();
private Set<String> allowlistPackageNames = emptySet();
private boolean disallowProxyMemberAccess = false;
private boolean disallowDefaultPackageAccess = false;
@Inject
public SecurityMemberAccess(@Inject ProviderAllowlist providerAllowlist) {
this.providerAllowlist = providerAllowlist;
}
private final boolean allowStaticFieldAccess;
private Set<Pattern> excludeProperties = Collections.emptySet();
private Set<Pattern> acceptProperties = Collections.emptySet();
private Set<Class<?>> excludedClasses = Collections.emptySet();
private Set<Pattern> excludedPackageNamePatterns = Collections.emptySet();
private Set<String> excludedPackageNames = Collections.emptySet();
private boolean disallowProxyMemberAccess;
/**
* SecurityMemberAccess
@@ -99,12 +55,9 @@ public class SecurityMemberAccess implements MemberAccess {
* - block or allow access to properties (configurable-after-construction)
*
* @param allowStaticFieldAccess if set to true static fields (constants) will be accessible
* @deprecated since 6.4.0, use {@link #SecurityMemberAccess(ProviderAllowlist)} instead.
*/
@Deprecated
public SecurityMemberAccess(boolean allowStaticFieldAccess) {
this(null);
useAllowStaticFieldAccess(String.valueOf(allowStaticFieldAccess));
this.allowStaticFieldAccess = allowStaticFieldAccess;
}
@Override
@@ -124,187 +77,86 @@ public class SecurityMemberAccess implements MemberAccess {
@Override
public void restore(Map context, Object target, Member member, String propertyName, Object state) {
if (state == null) {
return;
if (state != null) {
final AccessibleObject accessible = (AccessibleObject) member;
final boolean stateBoolean = ((Boolean) state).booleanValue(); // Using twice (avoid unboxing)
if (!stateBoolean) {
accessible.setAccessible(stateBoolean);
} else {
throw new IllegalArgumentException("Improper restore state [" + stateBoolean + "] for target [" + target +
"], member [" + member + "], propertyName [" + propertyName + "]");
}
}
if ((Boolean) state) {
throw new IllegalArgumentException(format(
"Improper restore state [true] for target [{0}], member [{1}], propertyName [{2}]",
target,
member,
propertyName));
}
((AccessibleObject) member).setAccessible(false);
}
@Override
public boolean isAccessible(Map context, Object target, Member member, String propertyName) {
LOG.debug("Checking access for [target: {}, member: {}, property: {}]", target, member, propertyName);
if (target != null) {
// Special case: Target is a Class object but not Class.class
if (Class.class.equals(target.getClass()) && !Class.class.equals(target)) {
if (!isStatic(member)) {
throw new IllegalArgumentException("Member expected to be static!");
}
if (!member.getDeclaringClass().equals(target)) {
throw new IllegalArgumentException("Target class does not match static member!");
}
target = null; // This information is not useful to us and conflicts with following logic which expects target to be null or an instance containing the member
// Standard case: Member should exist on target
} else if (!member.getDeclaringClass().isAssignableFrom(target.getClass())) {
throw new IllegalArgumentException("Member does not exist on target!");
}
}
final int memberModifiers = member.getModifiers();
if (!checkProxyMemberAccess(target, member)) {
LOG.warn("Access to proxy is blocked! Member class [{}] of target [{}], member [{}]", member.getDeclaringClass(), target, member);
return false;
}
if (!checkPublicMemberAccess(member)) {
if (!checkPublicMemberAccess(memberModifiers)) {
LOG.warn("Access to non-public [{}] is blocked!", member);
return false;
}
if (!checkStaticFieldAccess(member)) {
if (!checkStaticFieldAccess(member, memberModifiers)) {
LOG.warn("Access to static field [{}] is blocked!", member);
return false;
}
if (!checkStaticMethodAccess(member)) {
// it needs to be before calling #checkStaticMethodAccess()
if (checkEnumAccess(target, member)) {
LOG.trace("Allowing access to enum: target [{}], member [{}]", target, member);
return true;
}
if (!checkStaticMethodAccess(member, memberModifiers)) {
LOG.warn("Access to static method [{}] is blocked!", member);
return false;
}
if (!checkDefaultPackageAccess(target, member)) {
return false;
}
final Class<?> memberClass = member.getDeclaringClass();
if (!checkExclusionList(target, member)) {
return false;
}
if (!checkAllowlist(target, member)) {
return false;
}
if (!isAcceptableProperty(propertyName)) {
return false;
}
return true;
}
/**
* @return {@code true} if member access is allowed
*/
protected boolean checkAllowlist(Object target, Member member) {
Class<?> memberClass = member.getDeclaringClass();
if (!enforceAllowlistEnabled) {
return true;
}
if (!isClassAllowlisted(memberClass)) {
LOG.warn(format("Declaring class [{0}] of member type [{1}] is not allowlisted!", memberClass, member));
return false;
}
if (target == null || target.getClass() == memberClass) {
return true;
}
Class<?> targetClass = target.getClass();
if (!isClassAllowlisted(targetClass)) {
LOG.warn(format("Target class [{0}] of target [{1}] is not allowlisted!", targetClass, target));
return false;
}
return true;
}
protected boolean isClassAllowlisted(Class<?> clazz) {
return allowlistClasses.contains(clazz)
|| ALLOWLIST_REQUIRED_CLASSES.contains(clazz)
|| (providerAllowlist != null && providerAllowlist.getProviderAllowlist().contains(clazz))
|| isClassBelongsToPackages(clazz, ALLOWLIST_REQUIRED_PACKAGES)
|| isClassBelongsToPackages(clazz, allowlistPackageNames);
}
/**
* @return {@code true} if member access is allowed
*/
protected boolean checkExclusionList(Object target, Member member) {
Class<?> memberClass = member.getDeclaringClass();
if (isClassExcluded(memberClass)) {
LOG.warn("Declaring class of member type [{}] is excluded!", memberClass);
LOG.warn("Declaring class of member type [{}] is excluded!", member);
return false;
}
if (isPackageExcluded(memberClass)) {
LOG.warn("Package [{}] of member class [{}] of member [{}] is excluded!",
memberClass.getPackage(),
memberClass,
target);
// 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);
return false;
}
if (target == null || target.getClass() == memberClass) {
return true;
}
Class<?> targetClass = target.getClass();
if (isClassExcluded(targetClass)) {
LOG.warn("Target class [{}] of target [{}] is excluded!", targetClass, target);
return false;
}
if (isPackageExcluded(targetClass)) {
LOG.warn("Package [{}] of target [{}] is excluded!", targetClass.getPackage(), member);
return false;
}
return true;
}
/**
* @return {@code true} if member access is allowed
*/
protected boolean checkDefaultPackageAccess(Object target, Member member) {
if (!disallowDefaultPackageAccess) {
return true;
}
Class<?> memberClass = member.getDeclaringClass();
if (memberClass.getPackage() == null || memberClass.getPackage().getName().isEmpty()) {
LOG.warn("Class [{}] from the default package is excluded!", memberClass);
if (disallowProxyMemberAccess && ProxyUtil.isProxyMember(member, target)) {
LOG.warn("Access to proxy is blocked! Target class [{}] of target [{}], member [{}]", targetClass, target, member);
return false;
}
if (target == null || target.getClass() == memberClass) {
return true;
}
Class<?> targetClass = target.getClass();
if (targetClass.getPackage() == null || targetClass.getPackage().getName().isEmpty()) {
LOG.warn("Class [{}] from the default package is excluded!", targetClass);
return false;
}
return true;
}
/**
* @return {@code true} if member access is allowed
*/
protected boolean checkProxyMemberAccess(Object target, Member member) {
return !(disallowProxyMemberAccess && ProxyUtil.isProxyMember(member, target));
return isAcceptableProperty(propertyName);
}
/**
* Check access for static method (via modifiers).
* <p>
*
* Note: For non-static members, the result is always true.
*
* @return {@code true} if member access is allowed
* @param member
* @param memberModifiers
*
* @return
*/
protected boolean checkStaticMethodAccess(Member member) {
if (checkEnumAccess(member)) {
LOG.trace("Exempting Enum#values from static method check: class [{}]", member.getDeclaringClass());
return true;
}
return member instanceof Field || !isStatic(member);
}
private static boolean isStatic(Member member) {
return Modifier.isStatic(member.getModifiers());
protected boolean checkStaticMethodAccess(Member member, int memberModifiers) {
return !Modifier.isStatic(memberModifiers) || member instanceof Field;
}
/**
@@ -312,145 +164,130 @@ public class SecurityMemberAccess implements MemberAccess {
* <p>
* Note: For non-static members, the result is always true.
*
* @return {@code true} if member access is allowed
* @param member
* @param memberModifiers
* @return
*/
protected boolean checkStaticFieldAccess(Member member) {
if (allowStaticFieldAccess) {
protected boolean checkStaticFieldAccess(Member member, int memberModifiers) {
if (Modifier.isStatic(memberModifiers) && member instanceof Field) {
return allowStaticFieldAccess;
} else {
return true;
}
return !(member instanceof Field) || !isStatic(member);
}
/**
* Check access for public members (via modifiers)
* <p>
* Returns true if-and-only-if the member is public.
*
* @return {@code true} if member access is allowed
* @param memberModifiers
* @return
*/
protected boolean checkPublicMemberAccess(Member member) {
return Modifier.isPublic(member.getModifiers());
protected boolean checkPublicMemberAccess(int memberModifiers) {
return Modifier.isPublic(memberModifiers);
}
/**
* @return {@code true} if member access is allowed
*/
protected boolean checkEnumAccess(Member member) {
return member.getDeclaringClass().isEnum()
&& isStatic(member)
&& member instanceof Method
&& member.getName().equals("values")
&& ((Method) member).getParameterCount() == 0;
}
protected boolean isPackageExcluded(Class<?> clazz) {
return !excludedPackageExemptClasses.contains(clazz.getName()) && (isExcludedPackageNames(clazz) || isExcludedPackageNamePatterns(clazz));
}
public static String toPackageName(Class<?> clazz) {
if (clazz.getPackage() == null) {
return "";
protected boolean checkEnumAccess(Object target, Member member) {
if (target instanceof Class) {
final Class<?> clazz = (Class<?>) target;
return Enum.class.isAssignableFrom(clazz) && member.getName().equals("values");
}
return clazz.getPackage().getName();
return false;
}
protected boolean isExcludedPackageNamePatterns(Class<?> clazz) {
return excludedPackageNamePatterns.stream().anyMatch(pattern -> pattern.matcher(toPackageName(clazz)).matches());
protected boolean isPackageExcluded(Package targetPackage, Package memberPackage) {
if (targetPackage == null || memberPackage == null) {
LOG.warn("The use of the default (unnamed) package is discouraged!");
}
String targetPackageName = targetPackage == null ? "" : targetPackage.getName();
String memberPackageName = memberPackage == null ? "" : memberPackage.getName();
for (Pattern pattern : excludedPackageNamePatterns) {
if (pattern.matcher(targetPackageName).matches() || pattern.matcher(memberPackageName).matches()) {
return true;
}
}
targetPackageName = targetPackageName + ".";
memberPackageName = memberPackageName + ".";
for (String packageName : excludedPackageNames) {
if (targetPackageName.startsWith(packageName) || memberPackageName.startsWith(packageName)) {
return true;
}
}
return false;
}
protected boolean isExcludedPackageNames(Class<?> clazz) {
return isClassBelongsToPackages(clazz, excludedPackageNames);
}
public static boolean isClassBelongsToPackages(Class<?> clazz, Set<String> matchingPackages) {
List<String> packageParts = Arrays.asList(toPackageName(clazz).split("\\."));
for (int i = 0; i < packageParts.size(); i++) {
String parentPackage = String.join(".", packageParts.subList(0, i + 1));
if (matchingPackages.contains(parentPackage)) {
protected boolean isClassExcluded(Class<?> clazz) {
if (clazz == Object.class || (clazz == Class.class && !allowStaticFieldAccess)) {
return true;
}
for (Class<?> excludedClass : excludedClasses) {
if (clazz.isAssignableFrom(excludedClass)) {
return true;
}
}
return false;
}
protected boolean isClassExcluded(Class<?> clazz) {
return excludedClasses.contains(clazz.getName());
}
/**
* @return {@code true} if member access is allowed
*/
protected boolean isAcceptableProperty(String name) {
return name == null || !isExcluded(name) && isAccepted(name);
return name == null || ((!isExcluded(name)) && isAccepted(name));
}
protected boolean isAccepted(String paramName) {
if (acceptProperties.isEmpty()) {
return true;
if (!this.acceptProperties.isEmpty()) {
for (Pattern pattern : acceptProperties) {
Matcher matcher = pattern.matcher(paramName);
if (matcher.matches()) {
return true;
}
}
//no match, but acceptedParams is not empty
return false;
}
return acceptProperties.stream().map(pattern -> pattern.matcher(paramName)).anyMatch(Matcher::matches);
//empty acceptedParams
return true;
}
protected boolean isExcluded(String paramName) {
return excludeProperties.stream().map(pattern -> pattern.matcher(paramName)).anyMatch(Matcher::matches);
if (!this.excludeProperties.isEmpty()) {
for (Pattern pattern : excludeProperties) {
Matcher matcher = pattern.matcher(paramName);
if (matcher.matches()) {
return true;
}
}
}
return false;
}
public void useExcludeProperties(Set<Pattern> excludeProperties) {
public void setExcludeProperties(Set<Pattern> excludeProperties) {
this.excludeProperties = excludeProperties;
}
public void useAcceptProperties(Set<Pattern> acceptedProperties) {
public void setAcceptProperties(Set<Pattern> acceptedProperties) {
this.acceptProperties = acceptedProperties;
}
@Inject(value = StrutsConstants.STRUTS_ALLOW_STATIC_FIELD_ACCESS, required = false)
public void useAllowStaticFieldAccess(String allowStaticFieldAccess) {
this.allowStaticFieldAccess = BooleanUtils.toBoolean(allowStaticFieldAccess);
if (!this.allowStaticFieldAccess) {
useExcludedClasses(Class.class.getName());
}
public void setExcludedClasses(Set<Class<?>> excludedClasses) {
this.excludedClasses = excludedClasses;
}
@Inject(value = StrutsConstants.STRUTS_EXCLUDED_CLASSES, required = false)
public void useExcludedClasses(String commaDelimitedClasses) {
this.excludedClasses = toNewClassesSet(excludedClasses, commaDelimitedClasses);
public void setExcludedPackageNamePatterns(Set<Pattern> excludedPackageNamePatterns) {
this.excludedPackageNamePatterns = excludedPackageNamePatterns;
}
@Inject(value = StrutsConstants.STRUTS_EXCLUDED_PACKAGE_NAME_PATTERNS, required = false)
public void useExcludedPackageNamePatterns(String commaDelimitedPackagePatterns) {
this.excludedPackageNamePatterns = toNewPatternsSet(excludedPackageNamePatterns, commaDelimitedPackagePatterns);
public void setExcludedPackageNames(Set<String> excludedPackageNames) {
this.excludedPackageNames = excludedPackageNames;
}
@Inject(value = StrutsConstants.STRUTS_EXCLUDED_PACKAGE_NAMES, required = false)
public void useExcludedPackageNames(String commaDelimitedPackageNames) {
this.excludedPackageNames = toNewPackageNamesSet(excludedPackageNames, commaDelimitedPackageNames);
}
@Inject(value = StrutsConstants.STRUTS_EXCLUDED_PACKAGE_EXEMPT_CLASSES, required = false)
public void useExcludedPackageExemptClasses(String commaDelimitedClasses) {
this.excludedPackageExemptClasses = toClassesSet(commaDelimitedClasses);
}
@Inject(value = StrutsConstants.STRUTS_ALLOWLIST_ENABLE, required = false)
public void useEnforceAllowlistEnabled(String enforceAllowlistEnabled) {
this.enforceAllowlistEnabled = BooleanUtils.toBoolean(enforceAllowlistEnabled);
}
@Inject(value = StrutsConstants.STRUTS_ALLOWLIST_CLASSES, required = false)
public void useAllowlistClasses(String commaDelimitedClasses) {
this.allowlistClasses = toClassObjectsSet(commaDelimitedClasses);
}
@Inject(value = StrutsConstants.STRUTS_ALLOWLIST_PACKAGE_NAMES, required = false)
public void useAllowlistPackageNames(String commaDelimitedPackageNames) {
this.allowlistPackageNames = toPackageNamesSet(commaDelimitedPackageNames);
}
@Inject(value = StrutsConstants.STRUTS_DISALLOW_PROXY_MEMBER_ACCESS, required = false)
public void useDisallowProxyMemberAccess(String disallowProxyMemberAccess) {
this.disallowProxyMemberAccess = BooleanUtils.toBoolean(disallowProxyMemberAccess);
}
@Inject(value = StrutsConstants.STRUTS_DISALLOW_DEFAULT_PACKAGE_ACCESS, required = false)
public void useDisallowDefaultPackageAccess(String disallowDefaultPackageAccess) {
this.disallowDefaultPackageAccess = BooleanUtils.toBoolean(disallowDefaultPackageAccess);
public void setDisallowProxyMemberAccess(boolean disallowProxyMemberAccess) {
this.disallowProxyMemberAccess = disallowProxyMemberAccess;
}
}
@@ -22,12 +22,7 @@ import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.ognl.OgnlValueStack;
import com.opensymphony.xwork2.util.CompoundRoot;
import com.opensymphony.xwork2.util.ValueStack;
import ognl.MethodFailedException;
import ognl.NoSuchPropertyException;
import ognl.Ognl;
import ognl.OgnlContext;
import ognl.OgnlException;
import ognl.OgnlRuntime;
import ognl.*;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -36,11 +31,7 @@ import org.apache.struts2.StrutsException;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import static java.lang.String.format;
@@ -53,12 +44,11 @@ import static org.apache.commons.lang3.BooleanUtils.toBoolean;
* @author Rainer Hermanns
* @version $Revision$
*/
public class CompoundRootAccessor implements RootAccessor {
public class CompoundRootAccessor implements PropertyAccessor, MethodAccessor, ClassResolver {
/**
* Used by OGNl to generate bytecode
*/
@Override
public String getSourceAccessor(OgnlContext context, Object target, Object index) {
return null;
}
@@ -66,28 +56,20 @@ public class CompoundRootAccessor implements RootAccessor {
/**
* Used by OGNl to generate bytecode
*/
@Override
public String getSourceSetter(OgnlContext context, Object target, Object index) {
return null;
}
private final static Logger LOG = LogManager.getLogger(CompoundRootAccessor.class);
private final static Class[] EMPTY_CLASS_ARRAY = new Class[0];
private static final Map<MethodCall, Boolean> invalidMethods = new ConcurrentHashMap<>();
private static Map<MethodCall, Boolean> invalidMethods = new ConcurrentHashMap<>();
private boolean devMode;
private boolean disallowCustomOgnlMap;
@Inject(StrutsConstants.STRUTS_DEVMODE)
protected void setDevMode(String mode) {
this.devMode = BooleanUtils.toBoolean(mode);
}
@Inject(value = StrutsConstants.STRUTS_DISALLOW_CUSTOM_OGNL_MAP, required = false)
public void useDisallowCustomOgnlMap(String disallowCustomOgnlMap) {
this.disallowCustomOgnlMap = BooleanUtils.toBoolean(disallowCustomOgnlMap);
}
@Override
public void setProperty(Map context, Object target, Object name, Object value) throws OgnlException {
CompoundRoot root = (CompoundRoot) target;
OgnlContext ognlContext = (OgnlContext) context;
@@ -136,7 +118,6 @@ public class CompoundRootAccessor implements RootAccessor {
}
}
@Override
public Object getProperty(Map context, Object target, Object name) throws OgnlException {
CompoundRoot root = (CompoundRoot) target;
OgnlContext ognlContext = (OgnlContext) context;
@@ -146,7 +127,7 @@ public class CompoundRootAccessor implements RootAccessor {
return root.cutStack(index);
} else if (name instanceof String) {
if ("top".equals(name)) {
if (!root.isEmpty()) {
if (root.size() > 0) {
return root.get(0);
} else {
return null;
@@ -182,7 +163,6 @@ public class CompoundRootAccessor implements RootAccessor {
}
}
@Override
public Object callMethod(Map context, Object target, String name, Object[] objects) throws MethodFailedException {
CompoundRoot root = (CompoundRoot) target;
@@ -210,23 +190,26 @@ public class CompoundRootAccessor implements RootAccessor {
}
SortedSet<String> set = new TreeSet<>();
StringBuffer sb = new StringBuffer();
for (PropertyDescriptor pd : descriptors.values()) {
StringBuilder sb = new StringBuilder();
sb.append(pd.getName()).append(": ");
sb.append(pd.getName()).append(": ");
int padding = maxSize - pd.getName().length();
for (int i = 0; i < padding; i++) {
sb.append(" ");
}
sb.append(pd.getPropertyType().getName());
set.add(sb.toString());
sb = new StringBuffer();
}
StringBuilder sb = new StringBuilder();
for (String aSet : set) {
sb.append(aSet).append("\n");
sb = new StringBuffer();
for (Object aSet : set) {
String s = (String) aSet;
sb.append(s).append("\n");
}
return sb.toString();
} catch (IntrospectionException | OgnlException e) {
LOG.debug("Got exception in callMethod", e);
@@ -275,23 +258,13 @@ public class CompoundRootAccessor implements RootAccessor {
return null;
}
@Override
public Object callStaticMethod(Map transientVars, Class aClass, String s, Object[] objects) throws MethodFailedException {
return null;
}
@Override
public Class classForName(String className, Map context) throws ClassNotFoundException {
Object root = Ognl.getRoot(context);
if (disallowCustomOgnlMap) {
String nodeClassName = ((OgnlContext) context).getCurrentNode().getClass().getName();
if ("ognl.ASTMap".equals(nodeClassName)) {
LOG.error("Constructing OGNL ASTMap's from custom classes is forbidden. Attempted class: {}", className);
return null;
}
}
try {
if (root instanceof CompoundRoot) {
if (className.startsWith("vs")) {
@@ -348,14 +321,9 @@ public class CompoundRootAccessor implements RootAccessor {
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof MethodCall)) {
return false;
}
MethodCall mc = (MethodCall) obj;
return mc.clazz.equals(clazz) && mc.name.equals(name) && Arrays.equals(mc.args, args);
MethodCall mc = (CompoundRootAccessor.MethodCall) obj;
return (mc.clazz.equals(clazz) && mc.name.equals(name) && Arrays.equals(mc.args, args));
}
@Override
@@ -1,29 +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.
*/
package com.opensymphony.xwork2.ognl.accessor;
import ognl.ClassResolver;
import ognl.MethodAccessor;
import ognl.PropertyAccessor;
/**
* @since 6.4.0
*/
public interface RootAccessor extends PropertyAccessor, MethodAccessor, ClassResolver {
}
@@ -32,37 +32,37 @@ public interface AcceptedPatternsChecker {
* @param value to check
* @return object containing result of matched pattern and pattern itself
*/
IsAccepted isAccepted(String value);
public IsAccepted isAccepted(String value);
/**
* Sets excluded patterns during runtime
*
* @param commaDelimitedPatterns comma delimited string with patterns
*/
void setAcceptedPatterns(String commaDelimitedPatterns);
public void setAcceptedPatterns(String commaDelimitedPatterns);
/**
* Set excluded patterns during runtime
*
* @param patterns array of additional excluded patterns
*/
void setAcceptedPatterns(String[] patterns);
public void setAcceptedPatterns(String[] patterns);
/**
* Sets excluded patterns during runtime
*
* @param patterns set of additional patterns
*/
void setAcceptedPatterns(Set<String> patterns);
public void setAcceptedPatterns(Set<String> patterns);
/**
* Allow access list of all defined excluded patterns
*
* @return set of excluded patterns
*/
Set<Pattern> getAcceptedPatterns();
public Set<Pattern> getAcceptedPatterns();
final class IsAccepted {
public final static class IsAccepted {
private final boolean accepted;
private final String acceptedPattern;
@@ -43,7 +43,7 @@ public class DefaultAcceptedPatternsChecker implements AcceptedPatternsChecker {
"\\w+([:]?\\w+)?((\\.\\w+)|(\\[\\d+])|(\\(\\d+\\))|(\\['(\\w-?|[\\u4e00-\\u9fa5]-?)+'])|(\\('(\\w-?|[\\u4e00-\\u9fa5]-?)+'\\)))*([!]?\\w+)?"
};
protected Set<Pattern> acceptedPatterns;
private Set<Pattern> acceptedPatterns;
public DefaultAcceptedPatternsChecker() {
setAcceptedPatterns(ACCEPTED_PATTERNS);
@@ -62,19 +62,28 @@ public class DefaultAcceptedPatternsChecker implements AcceptedPatternsChecker {
@Inject(value = StrutsConstants.STRUTS_OVERRIDE_ACCEPTED_PATTERNS, required = false)
protected void setOverrideAcceptedPatterns(String acceptablePatterns) {
setAcceptedPatterns(acceptablePatterns);
LOG.warn("Overriding accepted patterns [{}] with [{}], be aware that this affects all instances and safety of your application!",
acceptedPatterns, acceptablePatterns);
acceptedPatterns = new HashSet<>();
try {
for (String pattern : TextParseUtil.commaDelimitedStringToSet(acceptablePatterns)) {
acceptedPatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
}
} finally {
acceptedPatterns = Collections.unmodifiableSet(acceptedPatterns);
}
}
@Inject(value = StrutsConstants.STRUTS_ADDITIONAL_ACCEPTED_PATTERNS, required = false)
protected void setAdditionalAcceptedPatterns(String acceptablePatterns) {
LOG.warn("Adding additional global patterns [{}] to accepted patterns!", acceptablePatterns);
Set<Pattern> newAcceptedPatterns = new HashSet<>(acceptedPatterns);
acceptedPatterns = new HashSet<>(acceptedPatterns); // Make mutable before adding
try {
for (String pattern : TextParseUtil.commaDelimitedStringToSet(acceptablePatterns)) {
newAcceptedPatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
acceptedPatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
}
} finally {
acceptedPatterns = Collections.unmodifiableSet(newAcceptedPatterns);
acceptedPatterns = Collections.unmodifiableSet(acceptedPatterns);
}
}
@@ -90,24 +99,20 @@ public class DefaultAcceptedPatternsChecker implements AcceptedPatternsChecker {
@Override
public void setAcceptedPatterns(Set<String> patterns) {
logPatternChange(patterns);
Set<Pattern> newAcceptedPatterns = new HashSet<>(patterns.size());
try {
for (String pattern : patterns) {
newAcceptedPatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
}
} finally {
acceptedPatterns = Collections.unmodifiableSet(newAcceptedPatterns);
}
}
protected void logPatternChange(Set<String> newPatterns) {
if (acceptedPatterns == null) {
// No need to warn on class initialisation
LOG.debug("Sets accepted patterns to [{}], note this impacts the safety of your application!", newPatterns);
// Limit unwanted log entries (for 1st call, acceptedPatterns null)
LOG.debug("Sets accepted patterns to [{}], note this impacts the safety of your application!", patterns);
} else {
LOG.warn("Replacing accepted patterns [{}] with [{}], be aware that this affects all instances and safety of your application!",
acceptedPatterns, newPatterns);
acceptedPatterns, patterns);
}
acceptedPatterns = new HashSet<>(patterns.size());
try {
for (String pattern : patterns) {
acceptedPatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
}
} finally {
acceptedPatterns = Collections.unmodifiableSet(acceptedPatterns);
}
}
@@ -36,9 +36,8 @@ public class DefaultExcludedPatternsChecker implements ExcludedPatternsChecker {
private static final Logger LOG = LogManager.getLogger(DefaultExcludedPatternsChecker.class);
public static final String[] EXCLUDED_PATTERNS = {
"(^|\\%\\{)((#?)(top(\\.|\\['|\\[\")|\\[\\d\\]\\.)?)(dojo|struts|session|request|response|application|servlet(Request|Response|Context)|parameters|context|_memberAccess)(\\.|\\[).*",
".*(^|\\.|\\[|\\'|\"|get)class(\\(\\.|\\[|\\'|\").*",
"actionErrors|actionMessages|fieldErrors"
"(^|\\%\\{)((#?)(top(\\.|\\['|\\[\")|\\[\\d\\]\\.)?)(dojo|struts|session|request|response|application|servlet(Request|Response|Context)|parameters|context|_memberAccess)(\\.|\\[).*",
".*(^|\\.|\\[|\\'|\"|get)class(\\(\\.|\\[|\\'|\").*"
};
private Set<Pattern> excludedPatterns;
@@ -49,7 +48,21 @@ public class DefaultExcludedPatternsChecker implements ExcludedPatternsChecker {
@Inject(value = StrutsConstants.STRUTS_OVERRIDE_EXCLUDED_PATTERNS, required = false)
protected void setOverrideExcludePatterns(String excludePatterns) {
setExcludedPatterns(excludePatterns);
if (excludedPatterns != null && excludedPatterns.size() > 0) {
LOG.warn("Overriding excluded patterns [{}] with [{}], be aware that this affects all instances and safety of your application!",
excludedPatterns, excludePatterns);
} else {
// Limit unwanted log entries (when excludedPatterns null/empty - usually 1st call)
LOG.debug("Overriding excluded patterns with [{}]", excludePatterns);
}
excludedPatterns = new HashSet<>();
try {
for (String pattern : TextParseUtil.commaDelimitedStringToSet(excludePatterns)) {
excludedPatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
}
} finally {
excludedPatterns = Collections.unmodifiableSet(excludedPatterns);
}
}
@Inject(value = StrutsConstants.STRUTS_ADDITIONAL_EXCLUDED_PATTERNS, required = false)
@@ -85,7 +98,7 @@ public class DefaultExcludedPatternsChecker implements ExcludedPatternsChecker {
@Override
public void setExcludedPatterns(Set<String> patterns) {
if (excludedPatterns != null && !excludedPatterns.isEmpty()) {
if (excludedPatterns != null && excludedPatterns.size() > 0) {
LOG.warn("Replacing excluded patterns [{}] with [{}], be aware that this affects all instances and safety of your application!",
excludedPatterns, patterns);
} else {

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