Compare commits

..

104 Commits

Author SHA1 Message Date
Lukasz Lenart 981885fd6a [maven-release-plugin] prepare release STRUTS_6_0_0_RC4 2022-05-24 13:59:22 +02:00
Lukasz Lenart 07b88fcc35 Upgrades integration tests config to match Spring and Jetty 2022-05-24 09:51:42 +02:00
Lukasz Lenart 2cbe47297e Merge pull request #554 from gregh3269/More_localdatetime_support
Add basic LocalDateTime support WW-5175
2022-05-23 07:35:43 +02:00
Lukasz Lenart 545add9365 Merge pull request #553 from apache/WW-5179-max-length
[WW-5179] Set default value of struts.ognl.expressionMaxLength to 256
2022-05-23 07:34:17 +02:00
Lukasz Lenart ebecc7d39d Merge pull request #552 from apache/WW-5165-upgrade-spring
[WW-5165] Upgrades Spring to version 5.3.20
2022-05-23 07:34:05 +02:00
Greg Huber 226a1d49ce Add basic LocalDateTime support WW-5175 2022-05-20 11:47:08 +01:00
Greg Huber 075ef7c4e1 Add basic LocalDateTime support WW-5175 2022-05-19 10:30:36 +01:00
Lukasz Lenart 1237110652 WW-5179 Adjusts test to match new default value 2022-05-17 21:17:56 +02:00
Lukasz Lenart 3f2518afa8 WW-5179 Set default value of struts.ognl.expressionMaxLength to 256 2022-05-17 20:59:51 +02:00
Lukasz Lenart 9f9edf4705 WW-5165 Upgrades Spring to version 5.3.20 2022-05-17 07:37:39 +02:00
gregh3269 7e912742ed Basic LocalDateTime support (#532)
* Basic LocalDateTime support

* Basic LocalDateTime support

* Basic LocalDateTime support

* Basic LocalDateTime support

* Basic LocalDateTime support

Co-authored-by: Greg Huber <ghuber@guestales.co.uk>
Co-authored-by: Greg Huber <ghuber@apache.org>
2022-05-17 07:31:30 +02:00
Lukasz Lenart c98b60cd9a Merge pull request #528 from JCgH4164838Gh792C124B5/localS2_26_OgnlUtilOptionalCache1
Potential expression cache enhancement for 2.6 series
2022-05-16 07:38:59 +02:00
Lukasz Lenart 5ae9688ae8 Merge pull request #551 from apache/dependabot/maven/xerces-xercesImpl-2.12.2
Bump xercesImpl from 2.12.0 to 2.12.2
2022-05-16 07:18:03 +02:00
Lukasz Lenart b72200aabf Merge branch 'master' into localS2_26_OgnlUtilOptionalCache1
# Conflicts:
#	core/src/test/java/com/opensymphony/xwork2/ognl/OgnlUtilTest.java
2022-05-16 07:16:03 +02:00
JCgH4164838Gh792C124B5 16e7c9b459 Merge pull request #1 from apache/use-extension-point
Ties cache extension points with implementation
2022-05-15 19:01:17 -04:00
Yasser Zamani bea1c6f09f Merge pull request #549 from apache/WW-5181-static-methods
[WW-5181] Blocks permanently access to static methods
2022-05-15 14:40:12 +04:30
dependabot[bot] 888884cc51 Bump xercesImpl from 2.12.0 to 2.12.2
Bumps xercesImpl from 2.12.0 to 2.12.2.

---
updated-dependencies:
- dependency-name: xerces:xercesImpl
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-15 10:02:32 +00:00
Yasser Zamani b387abae01 Merge pull request #550 from apache/servlet-3
[WW-5182] Upgrade to Servlet API 3.1
2022-05-15 14:27:47 +04:30
Lukasz Lenart 059a2a6117 WW-5182 Upgrade to Servlet API 3.1 2022-05-14 09:12:42 +02:00
Lukasz Lenart c0a3be9fd8 Uses two step build process on Travis similar to Jenkins pipeline 2022-05-10 07:41:34 +02:00
Lukasz Lenart 4a7cae029a Uses proper package to match Jetty Eclipse 2022-05-08 12:31:40 +02:00
Lukasz Lenart 9873a7f56b Tries to fix Travis build 2022-05-06 08:35:06 +02:00
Lukasz Lenart 086b514d59 Adjusts Travis build to be aligned with Jenkins pipeline 2022-05-06 07:47:12 +02:00
Lukasz Lenart 6024dd6fa4 Skips test during deploy 2022-05-06 07:29:29 +02:00
Lukasz Lenart 4eb2ee8384 Fixes typo in deploy step 2022-05-06 07:24:29 +02:00
Lukasz Lenart 8e49362bf8 Uses Maven Wrapper in Jenkins pipeline 2022-05-06 07:15:13 +02:00
Lukasz Lenart 7541d9ab35 WW-5181 Blocks permanently access to static methods 2022-05-05 12:28:09 +02:00
Lukasz Lenart c82fdb5e76 Upgrades Maven Wrapper 2022-05-05 12:27:51 +02:00
Lukasz Lenart f40f9f15e5 Merge pull request #547 from apache/dependabot/maven/junit-junit-4.13.1
Bump junit from 4.13 to 4.13.1
2022-05-04 11:18:43 +02:00
dependabot[bot] 2d9cdb73a5 Bump junit from 4.13 to 4.13.1
Bumps [junit](https://github.com/junit-team/junit4) from 4.13 to 4.13.1.
- [Release notes](https://github.com/junit-team/junit4/releases)
- [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.13.1.md)
- [Commits](https://github.com/junit-team/junit4/compare/r4.13...r4.13.1)

---
updated-dependencies:
- dependency-name: junit:junit
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-04 08:52:18 +00:00
Lukasz Lenart d07a496d5c Uses common Maven Compiler plugin to set proper source and target version 2022-05-04 10:45:02 +02:00
Lukasz Lenart bef1f67d4a Upgrades Maven Jetty Plugin to work with JDK8+ 2022-05-04 10:44:54 +02:00
Lukasz Lenart 05ca1ff087 Ties cache extension points with implementation 2022-05-02 09:23:31 +02:00
JCgH4164838Gh792C124B5 199f356669 Update:
- Added some IDE-recommended annotations and cleanup to some of the
  modified files.
- Applied easier-to-read/differentiate names "ognlExpressionCacheFactory"
  and "ognlBeanInfoCacheFactory" for the cache factory configuration
   extension points.
- Reorded default configuration factory init for the cache factories (did
  not help extension override).
- Cleanup of parameterized OgnlUtil constructor.
- Added extension point aliases to StrutsBeanSelectionProvider.
- Added beaninfo for cache factories to ConstantConfig (did not help
  extension override).
- Added cache factory references to default.properties, struts-default.xml.
2022-05-01 16:25:16 -04:00
Lukasz Lenart e2718aa482 Enables branch autodeleting after PR has been merged 2022-04-07 14:43:38 +02:00
Lukasz Lenart b73fec4de0 Drops unused class 2022-04-04 17:12:33 +02:00
Lukasz Lenart c2795d2e30 Uses project specific folder 2022-03-28 13:09:21 +02:00
Lukasz Lenart a987e31a0e Upgrades OWASP Dependency Check plugin to version 7.0.1 2022-03-28 07:45:51 +02:00
Lukasz Lenart 6aff5b7faa WW-5174 Upgrades Jackson-Core to version 2.13.2 and Jackson-Databind to 2.13.2.1 2022-03-28 07:26:50 +02:00
JCgH4164838Gh792C124B5 084c66723d Update:
- Implement a no-parameter build method in OgnlCacheFactory.
- Update OgnlUtil to use no-parameter cache build method.
- Add an additional code coverage test.
2022-03-20 20:39:14 -04:00
Lukasz Lenart 5b40f9f48e WW-5172 Upgrades Freemarker to 2.3.31 version 2022-03-15 14:32:02 +01:00
Lukasz Lenart e9d0a5518c Cleans up code 2022-03-15 14:27:57 +01:00
JCgH4164838Gh792C124B5 4c37011020 Merge branch 'master' of https://github.com/JCgH4164838Gh792C124B5/struts into localS2_26_OgnlUtilOptionalCache1
# Conflicts:
#	core/src/main/java/com/opensymphony/xwork2/config/providers/StrutsDefaultConfigurationProvider.java
#	core/src/main/resources/struts-default.xml

Manually resolved conflicts.
2022-03-06 21:51:33 -05:00
JCgH4164838Gh792C124B5 fbb31ee65b Update:
- Refactored the cache design to utilize a factory pattern.
- Updated unit tests to match refactoring.
2022-03-06 21:18:21 -05:00
Lukasz Lenart c094052969 Uses Java Reflections instead BeanUtils to read property 2022-03-04 12:52:33 +01:00
Lukasz Lenart 84164c422e Drops unused dependency 2022-03-03 12:18:11 +01:00
Lukasz Lenart 8b11a3873a WW-5171 Upgrades Log4j to version 2.17.2 2022-03-01 08:10:00 +01:00
Lukasz Lenart 115fef2af7 [maven-release-plugin] prepare for next development iteration 2022-02-24 07:11:22 +01:00
Lukasz Lenart 0c9f9c3400 [maven-release-plugin] prepare release STRUTS_6_0_0_RC3 2022-02-24 07:11:13 +01:00
Lukasz Lenart 3e65ec1687 Merge pull request #529 from apache/WW-5016-uses-proper-format
[WW-5016] Adds support for LocalDate and adjusts tests to use the new Java 8 API
2022-02-23 10:18:37 +01:00
Lukasz Lenart 59932a5179 WW-5016 Reduces calls to TextProvider 2022-02-21 19:31:52 +01:00
Lukasz Lenart 4746a49d17 WW-5016 Improves description of different patterns 2022-02-21 08:47:36 +01:00
Lukasz Lenart 059514d6ca Merge pull request #531 from apache/WW-5168-submit-unchecked
[WW-5168] Support submit unchecked in Javatemplates and fixes logic for FTL template
2022-02-20 16:58:26 +01:00
Lukasz Lenart 5648721d07 WW-5016 Fixes typo in instance name 2022-02-20 13:33:49 +01:00
Lukasz Lenart e3dff7691e WW-5016 Introduces different format adapters to allow use different APIs 2022-02-20 13:29:46 +01:00
Lukasz Lenart 38c63334a4 WW-4963 Adds missing references to the new versions 2022-02-20 10:37:25 +01:00
Lukasz Lenart fe7959e5d8 WW-5168 Adjusts tests to flipped if condition 2022-02-13 10:19:01 +01:00
Lukasz Lenart d454384a4b WW-5168 Reverts condition in if statement 2022-02-13 10:00:20 +01:00
Lukasz Lenart 1c3cab2e6f WW-5168 Fixes disabled attribute across all the Javatemplate tags 2022-02-10 18:42:10 +01:00
Lukasz Lenart b1e9eae01c WW-5168 Support submit unchecked in Javatemplates and fixes logic for all FTL template 2022-02-09 17:33:46 +01:00
Lukasz Lenart 804e1546f1 WW-5016 Adds support for LocalDate and adjusts tests to use the new Java 8 API 2022-02-06 12:23:39 +01:00
Lukasz Lenart f0b24d17da WW-5167 Upgrades Xstream to version 1.4.19 to address CVE-2021-43859 2022-02-06 11:26:51 +01:00
JCgH4164838Gh792C124B5 4d8108e766 Update:
- Add support for an optional basic LRU cache for OGNL expressions and
  OGNL BeanInfo.
- Add support for cache limits applying to both normal and LRU caches.
  For a normal cache the entire cache will flush when the limit is reached.
- Add flags to allow switching between normal and LRU caches, and setting
  the maximum sizes.
2022-01-30 20:57:59 -05:00
Lukasz Lenart 14c3a14c8f [maven-release-plugin] prepare for next development iteration 2022-01-29 17:36:34 +01:00
Lukasz Lenart f997d9b1ed [maven-release-plugin] prepare release STRUTS_6_0_0_RC2 2022-01-29 17:36:26 +01:00
Lukasz Lenart 3a7aabdcbc WW-5166 Upgrades OGNL to version 3.3.2 2022-01-29 17:26:55 +01:00
Lukasz Lenart 2851967a2e Merge pull request #526 from apache/WW-5115-dmi-logging
[WW-5115] Reduces logging for ignored DMI related params when DMI is disabled
2022-01-29 17:21:55 +01:00
Yasser Zamani 457a7c918f Merge pull request #527 from apache/WW-5112-reduce-logging
[WW-5112] Reduces logging in logic around missing resource key
2022-01-29 12:08:29 +03:30
Lukasz Lenart 1e32510ea6 Merge pull request #523 from apache/WW-5022-escape-false
[WW-5022] Sets escapeHtmlBody to false by default and defines a new flag
2022-01-23 13:02:48 +01:00
Lukasz Lenart 56595356d3 WW-5115 Reduces logging for ignored DMI related params when DMI is disabled 2022-01-23 11:16:35 +01:00
Lukasz Lenart e59abe4e34 WW-5164 Removes deprecated class 2022-01-23 10:58:14 +01:00
Lukasz Lenart 61e6d342ba WW-5022 Cleans up escapeHtmlBody flag with default value 2022-01-23 10:45:14 +01:00
Lukasz Lenart 0a0573952f WW-5112 Reduces logging in logic around missing resource key 2022-01-23 10:38:12 +01:00
Yasser Zamani 79e1b22cc6 Merge pull request #525 from apache/WW-5117-evaluate-dynamic-attributes-cherrypick
[WW-5117] Evaluates dynamic attributes - cherrypick
2022-01-18 10:04:11 +03:30
Lukasz Lenart 7ed77f5f04 WW-5117 Adds a new attribute to test the new behaviour 2022-01-14 09:22:21 +01:00
Lukasz Lenart 41e729205c WW-5117 Uses attribute translations in tests 2022-01-13 10:32:53 +01:00
Lukasz Lenart 9c05422bbe WW-5117 Adjusts expression checking 2022-01-13 10:28:30 +01:00
Lukasz Lenart b0e18e9c57 WW-5117 Uses translateVariables instead of findString to allow join expressions 2022-01-13 10:25:14 +01:00
Lukasz Lenart a4a4e9e262 WW-5117 Does a conditional evaluation depending on the tag
Some tags requires to perform a lazy evaluation which can only happen in ftl template as performing it in the component class is not possible
2022-01-13 10:23:02 +01:00
Lukasz Lenart 893a8924eb WW-5117 Evaluates dynamic attributes when assigning them to tag
Reverts https://github.com/apache/struts/pull/447/commits/8bbe1949e17d58e1b5aef9c71e1279ad12ad7ba7#diff-0a39f082871f48bd14037ab2e3a3911b0b1046506c1d93338024d77d412a7075L305-L309
2022-01-13 10:21:25 +01:00
Lukasz Lenart 3d39fba0db WW-5117 Restores previous behavior where tag was before action on stack 2022-01-13 10:17:33 +01:00
Lukasz Lenart effe6875b7 WW-5022 Pass escapeHtmlBody flag to JavaTemplates tags 2022-01-07 10:07:13 +01:00
Yasser Zamani 60e1212bcc Merge pull request #522 from apache/WW-4792-constants-cleanup
[WW-4792] Uses proper names for constants
2022-01-06 19:20:54 +03:30
Lukasz Lenart 7a696523f0 WW-5022 Documents that setting escapeHtmlBody per tag takes precedence over global flag 2022-01-04 19:49:01 +01:00
Lukasz Lenart ecef56b546 WW-5022 Sets escapeHtmlBody to false by default and defines new flag to switch to true globally 2022-01-04 09:01:21 +01:00
Lukasz Lenart bab276e29d Uses Log4j instead of SLF4J 2022-01-01 14:26:05 +01:00
Lukasz Lenart 5c9299b9c4 WW-4792 Uses proper names for constants 2022-01-01 14:04:38 +01:00
Lukasz Lenart 767f40b509 Merge pull request #521 from Chromico/master
Update log4j to 2.17.1 to address CVE-2021-44832.
2021-12-30 13:29:47 +01:00
Chromico Rek 0b5670f258 Update log4j to 2.17.1 2021-12-30 15:56:21 +04:00
Lukasz Lenart 74f1f86a26 Merge pull request #520 from JCgH4164838Gh792C124B5/localS2_26_RestShowcaseFix1
REST Showcase fix for broken JSPs using S2 link tags
2021-12-29 08:30:23 +01:00
JCgH4164838Gh792C124B5 583d399c24 Update:
- Fix REST Showcase JSP issue for s:link tags that replaced HTML link tags.
- s:link tags cannot use JSP EL in them.  Modified JSPs to use scriptlet,
JSP expression, and OGNL expression to work with s:link tag.
2021-12-27 21:23:35 -05:00
Lukasz Lenart a4e7034eab Merge pull request #519 from hboutemy/patch-3
upgrade maven-war-plugin to 3.3.1
2021-12-26 13:15:35 +01:00
Hervé Boutemy 62c85e8d11 upgrade maven-war-plugin to 3.3.1
will bring reproducible war
2021-12-24 16:22:08 +01:00
Lukasz Lenart 50e2c4ea91 Merge pull request #518 from hboutemy/patch-2
upgrade Felix maven-bundle-plugin to 5.1.3
2021-12-24 16:20:00 +01:00
Hervé Boutemy e1154d8b3a upgrade Felix maven-bundle-plugin to 5.1.3
this will fix Reproducible Builds issue
2021-12-24 16:17:14 +01:00
Lukasz Lenart 56c7621fd1 Merge pull request #517 from sepe81/update-spring-to-4.3.30
[WW-5161] Update spring to 4.3.30
2021-12-21 15:32:21 +01:00
Sebastian Peters 9a8301d51d WW-5161 Update spring to 4.3.30 2021-12-21 15:21:39 +01:00
Lukasz Lenart f2d522ed86 Creates LICENSE file with Apache-2.0 license 2021-12-21 07:33:04 +01:00
Lukasz Lenart a474fc97d0 Merge pull request #516 from sepe81/upgrade-to-mvnw-3.1.0
Update mvnw to 3.1.0 and migrate from takari to apache
2021-12-21 07:17:49 +01:00
Sebastian Peters 67000dd475 Update mvnw to 3.1.0 and migrate from takari to apache
see https://github.com/apache/maven-wrapper

changes generated by `mvn wrapper:wrapper`
2021-12-20 21:52:37 +01:00
Lukasz Lenart 80b39e2212 Adds moduleKey to avoid duplication 2021-12-20 08:32:14 +01:00
Lukasz Lenart 90001a98dd Moves Sonar config into pom.xml 2021-12-20 07:58:35 +01:00
Lukasz Lenart 867492d485 Upgrades Maven Dependency Check plugin 2021-12-20 07:42:04 +01:00
Lukasz Lenart 57f76d94f2 [maven-release-plugin] prepare for next development iteration 2021-12-19 12:50:38 +01:00
186 changed files with 4523 additions and 1841 deletions
+4
View File
@@ -1,3 +1,4 @@
# Documentation https://s.apache.org/asfyaml
notifications:
commits: commits@struts.apache.org
# Send all issue emails (new, closed, comments) to issues@
@@ -8,3 +9,6 @@ notifications:
pullrequests_comment: issues@struts.apache.org
# Link opened PRs with JIRA
jira_options: link label worklog
github:
del_branch_on_merge: true
+18 -2
View File
@@ -1,2 +1,18 @@
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
# 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.
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.5/apache-maven-3.8.5-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar
+4 -7
View File
@@ -14,17 +14,14 @@ global:
matrix:
include:
- jdk: oraclejdk8
env: STRUTS_IT=true # do integration tests and coverage reports when jdk7,9 and 11 tests prospered
env: STRUTS_IT=true # do integration tests and coverage reports when jdk 9 and 11 tests prospered
script:
- if [ "$STRUTS_IT" == "true" ]; then
./mvnw clean verify org.jacoco:jacoco-maven-plugin:report org.jacoco:jacoco-maven-plugin:report-integration org.eluder.coveralls:coveralls-maven-plugin:report -Ptravis-coveralls -DskipAssembly -B;
./mvnw clean install -DskipTests -DskipAssembly -B;
./mvnw test org.jacoco:jacoco-maven-plugin:report org.jacoco:jacoco-maven-plugin:report-integration org.eluder.coveralls:coveralls-maven-plugin:report -Ptravis-coveralls -DskipAssembly -B;
else
if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then
./mvnw test -DskipAssembly -B;
else
./mvnw test -DskipAssembly -Dupdate-impact -B;
fi;
./mvnw clean package test -DskipAssembly -B;
fi;
cache:
Vendored
+10 -10
View File
@@ -39,12 +39,12 @@ pipeline {
stages {
stage('Build') {
steps {
sh 'mvn -B clean install -DskipTests -DskipAssembly'
sh './mvnw -B clean install -DskipTests -DskipAssembly'
}
}
stage('Test') {
steps {
sh 'mvn -B test'
sh './mvnw -B test'
}
post {
always {
@@ -74,12 +74,12 @@ pipeline {
stages {
stage('Build') {
steps {
sh 'mvn -B clean install -DskipTests -DskipAssembly'
sh './mvnw -B clean install -DskipTests -DskipAssembly'
}
}
stage('Test') {
steps {
sh 'mvn -B test'
sh './mvnw -B test'
}
post {
always {
@@ -94,7 +94,7 @@ pipeline {
}
steps {
withCredentials([string(credentialsId: 'asf-struts-sonarcloud', variable: 'SONARCLOUD_TOKEN')]) {
sh 'mvn sonar:sonar -DskipAssembly -Dsonar.projectKey=apache_struts -Dsonar.organization=apache -Dsonar.host.url=https://sonarcloud.io -Dsonar.login=${SONARCLOUD_TOKEN}'
sh './mvnw sonar:sonar -DskipAssembly -Dsonar.login=${SONARCLOUD_TOKEN}'
}
}
}
@@ -119,12 +119,12 @@ pipeline {
stages {
stage('Build') {
steps {
sh 'mvn -B clean install -DskipTests -DskipAssembly'
sh './mvnw -B clean install -DskipTests -DskipAssembly'
}
}
stage('Test') {
steps {
sh 'mvn -B test'
sh './mvnw -B test'
// step([$class: 'JiraIssueUpdater', issueSelector: [$class: 'DefaultIssueSelector'], scm: scm])
}
post {
@@ -142,7 +142,7 @@ pipeline {
dir("local-snapshots-dir/") {
deleteDir()
}
sh 'mvn -B source:jar javadoc:jar -DskipAssembbly'
sh './mvnw -B source:jar javadoc:jar -DskipTests -DskipAssembly'
}
}
stage('Deploy Snapshot') {
@@ -151,7 +151,7 @@ pipeline {
}
steps {
withCredentials([file(credentialsId: 'lukaszlenart-repository-access-token', variable: 'CUSTOM_SETTINGS')]) {
sh 'mvn -s \${CUSTOM_SETTINGS} deploy'
sh './mvnw -s \${CUSTOM_SETTINGS} deploy -DskipTests -DskipAssembly'
}
}
}
@@ -165,7 +165,7 @@ pipeline {
configName: 'Nightlies',
transfers: [
sshTransfer(
remoteDirectory: '/x1/dist/struts',
remoteDirectory: '/struts/snapshot',
removePrefix: 'assembly/target/assembly/out',
sourceFiles: 'assembly/target/assembly/out/struts-*.zip'
)
+201
View File
@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>6.0.0-RC1</version>
<version>6.0.0-RC4</version>
</parent>
<artifactId>struts2-apps</artifactId>
<packaging>pom</packaging>
+4 -10
View File
@@ -24,12 +24,12 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-apps</artifactId>
<version>6.0.0-RC1</version>
<version>6.0.0-RC4</version>
</parent>
<artifactId>struts2-rest-showcase</artifactId>
<packaging>war</packaging>
<version>6.0.0-RC1</version>
<version>6.0.0-RC4</version>
<name>Struts 2 Rest Showcase Webapp</name>
<description>Struts 2 Rest Showcase Example</description>
@@ -108,18 +108,12 @@
<finalName>struts2-rest-showcase</finalName>
<plugins>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>8.1.16.v20140903</version>
<version>9.4.46.v20220331</version>
<configuration>
<stopKey>CTRL+C</stopKey>
<stopPort>8999</stopPort>
<systemProperties>
<systemProperty>
<name>xwork.loggerFactory</name>
<value>com.opensymphony.xwork2.util.logging.log4j2.Log4j2LoggerFactory</value>
</systemProperty>
</systemProperties>
<scanIntervalSeconds>10</scanIntervalSeconds>
<webAppSourceDirectory>${basedir}/src/main/webapp/</webAppSourceDirectory>
<webAppConfig>
@@ -27,8 +27,10 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Orders</title>
<s:link href="${pageContext.request.contextPath}/css/bootstrap.min.css" rel="stylesheet"></s:link>
<s:link href="${pageContext.request.contextPath}/css/app.css" rel="stylesheet"></s:link>
<!-- Using a standard HTML link tag with JSP EL to get the contextPath may be simpler, but this is an equivalent for s:link -->
<s:set var="pageContextPath"><%=((HttpServletRequest)request).getContextPath()%></s:set>
<s:link href="%{#pageContextPath}/css/bootstrap.min.css" rel="stylesheet"></s:link>
<s:link href="%{#pageContextPath}/css/app.css" rel="stylesheet"></s:link>
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
@@ -27,8 +27,10 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Orders</title>
<s:link href="${pageContext.request.contextPath}/css/bootstrap.min.css" rel="stylesheet"></s:link>
<s:link href="${pageContext.request.contextPath}/css/app.css" rel="stylesheet"></s:link>
<!-- Using a standard HTML link tag with JSP EL to get the contextPath may be simpler, but this is an equivalent for s:link -->
<s:set var="pageContextPath"><%=((HttpServletRequest)request).getContextPath()%></s:set>
<s:link href="%{#pageContextPath}/css/bootstrap.min.css" rel="stylesheet"></s:link>
<s:link href="%{#pageContextPath}/css/app.css" rel="stylesheet"></s:link>
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
@@ -27,8 +27,11 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Orders</title>
<s:link href="${pageContext.request.contextPath}/css/bootstrap.min.css" rel="stylesheet"></s:link>
<s:link href="${pageContext.request.contextPath}/css/app.css" rel="stylesheet"></s:link>
<!-- Using a standard HTML link tag with JSP EL to get the contextPath may be simpler, but this is an equivalent for s:link -->
<s:set var="pageContextPath"><%=((HttpServletRequest)request).getContextPath()%></s:set>
<s:link href="%{#pageContextPath}/css/bootstrap.min.css" rel="stylesheet"></s:link>
<s:link href="%{#pageContextPath}/css/app.css" rel="stylesheet"></s:link>
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
@@ -27,8 +27,10 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Orders</title>
<s:link href="${pageContext.request.contextPath}/css/bootstrap.min.css" rel="stylesheet"></s:link>
<s:link href="${pageContext.request.contextPath}/css/app.css" rel="stylesheet"></s:link>
<!-- Using a standard HTML link tag with JSP EL to get the contextPath may be simpler, but this is an equivalent for s:link -->
<s:set var="pageContextPath"><%=((HttpServletRequest)request).getContextPath()%></s:set>
<s:link href="%{#pageContextPath}/css/bootstrap.min.css" rel="stylesheet"></s:link>
<s:link href="%{#pageContextPath}/css/app.css" rel="stylesheet"></s:link>
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
@@ -27,8 +27,10 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Orders</title>
<s:link href="${pageContext.request.contextPath}/css/bootstrap.min.css" rel="stylesheet"></s:link>
<s:link href="${pageContext.request.contextPath}/css/app.css" rel="stylesheet"></s:link>
<!-- Using a standard HTML link tag with JSP EL to get the contextPath may be simpler, but this is an equivalent for s:link -->
<s:set var="pageContextPath"><%=((HttpServletRequest)request).getContextPath()%></s:set>
<s:link href="%{#pageContextPath}/css/bootstrap.min.css" rel="stylesheet"></s:link>
<s:link href="%{#pageContextPath}/css/app.css" rel="stylesheet"></s:link>
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
+8 -20
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-apps</artifactId>
<version>6.0.0-RC1</version>
<version>6.0.0-RC4</version>
</parent>
<artifactId>struts2-showcase</artifactId>
@@ -101,7 +101,7 @@
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
@@ -144,7 +144,7 @@
<dependency>
<groupId>net.sourceforge.htmlunit</groupId>
<artifactId>htmlunit</artifactId>
<version>2.39.0</version>
<version>2.61.0</version>
<scope>test</scope>
</dependency>
@@ -155,15 +155,6 @@
<version>6.1.2.Final</version>
</dependency>
<!-- The Servlet API mocks in Spring Framework 4.x only supports Servlet 3.0 and higher.
This is only necessary in tests-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
@@ -171,7 +162,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>3.0.0-M4</version>
<version>3.0.0-M6</version>
<configuration>
<includes>
<include>it.org.apache.struts2.showcase.*Test</include>
@@ -193,16 +184,16 @@
</executions>
</plugin>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>8.1.16.v20140903</version>
<version>9.4.46.v20220331</version>
<configuration>
<stopKey>CTRL+C</stopKey>
<stopPort>8999</stopPort>
<systemProperties>
<systemProperty>
<name>log4j.configuration</name>
<value>file:${basedir}/src/main/resources/log4j.properties</value>
<value>file:${basedir}/src/main/resources/log4j2.xml</value>
</systemProperty>
<systemProperty>
<name>slf4j</name>
@@ -223,11 +214,8 @@
<goals>
<!-- stop any previous instance to free up the port -->
<goal>stop</goal>
<goal>run-forked</goal>
<goal>start</goal>
</goals>
<configuration>
<waitForChild>false</waitForChild>
</configuration>
</execution>
<execution>
<id>stop-jetty</id>
@@ -27,22 +27,24 @@ import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.ParentPackage;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.interceptor.validation.SkipValidation;
import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.NotBlank;
import org.hibernate.validator.constraints.ScriptAssert;
import org.hibernate.validator.constraints.URL;
import javax.validation.constraints.*;
import javax.validation.constraints.Email;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import java.util.Date;
/**
* <!-- START SNIPPET: beanValidatationExample -->
*/
// <!-- START SNIPPET: beanValidationExample -->
@Namespace("/bean-validation")
@ParentPackage("bean-validation")
@Action(results = {
@Result(name = "input", location = "bean-validation.jsp"),
@Result(name = "success", location = "/WEB-INF/validation/successFieldValidatorsExample.jsp")
@Result(name = "input", location = "bean-validation.jsp"),
@Result(name = "success", location = "/WEB-INF/validation/successFieldValidatorsExample.jsp")
})
@FieldMatch(first = "fieldExpressionValidatorField", second = "requiredValidatorField", message = "requiredValidatorField and fieldExpressionValidatorField are not matching")
@ScriptAssert(lang = "javascript", script = "_this.dateValidatorField != null && _this.dateValidatorField.before(new java.util.Date())", message = "Date need to before now")
@@ -82,10 +84,10 @@ public class BeanValidationExampleAction extends ActionSupport {
private String fieldExpressionValidatorField = null;
@Action(value = "bean-validation", results = {
@Result(name = "success", location = "bean-validation.jsp")
@Result(name = "success", location = "bean-validation.jsp")
})
@SkipValidation
public String beanValidation(){
public String beanValidation() {
return SUCCESS;
}
@@ -150,7 +152,7 @@ public class BeanValidationExampleAction extends ActionSupport {
}
public void setFieldExpressionValidatorField(
String fieldExpressionValidatorField) {
String fieldExpressionValidatorField) {
this.fieldExpressionValidatorField = fieldExpressionValidatorField;
}
@@ -163,8 +165,4 @@ public class BeanValidationExampleAction extends ActionSupport {
}
}
/**
* <!-- END SNIPPET: beanValidatationExample -->
*/
// <!-- END SNIPPET: beanValidationExample -->
@@ -45,66 +45,46 @@
</bean>
<bean id="chatLoginAction" class="org.apache.struts2.showcase.chat.ChatLoginAction" scope="prototype">
<constructor-arg index="0">
<ref local="chatService"/>
</constructor-arg>
<constructor-arg index="0" ref="chatService"/>
</bean>
<bean id="chatLogoutAction" class="org.apache.struts2.showcase.chat.ChatLogoutAction" scope="prototype">
<constructor-arg index="0">
<ref local="chatService"/>
</constructor-arg>
<constructor-arg index="0" ref="chatService"/>
</bean>
<bean id="usersAvailableAction" class="org.apache.struts2.showcase.chat.UsersAvailableAction" scope="prototype">
<constructor-arg index="0">
<ref local="chatService"/>
</constructor-arg>
<constructor-arg index="0" ref="chatService"/>
</bean>
<bean id="roomsAvailableAction" class="org.apache.struts2.showcase.chat.RoomsAvailableAction" scope="prototype">
<constructor-arg index="0">
<ref local="chatService"/>
</constructor-arg>
<constructor-arg index="0" ref="chatService"/>
</bean>
<bean id="crudRoomAction" class="org.apache.struts2.showcase.chat.CrudRoomAction" scope="prototype">
<constructor-arg index="0">
<ref local="chatService"/>
</constructor-arg>
<constructor-arg index="0" ref="chatService"/>
</bean>
<bean id="enterRoomAction" class="org.apache.struts2.showcase.chat.EnterRoomAction" scope="prototype">
<constructor-arg index="0">
<ref local="chatService"/>
</constructor-arg>
<constructor-arg index="0" ref="chatService"/>
</bean>
<bean id="messagesAvailableInRoomAction" class="org.apache.struts2.showcase.chat.MessagesAvailableInRoomAction"
scope="prototype">
<constructor-arg index="0">
<ref local="chatService"/>
</constructor-arg>
<constructor-arg index="0" ref="chatService"/>
</bean>
<bean id="sendMessageToRoomAction" class="org.apache.struts2.showcase.chat.SendMessageToRoomAction"
scope="prototype">
<constructor-arg index="0">
<ref local="chatService"/>
</constructor-arg>
<constructor-arg index="0" ref="chatService"/>
</bean>
<bean id="usersAvailableInRoomAction" class="org.apache.struts2.showcase.chat.UsersAvailableInRoomAction"
scope="prototype">
<constructor-arg index="0">
<ref local="chatService"/>
</constructor-arg>
<constructor-arg index="0" ref="chatService"/>
</bean>
<bean id="exitRoomAction" class="org.apache.struts2.showcase.chat.ExitRoomAction" scope="prototype">
<constructor-arg index="0">
<ref local="chatService"/>
</constructor-arg>
<constructor-arg index="0" ref="chatService"/>
</bean>
@@ -129,9 +109,7 @@
</bean>
<bean id="startHangmanAction" class="org.apache.struts2.showcase.hangman.StartHangmanAction" scope="prototype">
<constructor-arg index="0">
<ref local="hangmanService"/>
</constructor-arg>
<constructor-arg index="0" ref="hangmanService"/>
</bean>
<bean id="guessCharacterAction" class="org.apache.struts2.showcase.hangman.GuessCharacterAction" scope="prototype"/>
@@ -113,7 +113,7 @@
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li><s:a value="%{home}" escapeHtmlBody="false"><i class="glyphicon glyphicon-home"></i> Home</s:a></li>
<li><s:a value="%{home}"><i class="glyphicon glyphicon-home"></i> Home</s:a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<i class="glyphicon glyphicon-cog"></i> Configuration
@@ -239,7 +239,7 @@
</li>
<li><s:a value="/person/index.html">Person Manager</s:a></li>
<li><s:a value="/skill/index.html">CRUD</s:a></li>
<li><s:a value="/wait/index.html" escapeHtmlBody="false">Execute &amp; Wait</s:a></li>
<li><s:a value="/wait/index.html">Execute &amp; Wait</s:a></li>
<li><s:a value="/token/index.html">Token</s:a></li>
<li><s:url var="url" namespace="/modelDriven" action="modelDriven"/><s:a
href="%{url}">Model Driven</s:a></li>
@@ -39,7 +39,7 @@
</div>
<s:url var="url" action="download"/>
<s:a href="%{url}" cssClass="btn btn-large btn-info" escapeHtmlBody="false"><i class="icon-picture"></i> Download image file.</s:a>
<s:a href="%{url}" cssClass="btn btn-large btn-info"><i class="icon-picture"></i> Download image file.</s:a>
</div>
<div class="col-md-6" style="text-align: center;">
<div class="alert alert-info">
@@ -47,7 +47,7 @@
</div>
<s:url var="url" action="download2"/>
<s:a href="%{url}" cssClass="btn btn-large btn-info" escapeHtmlBody="false"><i class="icon-download-alt"></i> Download ZIP file.</s:a>
<s:a href="%{url}" cssClass="btn btn-large btn-info"><i class="icon-download-alt"></i> Download ZIP file.</s:a>
</div>
</div>
</div>
@@ -125,7 +125,7 @@
<#else>
<img src="<@s.property value="%{#looseImageUrl}" />" width="381" height="44" />
</#if>
<@s.a href="%{#startHref}" escapeHtmlBody="false">
<@s.a href="%{#startHref}">
<img src="<@s.property value="%{#startImageUrl}" />" width="250" height="43" />
</@s.a>
<#else>
@@ -139,7 +139,7 @@
<@s.a href="%{#url}"
id="%{#currentCharacter}"
escapeHtmlBody="false"
>
<img height="36" alt="" src="<@s.property value="%{#chalkboardImageUrl}" />" width="36" border="0" />
</@s.a>
@@ -59,7 +59,7 @@
</div>
</div>
<div class="form-actions">
<@s.submit value="Create person" cssClass="btn btn-primary" escapeHtmlBody="false"/>
<@s.submit value="Create person" cssClass="btn btn-primary"/>
</div>
</@s.form>
</div>
@@ -35,7 +35,7 @@
<p>The text you've entered is ${text!''}<p/>
<@s.a href="javascript:history.back();" cssClass="btn btn-info" escapeHtmlBody="false"><i class="icon icon-arrow-left"></i> Back</@s.a>
<@s.a href="javascript:history.back();" cssClass="btn btn-info"><i class="icon icon-arrow-left"></i> Back</@s.a>
</div>
</div>
</div>
@@ -57,13 +57,13 @@
<@s.textfield label="Enter Some Text" name="text" />
<@s.submit action="actionPrefix" value="%{'action prefix'}" cssClass="btn" escapeHtmlBody="false" />
<@s.submit action="actionPrefix" value="%{'action prefix'}" cssClass="btn" />
<@s.submit method="alternateMethod" value="%{'method prefix'}" cssClass="btn" escapeHtmlBody="false" />
<@s.submit method="alternateMethod" value="%{'method prefix'}" cssClass="btn" />
<@s.submit value="Normal Submit" cssClass="btn" escapeHtmlBody="false" />
<@s.submit value="Normal Submit" cssClass="btn" />
<@s.submit action="redirectActionPrefixAction" value="%{'redirectAction without prefix'}" cssClass="btn" escapeHtmlBody="false" />
<@s.submit action="redirectActionPrefixAction" value="%{'redirectAction without prefix'}" cssClass="btn" />
</@s.form>
</div>
@@ -32,10 +32,10 @@
<div class="col-md-12">
<p>You have come to this page because you used an <strong>method</strong> prefix.<p/>
<p>The text you've enter is ${text!''}<p/>
<@s.a href="javascript:history.back();" cssClass="btn btn-info" escapeHtmlBody="false"><i class="icon icon-arrow-left"></i> Back</@s.a>
<@s.a href="javascript:history.back();" cssClass="btn btn-info"><i class="icon icon-arrow-left"></i> Back</@s.a>
</div>
</div>
</div>
@@ -32,10 +32,10 @@
<div class="col-md-12">
<p>You have come to this page because you did a normal submit.<p/>
<p>The text you've enter is %{text}<p/>
<@s.a href="javascript:history.back();" cssClass="btn btn-info" escapeHtmlBody="false"><i class="icon icon-arrow-left"></i> Back</@s.a>
<@s.a href="javascript:history.back();" cssClass="btn btn-info"><i class="icon icon-arrow-left"></i> Back</@s.a>
</div>
</div>
</div>
@@ -32,13 +32,13 @@
<div class="col-md-12">
<p>You have come to this page because you used an 'redirect-action' prefix.<p/>
<p>Because this is a <strong>redirect-action</strong>, the text will be lost, due to a redirection
implies a new request being issued from the client.<p/>
The text you've enter is ${text!''}<p/>
<@s.a href="javascript:history.back();" cssClass="btn btn-info" escapeHtmlBody="false"><i class="icon icon-arrow-left"></i> Back</@s.a>
<@s.a href="javascript:history.back();" cssClass="btn btn-info"><i class="icon icon-arrow-left"></i> Back</@s.a>
</div>
</div>
</div>
@@ -46,7 +46,7 @@
</s:iterator>
<s:url var="url" action="showAppendTagDemo" namespace="/tags/non-ui/appendIteratorTag" />
<s:a href="%{#url}" cssClass="btn btn-info" escapeHtmlBody="false"><i class="icon icon-arrow-left"></i> Back To Input</s:a>
<s:a href="%{#url}" cssClass="btn btn-info"><i class="icon icon-arrow-left"></i> Back To Input</s:a>
</div>
</div>
</div>
@@ -41,7 +41,7 @@
<s:url var="url" action="showGeneratorTagDemo" namespace="/tags/non-ui/iteratorGeneratorTag" />
<s:a href="%{#url}" cssClass="btn btn-info" escapeHtmlBody="false"><i class="icon icon-arrow-left"></i> Back To Input</s:a>
<s:a href="%{#url}" cssClass="btn btn-info"><i class="icon icon-arrow-left"></i> Back To Input</s:a>
</div>
</div>
</div>
@@ -45,7 +45,7 @@
</s:iterator>
<s:url var="url" action="showMergeTagDemo" namespace="/tags/non-ui/mergeIteratorTag" />
<s:a href="%{#url}" cssClass="btn btn-info" escapeHtmlBody="false"><i class="icon icon-arrow-left"></i> Back To Input</s:a>
<s:a href="%{#url}" cssClass="btn btn-info"><i class="icon icon-arrow-left"></i> Back To Input</s:a>
</div>
</div>
</div>
@@ -42,7 +42,7 @@
</s:subset>
<s:url var="url" action="showSubsetTagDemo" namespace="/tags/non-ui/subsetIteratorTag" />
<s:a href="%{#url}" cssClass="btn btn-info" escapeHtmlBody="false"><i class="icon icon-arrow-left"></i> Back To Input</s:a>
<s:a href="%{#url}" cssClass="btn btn-info"><i class="icon icon-arrow-left"></i> Back To Input</s:a>
</div>
</div>
</div>
@@ -51,7 +51,7 @@
<@s.form action="transfer4">
<@s.token/>
<@s.textfield label="Amount" name="amount" required=true value="400"/>
<@s.submit value="Transfer money" cssClass="btn btn-primary" escapeHtmlBody="false"/>
<@s.submit value="Transfer money" cssClass="btn btn-primary"/>
</@s.form>
</div>
</div>
@@ -32,7 +32,7 @@ public class ActionChainingTest {
try (final WebClient webClient = new WebClient()) {
final HtmlPage page = webClient.getPage(ParameterUtils.getBaseUrl() + "/actionchaining/actionChain1!input");
final String pageAsText = page.asText();
final String pageAsText = page.asNormalizedText();
Assert.assertTrue(pageAsText.contains("Action Chain 1 Property 1: Property Set In Action Chain 1"));
Assert.assertTrue(pageAsText.contains("Action Chain 2 Property 1: Property Set in Action Chain 2"));
Assert.assertTrue(pageAsText.contains("Action Chain 3 Property 1: Property set in Action Chain 3"));
@@ -32,7 +32,7 @@ public class ActionTagExampleTest {
try (final WebClient webClient = new WebClient()) {
final HtmlPage page = webClient.getPage(ParameterUtils.getBaseUrl() + "/tags/ui/actionTagExample!input.action");
final String pageAsText = page.asText();
final String pageAsText = page.asNormalizedText();
Assert.assertTrue(pageAsText.contains("This text is from the called class"));
}
}
@@ -46,7 +46,7 @@ public class AsyncTest {
final DomElement msgs = page2.getElementById("msgs");
Assert.assertEquals("hello", msgs.asText());
Assert.assertEquals("hello", msgs.asNormalizedText());
}
}
}
@@ -44,7 +44,7 @@ public class CRUDTest {
final HtmlSubmitInput button = form.getInputByValue("Save");
final HtmlPage page2 = button.click();
final String page2Text = page2.asText();
final String page2Text = page2.asNormalizedText();
Assert.assertTrue(page2Text.contains("somename1"));
Assert.assertTrue(page2Text.contains("somedescription1"));
@@ -33,7 +33,7 @@ public class ComponentTagExampleTest {
final HtmlPage page = webClient
.getPage(ParameterUtils.getBaseUrl() + "/tags/ui/componentTagExample.action");
final String pageAsText = page.asText();
final String pageAsText = page.asNormalizedText();
Assert.assertTrue(pageAsText.contains("Freemarker Custom Template - parameter 'paramName' - paramValue1"));
Assert.assertTrue(pageAsText.contains("Freemarker Custom Template - parameter 'paramName' - paramValue4"));
Assert.assertTrue(pageAsText.contains("JSP Custom Template - parameter 'paramName' - paramValue2"));
@@ -46,7 +46,7 @@ public class ConversionTest {
final HtmlSubmitInput button = form.getInputByValue("Submit");
final HtmlPage page2 = button.click();
final String page2Text = page2.asText();
final String page2Text = page2.asNormalizedText();
Assert.assertTrue(page2Text.contains("SET 0 Name: name0"));
Assert.assertTrue(page2Text.contains("SET 0 Age: 0"));
@@ -71,7 +71,7 @@ public class ConversionTest {
final HtmlSubmitInput button = form.getInputByValue("Submit");
final HtmlPage page2 = button.click();
final String page2Text = page2.asText();
final String page2Text = page2.asNormalizedText();
Assert.assertTrue(page2Text.contains("id0 -> address0"));
Assert.assertTrue(page2Text.contains("id1 -> address1"));
@@ -92,7 +92,7 @@ public class ConversionTest {
final HtmlSubmitInput button = form.getInputByValue("Submit");
final HtmlPage page2 = button.click();
final String page2Text = page2.asText();
final String page2Text = page2.asNormalizedText();
Assert.assertTrue(page2Text.contains("ADD"));
Assert.assertTrue(page2Text.contains("MINUS"));
@@ -43,12 +43,12 @@ public class ExecAndWaitTest {
final HtmlSubmitInput button = form.getInputByValue("submit");
final HtmlPage page2 = button.click();
Assert.assertTrue(page2.asText().contains("We are processing your request. Please wait."));
Assert.assertTrue(page2.asNormalizedText().contains("We are processing your request. Please wait."));
// hit it again
final HtmlPage page3 = webClient
.getPage(ParameterUtils.getBaseUrl() + "/wait/longProcess1.action?time=1000");
Assert.assertTrue(page3.asText().contains("We are processing your request. Please wait."));
Assert.assertTrue(page3.asNormalizedText().contains("We are processing your request. Please wait."));
}
}
}
@@ -43,6 +43,7 @@ public class FileDownloadTest {
}
}
@Test
public void testZip() throws Exception {
try (final WebClient webClient = new WebClient()) {
final Page page = webClient.getPage(ParameterUtils.getBaseUrl() + "/filedownload/download2.action");
@@ -36,11 +36,11 @@ public class FreeMarkerManagerTest {
final DomElement date = page.getElementById("todaysDate");
Assert.assertNotNull(date);
Assert.assertTrue(date.asText().length() > 0);
Assert.assertTrue(date.asNormalizedText().length() > 0);
final DomElement time = page.getElementById("timeNow");
Assert.assertNotNull(time);
Assert.assertTrue(time.asText().length() > 0);
Assert.assertTrue(time.asNormalizedText().length() > 0);
}
}
@@ -33,26 +33,23 @@ public class StaticContentTest {
webClient.getPage(ParameterUtils.getBaseUrl() + "/struts..");
Assert.fail("Previous request should have failed");
} catch (FailingHttpStatusCodeException e) {
Assert.assertEquals("Not Found", e.getStatusMessage());
Assert.assertEquals(404, e.getStatusCode());
}
}
}
@Test
public void testInvalidRersources2() throws Exception {
try (final WebClient webClient = new WebClient()) {
try {
webClient.getPage(ParameterUtils.getBaseUrl() + "/static/..%252f");
Assert.fail("Previous request should have failed");
} catch (FailingHttpStatusCodeException e) {
Assert.assertEquals("Not Found", e.getStatusMessage());
Assert.assertEquals(404, e.getStatusCode());
}
}
}
/*public void testInvalidRersources3() throws IOException {
try {
beginAt("/static/..%252f..%252f..%252fWEB-INF/classes/org/apache/struts2/showcase/action/EmployeeAction.class/");
fail("Previous request should have failed");
} catch (TestingEngineResponseException ex) {
// ok
}
}*/
}
@@ -65,11 +65,11 @@ public class UITagExampleTest {
final HtmlSubmitInput button = form.getInputByValue("Submit");
final HtmlPage page2 = button.click();
Assert.assertEquals("name", page2.getElementById("name").asText());
Assert.assertEquals("bio", page2.getElementById("bio").asText());
Assert.assertEquals("Red", page2.getElementById("favouriteColor").asText());
Assert.assertEquals("[Patrick, Jason]", page2.getElementById("friends").asText());
Assert.assertEquals("true", page2.getElementById("legalAge").asText());
Assert.assertEquals("name", page2.getElementById("name").asNormalizedText());
Assert.assertEquals("bio", page2.getElementById("bio").asNormalizedText());
Assert.assertEquals("Red", page2.getElementById("favouriteColor").asNormalizedText());
Assert.assertEquals("[Patrick, Jason]", page2.getElementById("friends").asNormalizedText());
Assert.assertEquals("true", page2.getElementById("legalAge").asNormalizedText());
}
}
}
@@ -47,7 +47,7 @@ public class ValidationTest {
final HtmlSubmitInput button = form.getInputByValue("Submit");
final HtmlPage page2 = button.click();
final String page2Text = page2.asText();
final String page2Text = page2.asNormalizedText();
Assert.assertTrue(page2Text.contains("Invalid field value for field \"dateValidatorField\""));
Assert.assertTrue(page2Text.contains("Invalid field value for field \"integerValidatorField\""));
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>6.0.0-RC1</version>
<version>6.0.0-RC4</version>
</parent>
<artifactId>struts2-assembly</artifactId>
+6 -3
View File
@@ -29,7 +29,7 @@
</parent>
<artifactId>struts2-bom</artifactId>
<version>6.0.0-RC1</version>
<version>6.0.0-RC4</version>
<packaging>pom</packaging>
<name>Struts 2 Bill of Materials</name>
@@ -44,7 +44,7 @@
</licenses>
<properties>
<struts-version.version>6.0.0-RC1</struts-version.version>
<struts-version.version>6.0.0-RC4</struts-version.version>
<maven.site.skip>true</maven.site.skip>
<maven.site.deploy.skip>true</maven.site.deploy.skip>
</properties>
@@ -175,6 +175,9 @@
</dependencyManagement>
<scm>
<tag>STRUTS_6_0_0_RC1</tag>
<tag>STRUTS_6_0_0_RC4</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>
</scm>
</project>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-osgi-bundles</artifactId>
<version>6.0.0-RC1</version>
<version>6.0.0-RC4</version>
</parent>
<artifactId>struts2-osgi-admin-bundle</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-osgi-bundles</artifactId>
<version>6.0.0-RC1</version>
<version>6.0.0-RC4</version>
</parent>
<artifactId>struts2-osgi-demo-bundle</artifactId>
+2 -4
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>6.0.0-RC1</version>
<version>6.0.0-RC4</version>
</parent>
<artifactId>struts2-osgi-bundles</artifactId>
@@ -53,12 +53,10 @@
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.4</version>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<reporting>
+12 -11
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>6.0.0-RC1</version>
<version>6.0.0-RC4</version>
</parent>
<artifactId>struts2-core</artifactId>
<packaging>jar</packaging>
@@ -188,7 +188,7 @@
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
@@ -242,6 +242,16 @@
<artifactId>spring-web</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
@@ -342,15 +352,6 @@
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<optional>true</optional>
</dependency>
<!-- The Servlet API mocks in Spring Framework 4.x only supports Servlet 3.0 and higher.
This is only necessary in tests-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>test</scope>
</dependency>
@@ -28,6 +28,10 @@ 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.OgnlReflectionProvider;
import com.opensymphony.xwork2.ognl.OgnlUtil;
import com.opensymphony.xwork2.ognl.OgnlValueStackFactory;
@@ -79,30 +83,37 @@ public class DefaultConfiguration implements Configuration {
}
@Override
public PackageConfig getPackageConfig(String name) {
return packageContexts.get(name);
}
@Override
public List<UnknownHandlerConfig> getUnknownHandlerStack() {
return unknownHandlerStack;
}
@Override
public void setUnknownHandlerStack(List<UnknownHandlerConfig> unknownHandlerStack) {
this.unknownHandlerStack = unknownHandlerStack;
}
@Override
public Set<String> getPackageConfigNames() {
return packageContexts.keySet();
}
@Override
public Map<String, PackageConfig> getPackageConfigs() {
return packageContexts;
}
@Override
public Set<String> getLoadedFileNames() {
return loadedFileNames;
}
@Override
public RuntimeConfiguration getRuntimeConfiguration() {
return runtimeConfiguration;
}
@@ -110,10 +121,12 @@ public class DefaultConfiguration implements Configuration {
/**
* @return the container
*/
@Override
public Container getContainer() {
return container;
}
@Override
public void addPackageConfig(String name, PackageConfig packageContext) {
PackageConfig check = packageContexts.get(name);
if (check != null) {
@@ -131,6 +144,7 @@ public class DefaultConfiguration implements Configuration {
packageContexts.put(name, packageContext);
}
@Override
public PackageConfig removePackageConfig(String packageName) {
return packageContexts.remove(packageName);
}
@@ -138,11 +152,13 @@ public class DefaultConfiguration implements Configuration {
/**
* Allows the configuration to clean up any resources used
*/
@Override
public void destroy() {
packageContexts.clear();
loadedFileNames.clear();
}
@Override
public void rebuildRuntimeConfiguration() {
runtimeConfiguration = buildRuntimeConfiguration();
}
@@ -151,10 +167,12 @@ public class DefaultConfiguration implements Configuration {
* Calls the ConfigurationProviderFactory.getConfig() to tell it to reload the configuration and then calls
* buildRuntimeConfiguration().
*
* @param providers list of ContainerProvider
* @return list of package providers
*
* @throws ConfigurationException in case of any configuration errors
*/
@Override
public synchronized List<PackageProvider> reloadContainer(List<ContainerProvider> providers) throws ConfigurationException {
packageContexts.clear();
loadedFileNames.clear();
@@ -172,6 +190,7 @@ public class DefaultConfiguration implements Configuration {
props.setConstants(builder);
builder.factory(Configuration.class, new Factory<Configuration>() {
@Override
public Configuration create(Context context) throws Exception {
return DefaultConfiguration.this;
}
@@ -279,14 +298,16 @@ public class DefaultConfiguration implements Configuration {
builder.factory(ObjectTypeDeterminer.class, DefaultObjectTypeDeterminer.class, Scope.SINGLETON);
builder.factory(PropertyAccessor.class, CompoundRoot.class.getName(), CompoundRootAccessor.class, Scope.SINGLETON);
builder.factory(ExpressionCacheFactory.class, "defaultOgnlExpressionCacheFactory", DefaultOgnlExpressionCacheFactory.class, Scope.SINGLETON);
builder.factory(BeanInfoCacheFactory.class, "defaultOgnlBeanInfoCacheFactory", DefaultOgnlBeanInfoCacheFactory.class, Scope.SINGLETON);
builder.factory(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_ENABLE_OGNL_EVAL_EXPRESSION, "false");
builder.constant(StrutsConstants.STRUTS_ENABLE_OGNL_EXPRESSION_CACHE, "true");
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");
@@ -417,10 +438,10 @@ public class DefaultConfiguration implements Configuration {
private static class RuntimeConfigurationImpl implements RuntimeConfiguration {
private Map<String, Map<String, ActionConfig>> namespaceActionConfigs;
private Map<String, ActionConfigMatcher> namespaceActionConfigMatchers;
private NamespaceMatcher namespaceMatcher;
private Map<String, String> namespaceConfigs;
private final Map<String, Map<String, ActionConfig>> namespaceActionConfigs;
private final Map<String, ActionConfigMatcher> namespaceActionConfigMatchers;
private final NamespaceMatcher namespaceMatcher;
private final Map<String, String> namespaceConfigs;
public RuntimeConfigurationImpl(Map<String, Map<String, ActionConfig>> namespaceActionConfigs,
Map<String, String> namespaceConfigs,
@@ -448,6 +469,7 @@ public class DefaultConfiguration implements Configuration {
* @param namespace the namespace for the action or null for the empty namespace, ""
* @return the configuration information for action requested
*/
@Override
public ActionConfig getActionConfig(String namespace, String name) {
ActionConfig config = findActionConfigInNamespace(namespace, name);
@@ -503,6 +525,7 @@ public class DefaultConfiguration implements Configuration {
*
* @return a Map of namespace - > Map of ActionConfig objects, with the key being the action name
*/
@Override
public Map<String, Map<String, ActionConfig>> getActionConfigs() {
return namespaceActionConfigs;
}
@@ -53,7 +53,7 @@ public class MockConfiguration implements Configuration {
new StrutsDefaultConfigurationProvider().register(builder, props);
builder.constant(StrutsConstants.STRUTS_DEVMODE, "false");
builder.constant(StrutsConstants.STRUTS_CONFIGURATION_XML_RELOAD, "true");
builder.constant(StrutsConstants.STRUTS_ENABLE_OGNL_EXPRESSION_CACHE, "true");
builder.constant(StrutsConstants.STRUTS_OGNL_ENABLE_EXPRESSION_CACHE, "true");
builder.constant(StrutsConstants.STRUTS_ENABLE_DYNAMIC_METHOD_INVOCATION, "false");
container = builder.create(true);
}
@@ -26,6 +26,8 @@ import com.opensymphony.xwork2.StrutsTextProviderFactory;
import com.opensymphony.xwork2.TextProviderFactory;
import com.opensymphony.xwork2.factory.DefaultUnknownHandlerFactory;
import com.opensymphony.xwork2.factory.UnknownHandlerFactory;
import com.opensymphony.xwork2.ognl.BeanInfoCacheFactory;
import com.opensymphony.xwork2.ognl.ExpressionCacheFactory;
import com.opensymphony.xwork2.ognl.accessor.HttpParametersPropertyAccessor;
import com.opensymphony.xwork2.ognl.accessor.ParameterPropertyAccessor;
import com.opensymphony.xwork2.security.AcceptedPatternsChecker;
@@ -56,6 +58,9 @@ import com.opensymphony.xwork2.conversion.impl.DateConverter;
import com.opensymphony.xwork2.conversion.impl.DefaultConversionAnnotationProcessor;
import com.opensymphony.xwork2.conversion.impl.DefaultConversionFileProcessor;
import com.opensymphony.xwork2.security.NotExcludedAcceptedPatternsChecker;
import org.apache.struts2.components.date.DateFormatter;
import org.apache.struts2.components.date.DateTimeFormatterAdapter;
import org.apache.struts2.components.date.SimpleDateFormatAdapter;
import org.apache.struts2.conversion.StrutsConversionPropertiesProcessor;
import com.opensymphony.xwork2.conversion.impl.DefaultObjectTypeDeterminer;
import org.apache.struts2.conversion.StrutsTypeConverterCreator;
@@ -91,6 +96,8 @@ import com.opensymphony.xwork2.ognl.accessor.XWorkMapPropertyAccessor;
import com.opensymphony.xwork2.ognl.accessor.XWorkMethodAccessor;
import com.opensymphony.xwork2.util.CompoundRoot;
import com.opensymphony.xwork2.LocalizedTextProvider;
import com.opensymphony.xwork2.ognl.DefaultOgnlBeanInfoCacheFactory;
import com.opensymphony.xwork2.ognl.DefaultOgnlExpressionCacheFactory;
import com.opensymphony.xwork2.util.StrutsLocalizedTextProvider;
import com.opensymphony.xwork2.util.OgnlTextParser;
import com.opensymphony.xwork2.util.PatternMatcher;
@@ -127,19 +134,24 @@ import java.util.Set;
public class StrutsDefaultConfigurationProvider implements ConfigurationProvider {
@Override
public void destroy() {
}
@Override
public void init(Configuration configuration) throws ConfigurationException {
}
@Override
public void loadPackages() throws ConfigurationException {
}
@Override
public boolean needsReload() {
return false;
}
@Override
public void register(ContainerBuilder builder, LocatableProperties props)
throws ConfigurationException {
@@ -205,6 +217,8 @@ public class StrutsDefaultConfigurationProvider implements ConfigurationProvider
.factory(TextProviderFactory.class, StrutsTextProviderFactory.class, Scope.SINGLETON)
.factory(LocaleProviderFactory.class, DefaultLocaleProviderFactory.class, Scope.SINGLETON)
.factory(ExpressionCacheFactory.class, "defaultOgnlExpressionCacheFactory", DefaultOgnlExpressionCacheFactory.class, Scope.SINGLETON)
.factory(BeanInfoCacheFactory.class, "defaultOgnlBeanInfoCacheFactory", DefaultOgnlBeanInfoCacheFactory.class, Scope.SINGLETON)
.factory(OgnlUtil.class, Scope.SINGLETON)
.factory(CollectionConverter.class, Scope.SINGLETON)
.factory(ArrayConverter.class, Scope.SINGLETON)
@@ -218,16 +232,18 @@ public class StrutsDefaultConfigurationProvider implements ConfigurationProvider
, Scope.SINGLETON)
.factory(ValueSubstitutor.class, EnvsValueSubstitutor.class, Scope.SINGLETON)
.factory(DateFormatter.class, "simpleDateFormatter", SimpleDateFormatAdapter.class, Scope.SINGLETON)
.factory(DateFormatter.class, "dateTimeFormatter", DateTimeFormatterAdapter.class, Scope.SINGLETON)
;
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_ENABLE_OGNL_EXPRESSION_CACHE, Boolean.TRUE.toString());
props.setProperty(StrutsConstants.STRUTS_ENABLE_OGNL_EVAL_EXPRESSION, 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_METHOD_ACCESS, 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());
}
@@ -38,7 +38,8 @@ import java.lang.annotation.Target;
*
* <p>
* Application wide conversion:<br>
* The conversion rules will be assembled within the <code>xwork-conversion.properties</code> file within the classpath root.
* The conversion rules will be assembled within the <code>struts-conversion.properties</code> or
* <code>xwork-conversion.properties</code> (deprecated) file within the classpath root.
* Set type to: <code>type = ConversionType.APPLICATION</code>
* </p>
* <!-- END SNIPPET: description -->
@@ -18,28 +18,36 @@
*/
package com.opensymphony.xwork2.conversion.impl;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.conversion.TypeConversionException;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.TextProvider;
import com.opensymphony.xwork2.util.ValueStack;
import java.lang.reflect.Constructor;
import java.lang.reflect.Member;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.temporal.TemporalAccessor;
import java.util.Date;
import java.util.Locale;
import java.util.Map;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.conversion.TypeConversionException;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.TextProvider;
import com.opensymphony.xwork2.util.ValueStack;
public class DateConverter extends DefaultTypeConverter {
private final static Logger LOG = LogManager.getLogger(DateConverter.class);
@Override
public Object convertValue(Map<String, Object> context, Object target, Member member, String propertyName, Object value, Class toType) {
public Object convertValue(Map<String, Object> context, Object target, Member member, String propertyName,
Object value, Class toType) {
Date result = null;
if (value instanceof String && ((String) value).length() > 0) {
@@ -52,15 +60,12 @@ public class DateConverter extends DefaultTypeConverter {
} else if (java.sql.Timestamp.class == toType) {
Date check = null;
SimpleDateFormat dtfmt = (SimpleDateFormat) DateFormat.getDateTimeInstance(DateFormat.SHORT,
DateFormat.MEDIUM,
locale);
SimpleDateFormat fullfmt = new SimpleDateFormat(dtfmt.toPattern() + MILLISECOND_FORMAT,
locale);
DateFormat.MEDIUM, locale);
SimpleDateFormat fullfmt = new SimpleDateFormat(dtfmt.toPattern() + MILLISECOND_FORMAT, locale);
SimpleDateFormat dfmt = (SimpleDateFormat) DateFormat.getDateInstance(DateFormat.SHORT,
locale);
SimpleDateFormat dfmt = (SimpleDateFormat) DateFormat.getDateInstance(DateFormat.SHORT, locale);
SimpleDateFormat[] fmts = {fullfmt, dtfmt, dfmt};
SimpleDateFormat[] fmts = { fullfmt, dtfmt, dfmt };
for (SimpleDateFormat fmt : fmts) {
try {
check = fmt.parse(sa);
@@ -85,8 +90,39 @@ public class DateConverter extends DefaultTypeConverter {
} catch (ParseException ignore) {
}
}
} else if (java.time.LocalDateTime.class == toType || java.time.LocalDate.class == toType
|| java.time.LocalTime.class == toType) {
DateTimeFormatter dtf = null;
TemporalAccessor check = null;
DateTimeFormatter[] dfs = getDateTimeFormats(ActionContext.of(context), locale);
for (DateTimeFormatter df1 : dfs) {
try {
check = df1.parseBest(sa, LocalDateTime::from, LocalDate::from, LocalTime::from);
dtf = df1;
if (check != null) {
break;
}
} catch (DateTimeParseException ignore) {
}
}
try {
if (dtf != null && check instanceof LocalDateTime) {
return LocalDateTime.parse(sa, dtf);
} else if (dtf != null && check instanceof LocalDate) {
return LocalDate.parse(sa, dtf);
} else if (dtf != null && check instanceof LocalTime) {
return LocalTime.parse(sa, dtf);
} else {
throw new TypeConversionException("Could not parse date");
}
} catch (DateTimeParseException e) {
throw new TypeConversionException("Could not parse date", e);
}
}
//final fallback for dates without time
// final fallback for dates without time
if (df == null) {
df = DateFormat.getDateInstance(DateFormat.SHORT, locale);
}
@@ -95,15 +131,17 @@ public class DateConverter extends DefaultTypeConverter {
result = df.parse(sa);
if (!(Date.class == toType)) {
try {
Constructor<?> constructor = toType.getConstructor(new Class[]{long.class});
return constructor.newInstance(new Object[]{Long.valueOf(result.getTime())});
Constructor<?> constructor = toType.getConstructor(new Class[] { long.class });
return constructor.newInstance(new Object[] { Long.valueOf(result.getTime()) });
} catch (Exception e) {
throw new TypeConversionException("Couldn't create class " + toType + " using default (long) constructor", e);
throw new TypeConversionException(
"Couldn't create class " + toType + " using default (long) constructor", e);
}
}
} catch (ParseException e) {
throw new TypeConversionException("Could not parse date", e);
}
} else if (Date.class.isAssignableFrom(value.getClass())) {
result = (Date) value;
}
@@ -111,16 +149,16 @@ public class DateConverter extends DefaultTypeConverter {
}
/**
* The user defined global date format,
* see {@link org.apache.struts2.components.Date#DATETAG_PROPERTY}
* The user defined global date format, see
* {@link org.apache.struts2.components.Date#DATETAG_PROPERTY}
*
* @param context current ActionContext
* @param locale current Locale to convert to
* @return defined global format
*
* @return defined global date string format
*/
protected DateFormat getGlobalDateFormat(ActionContext context, Locale locale) {
protected String getGlobalDateString(ActionContext context) {
final String dateTagProperty = org.apache.struts2.components.Date.DATETAG_PROPERTY;
SimpleDateFormat globalDateFormat = null;
String globalDateString = null;
final TextProvider tp = findProviderInStack(context.getValueStack());
@@ -130,23 +168,28 @@ public class DateConverter extends DefaultTypeConverter {
// is the same as input = DATETAG_PROPERTY
if (globalFormat != null && !dateTagProperty.equals(globalFormat)) {
LOG.debug("Found \"{}\" as \"{}\"", dateTagProperty, globalFormat);
globalDateFormat = new SimpleDateFormat(globalFormat, locale);
globalDateString = globalFormat;
} else {
LOG.debug("\"{}\" has not been defined, ignoring it", dateTagProperty);
}
}
return globalDateFormat;
return globalDateString;
}
/**
* Retrieves the list of date formats to be used when converting dates
*
* @param context the current ActionContext
* @param locale the current locale of the action
* @param locale the current locale of the action
* @return a list of DateFormat to be used for date conversion
*/
private DateFormat[] getDateFormats(ActionContext context, Locale locale) {
DateFormat globalDateFormat = getGlobalDateFormat(context, locale);
DateFormat globalDateFormat = null;
String globalFormat = getGlobalDateString(context);
if (globalFormat != null) {
globalDateFormat = new SimpleDateFormat(globalFormat, locale);
}
DateFormat dt1 = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.LONG, locale);
DateFormat dt2 = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM, locale);
@@ -156,15 +199,46 @@ public class DateConverter extends DefaultTypeConverter {
DateFormat d2 = DateFormat.getDateInstance(DateFormat.MEDIUM, locale);
DateFormat d3 = DateFormat.getDateInstance(DateFormat.LONG, locale);
DateFormat rfc3339 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
DateFormat rfc3339 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
DateFormat rfc3339dateOnly = new SimpleDateFormat("yyyy-MM-dd");
final DateFormat[] dateFormats;
if (globalDateFormat == null) {
dateFormats = new DateFormat[]{dt1, dt2, dt3, rfc3339, d1, d2, d3, rfc3339dateOnly};
dateFormats = new DateFormat[] { dt1, dt2, dt3, rfc3339, d1, d2, d3, rfc3339dateOnly };
} else {
dateFormats = new DateFormat[]{globalDateFormat, dt1, dt2, dt3, rfc3339, d1, d2, d3, rfc3339dateOnly};
dateFormats = new DateFormat[] { globalDateFormat, dt1, dt2, dt3, rfc3339, d1, d2, d3, rfc3339dateOnly };
}
return dateFormats;
}
/**
* Retrieves the list of date time formats to be used when converting dates
*
* @param context the current ActionContext
* @param locale the current locale of the action
*
* @return a list of DateTimeFormatter to be used for date conversion
*/
protected DateTimeFormatter[] getDateTimeFormats(ActionContext context, Locale locale) {
DateTimeFormatter globalDateFormat = null;
String globalFormat = getGlobalDateString(context);
if (globalFormat != null) {
globalDateFormat = DateTimeFormatter.ofPattern(globalFormat, locale);
}
DateTimeFormatter df1 = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
DateTimeFormatter df2 = DateTimeFormatter.ISO_LOCAL_DATE;
DateTimeFormatter df3 = DateTimeFormatter.ISO_LOCAL_TIME;
final DateTimeFormatter[] dateFormats;
if (globalDateFormat == null) {
dateFormats = new DateTimeFormatter[] { df1, df2, df3 };
} else {
dateFormats = new DateTimeFormatter[] { globalDateFormat, df1, df2, df3 };
}
return dateFormats;
@@ -25,6 +25,7 @@ import com.opensymphony.xwork2.inject.Inject;
import org.apache.struts2.StrutsConstants;
import java.lang.reflect.Member;
import java.time.LocalDateTime;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
@@ -99,6 +100,8 @@ public class XWorkBasicConverter extends DefaultTypeConverter {
result = doConvertToArray(context, o, member, propertyName, value, toType);
} else if (Date.class.isAssignableFrom(toType)) {
result = doConvertToDate(context, value, toType);
} else if (LocalDateTime.class.isAssignableFrom(toType)) {
result = doConvertToDate(context, value, toType);
} else if (Calendar.class.isAssignableFrom(toType)) {
result = doConvertToCalendar(context, value);
} else if (Collection.class.isAssignableFrom(toType)) {
@@ -1,187 +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.conversion.metadata;
import com.opensymphony.xwork2.conversion.annotations.ConversionRule;
import com.opensymphony.xwork2.conversion.impl.DefaultObjectTypeDeterminer;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import java.io.PrintWriter;
import java.io.StringWriter;
/**
* <code>ConversionDescription</code>
*
* @author Rainer Hermanns
* @version $Id$
*
* @deprecated class will be removed
*/
@Deprecated
public class ConversionDescription {
/**
* Jakarta commons-logging reference.
*/
protected static Logger log = null;
public static final String KEY_PREFIX = "Key_";
public static final String ELEMENT_PREFIX = "Element_";
public static final String KEY_PROPERTY_PREFIX = "KeyProperty_";
public static final String DEPRECATED_ELEMENT_PREFIX = "Collection_";
/**
* Key used for type conversion of maps.
*/
String MAP_PREFIX = "Map_";
public String property;
public String typeConverter = "";
public String rule = "";
public String value = "";
public String fullQualifiedClassName;
public String type = null;
public ConversionDescription() {
log = LogManager.getLogger(this.getClass());
}
/**
* Creates an ConversionDescription with the specified property name.
*
* @param property property
*/
public ConversionDescription(String property) {
this.property = property;
log = LogManager.getLogger(this.getClass());
}
/**
* <p>
* Sets the property name to be inserted into the related conversion.properties file.<br>
* Note: Do not add COLLECTION_PREFIX or MAP_PREFIX keys to property names.
* </p>
*
* @param property The property to be converted.
*/
public void setProperty(String property) {
this.property = property;
}
/**
* Sets the class name of the type converter to be used.
*
* @param typeConverter The class name of the type converter.
*/
public void setTypeConverter(String typeConverter) {
this.typeConverter = typeConverter;
}
/**
* @param rule the rule prefix for COLLECTION_PREFIX or MAP_PREFIX key. Defaults to en empty String.
*/
public void setRule(String rule) {
if (rule != null && rule.length() > 0) {
if (rule.equals(ConversionRule.COLLECTION.toString())) {
this.rule = DefaultObjectTypeDeterminer.DEPRECATED_ELEMENT_PREFIX;
} else if (rule.equals(ConversionRule.ELEMENT.toString())) {
this.rule = DefaultObjectTypeDeterminer.ELEMENT_PREFIX;
} else if (rule.equals(ConversionRule.KEY.toString())) {
this.rule = DefaultObjectTypeDeterminer.KEY_PREFIX;
} else if (rule.equals(ConversionRule.KEY_PROPERTY.toString())) {
this.rule = DefaultObjectTypeDeterminer.KEY_PROPERTY_PREFIX;
} else if (rule.equals(ConversionRule.MAP.toString())) {
this.rule = MAP_PREFIX;
}
}
}
public void setType(String type) {
this.type = type;
}
public String getType() {
return type;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
/**
* Returns the conversion description as property entry.
* <p>
* Example:<br>
* property.name = converter.className<br>
* Collection_property.name = converter.className<br>
* Map_property.name = converter.className
* KeyProperty_name = id
* </p>
*
* @return the conversion description as property entry.
*/
public String asProperty() {
StringWriter sw = new StringWriter();
PrintWriter writer = null;
try {
writer = new PrintWriter(sw);
writer.print(rule);
writer.print(property);
writer.print("=");
if ( rule.startsWith(DefaultObjectTypeDeterminer.KEY_PROPERTY_PREFIX) && value != null && value.length() > 0 ) {
writer.print(value);
} else {
writer.print(typeConverter);
}
} finally {
if (writer != null) {
writer.flush();
writer.close();
}
}
return sw.toString();
}
/**
* Returns the fullQualifiedClassName attribute is used to create the special <code>conversion.properties</code> file name.
*
* @return full qualified class name
*/
public String getFullQualifiedClassName() {
return fullQualifiedClassName;
}
/**
* The fullQualifiedClassName attribute is used to create the special <code>conversion.properties</code> file name.
*
* @param fullQualifiedClassName a full qualified class name
*/
public void setFullQualifiedClassName(String fullQualifiedClassName) {
this.fullQualifiedClassName = fullQualifiedClassName;
}
}
@@ -40,6 +40,7 @@ import java.util.Collection;
import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;
import java.util.regex.Pattern;
/**
* This interceptor sets all parameters on the value stack.
@@ -50,8 +51,11 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
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;
@@ -79,6 +83,11 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
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.
@@ -101,13 +110,10 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
/**
* Compares based on number of '.' and '[' characters (fewer is higher)
*/
static final Comparator<String> rbCollator = new Comparator<String>() {
public int compare(String s1, String s2) {
int l1 = countOGNLCharacters(s1);
int l2 = countOGNLCharacters(s2);
return l1 < l2 ? -1 : (l2 < l1 ? 1 : s1.compareTo(s2));
}
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
@@ -195,7 +201,7 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
ReflectionContextState.setReportingConversionErrors(context, true);
//keep locale from original context
newStack.getActionContext().withLocale(stack.getActionContext().getLocale());
newStack.getActionContext().withLocale(stack.getActionContext().getLocale()).withValueStack(stack);
}
boolean memberAccessStack = newStack instanceof MemberAccessValueStack;
@@ -286,6 +292,10 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
}
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);
@@ -293,6 +303,14 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
return accepted;
}
private boolean isIgnoredDMI(String name) {
if (dmiEnabled) {
return DMI_IGNORED_PATTERN.matcher(name).matches();
} else {
return false;
}
}
protected boolean isWithinLengthLimit(String name) {
boolean matchLength = name.length() <= paramNameMaxLength;
if (!matchLength) {
@@ -0,0 +1,23 @@
/*
* 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;
/**
* A proxy interface to be used with Struts DI mechanism
*/
public interface BeanInfoCacheFactory<Key, Value> extends OgnlCacheFactory <Key, Value> {
}
@@ -0,0 +1,44 @@
/*
* 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.opensymphony.xwork2.inject.Inject;
import org.apache.struts2.StrutsConstants;
/**
* Default OGNL Cache factory implementation.
*
* Currently used for BeanInfo cache creation.
*
* @param <Key> The type for the cache key entries
* @param <Value> The type for the cache value entries
*/
public class DefaultOgnlBeanInfoCacheFactory<Key, Value> extends DefaultOgnlCacheFactory<Key, Value>
implements BeanInfoCacheFactory<Key, Value> {
@Override
@Inject(value = StrutsConstants.STRUTS_OGNL_BEANINFO_CACHE_MAXSIZE, required = false)
protected void setCacheMaxSize(String maxSize) {
super.setCacheMaxSize(maxSize);
}
@Override
@Inject(value = StrutsConstants.STRUTS_OGNL_BEANINFO_CACHE_LRU_MODE, required = false)
protected void setUseLRUCache(String useLRUMode) {
super.setUseLRUCache(useLRUMode);
}
}
@@ -0,0 +1,66 @@
/*
* 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 java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.commons.lang3.BooleanUtils;
/**
* Default OGNL Cache factory implementation.
*
* Currently used for Expression cache and BeanInfo cache creation.
*
* @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 final AtomicBoolean useLRUCache = new AtomicBoolean(false);
private final AtomicInteger cacheMaxSize = new AtomicInteger(25000);
@Override
public OgnlCache<Key, Value> buildOgnlCache() {
return buildOgnlCache(getCacheMaxSize(), 16, 0.75f, getUseLRUCache());
}
@Override
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.get();
}
protected void setCacheMaxSize(String maxSize) {
cacheMaxSize.set(Integer.parseInt(maxSize));
}
@Override
public boolean getUseLRUCache() {
return useLRUCache.get();
}
protected void setUseLRUCache(String useLRUMode) {
useLRUCache.set(BooleanUtils.toBoolean(useLRUMode));
}
}
@@ -0,0 +1,44 @@
/*
* 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.opensymphony.xwork2.inject.Inject;
import org.apache.struts2.StrutsConstants;
/**
* Default OGNL Expression Cache factory implementation.
*
* Currently used for Expression cache creation.
*
* @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> {
@Override
@Inject(value = StrutsConstants.STRUTS_OGNL_EXPRESSION_CACHE_MAXSIZE, required = false)
protected void setCacheMaxSize(String maxSize) {
super.setCacheMaxSize(maxSize);
}
@Override
@Inject(value = StrutsConstants.STRUTS_OGNL_EXPRESSION_CACHE_LRU_MODE, required = false)
protected void setUseLRUCache(String useLRUMode) {
super.setUseLRUCache(useLRUMode);
}
}
@@ -0,0 +1,23 @@
/*
* 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;
/**
* A proxy interface to be used with Struts DI mechanism
*/
public interface ExpressionCacheFactory<Key, Value> extends OgnlCacheFactory <Key, Value> {
}
@@ -0,0 +1,41 @@
/*
* 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;
/**
* 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> {
public Value get(Key key);
public void put(Key key, Value value);
public void putIfAbsent(Key key, Value value);
public int size();
public void clear();
public int getEvictionLimit();
public void setEvictionLimit(int cacheEvictionLimit);
}
@@ -0,0 +1,30 @@
/*
* 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;
/**
* 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 <Value> The type for the cache value entries
*/
interface OgnlCacheFactory<Key, Value> {
OgnlCache<Key, Value> buildOgnlCache();
OgnlCache<Key, Value> buildOgnlCache(int evictionLimit, int initialCapacity, float loadFactor, boolean lruCache);
int getCacheMaxSize();
boolean getUseLRUCache();
}
@@ -0,0 +1,85 @@
/*
* 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 java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 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<Key, Value> implements OgnlCache<Key, Value> {
private final ConcurrentHashMap<Key, Value> ognlCache;
private final AtomicInteger cacheEvictionLimit = new AtomicInteger(25000);
public OgnlDefaultCache(int evictionLimit, int initialCapacity, float loadFactor) {
this.cacheEvictionLimit.set(evictionLimit);
ognlCache = new ConcurrentHashMap<>(initialCapacity, loadFactor);
}
@Override
public Value get(Key key) {
return ognlCache.get(key);
}
@Override
public void put(Key key, Value value) {
ognlCache.put(key, value);
this.clearIfEvictionLimitExceeded();
}
@Override
public void putIfAbsent(Key key, Value value) {
ognlCache.putIfAbsent(key, value);
this.clearIfEvictionLimitExceeded();
}
@Override
public int size() {
return ognlCache.size();
}
@Override
public void clear() {
ognlCache.clear();
}
@Override
public int getEvictionLimit() {
return this.cacheEvictionLimit.get();
}
@Override
public void setEvictionLimit(int cacheEvictionLimit) {
this.cacheEvictionLimit.set(cacheEvictionLimit);
}
/**
* Clear the cache if the eviction limit has been exceeded.
*/
private void clearIfEvictionLimitExceeded() {
if (ognlCache.size() > cacheEvictionLimit.get()) {
ognlCache.clear();
}
}
}
@@ -0,0 +1,87 @@
/*
* 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 java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 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<Key, Value> implements OgnlCache<Key, Value> {
private final Map<Key, Value> ognlLRUCache;
private final AtomicInteger cacheEvictionLimit = new AtomicInteger(2500);
public OgnlLRUCache(int evictionLimit, int initialCapacity, float loadFactor) {
this.cacheEvictionLimit.set(evictionLimit);
// Access-order mode selected (order mode true in LinkedHashMap constructor).
ognlLRUCache = Collections.synchronizedMap (new LinkedHashMap<Key, Value>(initialCapacity, loadFactor, true) {
@Override
protected boolean removeEldestEntry(Map.Entry<Key,Value> eldest) {
return (this.size() > cacheEvictionLimit.get());
}
});
}
@Override
public Value get(Key key) {
return ognlLRUCache.get(key);
}
@Override
public void put(Key key, Value value) {
ognlLRUCache.put(key, value);
}
@Override
public void putIfAbsent(Key key, Value value) {
ognlLRUCache.putIfAbsent(key, value);
}
@Override
public int size() {
return ognlLRUCache.size();
}
@Override
public void clear() {
ognlLRUCache.clear();
}
@Override
public int getEvictionLimit() {
return this.cacheEvictionLimit.get();
}
@Override
public void setEvictionLimit(int cacheEvictionLimit) {
this.cacheEvictionLimit.set(cacheEvictionLimit);
}
}
@@ -38,8 +38,6 @@ import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Pattern;
@@ -57,8 +55,8 @@ public class OgnlUtil {
// Flag used to reduce flooding logs with WARNs about using DevMode excluded packages
private final AtomicBoolean warnReported = new AtomicBoolean(false);
private final ConcurrentMap<String, Object> expressions = new ConcurrentHashMap<>();
private final ConcurrentMap<Class<?>, BeanInfo> beanInfoCache = new ConcurrentHashMap<>();
private final OgnlCache<String, Object> expressionCache;
private final OgnlCache<Class<?>, BeanInfo> beanInfoCache;
private TypeConverter defaultConverter;
private boolean devMode;
@@ -75,10 +73,33 @@ public class OgnlUtil {
private Container container;
private boolean allowStaticFieldAccess = true;
private boolean allowStaticMethodAccess;
private boolean disallowProxyMemberAccess;
/**
* Construct a new OgnlUtil instance for use with the framework
*
* @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(null, null); // Instantiate default Expression and BeanInfo caches (null factories)
}
/**
* Construct a new OgnlUtil instance for use with the framework, with optional
* cache factories for OGNL Expression and BeanInfo caches.
*
* 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
*/
public OgnlUtil(
@Inject(value = StrutsConstants.STRUTS_OGNL_EXPRESSION_CACHE_FACTORY, required = false) ExpressionCacheFactory<String, Object> ognlExpressionCacheFactory,
@Inject(value = StrutsConstants.STRUTS_OGNL_BEANINFO_CACHE_FACTORY, required = false) BeanInfoCacheFactory<Class<?>, BeanInfo> ognlBeanInfoCacheFactory
) {
excludedClasses = Collections.unmodifiableSet(new HashSet<>());
excludedPackageNamePatterns = Collections.unmodifiableSet(new HashSet<>());
excludedPackageNames = Collections.unmodifiableSet(new HashSet<>());
@@ -86,6 +107,12 @@ public class OgnlUtil {
devModeExcludedClasses = Collections.unmodifiableSet(new HashSet<>());
devModeExcludedPackageNamePatterns = Collections.unmodifiableSet(new HashSet<>());
devModeExcludedPackageNames = Collections.unmodifiableSet(new HashSet<>());
OgnlCacheFactory<String, Object> ognlExpressionCacheFactory1 = (ognlExpressionCacheFactory != null ? ognlExpressionCacheFactory : new DefaultOgnlExpressionCacheFactory<>());
OgnlCacheFactory<Class<?>, BeanInfo> ognlBeanInfoCacheFactory1 = (ognlBeanInfoCacheFactory != null ? ognlBeanInfoCacheFactory : new DefaultOgnlBeanInfoCacheFactory<>());
this.expressionCache = ognlExpressionCacheFactory1.buildOgnlCache();
this.beanInfoCache = ognlBeanInfoCacheFactory1.buildOgnlCache();
}
@Inject
@@ -98,12 +125,22 @@ public class OgnlUtil {
this.devMode = BooleanUtils.toBoolean(mode);
}
@Inject(StrutsConstants.STRUTS_ENABLE_OGNL_EXPRESSION_CACHE)
@Inject(StrutsConstants.STRUTS_OGNL_ENABLE_EXPRESSION_CACHE)
protected void setEnableExpressionCache(String cache) {
enableExpressionCache = BooleanUtils.toBoolean(cache);
}
@Inject(value = StrutsConstants.STRUTS_ENABLE_OGNL_EVAL_EXPRESSION, required = false)
@Inject(value = StrutsConstants.STRUTS_OGNL_EXPRESSION_CACHE_MAXSIZE, required = false)
protected void setExpressionCacheMaxSize(String maxSize) {
expressionCache.setEvictionLimit(Integer.parseInt(maxSize));
}
@Inject(value = StrutsConstants.STRUTS_OGNL_BEANINFO_CACHE_MAXSIZE, required = false)
protected void setBeanInfoCacheMaxSize(String maxSize) {
beanInfoCache.setEvictionLimit(Integer.parseInt(maxSize));
}
@Inject(value = StrutsConstants.STRUTS_OGNL_ENABLE_EVAL_EXPRESSION, required = false)
protected void setEnableEvalExpression(String evalExpression) {
this.enableEvalExpression = BooleanUtils.toBoolean(evalExpression);
if (this.enableEvalExpression) {
@@ -212,11 +249,6 @@ public class OgnlUtil {
this.allowStaticFieldAccess = BooleanUtils.toBoolean(allowStaticFieldAccess);
}
@Inject(value = StrutsConstants.STRUTS_ALLOW_STATIC_METHOD_ACCESS, required = false)
protected void setAllowStaticMethodAccess(String allowStaticMethodAccess) {
this.allowStaticMethodAccess = BooleanUtils.toBoolean(allowStaticMethodAccess);
}
@Inject(value = StrutsConstants.STRUTS_DISALLOW_PROXY_MEMBER_ACCESS, required = false)
protected void setDisallowProxyMemberAccess(String disallowProxyMemberAccess) {
this.disallowProxyMemberAccess = BooleanUtils.toBoolean(disallowProxyMemberAccess);
@@ -273,7 +305,7 @@ public class OgnlUtil {
* @since 2.5.21
*/
public void clearExpressionCache() {
expressions.clear();
expressionCache.clear();
}
/**
@@ -284,7 +316,7 @@ public class OgnlUtil {
* @since 2.5.21
*/
public int expressionCacheSize() {
return expressions.size();
return expressionCache.size();
}
/**
@@ -532,11 +564,11 @@ public class OgnlUtil {
private <T> Object compileAndExecute(String expression, Map<String, Object> context, OgnlTask<T> task) throws OgnlException {
Object tree;
if (enableExpressionCache) {
tree = expressions.get(expression);
tree = expressionCache.get(expression);
if (tree == null) {
tree = Ognl.parseExpression(expression);
checkEnableEvalExpression(tree, context);
expressions.putIfAbsent(expression, tree);
expressionCache.putIfAbsent(expression, tree);
}
} else {
tree = Ognl.parseExpression(expression);
@@ -549,11 +581,11 @@ public class OgnlUtil {
private <T> Object compileAndExecuteMethod(String expression, Map<String, Object> context, OgnlTask<T> task) throws OgnlException {
Object tree;
if (enableExpressionCache) {
tree = expressions.get(expression);
tree = expressionCache.get(expression);
if (tree == null) {
tree = Ognl.parseExpression(expression);
checkSimpleMethod(tree, context);
expressions.putIfAbsent(expression, tree);
expressionCache.putIfAbsent(expression, tree);
}
} else {
tree = Ognl.parseExpression(expression);
@@ -566,7 +598,7 @@ public class OgnlUtil {
public Object compile(String expression, Map<String, Object> context) throws OgnlException {
return compileAndExecute(expression, context, tree -> tree);
}
private void checkEnableEvalExpression(Object tree, Map<String, Object> context) throws OgnlException {
if (!enableEvalExpression && isEvalExpression(tree, context)) {
throw new OgnlException("Eval expressions/chained expressions have been disabled!");
@@ -798,7 +830,7 @@ public class OgnlUtil {
resolver = container.getInstance(CompoundRootAccessor.class);
}
SecurityMemberAccess memberAccess = new SecurityMemberAccess(allowStaticMethodAccess, allowStaticFieldAccess);
SecurityMemberAccess memberAccess = new SecurityMemberAccess(allowStaticFieldAccess);
memberAccess.setDisallowProxyMemberAccess(disallowProxyMemberAccess);
if (devMode) {
@@ -72,13 +72,13 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
private boolean devMode;
private boolean logMissingProperties;
protected OgnlValueStack(XWorkConverter xworkConverter, CompoundRootAccessor accessor, TextProvider prov, boolean allowStaticMethodAccess, boolean allowStaticFieldAccess) {
setRoot(xworkConverter, accessor, new CompoundRoot(), allowStaticMethodAccess, allowStaticFieldAccess);
protected OgnlValueStack(XWorkConverter xworkConverter, CompoundRootAccessor accessor, TextProvider prov, boolean allowStaticFieldAccess) {
setRoot(xworkConverter, accessor, new CompoundRoot(), allowStaticFieldAccess);
push(prov);
}
protected OgnlValueStack(ValueStack vs, XWorkConverter xworkConverter, CompoundRootAccessor accessor, boolean allowStaticMethodAccess, boolean allowStaticFieldAccess) {
setRoot(xworkConverter, accessor, new CompoundRoot(vs.getRoot()), allowStaticMethodAccess, allowStaticFieldAccess);
protected OgnlValueStack(ValueStack vs, XWorkConverter xworkConverter, CompoundRootAccessor accessor, boolean allowStaticFieldAccess) {
setRoot(xworkConverter, accessor, new CompoundRoot(vs.getRoot()), allowStaticFieldAccess);
}
@Inject
@@ -90,10 +90,9 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
securityMemberAccess.setDisallowProxyMemberAccess(ognlUtil.isDisallowProxyMemberAccess());
}
protected void setRoot(XWorkConverter xworkConverter, CompoundRootAccessor accessor, CompoundRoot compoundRoot,
boolean allowStaticMethodAccess, boolean allowStaticFieldAccess) {
protected void setRoot(XWorkConverter xworkConverter, CompoundRootAccessor accessor, CompoundRoot compoundRoot, boolean allowStaticFieldAccess) {
this.root = compoundRoot;
this.securityMemberAccess = new SecurityMemberAccess(allowStaticMethodAccess, allowStaticFieldAccess);
this.securityMemberAccess = new SecurityMemberAccess(allowStaticFieldAccess);
this.context = Ognl.createDefaultContext(this.root, securityMemberAccess, accessor, new OgnlTypeConverterWrapper(xworkConverter));
context.put(VALUE_STACK, this);
((OgnlContext) context).setTraceEvaluations(false);
@@ -219,7 +218,7 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
if (shouldLog) {
LOG.warn(msg, e);
}
if (throwExceptionOnFailure) {
throw new StrutsException(msg, e);
}
@@ -462,11 +461,10 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
XWorkConverter xworkConverter = cont.getInstance(XWorkConverter.class);
CompoundRootAccessor accessor = (CompoundRootAccessor) cont.getInstance(PropertyAccessor.class, CompoundRoot.class.getName());
TextProvider prov = cont.getInstance(TextProvider.class, "system");
final boolean allowStaticMethod = BooleanUtils.toBoolean(cont.getInstance(String.class, StrutsConstants.STRUTS_ALLOW_STATIC_METHOD_ACCESS));
final boolean allowStaticField = BooleanUtils.toBoolean(cont.getInstance(String.class, StrutsConstants.STRUTS_ALLOW_STATIC_FIELD_ACCESS));
OgnlValueStack aStack = new OgnlValueStack(xworkConverter, accessor, prov, allowStaticMethod, allowStaticField);
OgnlValueStack aStack = new OgnlValueStack(xworkConverter, accessor, prov, allowStaticField);
aStack.setOgnlUtil(cont.getInstance(OgnlUtil.class));
aStack.setRoot(xworkConverter, accessor, this.root, allowStaticMethod, allowStaticField);
aStack.setRoot(xworkConverter, accessor, this.root, allowStaticField);
return aStack;
}
@@ -43,8 +43,6 @@ import java.util.Set;
*/
public class OgnlValueStackFactory implements ValueStackFactory {
private static final Logger LOG = LogManager.getLogger(OgnlValueStackFactory.class);
protected XWorkConverter xworkConverter;
protected CompoundRootAccessor compoundRootAccessor;
protected TextProvider textProvider;
@@ -61,8 +59,7 @@ public class OgnlValueStackFactory implements ValueStackFactory {
}
public ValueStack createValueStack() {
ValueStack stack = new OgnlValueStack(xworkConverter, compoundRootAccessor, textProvider,
containerAllowsStaticMethodAccess(), containerAllowsStaticFieldAccess());
ValueStack stack = new OgnlValueStack(xworkConverter, compoundRootAccessor, textProvider, containerAllowsStaticFieldAccess());
container.inject(stack);
return stack.getActionContext()
.withContainer(container)
@@ -71,8 +68,7 @@ public class OgnlValueStackFactory implements ValueStackFactory {
}
public ValueStack createValueStack(ValueStack stack) {
ValueStack result = new OgnlValueStack(stack, xworkConverter, compoundRootAccessor,
containerAllowsStaticMethodAccess(), containerAllowsStaticFieldAccess());
ValueStack result = new OgnlValueStack(stack, xworkConverter, compoundRootAccessor, containerAllowsStaticFieldAccess());
container.inject(result);
return result.getActionContext()
.withContainer(container)
@@ -84,32 +80,23 @@ public class OgnlValueStackFactory implements ValueStackFactory {
protected void setContainer(Container container) throws ClassNotFoundException {
Set<String> names = container.getInstanceNames(PropertyAccessor.class);
for (String name : names) {
Class cls = Class.forName(name);
if (cls != null) {
if (Map.class.isAssignableFrom(cls)) {
PropertyAccessor acc = container.getInstance(PropertyAccessor.class, name);
}
OgnlRuntime.setPropertyAccessor(cls, container.getInstance(PropertyAccessor.class, name));
if (compoundRootAccessor == null && CompoundRoot.class.isAssignableFrom(cls)) {
compoundRootAccessor = (CompoundRootAccessor) container.getInstance(PropertyAccessor.class, name);
}
Class<?> cls = Class.forName(name);
OgnlRuntime.setPropertyAccessor(cls, container.getInstance(PropertyAccessor.class, name));
if (compoundRootAccessor == null && CompoundRoot.class.isAssignableFrom(cls)) {
compoundRootAccessor = (CompoundRootAccessor) container.getInstance(PropertyAccessor.class, name);
}
}
names = container.getInstanceNames(MethodAccessor.class);
for (String name : names) {
Class cls = Class.forName(name);
if (cls != null) {
OgnlRuntime.setMethodAccessor(cls, container.getInstance(MethodAccessor.class, name));
}
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);
if (cls != null) {
OgnlRuntime.setNullHandler(cls, new OgnlNullHandlerWrapper(container.getInstance(NullHandler.class, name)));
}
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");
@@ -117,19 +104,8 @@ public class OgnlValueStackFactory implements ValueStackFactory {
this.container = container;
}
/**
* Retrieve allowsStaticMethodAccess state from the container (allows for lazy fetching)
*
* @return
*/
protected boolean containerAllowsStaticMethodAccess() {
return BooleanUtils.toBoolean(container.getInstance(String.class, StrutsConstants.STRUTS_ALLOW_STATIC_METHOD_ACCESS));
}
/**
* Retrieve allowStaticFieldAccess state from the container (allows for lazy fetching)
*
* @return
*/
protected boolean containerAllowsStaticFieldAccess() {
return BooleanUtils.toBoolean(container.getInstance(String.class, StrutsConstants.STRUTS_ALLOW_STATIC_FIELD_ACCESS));
@@ -42,7 +42,6 @@ public class SecurityMemberAccess implements MemberAccess {
private static final Logger LOG = LogManager.getLogger(SecurityMemberAccess.class);
private final boolean allowStaticFieldAccess;
private final boolean allowStaticMethodAccess;
private Set<Pattern> excludeProperties = Collections.emptySet();
private Set<Pattern> acceptProperties = Collections.emptySet();
private Set<Class<?>> excludedClasses = Collections.emptySet();
@@ -52,25 +51,15 @@ public class SecurityMemberAccess implements MemberAccess {
/**
* SecurityMemberAccess
* - access decisions based on whether member is static (or not)
* - block or allow access to properties (configurable-after-construction)
*
* @param allowStaticMethodAccess
* @param allowStaticFieldAccess
* - access decisions based on whether member is static (or not)
* - block or allow access to properties (configurable-after-construction)
*
* @param allowStaticFieldAccess if set to true static fields (constants) will be accessible
*/
public SecurityMemberAccess(boolean allowStaticMethodAccess, boolean allowStaticFieldAccess) {
this.allowStaticMethodAccess = allowStaticMethodAccess;
public SecurityMemberAccess(boolean allowStaticFieldAccess) {
this.allowStaticFieldAccess = allowStaticFieldAccess;
}
public final boolean getAllowStaticMethodAccess() {
return allowStaticMethodAccess;
}
public final boolean getAllowStaticFieldAccess() {
return allowStaticFieldAccess;
}
@Override
public Object setup(Map context, Object target, Member member, String propertyName) {
Object result = null;
@@ -90,13 +79,12 @@ public class SecurityMemberAccess implements MemberAccess {
public void restore(Map context, Object target, Member member, String propertyName, Object state) {
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 + "]");
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 + "]");
}
}
}
@@ -117,6 +105,7 @@ public class SecurityMemberAccess implements MemberAccess {
return false;
}
// it needs to be before calling #checkStaticMethodAccess()
if (checkEnumAccess(target, member)) {
LOG.trace("Allowing access to enum: target [{}], member [{}]", target, member);
return true;
@@ -127,7 +116,7 @@ public class SecurityMemberAccess implements MemberAccess {
return false;
}
final Class memberClass = member.getDeclaringClass();
final Class<?> memberClass = member.getDeclaringClass();
if (isClassExcluded(memberClass)) {
LOG.warn("Declaring class of member type [{}] is excluded!", member);
@@ -135,11 +124,11 @@ public class SecurityMemberAccess implements MemberAccess {
}
// target can be null in case of accessing static fields, since OGNL 3.2.8
final Class targetClass = Modifier.isStatic(memberModifiers) ? memberClass : target.getClass();
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);
target, memberClass.getPackage(), member);
return false;
}
@@ -158,33 +147,25 @@ public class SecurityMemberAccess implements MemberAccess {
/**
* Check access for static method (via modifiers).
*
*
* Note: For non-static members, the result is always true.
*
*
* @param member
* @param memberModifiers
*
*
* @return
*/
protected boolean checkStaticMethodAccess(Member member, int memberModifiers) {
if (Modifier.isStatic(memberModifiers) && !(member instanceof Field)) {
if (allowStaticMethodAccess) {
LOG.debug("Support for accessing static methods [member: {}] is deprecated!", member);
}
return allowStaticMethodAccess;
} else {
return true;
}
return !Modifier.isStatic(memberModifiers) || member instanceof Field;
}
/**
* Check access for static field (via modifiers).
*
* <p>
* Note: For non-static members, the result is always true.
*
*
* @param member
* @param memberModifiers
*
* @return
*/
protected boolean checkStaticFieldAccess(Member member, int memberModifiers) {
@@ -195,13 +176,12 @@ public class SecurityMemberAccess implements MemberAccess {
}
}
/**
/**
* Check access for public members (via modifiers)
*
* <p>
* Returns true if-and-only-if the member is public.
*
*
* @param memberModifiers
*
* @return
*/
protected boolean checkPublicMemberAccess(int memberModifiers) {
@@ -210,10 +190,8 @@ public class SecurityMemberAccess implements MemberAccess {
protected boolean checkEnumAccess(Object target, Member member) {
if (target instanceof Class) {
final Class clazz = (Class) target;
if (Enum.class.isAssignableFrom(clazz) && member.getName().equals("values")) {
return true;
}
final Class<?> clazz = (Class<?>) target;
return Enum.class.isAssignableFrom(clazz) && member.getName().equals("values");
}
return false;
}
@@ -222,7 +200,7 @@ public class SecurityMemberAccess implements MemberAccess {
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();
@@ -235,7 +213,7 @@ public class SecurityMemberAccess implements MemberAccess {
targetPackageName = targetPackageName + ".";
memberPackageName = memberPackageName + ".";
for (String packageName: excludedPackageNames) {
for (String packageName : excludedPackageNames) {
if (targetPackageName.startsWith(packageName) || memberPackageName.startsWith(packageName)) {
return true;
}
@@ -245,7 +223,7 @@ public class SecurityMemberAccess implements MemberAccess {
}
protected boolean isClassExcluded(Class<?> clazz) {
if (clazz == Object.class || (clazz == Class.class && !allowStaticMethodAccess)) {
if (clazz == Object.class || (clazz == Class.class && !allowStaticFieldAccess)) {
return true;
}
for (Class<?> excludedClass : excludedClasses) {
@@ -36,7 +36,7 @@ import java.util.Map;
* @author tmjee
*/
public class XWorkMethodAccessor extends ObjectMethodAccessor {
private static final Logger LOG = LogManager.getLogger(XWorkMethodAccessor.class);
@Override
@@ -58,9 +58,9 @@ public class XWorkMethodAccessor extends ObjectMethodAccessor {
//so that property strings are not cleared
//i.e. OgnlUtil should be used initially, OgnlRuntime
//thereafter
Object propVal=OgnlRuntime.getProperty(ogContext, object, string);
//use the Collection property accessor instead of the individual property accessor, because
//use the Collection property accessor instead of the individual property accessor, because
//in the case of Lists otherwise the index property could be used
PropertyAccessor accessor=OgnlRuntime.getPropertyAccessor(Collection.class);
ReflectionContextState.setGettingByKeyProperty(ogContext,true);
@@ -83,8 +83,8 @@ public class XWorkMethodAccessor extends ObjectMethodAccessor {
return callMethodWithDebugInfo(context, object, string, objects);
}
}
Boolean exec = (Boolean) context.get(ReflectionContextState.DENY_METHOD_EXECUTION);
boolean e = ((exec == null) ? false : exec.booleanValue());
Boolean exec = ReflectionContextState.isDenyMethodExecution(context);
boolean e = (exec != null && exec);
if (!e) {
return callMethodWithDebugInfo(context, object, string, objects);
@@ -110,7 +110,7 @@ public class XWorkMethodAccessor extends ObjectMethodAccessor {
@Override
public Object callStaticMethod(Map context, Class aClass, String string, Object[] objects) throws MethodFailedException {
Boolean exec = (Boolean) context.get(ReflectionContextState.DENY_METHOD_EXECUTION);
Boolean exec = ReflectionContextState.isDenyMethodExecution(context);
boolean e = ((exec == null) ? false : exec.booleanValue());
if (!e) {
@@ -306,13 +306,13 @@ abstract class AbstractLocalizedTextProvider implements LocalizedTextProvider {
/**
* A helper method for {@link ResourceBundle} bundle reload logic.
*
* Uses standard {@link ResourceBundle} methods to clear the bundle caches for the
*
* Uses standard {@link ResourceBundle} methods to clear the bundle caches for the
* {@link ClassLoader} instances that this class is aware of at the time of the call.
*
* The <code>clearCache()</code> methods have been available since Java 1.6, so
*
* The <code>clearCache()</code> methods have been available since Java 1.6, so
* it is anticipated the logic will work on any subsequent JVM versions.
*
*
* @since 2.6
*/
private void clearResourceBundleClassloaderCaches() {
@@ -326,14 +326,14 @@ abstract class AbstractLocalizedTextProvider implements LocalizedTextProvider {
/**
* "Hacky" helper method that attempts to clear the Tomcat <code>ResourceEntry</code>
* {@link Map} using knowledge of the Tomcat source code.
*
* It relies on the {@link #TOMCAT_RESOURCE_ENTRIES_FIELD} field name, base class name
*
* It relies on the {@link #TOMCAT_RESOURCE_ENTRIES_FIELD} field name, base class name
* {@link #TOMCAT_WEBAPP_CLASSLOADER_BASE}. and descendant class names {@link #TOMCAT_WEBAPP_CLASSLOADER},
* {@link #TOMCAT_PARALLEL_WEBAPP_CLASSLOADER}, to keep the values identified in the constants.
* It appears to be valid for Tomcat versions 7-10 so far, but could become invalid at any time in the future
* when the resource handling logic in Tomcat changes.
*
* Note: With Java 9+, calling this method may result in "Illegal reflective access" warnings. Be aware
*
* Note: With Java 9+, calling this method may result in "Illegal reflective access" warnings. Be aware
* its logic may fail in a future version of Java that blocks the reflection calls needed for this method.
*/
private void clearTomcatCache() {
@@ -367,10 +367,10 @@ abstract class AbstractLocalizedTextProvider implements LocalizedTextProvider {
/**
* Helper method that is intended to clear a {@link Map} instance by name.
*
*
* This method relies on reflection to perform its operations, and may be blocked in Java 9 and later,
* depending on the accessibility of the field.
*
*
* @param cl The {@link Class} of the obj parameter.
* @param obj The {@link Object} from which the named field is to be extracted (may be <code>null</code> for a static field).
* @param name The name of the field containing a {@link Map} reference.
@@ -433,9 +433,9 @@ abstract class AbstractLocalizedTextProvider implements LocalizedTextProvider {
* Set the {@link #searchDefaultBundlesFirst} flag state. This flag may be used by descendant TextProvider
* implementations to determine if default bundles should be searched for messages first (before the standard
* flow of the {@link LocalizedTextProvider} implementation the descendant provides).
*
*
* @param searchDefaultBundlesFirst provide {@link String} "true" or "false" to set the flag state accordingly.
*
*
* @since 2.6
*/
@Inject(value = StrutsConstants.STRUTS_I18N_SEARCH_DEFAULTBUNDLES_FIRST, required = false)
@@ -490,7 +490,7 @@ abstract class AbstractLocalizedTextProvider implements LocalizedTextProvider {
}
return bundle;
}
/**
* Clears all the internal lists.
*
@@ -566,17 +566,17 @@ abstract class AbstractLocalizedTextProvider implements LocalizedTextProvider {
* against the default resource bundles. The default resource bundles are searched for a value using key first, then
* alternateKey when the first search fails, then utilizing defaultMessage (which may be <code>null</code>) if <em>both</em>
* key lookup operations fail.
*
*
* <p>
* A known use case is when a key indexes a collection (e.g. user.phone[0]) for which some specific keys may exist, but not all,
* along with a general key (e.g. user.phone[*]). In such cases the specific key would be passed in the key parameter and the
* general key would be passed in the alternateKey parameter.
* </p>
*
*
* @param key the initial key to search for a value within the default resource bundles.
* @param alternateKey the alternate (fall-back) key to search for a value within the default resource bundles, if the initial key lookup fails.
* @param locale the {@link Locale} to be used for the default resource bundle lookup.
* @param valueStack the {@link ValueStack} associated with the operation.
* @param valueStack the {@link ValueStack} associated with the operation.
* @param args the argument array for parameterized messages (may be <code>null</code>).
* @param defaultMessage the default message {@link String} to use if both key lookup operations fail.
* @return the {@link GetDefaultMessageReturnArg} result containing the processed message lookup (by key first, then alternateKey if key's lookup fails).
@@ -616,11 +616,7 @@ abstract class AbstractLocalizedTextProvider implements LocalizedTextProvider {
MessageFormat mf = buildMessageFormat(message, locale);
return formatWithNullDetection(mf, args);
} catch (MissingResourceException e) {
if (devMode) {
LOG.warn("Missing key [{}] in bundle [{}]!", key, bundleName);
} else {
LOG.debug("Missing key [{}] in bundle [{}]!", key, bundleName);
}
LOG.debug("Missing key [{}] in bundle [{}]!", key, bundleName);
return null;
}
}
@@ -18,6 +18,7 @@
*/
package org.apache.struts2;
import org.apache.struts2.components.date.DateFormatter;
import org.apache.struts2.dispatcher.mapper.CompositeActionMapper;
/**
@@ -35,13 +36,13 @@ public final class StrutsConstants {
/** The encoding to use for localization messages */
public static final String STRUTS_I18N_ENCODING = "struts.i18n.encoding";
/**
/**
* Whether the default bundles should be searched for messages first. Can be used to modify the
* standard processing order for message lookup in TextProvider implementations.
* <p>
* Note: This control flag may not be meaningful to all provider implementations, and should be false by default.
* </p>
*
*
* @since 2.6
*/
public static final String STRUTS_I18N_SEARCH_DEFAULTBUNDLES_FIRST = "struts.i18n.search.defaultbundles.first";
@@ -104,10 +105,10 @@ public final class StrutsConstants {
/** Update freemarker templates cache in seconds */
public static final String STRUTS_FREEMARKER_TEMPLATES_CACHE_UPDATE_DELAY = "struts.freemarker.templatesCache.updateDelay";
/** Cache model instances at BeanWrapper level */
public static final String STRUTS_FREEMARKER_BEANWRAPPER_CACHE = "struts.freemarker.beanwrapperCache";
/** Maximum strong sizing for MruCacheStorage for freemarker */
public static final String STRUTS_FREEMARKER_MRU_MAX_STRONG_SIZE = "struts.freemarker.mru.max.strong.size";
@@ -132,6 +133,9 @@ public final class StrutsConstants {
/** A path to static content, by default and from historical point of view it's /static. */
public static final String STRUTS_UI_STATIC_CONTENT_PATH = "struts.ui.staticContentPath";
/** A global flag to enable/disable html body escaping in tags, can be overwritten per tag */
public static final String STRUTS_UI_ESCAPE_HTML_BODY = "struts.ui.escapeHtmlBody";
/** The maximize size of a multipart request (file upload) */
public static final String STRUTS_MULTIPART_MAXSIZE = "struts.multipart.maxSize";
@@ -188,7 +192,7 @@ public final class StrutsConstants {
* You can specify different prefixes that will be handled by different mappers
*/
public static final String PREFIX_BASED_MAPPER_CONFIGURATION = "struts.mapper.prefixMapping";
/** Whether the Struts filter should serve static content or not */
public static final String STRUTS_SERVE_STATIC_CONTENT = "struts.serve.static";
@@ -222,9 +226,6 @@ public final class StrutsConstants {
/** The name of the parameter to determine whether static field access will be allowed in OGNL expressions or not */
public static final String STRUTS_ALLOW_STATIC_FIELD_ACCESS = "struts.ognl.allowStaticFieldAccess";
/** The name of the parameter to determine whether static method access will be allowed in OGNL expressions or not */
public static final String STRUTS_ALLOW_STATIC_METHOD_ACCESS = "struts.ognl.allowStaticMethodAccess";
/** The com.opensymphony.xwork2.validator.ActionValidatorManager implementation class */
public static final String STRUTS_ACTIONVALIDATORMANAGER = "struts.actionValidatorManager";
@@ -249,6 +250,47 @@ public final class StrutsConstants {
/** Throw RuntimeException when a property is not found, or the evaluation of the expression fails */
public static final String STRUTS_EL_THROW_EXCEPTION = "struts.el.throwExceptionOnFailure";
/**
* Specifies an OGNL expression cache factory implementation. A default implementation is provided, but
* could be replaced by a custom one if desired.
*
* @since 2.6
*/
public static final String STRUTS_OGNL_EXPRESSION_CACHE_FACTORY = "struts.ognl.expressionCacheFactory";
/**
* Specifies an OGNL BeanInfo cache factory implementation. A default implementation is provided, but
* could be replaced by a custom one if desired.
*
* @since 2.6
*/
public static final String STRUTS_OGNL_BEANINFO_CACHE_FACTORY = "struts.ognl.beanInfoCacheFactory";
/**
* Specifies a maximum number of cached BeanInfo used by OgnlUtility. Not specified/set by default. If
* a positive integer is specified, it will set a limit whose behaviour depends on whether the
* normal (default) cache or optional LRU cache is in place.
*
* For the normal (default) cache, exceeding the maximum will cause the entire cache to flush (clear).
* For the optional LRU cache, once the maximum is reached, the least-recently-used (LRU) entry will be
* removed when a new entry needs to be added (cache is fully-utilized).
*
* @since 2.6
*/
public static final String STRUTS_OGNL_BEANINFO_CACHE_MAXSIZE = "struts.ognl.beanInfoCacheMaxSize";
/**
* Set the cache mode of the BeanInfo cache used by OgnlUtility. A value of true means enable
* least-recently-used (LRU) mode, a value of false (or any non-true value) means to use the
* default cache.
*
* Note: When enabling LRU cache mode you must also set a maximum size (via {@link #STRUTS_OGNL_BEANINFO_CACHE_MAXSIZE})
* for it to be effective. Otherwise, there is no condition to evict a LRU entry (cache has no limit).
*
* @since 2.6
*/
public static final String STRUTS_OGNL_BEANINFO_CACHE_LRU_MODE = "struts.ognl.beanInfoCacheLRUMode";
/**
* Logs properties that are not found (very verbose)
* @since 2.6
@@ -262,11 +304,54 @@ public final class StrutsConstants {
@Deprecated
public static final String STRUTS_LOG_MISSING_PROPERTIES = STRUTS_OGNL_LOG_MISSING_PROPERTIES;
/** Enables caching of parsed OGNL expressions */
public static final String STRUTS_ENABLE_OGNL_EXPRESSION_CACHE = "struts.ognl.enableExpressionCache";
/**
* Enables caching of parsed OGNL expressions
* @since 2.6
*/
public static final String STRUTS_OGNL_ENABLE_EXPRESSION_CACHE = "struts.ognl.enableExpressionCache";
/** Enables evaluation of OGNL expressions */
public static final String STRUTS_ENABLE_OGNL_EVAL_EXPRESSION = "struts.ognl.enableOGNLEvalExpression";
/**
* Enables caching of parsed OGNL expressions
* @deprecated as of 2.6. Use {@link #STRUTS_OGNL_ENABLE_EXPRESSION_CACHE} instead.
*/
public static final String STRUTS_ENABLE_OGNL_EXPRESSION_CACHE = STRUTS_OGNL_ENABLE_EXPRESSION_CACHE;
/**
* Specifies a maximum number of cached parsed OGNL expressions. Not specified/set by default. If
* a positive integer is specified, it will set a limit whose behaviour depends on whether the
* normal (default) cache or optional LRU cache is in place.
*
* For the normal (default) cache, exceeding the maximum will cause the entire cache to flush (clear).
* For the optional LRU cache, once the maximum is reached, the least-recently-used (LRU) entry will be
* removed when a new entry needs to be added (cache is fully-utilized).
*
* @since 2.6
*/
public static final String STRUTS_OGNL_EXPRESSION_CACHE_MAXSIZE = "struts.ognl.expressionCacheMaxSize";
/**
* Set the cache mode of the parsed OGNL expression cache. A value of true means enable
* least-recently-used (LRU) mode, a value of false (or any non-true value) means to use the
* default cache.
*
* Note: When enabling LRU cache mode you must also set a maximum size (via {@link #STRUTS_OGNL_EXPRESSION_CACHE_MAXSIZE})
* for it to be effective. Otherwise, there is no condition to evict a LRU entry (cache has no limit).
*
* @since 2.6
*/
public static final String STRUTS_OGNL_EXPRESSION_CACHE_LRU_MODE = "struts.ognl.expressionCacheLRUMode";
/**
* Enables evaluation of OGNL expressions
* @since 2.6
*/
public static final String STRUTS_OGNL_ENABLE_EVAL_EXPRESSION = "struts.ognl.enableEvalExpression";
/**
* Enables evaluation of OGNL expressions
* @deprecated as of 2.6. Use {@link #STRUTS_OGNL_ENABLE_EVAL_EXPRESSION} instead.
*/
public static final String STRUTS_ENABLE_OGNL_EVAL_EXPRESSION = STRUTS_OGNL_ENABLE_EVAL_EXPRESSION;
/** The maximum length of an expression (OGNL) */
public static final String STRUTS_OGNL_EXPRESSION_MAX_LENGTH = "struts.ognl.expressionMaxLength";
@@ -362,4 +447,7 @@ public final class StrutsConstants {
public static final String STRUTS_CHAINING_COPY_FIELD_ERRORS = "struts.chaining.copyFieldErrors";
public static final String STRUTS_CHAINING_COPY_MESSAGES = "struts.chaining.copyMessages";
public static final String STRUTS_OBJECT_FACTORY_CLASSLOADER = "struts.objectFactory.classloader";
/** See {@link org.apache.struts2.components.Date#setDateFormatter(DateFormatter)} */
public static final String STRUTS_DATE_FORMATTER = "struts.date.formatter";
}
@@ -20,6 +20,7 @@ package org.apache.struts2.components;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.ValueStack;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -69,8 +70,7 @@ public class Anchor extends ClosingUIBean {
protected UrlProvider urlProvider;
protected UrlRenderer urlRenderer;
protected boolean processingTagBody = false;
protected boolean escapeHtmlBody = true;
//these params are passed by the Param tag
protected Map urlParameters = new LinkedHashMap();
@@ -96,18 +96,6 @@ public class Anchor extends ClosingUIBean {
return true;
}
/**
* Override to set if body content should be HTML-escaped.
*
* @return true if body should be HTML-escaped, false otherwise.
*
* @since 2.6
*/
@Override
public boolean escapeHtmlBody() {
return escapeHtmlBody;
}
@Override
protected void evaluateExtraParams() {
super.evaluateExtraParams();
@@ -124,6 +112,8 @@ public class Anchor extends ClosingUIBean {
addParameter("href", ensureAttributeSafelyNotEscaped(builtHref));
}
}
addParameter("escapeHtmlBody", escapeHtmlBody);
}
@Inject(StrutsConstants.STRUTS_URL_INCLUDEPARAMS)
@@ -276,7 +266,7 @@ public class Anchor extends ClosingUIBean {
urlProvider.setForceAddSchemeHostAndPort(forceAddSchemeHostAndPort);
}
@StrutsTagAttribute(required = false, description = "Specifies whether to HTML-escape the tag body or not", type = "Boolean", defaultValue = "true")
@StrutsTagAttribute(description = "Specifies whether to HTML-escape the tag body or not", type = "Boolean", defaultValue = "false")
public void setEscapeHtmlBody(boolean escapeHtmlBody) {
this.escapeHtmlBody = escapeHtmlBody;
}
@@ -75,7 +75,8 @@ public class Checkbox extends UIBean {
}
if (submitUnchecked != null) {
addParameter("submitUnchecked", findValue(submitUnchecked, Boolean.class));
Object parsedValue = findValue(submitUnchecked, Boolean.class);
addParameter("submitUnchecked", parsedValue == null ? Boolean.valueOf(submitUnchecked) : parsedValue);
} else {
addParameter("submitUnchecked", false);
}
@@ -48,7 +48,7 @@ import com.opensymphony.xwork2.util.ValueStack;
allowDynamicAttributes = true)
public class CheckboxList extends ListUIBean {
final public static String TEMPLATE = "checkboxlist";
public CheckboxList(ValueStack stack, HttpServletRequest request, HttpServletResponse response) {
super(stack, request, response);
}
@@ -56,9 +56,19 @@ public class CheckboxList extends ListUIBean {
protected String getDefaultTemplate() {
return TEMPLATE;
}
public void evaluateExtraParams() {
super.evaluateExtraParams();
}
}
/**
* Checkboxlist tag requires lazy evaluation as list of tags is dynamically generated using <s:iterator/>
*
* @return boolean true by default
*/
@Override
protected boolean lazyEvaluation() {
return true;
}
}
@@ -68,6 +68,7 @@ public class Component {
protected static ConcurrentMap<Class<?>, Collection<String>> standardAttributesMap = new ConcurrentHashMap<>();
protected boolean devMode = false;
protected boolean escapeHtmlBody = false;
protected ValueStack stack;
protected Map<String, Object> parameters;
protected ActionMapper actionMapper;
@@ -116,6 +117,11 @@ public class Component {
this.throwExceptionOnELFailure = BooleanUtils.toBoolean(throwException);
}
@Inject(value = StrutsConstants.STRUTS_UI_ESCAPE_HTML_BODY, required = false)
public void setEscapeHtmlBody(String escapeHtmlBody) {
this.escapeHtmlBody = BooleanUtils.toBoolean(escapeHtmlBody);
}
@Inject
public void setUrlHelper(UrlHelper urlHelper) {
this.urlHelper = urlHelper;
@@ -213,13 +219,13 @@ public class Component {
* @return the component if found, <tt>null</tt> if not.
*/
protected Component findAncestor(Class<?> clazz) {
Stack componentStack = getComponentStack();
Stack<Component> componentStack = getComponentStack();
int currPosition = componentStack.search(this);
if (currPosition >= 0) {
int start = componentStack.size() - currPosition - 1;
for (int i = start; i >= 0; i--) {
Component component = (Component) componentStack.get(i);
Component component = componentStack.get(i);
if (clazz.isAssignableFrom(component.getClass()) && component != this) {
return component;
}
@@ -378,9 +384,8 @@ public class Component {
return expression;
}
} else {
expression = stripExpression(expression);
return getStack().findValue(expression, toType, throwExceptionOnELFailure);
String strippedExpression = stripExpression(expression);
return getStack().findValue(strippedExpression, toType, throwExceptionOnELFailure);
}
}
@@ -536,7 +541,7 @@ public class Component {
* @since 2.6
*/
public boolean escapeHtmlBody() {
return true;
return escapeHtmlBody;
}
/**
@@ -572,16 +577,16 @@ public class Component {
/**
* Request that the tag state be cleared during {@link org.apache.struts2.views.jsp.StrutsBodyTagSupport#doEndTag()} processing,
* which may help with certain edge cases with tag logic running on servers that implement JSP Tag Pooling.
*
* <em>Note:</em> All Tag classes that extend {@link org.apache.struts2.views.jsp.StrutsBodyTagSupport} must implement a setter for
*
* <em>Note:</em> All Tag classes that extend {@link org.apache.struts2.views.jsp.StrutsBodyTagSupport} must implement a setter for
* this attribute (same name), and it must be defined at the Tag class level.
* Defining a setter in the superclass alone is insufficient (results in "Cannot find a setter method for the attribute").
*
*
* See {@link org.apache.struts2.views.jsp.StrutsBodyTagSupport#clearTagStateForTagPoolingServers() for additional details.
*
*
* @param performClearTagStateForTagPoolingServers true if tag state should be cleared, false otherwise.
*/
@StrutsTagAttribute(description="Whether to clear all tag state during doEndTag() processing (if applicable)", type="Boolean", defaultValue="false", required = false)
@StrutsTagAttribute(description="Whether to clear all tag state during doEndTag() processing (if applicable)", type="Boolean", defaultValue="false")
public void setPerformClearTagStateForTagPoolingServers(boolean performClearTagStateForTagPoolingServers) {
this.performClearTagStateForTagPoolingServers = performClearTagStateForTagPoolingServers;
}
@@ -18,29 +18,29 @@
*/
package org.apache.struts2.components;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.TextProvider;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.ValueStack;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.components.date.DateFormatter;
import org.apache.struts2.views.annotations.StrutsTag;
import org.apache.struts2.views.annotations.StrutsTagAttribute;
import java.io.IOException;
import java.io.Writer;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
/**
* <!-- START SNIPPET: javadoc -->
*
* <p>
* Format Date object in different ways.
* <p>
* The date tag will allow you to format a Date in a quick and easy way.
@@ -59,6 +59,12 @@ import java.util.List;
* </p>
*
* <p>
* <b>Note</b>: Since Struts 2.6 a new Java 8 API has been used to format the Date, it's based on
* <a href="https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html">DateTimeFormatter</a>
* which uses a bit different patterns.
* </p>
*
* <p>
* Configurable attributes are:
* </p>
*
@@ -130,8 +136,8 @@ import java.util.List;
* <td>if one is not found DateFormat.MEDIUM format will be used</td>
* </tr>
* </table>
*
*
* <p>
* <p>
* <!-- END SNIPPET: javadoc -->
*
* <p><b>Examples</b></p>
@@ -145,12 +151,12 @@ import java.util.List;
* </pre>
*
* <code>Date</code>
*
*/
@StrutsTag(name="date", tldBodyContent="empty", tldTagClass="org.apache.struts2.views.jsp.DateTag", description="Render a formatted date.")
@StrutsTag(name = "date", tldBodyContent = "empty", tldTagClass = "org.apache.struts2.views.jsp.DateTag", description = "Render a formatted date.")
public class Date extends ContextBean {
private static final Logger LOG = LogManager.getLogger(Date.class);
/**
* Property name to fall back when no format is specified
*/
@@ -202,17 +208,18 @@ public class Date extends ContextBean {
private String timezone;
private DateFormatter dateFormatter;
public Date(ValueStack stack) {
super(stack);
}
private TextProvider findProviderInStack() {
for (Object o : getStack().getRoot()) {
if (o instanceof TextProvider) {
return (TextProvider) o;
}
}
return null;
/**
* An instance of {@link DateFormatter}
*/
@Inject
public void setDateFormatter(DateFormatter dateFormatter) {
this.dateFormatter = dateFormatter;
}
/**
@@ -280,6 +287,8 @@ public class Date extends ContextBean {
@Override
public boolean end(Writer writer, String body) {
TextProvider textProvider = findProviderInStack();
ZonedDateTime date = null;
final ZoneId tz = getTimeZone();
// find the name on the valueStack
@@ -292,26 +301,27 @@ public class Date extends ContextBean {
date = Instant.ofEpochMilli((long) dateObject).atZone(tz);
} else if (dateObject instanceof LocalDateTime) {
date = ((LocalDateTime) dateObject).atZone(tz);
} else if (dateObject instanceof LocalDate) {
date = ((LocalDate) dateObject).atStartOfDay(tz);
} else if (dateObject instanceof Instant) {
date = ((Instant) dateObject).atZone(tz);
} else {
if (devMode) {
TextProvider tp = findProviderInStack();
String developerNotification = "";
if (tp != null) {
developerNotification = findProviderInStack().getText(
"devmode.notification",
"Developer Notification:\n{0}",
new String[]{
"Expression [" + name + "] passed to <s:date/> tag which was evaluated to [" + dateObject + "]("
+ (dateObject != null ? dateObject.getClass() : "null") + ") isn't supported!"
}
if (textProvider != null) {
developerNotification = textProvider.getText(
"devmode.notification",
"Developer Notification:\n{0}",
new String[]{
"Expression [" + name + "] passed to <s:date/> tag which was evaluated to [" + dateObject + "]("
+ (dateObject != null ? dateObject.getClass() : "null") + ") isn't supported!"
}
);
}
LOG.warn(developerNotification);
} else {
LOG.debug("Expression [{}] passed to <s:date/> tag which was evaluated to [{}]({}) isn't supported!",
name, dateObject, (dateObject != null ? dateObject.getClass() : "null"));
name, dateObject, (dateObject != null ? dateObject.getClass() : "null"));
}
}
@@ -321,33 +331,11 @@ public class Date extends ContextBean {
}
String msg;
if (date != null) {
TextProvider tp = findProviderInStack();
if (tp != null) {
if (textProvider != null) {
if (nice) {
msg = formatTime(tp, date);
msg = formatTime(textProvider, date);
} else {
DateTimeFormatter dtf;
if (format == null) {
String globalFormat = null;
// if the format is not specified, fall back using the
// defined property DATETAG_PROPERTY
globalFormat = tp.getText(DATETAG_PROPERTY);
// if tp.getText can not find the property then the
// returned string is the same as input =
// DATETAG_PROPERTY
if (globalFormat != null
&& !DATETAG_PROPERTY.equals(globalFormat)) {
dtf = DateTimeFormatter.ofPattern(globalFormat, ActionContext.getContext().getLocale());
} else {
dtf = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)
.withLocale(ActionContext.getContext().getLocale());
}
} else {
dtf = DateTimeFormatter.ofPattern(format, ActionContext.getContext().getLocale());
}
msg = dtf.format(date);
msg = formatDate(textProvider, date);
}
if (msg != null) {
try {
@@ -365,6 +353,20 @@ public class Date extends ContextBean {
return super.end(writer, "");
}
private String formatDate(TextProvider textProvider, ZonedDateTime date) {
String useFormat = format;
if (useFormat == null) {
// if the format is not specified, fall back using the defined property DATETAG_PROPERTY
useFormat = textProvider.getText(DATETAG_PROPERTY);
if (DATETAG_PROPERTY.equals(useFormat)) {
// if tp.getText can not find the property then the
// returned string is the same as input = DATETAG_PROPERTY
useFormat = null;
}
}
return dateFormatter.format(date, useFormat);
}
private ZoneId getTimeZone() {
ZoneId tz = ZoneId.systemDefault();
if (timezone != null) {
@@ -378,17 +380,26 @@ public class Date extends ContextBean {
return tz;
}
@StrutsTagAttribute(description="Date or DateTime format pattern", rtexprvalue=false)
private TextProvider findProviderInStack() {
for (Object o : getStack().getRoot()) {
if (o instanceof TextProvider) {
return (TextProvider) o;
}
}
return null;
}
@StrutsTagAttribute(description = "Date or DateTime format pattern")
public void setFormat(String format) {
this.format = format;
}
@StrutsTagAttribute(description="Whether to print out the date nicely", type="Boolean", defaultValue="false")
@StrutsTagAttribute(description = "Whether to print out the date nicely", type = "Boolean", defaultValue = "false")
public void setNice(boolean nice) {
this.nice = nice;
}
@StrutsTagAttribute(description = "The specific timezone in which to format the date", required = false)
@StrutsTagAttribute(description = "The specific timezone in which to format the date")
public void setTimezone(String timezone) {
this.timezone = timezone;
}
@@ -400,7 +411,7 @@ public class Date extends ContextBean {
return name;
}
@StrutsTagAttribute(description="The date value to format", required=true)
@StrutsTagAttribute(description = "The date value to format", required = true)
public void setName(String name) {
this.name = name;
}
@@ -25,6 +25,7 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.RequestUtils;
import org.apache.struts2.StrutsConstants;
import org.apache.struts2.StrutsException;
import org.apache.struts2.util.FastByteArrayOutputStream;
import org.apache.struts2.views.annotations.StrutsTag;
import org.apache.struts2.views.annotations.StrutsTagAttribute;
@@ -33,6 +34,7 @@ import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
@@ -296,6 +298,19 @@ public class Include extends Component {
buffer = new FastByteArrayOutputStream();
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setWriteListener(WriteListener writeListener) {
try {
writeListener.onWritePossible();
} catch (IOException e) {
throw new StrutsException(e);
}
}
/**
* Return all data that has been written to this OutputStream.
@@ -195,7 +195,6 @@ public abstract class ListUIBean extends UIBean {
this.listTitle = listTitle;
}
public void setThrowExceptionOnNullValueAttribute(boolean throwExceptionOnNullValueAttribute) {
this.throwExceptionOnNullValueAttribute = throwExceptionOnNullValueAttribute;
}
@@ -57,7 +57,7 @@ import javax.servlet.http.HttpServletResponse;
allowDynamicAttributes = true)
public class Radio extends ListUIBean {
final public static String TEMPLATE = "radiomap";
public Radio(ValueStack stack, HttpServletRequest request, HttpServletResponse response) {
super(stack, request, response);
}
@@ -65,8 +65,19 @@ public class Radio extends ListUIBean {
protected String getDefaultTemplate() {
return TEMPLATE;
}
public void evaluateExtraParams() {
super.evaluateExtraParams();
}
}
/**
* Radio tag requires lazy evaluation as list of tags is dynamically generated using <s:iterator/>
*
* @return boolean true by default
*/
@Override
protected boolean lazyEvaluation() {
return true;
}
}
@@ -321,7 +321,7 @@ public class ServletUrlRenderer implements UrlRenderer {
* @param parameters component parameters
* @param contextParameters request parameters
*/
protected void mergeRequestParameters(String value, Map<String, Object> parameters, Map<String, Object> contextParameters) {
protected void mergeRequestParameters(String value, Map<String, Object> parameters, Map<String, ?> contextParameters) {
Map<String, Object> mergedParams = new LinkedHashMap<>(contextParameters);
@@ -333,7 +333,7 @@ public class ServletUrlRenderer implements UrlRenderer {
String queryString = value.substring(value.indexOf('?') + 1);
mergedParams = urlHelper.parseQueryString(queryString, false);
for (Map.Entry<String, Object> entry : contextParameters.entrySet()) {
for (Map.Entry<String, ?> entry : contextParameters.entrySet()) {
if (!mergedParams.containsKey(entry.getKey())) {
mergedParams.put(entry.getKey(), entry.getValue());
}
@@ -23,6 +23,9 @@ import java.io.Writer;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.opensymphony.xwork2.inject.Inject;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.struts2.StrutsConstants;
import org.apache.struts2.views.annotations.StrutsTag;
import org.apache.struts2.views.annotations.StrutsTagAttribute;
@@ -54,7 +57,6 @@ public class Submit extends FormButton {
final public static String OPEN_TEMPLATE = "submit";
final public static String TEMPLATE = "submit-close";
protected String src;
protected boolean escapeHtmlBody = true;
public Submit(ValueStack stack, HttpServletRequest request, HttpServletResponse response) {
super(stack, request, response);
@@ -73,7 +75,7 @@ public class Submit extends FormButton {
value = "Submit";
}
if (((key != null)) && (value == null)) {
if ((key != null) && (value == null)) {
this.value = "%{getText('"+key +"')}";
}
@@ -83,8 +85,11 @@ public class Submit extends FormButton {
public void evaluateExtraParams() {
super.evaluateExtraParams();
if (src != null)
if (src != null) {
addParameter("src", findString(src));
}
addParameter("escapeHtmlBody", escapeHtmlBody);
}
/**
@@ -101,7 +106,7 @@ public class Submit extends FormButton {
this.src = src;
}
@StrutsTagAttribute(required = false, description = "Specifies whether to HTML-escape the tag body or not", type = "Boolean", defaultValue = "true")
@StrutsTagAttribute(description = "Specifies whether to HTML-escape the tag body or not", type = "Boolean", defaultValue = "false")
public void setEscapeHtmlBody(boolean escapeHtmlBody) {
this.escapeHtmlBody = escapeHtmlBody;
}
@@ -111,18 +116,6 @@ public class Submit extends FormButton {
return true;
}
/**
* Override to set if body content should be HTML-escaped.
*
* @return true if body should be HTML-escaped, false otherwise.
*
* @since 2.6
*/
@Override
public boolean escapeHtmlBody() {
return escapeHtmlBody;
}
/**
* Overrides to be able to render body in a template rather than always before the template
*/
@@ -20,7 +20,9 @@ package org.apache.struts2.components;
import com.opensymphony.xwork2.config.ConfigurationException;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.TextParseUtil;
import com.opensymphony.xwork2.util.ValueStack;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -31,6 +33,7 @@ import org.apache.struts2.components.template.TemplateEngine;
import org.apache.struts2.components.template.TemplateEngineManager;
import org.apache.struts2.components.template.TemplateRenderingContext;
import org.apache.struts2.dispatcher.StaticContentLoader;
import org.apache.struts2.util.ComponentUtils;
import org.apache.struts2.util.TextProviderHelper;
import org.apache.struts2.views.annotations.StrutsTagAttribute;
import org.apache.struts2.views.util.ContextUtil;
@@ -699,11 +702,13 @@ public abstract class UIBean extends Component {
}
if (requiredLabel != null) {
addParameter("required", findValue(requiredLabel, Boolean.class));
Object parsedValue = findValue(requiredLabel, Boolean.class);
addParameter("required", parsedValue == null ? Boolean.valueOf(requiredLabel) : parsedValue);
}
if (disabled != null) {
addParameter("disabled", findValue(disabled, Boolean.class));
Object parsedValue = findValue(disabled, Boolean.class);
addParameter("disabled", parsedValue == null ? Boolean.valueOf(disabled) : parsedValue);
}
if (tabindex != null) {
@@ -883,9 +888,9 @@ public abstract class UIBean extends Component {
this.addParameter("tooltipDelay", findString(this.tooltipDelay));
if (this.javascriptTooltip != null) {
Boolean jsTooltips = (Boolean) findValue(this.javascriptTooltip, Boolean.class);
Object jsTooltips = findValue(this.javascriptTooltip, Boolean.class);
//TODO use a Boolean model when tooltipConfig is dropped
this.addParameter("jsTooltipEnabled", jsTooltips.toString());
this.addParameter("jsTooltipEnabled", jsTooltips == null ? this.javascriptTooltip : jsTooltips.toString());
if (form != null)
form.addParameter("hasTooltip", jsTooltips);
@@ -965,7 +970,7 @@ public abstract class UIBean extends Component {
// 1] UI component's tooltipConfig attribute OR
// 2] <param name="tooltip" value="" /> param tag value attribute
result = new LinkedHashMap<>((Map) tooltipConfigObj);
result = new LinkedHashMap<String, String>((Map) tooltipConfigObj);
} else if (tooltipConfigObj instanceof String) {
// we get this if its configured using
@@ -1272,10 +1277,16 @@ public abstract class UIBean extends Component {
public void setDynamicAttributes(Map<String, String> tagDynamicAttributes) {
for (Map.Entry<String, String> entry : tagDynamicAttributes.entrySet()) {
String entryKey = entry.getKey();
String attrName = entry.getKey();
String attrValue = entry.getValue();
if (!isValidTagAttribute(entryKey)) {
dynamicAttributes.put(entryKey, entry.getValue());
if (!isValidTagAttribute(attrName)) {
if (ComponentUtils.containsExpression(attrValue) && !lazyEvaluation()) {
String translated = TextParseUtil.translateVariables('%', attrValue, stack);
dynamicAttributes.put(attrName, ObjectUtils.defaultIfNull(translated, attrValue));
} else {
dynamicAttributes.put(attrName, attrValue);
}
}
}
}
@@ -1296,4 +1307,14 @@ public abstract class UIBean extends Component {
}
}
/**
* Used to avoid evaluating attributes in {@link #evaluateParams()} or {@link #evaluateExtraParams()}
* as evaluation will happen in tag's template
*
* @return boolean false if evaluation should be performed in ftl
*/
protected boolean lazyEvaluation() {
return false;
}
}
@@ -0,0 +1,40 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.struts2.components.date;
import java.time.temporal.TemporalAccessor;
/**
* Allows defines a wrapper around different formatting APIs, like old SimpleDateFormat
* and new DateTimeFormatter introduced in Java 8 Date/Time API
* <p>
* New instance will be injected using {@link org.apache.struts2.StrutsConstants#STRUTS_DATE_FORMATTER}
*/
public interface DateFormatter {
/**
* Formats provided temporal with the given format
*
* @param temporal Java 8 {@link TemporalAccessor}
* @param format implementation specific format
* @return a string representation of the formatted `temporal`
*/
String format(TemporalAccessor temporal, String format);
}
@@ -0,0 +1,43 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.struts2.components.date;
import com.opensymphony.xwork2.ActionContext;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.time.temporal.TemporalAccessor;
import java.util.Locale;
public class DateTimeFormatterAdapter implements DateFormatter {
@Override
public String format(TemporalAccessor temporal, String format) {
DateTimeFormatter dtf;
Locale locale = ActionContext.getContext().getLocale();
if (format == null) {
dtf = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)
.withLocale(locale);
} else {
dtf = DateTimeFormatter.ofPattern(format, locale);
}
return dtf.format(temporal);
}
}
@@ -0,0 +1,44 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.struts2.components.date;
import com.opensymphony.xwork2.ActionContext;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.temporal.TemporalAccessor;
import java.util.Date;
import java.util.Locale;
public class SimpleDateFormatAdapter implements DateFormatter {
@Override
public String format(TemporalAccessor temporal, String format) {
DateFormat df;
Locale locale = ActionContext.getContext().getLocale();
if (format == null) {
df = SimpleDateFormat.getDateInstance(DateFormat.MEDIUM, locale);
} else {
df = new SimpleDateFormat(format, locale);
}
return df.format(new Date(Instant.from(temporal).toEpochMilli()));
}
}
@@ -148,16 +148,13 @@ public class FreemarkerTemplateEngine extends BaseTemplateEngine {
}
};
LOG.debug("Puts action on the top of ValueStack, just before the tag");
action = stack.pop();
LOG.debug("Push tag on top of the stack");
stack.push(templateContext.getTag());
stack.push(action);
try {
template.process(model, writer);
} finally {
stack.pop(); // removes action
stack.pop(); // removes tag
stack.push(action); // puts back action
LOG.debug("Removes tag from top of the stack");
stack.pop();
}
}
@@ -23,6 +23,8 @@ import com.opensymphony.xwork2.LocaleProviderFactory;
import com.opensymphony.xwork2.LocalizedTextProvider;
import com.opensymphony.xwork2.TextProviderFactory;
import com.opensymphony.xwork2.factory.UnknownHandlerFactory;
import com.opensymphony.xwork2.ognl.BeanInfoCacheFactory;
import com.opensymphony.xwork2.ognl.ExpressionCacheFactory;
import com.opensymphony.xwork2.security.AcceptedPatternsChecker;
import com.opensymphony.xwork2.security.ExcludedPatternsChecker;
import com.opensymphony.xwork2.FileManager;
@@ -54,13 +56,12 @@ import com.opensymphony.xwork2.util.PatternMatcher;
import com.opensymphony.xwork2.util.TextParser;
import com.opensymphony.xwork2.util.ValueStackFactory;
import com.opensymphony.xwork2.util.location.LocatableProperties;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import com.opensymphony.xwork2.util.reflection.ReflectionContextFactory;
import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
import com.opensymphony.xwork2.validator.ActionValidatorManager;
import org.apache.struts2.StrutsConstants;
import org.apache.struts2.components.UrlRenderer;
import org.apache.struts2.components.date.DateFormatter;
import org.apache.struts2.dispatcher.DispatcherErrorHandler;
import org.apache.struts2.dispatcher.StaticContentLoader;
import org.apache.struts2.dispatcher.mapper.ActionMapper;
@@ -367,6 +368,7 @@ import org.apache.struts2.views.util.UrlHelper;
*/
public class StrutsBeanSelectionProvider extends AbstractBeanSelectionProvider {
@Override
public void register(ContainerBuilder builder, LocatableProperties props) {
alias(ObjectFactory.class, StrutsConstants.STRUTS_OBJECTFACTORY, builder, props);
alias(ActionFactory.class, StrutsConstants.STRUTS_OBJECTFACTORY_ACTIONFACTORY, builder, props);
@@ -422,6 +424,11 @@ public class StrutsBeanSelectionProvider extends AbstractBeanSelectionProvider {
alias(NotExcludedAcceptedPatternsChecker.class, StrutsConstants.STRUTS_NOT_EXCLUDED_ACCEPTED_PATTERNS_CHECKER
, builder, props, Scope.SINGLETON);
alias(DateFormatter.class, StrutsConstants.STRUTS_DATE_FORMATTER, builder, props, Scope.SINGLETON);
alias(ExpressionCacheFactory.class, StrutsConstants.STRUTS_OGNL_EXPRESSION_CACHE_FACTORY, builder, props, Scope.SINGLETON);
alias(BeanInfoCacheFactory.class, StrutsConstants.STRUTS_OGNL_BEANINFO_CACHE_FACTORY, builder, props, Scope.SINGLETON);
switchDevMode(props);
}
@@ -90,7 +90,6 @@ public class ConstantConfig {
private BeanConfig localeProviderFactory;
private String mapperIdParameterName;
private Boolean ognlAllowStaticFieldAccess;
private Boolean ognlAllowStaticMethodAccess;
private BeanConfig actionValidatorManager;
private BeanConfig valueStackFactory;
private BeanConfig reflectionProvider;
@@ -101,7 +100,7 @@ public class ConstantConfig {
private Boolean elThrowExceptionOnFailure;
private Boolean ognlLogMissingProperties;
private Boolean ognlEnableExpressionCache;
private Boolean ognlEnableOGNLEvalExpression;
private Boolean ognlEnableEvalExpression;
private Boolean disableRequestAttributeValueStackLookup;
private BeanConfig viewUrlHelper;
private BeanConfig converterCollection;
@@ -144,6 +143,8 @@ public class ConstantConfig {
private Boolean disallowProxyMemberAccess;
private Integer ognlAutoGrowthCollectionLimit;
private String staticContentPath;
private BeanConfig expressionCacheFactory;
private BeanConfig beaninfoCacheFactory;
protected String beanConfToString(BeanConfig beanConf) {
return beanConf == null ? null : beanConf.getName();
@@ -220,7 +221,6 @@ public class ConstantConfig {
map.put(StrutsConstants.STRUTS_LOCALE_PROVIDER_FACTORY, beanConfToString(localeProviderFactory));
map.put(StrutsConstants.STRUTS_ID_PARAMETER_NAME, mapperIdParameterName);
map.put(StrutsConstants.STRUTS_ALLOW_STATIC_FIELD_ACCESS, Objects.toString(ognlAllowStaticFieldAccess, null));
map.put(StrutsConstants.STRUTS_ALLOW_STATIC_METHOD_ACCESS, Objects.toString(ognlAllowStaticMethodAccess, null));
map.put(StrutsConstants.STRUTS_ACTIONVALIDATORMANAGER, beanConfToString(actionValidatorManager));
map.put(StrutsConstants.STRUTS_VALUESTACKFACTORY, beanConfToString(valueStackFactory));
map.put(StrutsConstants.STRUTS_REFLECTIONPROVIDER, beanConfToString(reflectionProvider));
@@ -230,8 +230,8 @@ public class ConstantConfig {
map.put(StrutsConstants.STRUTS_UNKNOWN_HANDLER_MANAGER, beanConfToString(unknownHandlerManager));
map.put(StrutsConstants.STRUTS_EL_THROW_EXCEPTION, Objects.toString(elThrowExceptionOnFailure, null));
map.put(StrutsConstants.STRUTS_OGNL_LOG_MISSING_PROPERTIES, Objects.toString(ognlLogMissingProperties, null));
map.put(StrutsConstants.STRUTS_ENABLE_OGNL_EXPRESSION_CACHE, Objects.toString(ognlEnableExpressionCache, null));
map.put(StrutsConstants.STRUTS_ENABLE_OGNL_EVAL_EXPRESSION, Objects.toString(ognlEnableOGNLEvalExpression, null));
map.put(StrutsConstants.STRUTS_OGNL_ENABLE_EXPRESSION_CACHE, Objects.toString(ognlEnableExpressionCache, null));
map.put(StrutsConstants.STRUTS_OGNL_ENABLE_EVAL_EXPRESSION, Objects.toString(ognlEnableEvalExpression, null));
map.put(StrutsConstants.STRUTS_DISABLE_REQUEST_ATTRIBUTE_VALUE_STACK_LOOKUP, Objects.toString(disableRequestAttributeValueStackLookup, null));
map.put(StrutsConstants.STRUTS_URL_HELPER, beanConfToString(viewUrlHelper));
map.put(StrutsConstants.STRUTS_CONVERTER_COLLECTION, beanConfToString(converterCollection));
@@ -274,6 +274,8 @@ public class ConstantConfig {
map.put(StrutsConstants.STRUTS_DISALLOW_PROXY_MEMBER_ACCESS, Objects.toString(disallowProxyMemberAccess, null));
map.put(StrutsConstants.STRUTS_OGNL_AUTO_GROWTH_COLLECTION_LIMIT, Objects.toString(ognlAutoGrowthCollectionLimit, null));
map.put(StrutsConstants.STRUTS_UI_STATIC_CONTENT_PATH, Objects.toString(staticContentPath, StaticContentLoader.DEFAULT_STATIC_CONTENT_PATH));
map.put(StrutsConstants.STRUTS_OGNL_EXPRESSION_CACHE_FACTORY, beanConfToString(expressionCacheFactory));
map.put(StrutsConstants.STRUTS_OGNL_BEANINFO_CACHE_FACTORY, beanConfToString(beaninfoCacheFactory));
return map;
}
@@ -806,14 +808,6 @@ public class ConstantConfig {
this.ognlAllowStaticFieldAccess = ognlAllowStaticFieldAccess;
}
public Boolean getOgnlAllowStaticMethodAccess() {
return ognlAllowStaticMethodAccess;
}
public void setOgnlAllowStaticMethodAccess(Boolean ognlAllowStaticMethodAccess) {
this.ognlAllowStaticMethodAccess = ognlAllowStaticMethodAccess;
}
public BeanConfig getActionValidatorManager() {
return actionValidatorManager;
}
@@ -922,12 +916,12 @@ public class ConstantConfig {
this.ognlEnableExpressionCache = ognlEnableExpressionCache;
}
public Boolean getOgnlEnableOGNLEvalExpression() {
return ognlEnableOGNLEvalExpression;
public Boolean getOgnlEnableEvalExpression() {
return ognlEnableEvalExpression;
}
public void setOgnlEnableOGNLEvalExpression(Boolean ognlEnableOGNLEvalExpression) {
this.ognlEnableOGNLEvalExpression = ognlEnableOGNLEvalExpression;
public void setOgnlEnableEvalExpression(Boolean ognlEnableEvalExpression) {
this.ognlEnableEvalExpression = ognlEnableEvalExpression;
}
public Boolean getDisableRequestAttributeValueStackLookup() {
@@ -1341,4 +1335,28 @@ public class ConstantConfig {
public void setStaticContentPath(String staticContentPath) {
this.staticContentPath = StaticContentLoader.Validator.validateStaticContentPath(staticContentPath);
}
public BeanConfig getExpressionCacheFactory() {
return expressionCacheFactory;
}
public void setExpressionCacheFactory(BeanConfig expressionCacheFactory) {
this.expressionCacheFactory = expressionCacheFactory;
}
public void setExpressionCacheFactory(Class<?> clazz) {
this.expressionCacheFactory = new BeanConfig(clazz, clazz.getName());
}
public BeanConfig getBeaninfoCacheFactory() {
return beaninfoCacheFactory;
}
public void setBeaninfoCacheFactory(BeanConfig beaninfoCacheFactory) {
this.beaninfoCacheFactory = beaninfoCacheFactory;
}
public void setBeaninfoCacheFactory(Class<?> clazz) {
this.beaninfoCacheFactory = new BeanConfig(clazz, clazz.getName());
}
}
@@ -22,28 +22,26 @@ import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
import com.opensymphony.xwork2.interceptor.PreResultListener;
import com.opensymphony.xwork2.util.TextParseUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* Interceptor that implements Cross-Origin Embedder Policy on incoming requests used to protect a
* document from loading any non-same-origin resources which don't explicitly grant the document
* permission to be loaded.
*
*
* @see <a href="https://web.dev/why-coop-coep/#coep">https://web.dev/why-coop-coep/#coep</a>
* @see <a href="https://wicg.github.io/cross-origin-embedder-policy/">https://wicg.github.io/cross-origin-embedder-policy/</a>
**/
public class CoepInterceptor extends AbstractInterceptor implements PreResultListener {
private static final Logger LOG = LoggerFactory.getLogger(CoepInterceptor.class);
private static final Logger LOG = LogManager.getLogger(CoepInterceptor.class);
private static final String REQUIRE_COEP_HEADER = "require-corp";
private static final String COEP_ENFORCING_HEADER = "Cross-Origin-Embedder-Policy";
private static final String COEP_REPORT_HEADER = "Cross-Origin-Embedder-Policy-Report-Only";
@@ -64,28 +62,29 @@ public class CoepInterceptor extends AbstractInterceptor implements PreResultLis
HttpServletResponse res = invocation.getInvocationContext().getServletResponse();
final String path = req.getContextPath();
if (exemptedPaths.contains(path)){
if (exemptedPaths.contains(path)) {
// no need to add headers
LOG.debug(String.format("Skipping COEP header for exempted path %s", path));
} else if (!disabled){
LOG.debug("Skipping COEP header for exempted path {}", path);
} else if (!disabled) {
res.setHeader(header, REQUIRE_COEP_HEADER);
}
}
public void setExemptedPaths(String paths){
public void setExemptedPaths(String paths) {
this.exemptedPaths.addAll(TextParseUtil.commaDelimitedStringToSet(paths));
}
public void setEnforcingMode(String mode){
public void setEnforcingMode(String mode) {
boolean enforcingMode = Boolean.parseBoolean(mode);
if (enforcingMode){
if (enforcingMode) {
header = COEP_ENFORCING_HEADER;
} else {
header = COEP_REPORT_HEADER;
}
}
public void setDisabled(String value){
public void setDisabled(String value) {
disabled = Boolean.parseBoolean(value);
}
}
@@ -22,15 +22,14 @@ import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
import com.opensymphony.xwork2.interceptor.PreResultListener;
import com.opensymphony.xwork2.util.TextParseUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashSet;
import java.util.Set;
/**
* Interceptor that implements Cross-Origin Opener Policy on incoming requests. COOP is a mitigation against
* cross-origin information leaks and is used to make websites, cross-origin isolated. Setting the COOP header allows you to ensure that a top-level window is
@@ -42,7 +41,8 @@ import java.util.Set;
**/
public class CoopInterceptor extends AbstractInterceptor implements PreResultListener {
private static final Logger LOG = LoggerFactory.getLogger(CoopInterceptor.class);
private static final Logger LOG = LogManager.getLogger(CoopInterceptor.class);
private static final String SAME_ORIGIN = "same-origin";
private static final String SAME_ORIGIN_ALLOW_POPUPS = "same-origin-allow-popups";
private static final String UNSAFE_NONE = "unsafe-none";
@@ -63,30 +63,31 @@ public class CoopInterceptor extends AbstractInterceptor implements PreResultLis
HttpServletResponse response = invocation.getInvocationContext().getServletResponse();
String path = request.getContextPath();
if (isExempted(path)){
if (isExempted(path)) {
// no need to add headers
LOG.debug(String.format("Skipping COOP header for exempted path %s", path));
LOG.debug("Skipping COOP header for exempted path {}", path);
} else {
response.setHeader(COOP_HEADER, getMode());
}
}
public boolean isExempted(String path){
public boolean isExempted(String path) {
return exemptedPaths.contains(path);
}
public void setExemptedPaths(String paths){
public void setExemptedPaths(String paths) {
exemptedPaths.addAll(TextParseUtil.commaDelimitedStringToSet(paths));
}
private String getMode(){
private String getMode() {
return mode;
}
public void setMode(String mode) {
if (!(mode.equals(SAME_ORIGIN) || mode.equals(SAME_ORIGIN_ALLOW_POPUPS) || mode.equals(UNSAFE_NONE))){
if (!(mode.equals(SAME_ORIGIN) || mode.equals(SAME_ORIGIN_ALLOW_POPUPS) || mode.equals(UNSAFE_NONE))) {
throw new IllegalArgumentException(String.format("Mode '%s' not recognized!", mode));
}
this.mode = mode;
}
}
}
@@ -45,25 +45,23 @@ import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
* </p>
*
* <ul>
*
* <li>{@link ServletContextAware}</li>
*
* <li>{@link org.apache.struts2.action.ServletContextAware}</li>
* <li>{@link ServletRequestAware}</li>
*
* <li>{@link org.apache.struts2.action.ServletRequestAware}</li>
* <li>{@link ServletResponseAware}</li>
*
* <li>{@link org.apache.struts2.action.ServletResponseAware}</li>
* <li>{@link ParameterAware} - deprecated since 2.5.4, please use {@link HttpParametersAware}</li>
*
* <li>{@link HttpParametersAware}</li>
*
* <li>{@link org.apache.struts2.action.ParametersAware}</li>
* <li>{@link RequestAware}</li>
*
* <li>{@link org.apache.struts2.action.ServletRequestAware}</li>
* <li>{@link SessionAware}</li>
*
* <li>{@link org.apache.struts2.action.SessionAware}</li>
* <li>{@link ApplicationAware}</li>
*
* <li>{@link org.apache.struts2.action.ApplicationAware}</li>
* <li>{@link PrincipalAware}</li>
*
* <li>{@link org.apache.struts2.action.PrincipalAware}</li>
* </ul>
*
* <!-- END SNIPPET: description -->
@@ -101,13 +99,19 @@ import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
* </pre>
*
* @see ServletContextAware
* @see org.apache.struts2.action.ServletContextAware
* @see ServletRequestAware
* @see org.apache.struts2.action.ServletRequestAware
* @see ServletResponseAware
* @see org.apache.struts2.action.ServletResponseAware
* @see ParameterAware
* @see ParametersAware
* @see org.apache.struts2.action.ParametersAware
* @see SessionAware
* @see org.apache.struts2.action.SessionAware
* @see ApplicationAware
* @see org.apache.struts2.action.ApplicationAware
* @see PrincipalAware
* @see org.apache.struts2.action.PrincipalAware
*/
public class ServletConfigInterceptor extends AbstractInterceptor implements StrutsStatics {
@@ -159,7 +163,7 @@ public class ServletConfigInterceptor extends AbstractInterceptor implements Str
if (action instanceof ApplicationAware) {
((ApplicationAware) action).setApplication(context.getApplication());
}
if (action instanceof org.apache.struts2.action.ApplicationAware) {
((org.apache.struts2.action.ApplicationAware) action).withApplication(context.getApplication());
}
@@ -167,7 +171,7 @@ public class ServletConfigInterceptor extends AbstractInterceptor implements Str
if (action instanceof SessionAware) {
((SessionAware) action).setSession(context.getSession());
}
if (action instanceof org.apache.struts2.action.SessionAware) {
((org.apache.struts2.action.SessionAware) action).withSession(context.getSession());
}
@@ -179,7 +183,7 @@ public class ServletConfigInterceptor extends AbstractInterceptor implements Str
if (action instanceof PrincipalAware) {
HttpServletRequest request = context.getServletRequest();
if(request != null) {
// We are in servtlet environment, so principal information resides in HttpServletRequest
// We are in servlet environment, so principal information resides in HttpServletRequest
((PrincipalAware) action).setPrincipalProxy(new ServletPrincipalProxy(request));
}
}
@@ -1,91 +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 org.apache.struts2.util;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* A bean that can be used to format dates
*
* FIXME: remove or use to format Dates
*/
public class DateFormatter {
Date date;
DateFormat format;
// Attributes ----------------------------------------------------
DateFormat parser;
// Public --------------------------------------------------------
public DateFormatter() {
this.parser = new SimpleDateFormat();
this.format = new SimpleDateFormat();
this.date = new Date();
}
public void setDate(String date) {
try {
this.date = parser.parse(date);
} catch (ParseException e) {
throw new IllegalArgumentException(e.getMessage());
}
}
public void setDate(Date date) {
this.date = (date == null) ? null : (Date)date.clone();
}
public void setDate(int date) {
setDate(Integer.toString(date));
}
public Date getDate() {
return this.date;
}
public void setFormat(String format) {
this.format = new SimpleDateFormat(format);
}
public void setFormat(DateFormat format) {
this.format = format;
}
public String getFormattedDate() {
return format.format(date);
}
public void setParseFormat(String format) {
this.parser = new SimpleDateFormat(format);
}
public void setParser(DateFormat parser) {
this.parser = parser;
}
public void setTime(long time) {
date.setTime(time);
}
}
@@ -24,11 +24,13 @@ 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 org.apache.struts2.views.jsp.ui.OgnlTool;
import org.apache.struts2.views.util.UrlHelper;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
@@ -278,6 +280,20 @@ public class StrutsUtil {
public void write(int aByte) {
writer.write(aByte);
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setWriteListener(WriteListener writeListener) {
try {
writeListener.onWritePossible();
} catch (IOException e) {
throw new StrutsException(e);
}
}
}
}
@@ -71,11 +71,6 @@ public class TextProviderHelper {
} else {
LOG.warn("Could not locate the message resource '{}' as there is no TextProvider in the ValueStack.", key);
}
if (defaultMessage.equals(msg)) {
LOG.warn("The default value expression '{}' was evaluated and did not match a property. The literal value '{}' will be used.", defaultMessage, defaultMessage);
} else {
LOG.warn("The default value expression '{}' evaluated to '{}'", defaultMessage, msg);
}
}
}
return msg;
@@ -19,7 +19,6 @@
package org.apache.struts2.util;
import com.opensymphony.xwork2.ActionContext;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.views.util.DefaultUrlHelper;
import org.apache.struts2.views.util.UrlHelper;
@@ -58,20 +57,10 @@ public class URLBean {
public String getURL() {
// all this trickier with maps is to reduce the number of objects created
Map<String, Object> fullParams = null;
if (params != null) {
fullParams = new HashMap<String, Object>();
}
Map<String, Object> fullParams = new HashMap<>();
if (page == null) {
// No particular page requested, so go to "same page"
// Add query params to parameters
if (fullParams != null) {
fullParams.putAll(request.getParameterMap());
} else {
fullParams = request.getParameterMap();
}
fullParams.putAll(request.getParameterMap());
}
// added parameters override, just like in URLTag
@@ -84,7 +73,7 @@ public class URLBean {
public URLBean addParameter(String name, Object value) {
if (params == null) {
params = new HashMap<String, String>();
params = new HashMap<>();
}
if (value == null) {
@@ -47,13 +47,13 @@ public class AnchorTag extends AbstractClosingTag {
protected String portletUrlType;
protected String anchor;
protected String forceAddSchemeHostAndPort;
protected boolean escapeHtmlBody = true; // Default - escape HTML body
protected String escapeHtmlBody;
@Override
public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
return new Anchor(stack, req, res);
}
@Override
protected void populateParams() {
super.populateParams();
@@ -80,10 +80,12 @@ public class AnchorTag extends AbstractClosingTag {
if (escapeAmp != null) {
tag.setEscapeAmp(BooleanUtils.toBoolean(escapeAmp));
}
if (forceAddSchemeHostAndPort != null) {
if (forceAddSchemeHostAndPort != null) {
tag.setForceAddSchemeHostAndPort(BooleanUtils.toBoolean(forceAddSchemeHostAndPort));
}
tag.setEscapeHtmlBody(escapeHtmlBody);
if (escapeHtmlBody != null) {
tag.setEscapeHtmlBody(escapeHtmlBody);
}
}
public void setHref(String href) {
@@ -149,12 +151,12 @@ public class AnchorTag extends AbstractClosingTag {
/**
* Set via parameter to control if body content should be HTML-escaped.
*
* @param escapeHtmlBody
*
*
* @param escapeHtmlBody
*
* @since 2.6
*/
public void setEscapeHtmlBody(boolean escapeHtmlBody) {
public void setEscapeHtmlBody(String escapeHtmlBody) {
this.escapeHtmlBody = escapeHtmlBody;
}
@@ -37,7 +37,7 @@ public class SubmitTag extends AbstractClosingTag {
protected String method;
protected String type;
protected String src;
protected boolean escapeHtmlBody = true; // Default - escape HTML body
protected boolean escapeHtmlBody = false;
@Override
public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
@@ -78,9 +78,9 @@ public class SubmitTag extends AbstractClosingTag {
/**
* Set via parameter to control if body content should be HTML-escaped.
*
* @param escapeHtmlBody
*
*
* @param escapeHtmlBody
*
* @since 2.6
*/
public void setEscapeHtmlBody(boolean escapeHtmlBody) {
@@ -151,6 +151,11 @@ struts.ui.theme.expansion.token=~~~
### Sets the default template type. Either ftl, vm, or jsp
struts.ui.templateSuffix=ftl
### Sets a global flag which will escape html body of Anchor, Submit and Component tag
### You can control this flag per tag, e.g.: <s:a ... escapeHtmlTag="true">...</s:a>
### and this take precedence over the global flag
# struts.ui.escapeHtmlBody=true
### Configuration reloading
### This will cause the configuration to reload struts.xml when it is changed
# struts.configuration.xml.reload=false
@@ -224,18 +229,54 @@ struts.ognl.logMissingProperties=false
### if the application generates a lot of different expressions
struts.ognl.enableExpressionCache=true
### Specify the OGNL expression cache factory and BeanInfo cache factory to use.
### Currently, the default implementations are used, but can be replaced with custom ones if desired.
struts.ognl.expressionCacheFactory=defaultOgnlExpressionCacheFactory
struts.ognl.beanInfoCacheFactory=defaultOgnlBeanInfoCacheFactory
### Specify a limit to the number of entries in the OGNL expressionCache.
### For the standard expressionCache mode, when the limit is exceeded the entire cache's
### content will be cleared (can help prevent memory leaks).
### For expressionCacheLRUMode true, the limit will ensure the cache does not exceed
### that size, dropping the oldest (least-recently-used) expressions to add new ones.
### NOTE: If not set, the default is 25000, which may be excessive.
# struts.ognl.expressionCacheMaxSize=1000
### Indicates if the OGNL expressionCache should use LRU mode.
### NOTE: When true, make sure to set the expressionCacheMaxSize to a reasonable value
### for your application. Otherwise the default limit will never (practically) be reached.
# struts.ognl.expressionCacheLRUMode=false
### Specify a limit to the number of entries in the OGNL beanInfoCache.
### For the standard beanInfoCache mode, when the limit is exceeded the entire cache's
### content will be cleared (can help prevent memory leaks).
### For beanInfoCacheLRUMode true, the limit will ensure the cache does not exceed
### that size, dropping the oldest (least-recently-used) expressions to add new ones.
### NOTE: If not set, the default is 25000, which may be excessive.
# struts.ognl.beanInfoCacheMaxSize=1000
### Indicates if the OGNL beanInfoCache should use LRU mode.
### NOTE: When true, make sure to set the beanInfoCacheMaxSize to a reasonable value
### for your application. Otherwise the default limit will never (practically) be reached.
# struts.ognl.beanInfoCacheLRUMode=false
### Indicates if Dispatcher should handle unexpected exceptions by calling sendError()
### or simply rethrow it as a ServletException to allow future processing by other frameworks like Spring Security
struts.handle.exception=true
### Applies maximum length allowed on OGNL expressions for security enhancement (optional)
###
### **WARNING**: If developers enable this option (by configuration) they should make sure that they understand the implications of setting
### struts.ognl.expressionMaxLength. They must choose a value large enough to permit ALL valid OGNL expressions used within the application.
### Values larger than the 200-400 range have diminishing security value (at which point it is really only a "style guard" for long OGNL
### expressions in an application. Setting a value of null or "" will also disable the feature.
###
### NOTE: The sample line below is *INTENTIONALLY* commented out, as this feature is disabled by default.
# struts.ognl.expressionMaxLength=256
### **WARNING**: If developers change this option (by configuration) they should make sure that they understand
### the implications of setting 'struts.ognl.expressionMaxLength'. They must choose a value large enough to permit
### ALL valid OGNL expressions used within the application. Values larger than the 200-400 range have diminishing
### security value (at which point it is really only a "style guard" for long OGNL expressions in an application.
### Setting a value of null or "" will also disable the feature.
struts.ognl.expressionMaxLength=256
### Defines which named instance of DateFormatter to use, there are two instances:
### - simpleDateFormatter (based on SimpleDateFormat)
### - dateTimeFormatter (based on Java 8 Date/Time API)
### These formatters are using a slightly different patterns, please check JavaDocs of both and more details is in WW-5016
struts.date.formatter=dateTimeFormatter
### END SNIPPET: complete_file
+7 -1
View File
@@ -216,7 +216,7 @@
<bean type="com.opensymphony.xwork2.UnknownHandlerManager" class="com.opensymphony.xwork2.DefaultUnknownHandlerManager" name="struts" />
<bean type="org.apache.struts2.dispatcher.DispatcherErrorHandler" name="struts" class="org.apache.struts2.dispatcher.DefaultDispatcherErrorHandler" />
<!-- Silly workarounds for OGNL since there is currently no way to flush its internal caches -->
<bean type="ognl.PropertyAccessor" name="java.util.ArrayList" class="com.opensymphony.xwork2.ognl.accessor.XWorkListPropertyAccessor" />
<bean type="ognl.PropertyAccessor" name="java.util.HashSet" class="com.opensymphony.xwork2.ognl.accessor.XWorkCollectionPropertyAccessor" />
@@ -228,6 +228,12 @@
<bean type="com.opensymphony.xwork2.config.providers.ValueSubstitutor" class="com.opensymphony.xwork2.config.providers.EnvsValueSubstitutor" scope="singleton"/>
<bean type="org.apache.struts2.components.date.DateFormatter" name="simpleDateFormatter" class="org.apache.struts2.components.date.SimpleDateFormatAdapter" scope="singleton"/>
<bean type="org.apache.struts2.components.date.DateFormatter" name="dateTimeFormatter" class="org.apache.struts2.components.date.DateTimeFormatterAdapter" scope="singleton"/>
<bean type="com.opensymphony.xwork2.ognl.OgnlCacheFactory" name="ognlExpressionCacheFactory" class="com.opensymphony.xwork2.ognl.DefaultOgnlExpressionCacheFactory" scope="singleton"/>
<bean type="com.opensymphony.xwork2.ognl.OgnlCacheFactory" name="ognlBeanInfoCacheFactory" class="com.opensymphony.xwork2.ognl.DefaultOgnlBeanInfoCacheFactory" scope="singleton"/>
<package name="struts-default" abstract="true">
<result-types>
<result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/>
@@ -30,7 +30,7 @@
<#assign itemKeyStr = stack.findString('top')>
</#if>
<#if parameters.listLabelKey??>
<#-- checks the valueStack for the 'valueKey.' The valueKey is then looked-up in the locale
<#-- checks the valueStack for the 'valueKey.' The valueKey is then looked-up in the locale
file for it's localized value. This is then used as a label -->
<#assign itemValue = struts.getText(stack.findString(parameters.listLabelKey))/>
<#elseif parameters.listValue??>
@@ -95,9 +95,10 @@
<#include "/${parameters.templateDir}/${parameters.expandTheme}/css.ftl" />
<#include "/${parameters.templateDir}/${parameters.expandTheme}/scripting-events.ftl" />
<#include "/${parameters.templateDir}/${parameters.expandTheme}/common-attributes.ftl" />
<#global evaluate_dynamic_attributes = true/>
<#include "/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" />
/>
<label<#rt/>
<label<#rt/>
<#if parameters.id?has_content>
for="${parameters.id}-${itemCount}"<#rt/>
<#else>
@@ -106,11 +107,10 @@
class="checkboxLabel">${itemValue}</label>
</@s.iterator>
<#else>
&nbsp;
</#if>
<input type="hidden" id="__multiselect_${parameters.id}" name="__multiselect_${parameters.name}"
value=""<#rt/>
<#if parameters.disabled!false>
disabled="disabled"<#rt/>
</#if>
/>
/>

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