mirror of
https://github.com/apache/struts.git
synced 2026-09-05 05:39:39 +00:00
Compare commits
104 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 981885fd6a | |||
| 07b88fcc35 | |||
| 2cbe47297e | |||
| 545add9365 | |||
| ebecc7d39d | |||
| 226a1d49ce | |||
| 075ef7c4e1 | |||
| 1237110652 | |||
| 3f2518afa8 | |||
| 9f9edf4705 | |||
| 7e912742ed | |||
| c98b60cd9a | |||
| 5ae9688ae8 | |||
| b72200aabf | |||
| 16e7c9b459 | |||
| bea1c6f09f | |||
| 888884cc51 | |||
| b387abae01 | |||
| 059a2a6117 | |||
| c0a3be9fd8 | |||
| 4a7cae029a | |||
| 9873a7f56b | |||
| 086b514d59 | |||
| 6024dd6fa4 | |||
| 4eb2ee8384 | |||
| 8e49362bf8 | |||
| 7541d9ab35 | |||
| c82fdb5e76 | |||
| f40f9f15e5 | |||
| 2d9cdb73a5 | |||
| d07a496d5c | |||
| bef1f67d4a | |||
| 05ca1ff087 | |||
| 199f356669 | |||
| e2718aa482 | |||
| b73fec4de0 | |||
| c2795d2e30 | |||
| a987e31a0e | |||
| 6aff5b7faa | |||
| 084c66723d | |||
| 5b40f9f48e | |||
| e9d0a5518c | |||
| 4c37011020 | |||
| fbb31ee65b | |||
| c094052969 | |||
| 84164c422e | |||
| 8b11a3873a | |||
| 115fef2af7 | |||
| 0c9f9c3400 | |||
| 3e65ec1687 | |||
| 59932a5179 | |||
| 4746a49d17 | |||
| 059514d6ca | |||
| 5648721d07 | |||
| e3dff7691e | |||
| 38c63334a4 | |||
| fe7959e5d8 | |||
| d454384a4b | |||
| 1c3cab2e6f | |||
| b1e9eae01c | |||
| 804e1546f1 | |||
| f0b24d17da | |||
| 4d8108e766 | |||
| 14c3a14c8f | |||
| f997d9b1ed | |||
| 3a7aabdcbc | |||
| 2851967a2e | |||
| 457a7c918f | |||
| 1e32510ea6 | |||
| 56595356d3 | |||
| e59abe4e34 | |||
| 61e6d342ba | |||
| 0a0573952f | |||
| 79e1b22cc6 | |||
| 7ed77f5f04 | |||
| 41e729205c | |||
| 9c05422bbe | |||
| b0e18e9c57 | |||
| a4a4e9e262 | |||
| 893a8924eb | |||
| 3d39fba0db | |||
| effe6875b7 | |||
| 60e1212bcc | |||
| 7a696523f0 | |||
| ecef56b546 | |||
| bab276e29d | |||
| 5c9299b9c4 | |||
| 767f40b509 | |||
| 0b5670f258 | |||
| 74f1f86a26 | |||
| 583d399c24 | |||
| a4e7034eab | |||
| 62c85e8d11 | |||
| 50e2c4ea91 | |||
| e1154d8b3a | |||
| 56c7621fd1 | |||
| 9a8301d51d | |||
| f2d522ed86 | |||
| a474fc97d0 | |||
| 67000dd475 | |||
| 80b39e2212 | |||
| 90001a98dd | |||
| 867492d485 | |||
| 57f76d94f2 |
@@ -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
@@ -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
@@ -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
@@ -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'
|
||||
)
|
||||
|
||||
@@ -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
@@ -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>
|
||||
|
||||
@@ -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
@@ -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>
|
||||
|
||||
+14
-16
@@ -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 & Wait</s:a></li>
|
||||
<li><s:a value="/wait/index.html">Execute & 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>
|
||||
|
||||
+4
-4
@@ -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>
|
||||
|
||||
+2
-2
@@ -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>
|
||||
|
||||
+1
-1
@@ -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>
|
||||
|
||||
+1
-1
@@ -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>
|
||||
|
||||
+1
-1
@@ -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>
|
||||
|
||||
+1
-1
@@ -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"));
|
||||
|
||||
+1
-1
@@ -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"));
|
||||
|
||||
+1
-1
@@ -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");
|
||||
|
||||
+2
-2
@@ -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
@@ -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
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
+19
-3
@@ -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());
|
||||
}
|
||||
|
||||
+2
-1
@@ -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)) {
|
||||
|
||||
-187
@@ -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));
|
||||
}
|
||||
}
|
||||
+44
@@ -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) {
|
||||
|
||||
+18
-22
@@ -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()));
|
||||
}
|
||||
|
||||
}
|
||||
+3
-6
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
</#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
Reference in New Issue
Block a user