Compare commits

...

136 Commits

Author SHA1 Message Date
Lukasz Lenart a054e9259c [maven-release-plugin] prepare for next development iteration 2023-09-11 08:47:17 +02:00
Lukasz Lenart 6d8cf56977 [maven-release-plugin] prepare release STRUTS_6_1_2_2 2023-09-11 08:47:08 +02:00
Lukasz Lenart 7b7d3ca0e1 Sets proper Maven version 2023-09-11 08:42:18 +02:00
Lukasz Lenart 3292152f8c Always delete uploaded file 2023-09-03 08:57:26 +02:00
Lukasz Lenart 28498a807d [maven-release-plugin] prepare release STRUTS_6_1_2_1 2023-06-13 09:31:55 +02:00
Yasser Zamani 2d6f1bc0a6 add some improvements 2023-06-05 13:37:59 +04:30
Lukasz Lenart 36e4e7410e [maven-release-plugin] prepare for next development iteration 2023-03-08 17:28:00 +01:00
Lukasz Lenart e903739624 [maven-release-plugin] prepare release STRUTS_6_1_2 2023-03-08 17:27:52 +01:00
Lukasz Lenart a85328e74e Sets proper SNAPSHOT version to start release process 2023-03-08 17:23:24 +01:00
Lukasz Lenart c3deb39237 WW-5285 Uses Long and null to check if option has been defined 2023-03-08 17:15:40 +01:00
Lukasz Lenart 483a0c8d01 WW-5285 Limits max number of files to upload at once
Upgrades commons-fileupload to ver. 1.5 and sets default limit to 256 files
2023-03-08 17:15:29 +01:00
Lukasz Lenart f5c0aa5f3a [maven-release-plugin] prepare release STRUTS_6_1_1 2022-11-15 15:30:05 +01:00
Lukasz Lenart a77a80a803 Refactors name/value calculation logic 2022-11-13 10:39:37 +01:00
Lukasz Lenart 4d58dd1960 Merge pull request #636 from apache/WW-5260-global-submit-unchecked
[WW-5260] Introduces a constant to set submitUnchecked attribute of checkbox tag globally
2022-11-13 09:42:40 +01:00
Lukasz Lenart 09da11ba06 Merge pull request #635 from apache/WW-4173-optional
[WW-4173] Introduces a dedicated interface to allow conditionally disabling a given interceptor
2022-11-13 09:42:21 +01:00
Lukasz Lenart bcfa8113ac WW-4173 Introduces a dedicated interface to allow conditionally executing a given interceptor 2022-11-11 15:48:41 +01:00
Lukasz Lenart 65ae2b7e4f WW-5260 Introduces a constant to set submitUnchecked attribute of checkbox tag globally 2022-11-11 09:30:55 +01:00
Lukasz Lenart 1af3e31548 [maven-release-plugin] prepare for next development iteration 2022-11-08 15:49:51 +01:00
Lukasz Lenart 18a72c3dff [maven-release-plugin] prepare release STRUTS_6_1_0 2022-11-08 15:49:42 +01:00
Lukasz Lenart 6ea50122d6 Merge pull request #634 from apache/WW-5259-parser
[WW-5259] Extracts UrlHelper#parseQueryString into a dedicated bean
2022-11-08 07:43:59 +01:00
Lukasz Lenart ea7fba2846 WW-5259 Extracts UrlHelper#parseQueryString into a dedicated bean 2022-11-07 12:13:43 +01:00
Lukasz Lenart 4ab427f2f2 Merge pull request #633 from apache/WW-5258-annotations
[WW-5258] Upgrades Struts Annotations to version 1.0.8
2022-11-06 18:01:20 +01:00
Lukasz Lenart 0a30eb44e6 Merge pull request #632 from apache/WW-5257-checkbox
[WW-5257] Cleans up template
2022-11-06 18:00:08 +01:00
Lukasz Lenart 138a8d74f0 WW-5257 Cleans up template 2022-11-06 17:03:48 +01:00
Lukasz Lenart 341a8f20e0 Cleans remote before uploading new packages 2022-11-06 11:13:26 +01:00
Lukasz Lenart 9a431ea470 Packages with assembly before publishing nightlies 2022-11-06 10:14:17 +01:00
Lukasz Lenart 0fe27a673d Merge pull request #631 from apache/WW-5240-doubleselect
[WW-5240] Uses doubleOn* attributes in the template
2022-11-06 09:57:14 +01:00
Lukasz Lenart 35a50ea836 Merge pull request #630 from apache/WW-4514-url
[WW-4514] Fixes building query string with empty parameters
2022-11-06 09:50:51 +01:00
Lukasz Lenart 927c58b7d3 WW-5258 Upgrades Struts Annotations to version 1.0.8 2022-11-05 09:34:29 +01:00
Lukasz Lenart ac168968a0 WW-5240 Uses doubleOn* attributes in the template
This allows assign custom JS event handlers to the second select box
2022-11-05 08:37:26 +01:00
Lukasz Lenart 5a30cdbede Merge pull request #629 from apache/WW-5255-script-link
[WW-5255] Fixes <s:script/> and <s:link/> and uses them across tags
2022-11-05 08:28:57 +01:00
Lukasz Lenart bfcc0b5898 Swaps expected with actual 2022-11-04 09:03:54 +01:00
Lukasz Lenart 4a51aacf40 WW-4514 Avoids logging user provided data 2022-11-04 09:02:46 +01:00
Lukasz Lenart 728edbef56 Increases sleep to avoid false test failures 2022-11-04 09:00:26 +01:00
Lukasz Lenart 6135a71335 WW-4514 Extracts parameters string building into a dedicated class with a proper extension point 2022-11-04 08:49:57 +01:00
Lukasz Lenart 6330bb1ce3 WW-4514 Fixes building query string with empty parameters 2022-11-04 07:58:47 +01:00
Lukasz Lenart 4a585347ed WW-5255 Fixes debug console and validators' details page 2022-11-02 12:20:33 +01:00
Lukasz Lenart 4bb81d5909 WW-5255 Fixes <s:script/> and <s:link/> and uses them across tags 2022-11-02 12:20:33 +01:00
Lukasz Lenart 054f1f4cdb Includes apps in code Coverage scan 2022-11-02 11:54:35 +01:00
Lukasz Lenart 79a84a8f02 Merge pull request #628 from apache/WW-5252-external
[WW-5252] Disables parsing external entities
2022-11-02 10:32:36 +01:00
Lukasz Lenart 2660aaec56 [WW-4692] Extracts Url encoder/decoder into dedicated interfaces/classes (#626)
* WW-4692 Extracts Url encoder/decoder into dedicated interfaces/classes

* WW-4692 Removes code smells reported by Sonar and adds additional test case

* WW-4692 Adds missing header with license

* WW-4692 Prevents int promotion behaviour

* WW-4692 Drops unused imports

* WW-4692 Fixes int promotion behaviour

* WW-4692 Improves code coverage
2022-11-02 10:32:13 +01:00
Lukasz Lenart 34aeeded55 Merge pull request #627 from apache/WW-4440-readme
[WW-4440] Adds basic README.md to all submodules
2022-11-02 10:31:49 +01:00
Lukasz Lenart 8d2d996056 WW-5252 Reuses factory method 2022-10-30 12:37:23 +01:00
Lukasz Lenart 6658c6360e WW-5252 Disables parsing external entities 2022-10-27 13:35:10 +02:00
Lukasz Lenart 9c078aef14 WW-4440 Adds basic README.md to all submodules 2022-10-27 12:53:05 +02:00
Lukasz Lenart eb51594ed3 Adds proper header with Apache 2.0 license 2022-10-25 13:40:35 +02:00
Lukasz Lenart b536136191 Introduces CodeQL analyses 2022-10-25 13:29:21 +02:00
Lukasz Lenart c74287f741 Merge pull request #625 from apache/WW-4173-disable
[WW-4173] Adds support to disable processing a given interceptor
2022-10-24 10:24:57 +02:00
Lukasz Lenart 9e7e0b395d Merge pull request #624 from apache/WW-5137-class
[WW-5137] Removes deprecated class attribute
2022-10-24 10:24:19 +02:00
Lukasz Lenart 784293e46f Merge pull request #623 from apache/WW-3725-remove
[WW-3725] Removes unused template
2022-10-24 10:13:13 +02:00
Lukasz Lenart 58dcc830ea Merge pull request #622 from apache/WW-5133-labelposition
[WW-5133] Drops labelposition attribute
2022-10-24 10:12:39 +02:00
Lukasz Lenart 936481e7ea Adds badge with OSSF CII best practises scoring 2022-10-24 09:33:47 +02:00
Lukasz Lenart a02cc6507c Defines OSSF Scorecard action to perform analysis 2022-10-24 08:30:09 +02:00
Lukasz Lenart 084c257d6e WW-4173 Passes current ActionInvocation to allow based disabling interceptor on it 2022-10-24 08:27:12 +02:00
Lukasz Lenart 94a0c6e425 WW-4173 Adds support to disable processing a given interceptor 2022-10-21 21:12:41 +02:00
Lukasz Lenart 3e30d0c375 WW-5137 Removes deprecated class attribute 2022-10-21 13:33:11 +02:00
Lukasz Lenart 9568888700 WW-3725 Removes unused template 2022-10-21 11:36:54 +02:00
Lukasz Lenart 943cb62953 WW-5133 Drops labelposition attribute 2022-10-21 10:49:31 +02:00
Lukasz Lenart 79d6c07a57 Merge pull request #621 from apache/WW-3737-custom-separator
[WW-3737] Allows to define a custom separator used to split patterns
2022-10-21 09:23:52 +02:00
Lukasz Lenart 7064c53f5c Merge pull request #620 from apache/WW-3529-named-pattern
[WW-3529] Fixes usage of RegEx related characters in named pattern
2022-10-21 07:16:43 +02:00
Lukasz Lenart 3fdb918127 Merge pull request #619 from apache/WW-5230-ognl
[WW-5230] Upgrades OGNL to version 3.3.4
2022-10-20 19:18:47 +02:00
Lukasz Lenart 1069cf8c09 Merge pull request #615 from apache/WW-2815-xstream
[WW-2815] Refactors XStreamHandler to allow to provide a custom configuration
2022-10-20 13:15:03 +02:00
Lukasz Lenart c41f05fe68 WW-3737 Allows to define a custom separator used to split patterns 2022-10-19 13:57:59 +02:00
Lukasz Lenart 993c4c4cab WW-3529 Fixes using RegEx related characters in named pattern 2022-10-19 12:22:31 +02:00
Lukasz Lenart 01164c4d74 WW-5230 Upgrades OGNL to version 3.3.4 2022-10-19 11:38:50 +02:00
Lukasz Lenart 738d27952f Merge pull request #618 from apache/WW-5245-jackson-databind
[WW-5245] Upgrades Jackson Databind to version 2.13.4.2
2022-10-19 09:07:57 +02:00
Lukasz Lenart 984f8eff2e WW-5245 Upgrades Jackson Databind to version 2.13.4.2 2022-10-19 08:49:37 +02:00
Lukasz Lenart 802afb0cc0 WW-2815 Drops unused import 2022-10-17 10:55:46 +02:00
Lukasz Lenart 339c30320a WW-2815 Drops clearing existing permissions to avoid messing with user provided configuration 2022-10-17 10:14:51 +02:00
Lukasz Lenart a562f8f806 WW-2815 Fixes support for Collections and String 2022-10-17 09:52:07 +02:00
Lukasz Lenart ac13c32bdb WW-2815 Drops deprecated API 2022-10-17 09:47:37 +02:00
Lukasz Lenart bb71610296 WW-2815 Refactors XStreamHandler to allow to provide a custom configuration 2022-10-17 08:54:03 +02:00
Lukasz Lenart 3d7f66f428 Merge pull request #614 from apache/WW-5244-commons-text
[WW-5244] Upgrades commons-text to version 1.10.0
2022-10-17 08:14:16 +02:00
Lukasz Lenart 50842e8d19 WW-5242 Marks constant definition as deprecated 2022-10-16 08:55:27 +02:00
Lukasz Lenart cca986df35 Merge pull request #613 from apache/WW-5242-cross-action
[WW-5242] Marks struts.mapper.action.prefix.crossNamespaces as deprecated
2022-10-16 08:53:23 +02:00
Lukasz Lenart 511ec62105 Merge pull request #611 from apache/WW-5241-exec-and-wait
[WW-5241] Fixes issue with includeParams=all when using exec & wait
2022-10-16 08:52:07 +02:00
Lukasz Lenart 2cb0d28960 WW-5244 Upgrades commons-text to version 1.10.0 2022-10-16 08:51:09 +02:00
Lukasz Lenart e783d18715 WW-5242 Marks struts.mapper.action.prefix.crossNamespaces as deprecated 2022-10-14 12:49:22 +02:00
Lukasz Lenart 864f513365 WW-5241 Adds test cases to cover DMI when mapping action 2022-10-14 12:29:40 +02:00
Lukasz Lenart 9dd2560172 WW-5241 Adds test cases to cover checking namespace, action and method names 2022-10-14 12:03:02 +02:00
Lukasz Lenart 415e0fbd44 WW-5241 Ignores calls to append !method when DMI is disabled 2022-10-14 11:32:10 +02:00
Lukasz Lenart 26effbf05d WW-5241 Improves Exec&Wait example in Showcase app 2022-10-13 15:39:07 +02:00
Lukasz Lenart 4c8cc361d5 Merge pull request #609 from apache/WW-3691-executor
WW-3691 Converts BackgroundProcess into interface and uses Executor to execute BackgroundProcess
2022-10-13 08:07:50 +02:00
Lukasz Lenart a21bd994eb WW-5238 Uses proper order of mapping functions to support action: prefix 2022-10-06 08:54:14 +02:00
Lukasz Lenart 8f0db1d22a WW-3691 Converts BackgroundProcess into interface and uses Executor to execute BackgroundProcess 2022-10-05 09:10:24 +02:00
Lukasz Lenart 58f287bf4a Merge pull request #607 from apache/WW-5184-log
[WW-5184] Uses debug log level when parameter value was not accepted
2022-09-30 11:21:34 +02:00
Lukasz Lenart e0da03c262 Sets proper Sonar options in Jenkins pipeline and removes duplicated sonar properties 2022-09-30 10:38:15 +02:00
Lukasz Lenart 04cf1dae5f Puts back JaCoCo report generation 2022-09-30 09:50:51 +02:00
Lukasz Lenart 5338b44c23 WW-5184 Reduces code complexity when handling excluded/accepted values patterns 2022-09-30 08:25:44 +02:00
Lukasz Lenart 2217e2c1ca Disables Code quality step in Jenkins pipeline to avoid overriding GH Actions results 2022-09-30 08:19:09 +02:00
Lukasz Lenart cbfd3a7ab9 WW-5184 Improves logging around excluding/accepting values of incoming parameters 2022-09-30 08:12:35 +02:00
Lukasz Lenart 097297affd WW-5232 Stop generating Jacoco reports which are not used 2022-09-28 11:32:01 +02:00
Lukasz Lenart ddfaa1683c Merge pull request #606 from apache/WW-5235-ognl
[WW-5235] Uses debug log level when setting expression max length to avoid cluttering logs
2022-09-28 11:13:58 +02:00
Lukasz Lenart ddbd02e6bb WW-5184 Uses debug log level when parameter value was not accepted 2022-09-28 11:09:16 +02:00
Lukasz Lenart 6887ef900b WW-5235 Uses debug log level when setting expression max length to avoid cluttering logs 2022-09-28 10:58:26 +02:00
Lukasz Lenart 326de812ac Merge pull request #605 from apache/WW-5232-coverage
[WW-5232] Applies proper coverage settings for Jacoco plugin and Sonar
2022-09-28 10:34:35 +02:00
Lukasz Lenart 32201b2ef2 WW-5232 Applies proper coverage settings for Jacoco plugin and Sonar 2022-09-28 10:02:55 +02:00
Lukasz Lenart a4fe51f5b3 Merge pull request #604 from apache/WW-5234-dtds
[WW-5234] Improves DTD definitions to use proper URL
2022-09-27 09:49:33 +02:00
Lukasz Lenart 3774ffa7c3 WW-5234 Improves DTD definitions to use proper URL 2022-09-27 09:21:03 +02:00
Lukasz Lenart cf2078d1de Merge pull request #603 from apache/lukaszlenart-patch-1
Replaces Travis badge with GH Actions badge
2022-09-25 10:29:54 +02:00
Lukasz Lenart 7183d32caf Replaces Travis badge with GH Actions badge 2022-09-25 10:09:02 +02:00
Lukasz Lenart e0e7e3cb20 Merge pull request #602 from apache/WW-5232-gh-actions
[WW-5232] Introduces GH Actions build instead of using Travis
2022-09-25 10:06:59 +02:00
Lukasz Lenart eb68368280 WW-5232 Avoids building assemblies 2022-09-21 22:29:14 +02:00
Lukasz Lenart 296b3cc89c Uses SONAR_TOKEN: ${{ secrets.SONARCLOUD_TOKEN }} 2022-09-21 17:27:20 +02:00
Lukasz Lenart 9fb86054bb Reverts back to SONAR_TOKEN 2022-09-21 17:17:07 +02:00
Lukasz Lenart 0fc71949ca WW-5232 Uses Apache specific SONARCLOUD_TOKEN secret 2022-09-21 15:44:47 +02:00
Lukasz Lenart 9faa002fa7 Merge pull request #601 from apache/WW-5231-rat
[WW-5231] Upgrades apache-rat-plugin to version 0.15
2022-09-21 07:30:13 +02:00
Lukasz Lenart 88bc5c6802 Merge pull request #600 from apache/WW-5228-owasp
[WW-5228] Upgrades OWASP dependency-check-maven to version 7.2.0
2022-09-21 07:29:56 +02:00
Lukasz Lenart dc1a663d69 WW-5232 Uses default SONAR_TOKEN name 2022-09-20 16:22:59 +02:00
Lukasz Lenart 36ac23acf5 WW-5232 Uses Sonar Coverage badge instead of Travis on 2022-09-20 16:18:17 +02:00
Lukasz Lenart 6b2cc20b2d WW-5232 Defines default ENV settings 2022-09-20 16:08:32 +02:00
Lukasz Lenart ab22c7377d WW-5232 Introduces GH Actions build instead of using Travis 2022-09-20 15:48:21 +02:00
Lukasz Lenart 062b76718b WW-5231 Upgrades apache-rat-plugin to version 0.15 2022-09-20 15:33:11 +02:00
Lukasz Lenart dc5dac7be2 WW-5228 Upgrades OWASP dependency-check-maven to version 7.2.0 2022-09-20 15:27:39 +02:00
Lukasz Lenart 6334a3e19e Merge pull request #599 from apache/WW-5229-spring
[WW-5229] Upgrades Spring to version 5.3.23
2022-09-20 15:25:48 +02:00
Lukasz Lenart 858f19557d WW-5229 Upgrades Spring to version 5.3.23 2022-09-20 11:13:18 +02:00
Lukasz Lenart d97fa1eb38 Merge pull request #598 from apache/WW-5227-log4j
[WW-5227] Upgrades Log4j to version 2.19.0
2022-09-20 11:05:04 +02:00
Lukasz Lenart b9cd7d8159 Merge pull request #597 from apache/WW-5226-weld
[WW-5226] Upgrade weld-core to version 2.4.8.Final
2022-09-20 11:04:53 +02:00
Lukasz Lenart 03edeee0d8 WW-5227 Upgrades Log4j to version 2.19.0 2022-09-18 15:43:37 +02:00
Lukasz Lenart f04b68db03 Blocks force pushes to the master branch 2022-09-17 10:02:31 +02:00
Lukasz Lenart 7b655207ec Includes commons-text in minimal library set 2022-09-17 09:40:54 +02:00
Lukasz Lenart c9c39130f6 WW-5226 Upgrades Weld to version 2.4.8.Final 2022-09-17 09:40:38 +02:00
Lukasz Lenart 12beb20949 Merge pull request #596 from apache/WW-5213-javax-el
[WW-5213] Upgrades javax.el to 3.0.1-b12
2022-09-14 20:18:03 +02:00
Lukasz Lenart a5899726a0 Merge pull request #559 from brianandle/WW-5184_v2
WW-5184 - Add optional parameter value check to ParametersInterceptor
2022-09-14 16:53:10 +02:00
Lukasz Lenart 22a80f9c51 WW-5213 Upgrades javax.el to 3.0.1-b12 2022-09-14 16:49:50 +02:00
Lukasz Lenart 854572905e Merge pull request #595 from apache/WW-5220-move-junit
WW-5220 Moves JUnit related test into the JUnit plugin
2022-09-14 16:37:34 +02:00
Lukasz Lenart b40f3efc74 Merge pull request #594 from apache/WW-5219-move-testng
WW-5219 Moves TestNG related classes into TestNG plugin
2022-09-14 16:37:11 +02:00
Lukasz Lenart 980387e91c WW-5220 Moves JUnit related test into the JUnit plugin
Also moves all the plugin classes under junit package
2022-09-04 11:59:01 +02:00
Lukasz Lenart 5a3d2c4eb9 WW-5219 Moves TestNG related classes into TestNG plugin
Also moves test classes under testng package
2022-09-04 11:15:28 +02:00
Lukasz Lenart 8514406142 Removes duplicated code coverage badge 2022-09-04 10:59:52 +02:00
Lukasz Lenart bbb5b6bb37 Adds OpenSSF Scorecard badge 2022-09-04 10:58:58 +02:00
Lukasz Lenart ce2975e2f0 Updates supported versions 2022-09-03 08:47:41 +02:00
Lukasz Lenart 49240c50a2 [maven-release-plugin] prepare for next development iteration 2022-09-02 14:41:56 +02:00
Brian Andle 584634a9b5 WW-5184 - Added ParameterValueAware interface and unit test 2022-06-07 21:50:34 -07:00
Brian Andle 5763476830 WW-5184 - Change info to warn from peer review 2022-06-06 11:11:20 -07:00
Brian Andle 105b22fb9a WW-5184 - Add optional parameter value check to ParametersInterceptor 2022-06-04 18:03:57 -07:00
511 changed files with 20555 additions and 22801 deletions
+2
View File
@@ -12,3 +12,5 @@ notifications:
github:
del_branch_on_merge: true
protected_branches:
master: { }
+47
View File
@@ -0,0 +1,47 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache license, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the license for the specific language governing permissions and
# limitations under the license.
name: "CodeQL"
on:
push:
branches: [ "master" ]
pull_request:
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'java' ]
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
- name: Autobuild
uses: github/codeql-action/autobuild@v2
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
category: "/language:${{matrix.language}}"
+57
View File
@@ -0,0 +1,57 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
name: Java Build
on:
pull_request:
push:
branches:
- master
env:
MAVEN_OPTS: -Xmx2048m -Xms1024m
LANG: en_US.utf8
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
java: [ '8', '11', '17' ]
steps:
- name: Checkout code
uses: actions/checkout@v3.0.2
- name: Set up cache
uses: actions/cache@v3.0.8
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-
- name: Set up JDK ${{ matrix.java }}
uses: actions/setup-java@v3
with:
distribution: adopt
java-version: ${{ matrix.java }}
- name: Build with Maven on Java ${{ matrix.java }}
if: matrix.java != '11'
run: mvn -B -V -DskipAssembly test --no-transfer-progress
- name: Code coverage on Java ${{ matrix.java }}
if: matrix.java == '11'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONARCLOUD_TOKEN }}
run: mvn -B -V -Pcoverage -DskipAssembly verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar --no-transfer-progress
@@ -0,0 +1,69 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache license, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the license for the specific language governing permissions and
# limitations under the license.
name: "Scorecards supply-chain security"
on:
branch_protection_rule:
schedule:
- cron: "30 1 * * 6" # Weekly on Saturdays
push:
branches: [ "master" ]
permissions: read-all
jobs:
analysis:
name: "Scorecards analysis"
runs-on: ubuntu-latest
permissions:
# Needed to upload the results to the code-scanning dashboard.
security-events: write
actions: read
id-token: write # This is required for requesting the JWT
contents: read # This is required for actions/checkout
steps:
- name: "Checkout code"
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # 3.1.0
with:
persist-credentials: false
- name: "Run analysis"
uses: ossf/scorecard-action@99c53751e09b9529366343771cc321ec74e9bd3d # 2.0.6
with:
results_file: results.sarif
results_format: sarif
# A read-only PAT token, which is sufficient for the action to function.
# The relevant discussion: https://github.com/ossf/scorecard-action/issues/188
repo_token: ${{ secrets.GITHUB_TOKEN }}
# Publish the results for public repositories to enable scorecard badges.
# For more details: https://github.com/ossf/scorecard-action#publishing-results
publish_results: true
- name: "Upload artifact"
uses: actions/upload-artifact@3cea5372237819ed00197afe530f5a7ea3e805c8 # 3.1.0
with:
name: SARIF file
path: results.sarif
retention-days: 5
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@b398f525a5587552e573b247ac661067fafa920b # 2.1.22
with:
sarif_file: results.sarif
-29
View File
@@ -1,29 +0,0 @@
dist: jammy
language: java
sudo: false
jdk:
- openjdk11
- openjdk17
install: true
env:
global:
- secure: iI7IpfDtS+LUyS2yNuRCR3KelNyvBHuoMQ3gb1UNmR5SSL7jO/p3olQWrQROs28FJ+dpE3lHyIjoHrebKQGJHHAgTG2XWxn+G3fDsf+wSSFSLoDGj0o2SgGXooBbR2dccnNZHCyQaOyE2cIPWaOxrQZFE4No70LQB4mrP/gdkoc=
matrix:
include:
- jdk: openjdk8
env: STRUTS_IT=true # do integration tests and coverage reports when jdk 11 and 17 tests prospered
script:
- if [ "$STRUTS_IT" == "true" ]; then
./mvnw clean install -DskipTests -DskipAssembly -B;
./mvnw test org.jacoco:jacoco-maven-plugin:report org.jacoco:jacoco-maven-plugin:report-integration org.eluder.coveralls:coveralls-maven-plugin:report -Ptravis-coveralls -DskipAssembly -B;
else
./mvnw clean package test -DskipAssembly -B;
fi;
cache:
directories:
- $HOME/.m2
Vendored
+5 -3
View File
@@ -79,7 +79,7 @@ pipeline {
}
stage('Test') {
steps {
sh './mvnw -B test'
sh './mvnw -B verify -Pcoverage -DskipAssembly'
}
post {
always {
@@ -94,7 +94,7 @@ pipeline {
}
steps {
withCredentials([string(credentialsId: 'asf-struts-sonarcloud', variable: 'SONARCLOUD_TOKEN')]) {
sh './mvnw sonar:sonar -DskipAssembly -Dsonar.login=${SONARCLOUD_TOKEN}'
sh './mvnw -B -Pcoverage -DskipAssembly -Dsonar.login=${SONARCLOUD_TOKEN} verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar'
}
}
}
@@ -160,6 +160,7 @@ pipeline {
branch 'master'
}
steps {
sh './mvnw -B package -DskipTests'
sshPublisher(publishers: [
sshPublisherDesc(
configName: 'Nightlies',
@@ -167,7 +168,8 @@ pipeline {
sshTransfer(
remoteDirectory: '/struts/snapshot',
removePrefix: 'assembly/target/assembly/out',
sourceFiles: 'assembly/target/assembly/out/struts-*.zip'
sourceFiles: 'assembly/target/assembly/out/struts-*.zip',
cleanRemote: true
)
],
verbose: true
+5 -3
View File
@@ -17,11 +17,13 @@
The Apache Struts web framework
-------------------------------
[![Build Status @ Jenkins](https://builds.apache.org/buildStatus/icon?job=Struts%2FStruts+Core%2Fmaster)](https://ci-builds.apache.org/job/Struts/job/Struts%20Core/job/master/)
[![Build Status @ Travis](https://travis-ci.com/apache/struts.svg?branch=master)](https://app.travis-ci.com/apache/struts)
[![Jenkins Build](https://builds.apache.org/buildStatus/icon?job=Struts%2FStruts+Core%2Fmaster)](https://ci-builds.apache.org/job/Struts/job/Struts%20Core/job/master/)
[![Java Build](https://github.com/apache/struts/actions/workflows/maven.yml/badge.svg)](https://github.com/apache/struts/actions/workflows/maven.yml)
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.apache.struts/struts2-core/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.apache.struts/struts2-core/)
[![Javadocs](https://javadoc.io/badge/org.apache.struts/struts2-core.svg)](https://javadoc.io/doc/org.apache.struts/struts2-core)
[![Coverage Status](https://coveralls.io/repos/github/apache/struts/badge.svg)](https://coveralls.io/github/apache/struts)
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=apache_struts&metric=coverage)](https://sonarcloud.io/summary/new_code?id=apache_struts)
[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/apache/struts/badge)](https://deps.dev/maven/org.apache.struts%3Astruts2-core)
[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/6618/badge)](https://bestpractices.coreinfrastructure.org/projects/6618)
[![License](http://img.shields.io/:license-apache-blue.svg)](http://www.apache.org/licenses/LICENSE-2.0.html)
The Apache Struts web framework is a free open-source solution for creating Java web applications.
+2 -2
View File
@@ -7,8 +7,8 @@ and what potential vulnerability it can have:
| Version | Supported |
| ------- | ------------------ |
| 2.5.20 | :white_check_mark: |
| 2.3.37 | :white_check_mark: |
| 6.0.0 | :white_check_mark: |
| 2.5.30 | :white_check_mark: |
## Reporting New Security Issues with thr Apache Struts
+12
View File
@@ -0,0 +1,12 @@
# Struts 2 Apps
These module consists of two example applications, which were built using the Apache Struts project.
One is an old-fashioned Web application and another is a modern REST based single page app.
## Installation
Enter a given folder, either `showcase/` or `rest-showcase/` and start the app using Maven:
```
mvn jetty:run
```
then open your browser at http://localhost:8080 and navigate to a proper context.
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>6.0.3</version>
<version>6.1.2.3-SNAPSHOT</version>
</parent>
<artifactId>struts2-apps</artifactId>
<packaging>pom</packaging>
+3 -2
View File
@@ -24,12 +24,12 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-apps</artifactId>
<version>6.0.3</version>
<version>6.1.2.3-SNAPSHOT</version>
</parent>
<artifactId>struts2-rest-showcase</artifactId>
<packaging>war</packaging>
<version>6.0.3</version>
<version>6.1.2.3-SNAPSHOT</version>
<name>Struts 2 Rest Showcase Webapp</name>
<description>Struts 2 Rest Showcase Example</description>
@@ -125,6 +125,7 @@
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>@{argLine}</argLine>
<excludes>
<exclude>it/**</exclude>
<exclude>**/*$*</exclude>
@@ -21,7 +21,7 @@
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
@@ -41,4 +41,4 @@
<package name="rest-showcase" extends="rest-default">
<global-allowed-methods>index,show,create,update,destroy,deleteConfirm,edit,editNew</global-allowed-methods>
</package>
</struts>
</struts>
+2 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-apps</artifactId>
<version>6.0.3</version>
<version>6.1.2.3-SNAPSHOT</version>
</parent>
<artifactId>struts2-showcase</artifactId>
@@ -237,6 +237,7 @@
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>@{argLine}</argLine>
<excludes>
<exclude>it/**</exclude>
<exclude>**/*$*</exclude>
@@ -0,0 +1,56 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.struts2.showcase.wait;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.interceptor.exec.ExecutorProvider;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPoolExecutorProvider implements ExecutorProvider {
private static final Logger LOG = LogManager.getLogger(ThreadPoolExecutorProvider.class);
private final ExecutorService executor;
public ThreadPoolExecutorProvider() {
this.executor = new ThreadPoolExecutor(1, 2, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingDeque<>());
}
@Override
public void execute(Runnable task) {
LOG.info("Executing task: {}", task);
executor.execute(task);
}
@Override
public boolean isShutdown() {
return executor.isShutdown();
}
@Override
public void shutdown() {
LOG.info("Shutting down executor");
executor.shutdown();
}
}
@@ -21,31 +21,31 @@
-->
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator 1.0.2//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
"https://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
<validators>
<field name="description1">
<field-validator type="requiredstring">
<message>Description1 Is Required !!!</message>
</field-validator>
</field>
<field name="description2">
<field-validator type="requiredstring">
<message>Description2 Is Required !!!</message>
</field-validator>
</field>
<field name="description3">
<field-validator type="requiredstring">
<message>Description3 Is Required !!!</message>
</field-validator>
</field>
<field name="description4">
<field-validator type="requiredstring">
<message>Description4 Is Required !!!</message>
</field-validator>
</field>
</validators>
</validators>
@@ -19,7 +19,7 @@
* under the License.
*/
-->
<!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator 1.0//EN" "http://struts.apache.org/dtds/xwork-validator-1.0.dtd">
<!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator 1.0//EN" "https://struts.apache.org/dtds/xwork-validator-1.0.dtd">
<validators>
<!-- Field-Validator Syntax -->
<field name="currentEmployee.empId">
@@ -19,7 +19,7 @@
* under the License.
*/
-->
<!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator 1.0//EN" "http://struts.apache.org/dtds/xwork-validator-1.0.dtd">
<!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator 1.0//EN" "https://struts.apache.org/dtds/xwork-validator-1.0.dtd">
<validators>
<!-- Field-Validator Syntax -->
<field name="currentSkill.name">
@@ -21,7 +21,7 @@
-->
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator 1.0.2//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
"https://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
<validators>
<field name="upload">
@@ -35,5 +35,4 @@
<message>Caption cannot be empty</message>
</field-validator>
</field>
</validators>
</validators>
@@ -19,7 +19,7 @@
* under the License.
*/
-->
<!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator 1.0//EN" "http://struts.apache.org/dtds/xwork-validator-1.0.dtd">
<!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator 1.0//EN" "https://struts.apache.org/dtds/xwork-validator-1.0.dtd">
<validators>
<field name="person">
<field-validator type="visitor">
@@ -19,7 +19,7 @@
* under the License.
*/
-->
<!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator 1.0//EN" "http://struts.apache.org/dtds/xwork-validator-1.0.dtd">
<!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator 1.0//EN" "https://struts.apache.org/dtds/xwork-validator-1.0.dtd">
<validators>
<field name="name">
<field-validator type="requiredstring">
@@ -21,8 +21,8 @@
-->
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator 1.0.2//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
"https://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
<validators>
<field name="value">
<field-validator type="requiredstring">
@@ -30,13 +30,13 @@
<message>Value must not be empty</message>
</field-validator>
</field>
<field name="count">
<field-validator type="int">
<message>Count must be an integer</message>
</field-validator>
</field-validator>
</field>
</validators>
</validators>
@@ -21,10 +21,10 @@
-->
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator 1.0//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
<!-- START SNIPPET: fieldValidatorsExample -->
"https://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
<!-- START SNIPPET: fieldValidatorsExample -->
<validators>
<field name="requiredValidatorField">
<field-validator type="required">
@@ -19,12 +19,12 @@
* under the License.
*/
-->
<!DOCTYPE validators PUBLIC
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator 1.0//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
<!-- START SNIPPET: fieldValidatorsExample -->
"https://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
<!-- START SNIPPET: fieldValidatorsExample -->
<validators>
<field name="requiredValidatorField">
<field-validator type="required">
@@ -19,13 +19,13 @@
* under the License.
*/
-->
<!DOCTYPE validators PUBLIC
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator 1.0//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
<!-- START SNIPPET: nonFieldValidatorsExample -->
"https://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
<!-- START SNIPPET: nonFieldValidatorsExample -->
<validators>
<validator type="expression">
<param name="expression"><![CDATA[ ( (someText == someTextRetype) && (someTextRetype == someTextRetypeAgain) ) ]]></param>
@@ -19,11 +19,11 @@
* under the License.
*/
-->
<!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator 1.0.2//EN" "http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
<!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator 1.0.2//EN" "https://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
<!-- START SNIPPET: quizValidators -->
<!--
Add the following DOCTYPE declaration as first line of your XXX-validation.xml file:
<!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator 1.0.2//EN" "http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
<!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator 1.0.2//EN" "https://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
-->
<validators>
<field name="name">
@@ -21,7 +21,7 @@
-->
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator 1.0//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
"https://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
<validators>
<field name="name">
@@ -19,17 +19,17 @@
* under the License.
*/
-->
<!DOCTYPE validators PUBLIC
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator 1.0//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
"https://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
<validators>
<field name="name">
<field-validator type="requiredstring">
<message>Name Required</message>
</field-validator>
</field>
<field name="age">
<field name="age">
<field-validator type="int">
<param name="min">1</param>
<param name="max">100</param>
@@ -41,6 +41,6 @@
<message>Birthday Required</message>
</field-validator>
</field>
</validators>
</validators>
@@ -19,10 +19,10 @@
* under the License.
*/
-->
<!DOCTYPE validators PUBLIC
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator 1.0//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
"https://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
<!-- START SNIPPET: visitorValidatorsExample -->
<validators>
@@ -21,12 +21,12 @@
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="actionchaining" extends="struts-default" namespace="/actionchaining">
<action name="actionChain1" class="org.apache.struts2.showcase.actionchaining.ActionChain1">
<result type="chain">actionChain2</result>
<result type="chain">actionChain2</result>
</action>
<action name="actionChain2" class="org.apache.struts2.showcase.actionchaining.ActionChain2">
<result type="chain">actionChain3</result>
@@ -21,7 +21,7 @@
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="async" extends="json-default" namespace="/async">
@@ -21,15 +21,15 @@
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="conversion" namespace="/conversion" extends="struts-default">
<action name="index">
<result>/WEB-INF/conversion/index.jsp</result>
</action>
<!-- Example populating a List inside an Action -->
<action name="enterPersonsInfo" method="input"
class="org.apache.struts2.showcase.conversion.PersonAction">
@@ -49,8 +49,8 @@
<action name="showPersonJavaCode">
<result type="plainText">/WEB-INF/conversion/Person.java.txt</result>
</action>
<!-- Example populating a Set inside an Action -->
<action name="enterAddressesInfo" class="org.apache.struts2.showcase.conversion.AddressAction" method="input">
<result>/WEB-INF/conversion/enterAddressInfo.jsp</result>
@@ -68,9 +68,9 @@
<action name="showAddressJavaCode">
<result type="plainText">/WEB-INF/conversion/Address.java.txt</result>
</action>
<!-- Example populating a List with Tiger 5 Enum -->
<!-- Example populating a List with Tiger 5 Enum -->
<action name="enterOperationEnumInfo" class="org.apache.struts2.showcase.conversion.OperationsEnumAction" method="input">
<result>/WEB-INF/conversion/enterOperations.jsp</result>
</action>
@@ -93,6 +93,6 @@
<action name="showOperationsEnumActionConversionProperties">
<result type="plainText">/WEB-INF/conversion/OperationsEnumActionConversion.txt</result>
</action>
</package>
</struts>
</struts>
@@ -21,7 +21,7 @@
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="dispatcher" extends="struts-default" namespace="/dispatcher">
@@ -21,7 +21,7 @@
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="filedownload" extends="struts-default" namespace="/filedownload">
@@ -21,11 +21,11 @@
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="fileupload" extends="struts-default" namespace="/fileupload">
<action name="upload" class="org.apache.struts2.showcase.fileupload.FileUploadAction" method="input">
<result>/WEB-INF/fileupload/upload.jsp</result>
</action>
@@ -34,11 +34,11 @@
<result name="input">/WEB-INF/fileupload/upload.jsp</result>
<result>/WEB-INF/fileupload/upload-success.jsp</result>
</action>
<action name="multipleUploadUsingList">
<result>/WEB-INF/fileupload/multipleUploadUsingList.jsp</result>
</action>
<action name="doMultipleUploadUsingList" class="org.apache.struts2.showcase.fileupload.MultipleFileUploadUsingListAction" method="upload">
<result name="input">/WEB-INF/fileupload/multipleUploadUsingList.jsp</result>
<result>/WEB-INF/fileupload/multiple-success.jsp</result>
@@ -21,16 +21,16 @@
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="freemarker" namespace="/freemarker" extends="struts-default">
<action name="customFreemarkerManagerDemo">
<result type="freemarker">/WEB-INF/freemarker/customFreemarkerManagerUsage.ftl</result>
</action>
<action name="standardTags" class="org.apache.struts2.showcase.freemarker.StandardTagsAction">
<result type="freemarker">/WEB-INF/freemarker/standardTags.ftl</result>
</action>
</package>
</struts>
</struts>
@@ -21,7 +21,7 @@
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="hangman" extends="struts-default" namespace="/hangman">
@@ -41,9 +41,9 @@
<result type="freemarker">/WEB-INF/hangman/hangmanNonAjax.ftl</result>
</action>
</package>
<package name="hangman-ajax" extends="struts-default" namespace="/hangman/ajax">
<action name="blank">
<result type="freemarker">/WEB-INF/hangman/blank.ftl</result>
@@ -61,4 +61,4 @@
<result type="freemarker">/WEB-INF/hangman/updateGuessLeft.ftl</result>
</action>
</package>
</struts>
</struts>
@@ -21,7 +21,7 @@
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="interactive" namespace="/interactive" extends="struts-default">
@@ -38,4 +38,4 @@
</action>
<action name="example" class="org.apache.struts2.showcase.action.ExampleAction" />
</package>
</struts>
</struts>
@@ -21,7 +21,7 @@
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="modelDriven" extends="struts-default" namespace="/modelDriven">
@@ -29,11 +29,10 @@
<action name="modelDriven" class="org.apache.struts2.showcase.modelDriven.ModelDrivenAction" method="input">
<result>/WEB-INF/modelDriven/modelDriven.jsp</result>
</action>
<!-- Display the result page whose content is populated using the Model-Driven technique -->
<action name="modelDrivenResult" class="org.apache.struts2.showcase.modelDriven.ModelDrivenAction">
<result>/WEB-INF/modelDriven/modelDrivenResult.jsp</result>
</action>
</package>
</struts>
</struts>
@@ -21,7 +21,7 @@
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
"https://struts.apache.org/dtds/struts-2.5.dtd">
<!-- START SNIPPET: xworkSample -->
@@ -21,14 +21,14 @@
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<!-- ================================== -->
<!-- === Package for ActionTag Demo === -->
<!-- ================================== -->
<package name="actionTag" extends="struts-default" namespace="/tags/non-ui/actionTag">
<action name="showActionTagDemo" class="org.apache.struts2.showcase.tag.nonui.actiontag.ActionTagDemo" method="show">
<result>/WEB-INF/tags/non-ui/actionTag/showActionTagDemo.jsp</result>
@@ -46,11 +46,11 @@
<result type="plainText">/WEB-INF/tags/non-ui/actionTag/showActionTagDemo.jsp</result>
</action>
</package>
<!-- ==================================== -->
<!-- === Package for IteratorGeneratorTag Demo === -->
<!-- ==================================== -->
<package name="iteratorGeneratorTag" extends="struts-default" namespace="/tags/non-ui/iteratorGeneratorTag">
<action name="showGeneratorTagDemo" class="org.apache.struts2.showcase.tag.nonui.iteratortag.IteratorGeneratorTagDemo" method="input">
<result>/WEB-INF/tags/non-ui/iteratorTag/showIteratorGeneratorTagDemo.jsp</result>
@@ -60,8 +60,8 @@
<result>/WEB-INF/tags/non-ui/iteratorTag/iteratorGeneratorTagDemoResult.jsp</result>
</action>
</package>
<!-- ===================================== -->
<!-- === Package for AppendIteratorTag Demo === -->
<!-- ========================================== -->
@@ -74,8 +74,8 @@
<result>/WEB-INF/tags/non-ui/iteratorTag/appendIteratorTagDemoResult.jsp</result>
</action>
</package>
<!-- ====================================== -->
<!-- === Package for MergeIteratorTag Demo === -->
<!-- ========================================= -->
@@ -88,7 +88,7 @@
<result>/WEB-INF/tags/non-ui/iteratorTag/mergeIteratorTagDemoResult.jsp</result>
</action>
</package>
<!-- ============================================= -->
<!-- === Package for SubsetIteratorTag Demo === -->
<!-- =========================================== -->
@@ -101,14 +101,14 @@
<result>/WEB-INF/tags/non-ui/iteratorTag/subsetIteratorTagDemoResult.jsp</result>
</action>
</package>
<!-- ========================================= -->
<!-- === Package for Action Prefix Example === -->
<!-- ========================================= -->
<package name="actionPrefix" extends="struts-default" namespace="/tags/non-ui/actionPrefix">
<action name="actionPrefixExampleUsingFreemarker">
<result type="freemarker">/WEB-INF/tags/non-ui/actionPrefix/actionPrefixExample.ftl</result>
</action>
</action>
<action name="submit" class="org.apache.struts2.showcase.tag.nonui.actionPrefix.SubmitAction">
<result type="freemarker">/WEB-INF/tags/non-ui/actionPrefix/normalSubmit.ftl</result>
</action>
@@ -131,7 +131,7 @@
<result type="plainText">/WEB-INF/tags/non-ui/actionPrefix/actionPrefixExample.ftl</result>
</action>
</package>
<!-- ========================================== -->
<!-- === Package for If Tag Test / Examples === -->
<!-- ========================================== -->
@@ -21,7 +21,7 @@
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="ui-tags" extends="velocity-default" namespace="/tags/ui">
@@ -21,7 +21,7 @@
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<include file="struts-tags-ui.xml"/>
@@ -21,7 +21,7 @@
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="tiles" extends="tiles-default" namespace="/tiles">
@@ -21,7 +21,7 @@
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="token" extends="struts-default" namespace="/token">
@@ -21,8 +21,8 @@
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<constant name="struts.beanValidation.providerClass" value="org.hibernate.validator.HibernateValidator"/>
@@ -74,7 +74,7 @@
<action name="ajaxFormSubmitSuccess" class="org.apache.struts2.showcase.validation.AjaxFormSubmitSuccessAction">
<result>/WEB-INF/validation/ajaxFormSubmitSuccess.jsp</result>
</action>
</package>
@@ -83,46 +83,46 @@
<action name="list">
<result>index.jsp</result>
</action>
<!-- ======================== -->
<!-- === Field Validators === -->
<!-- ======================== -->
<action name="showFieldValidatorsExamples" class="org.apache.struts2.showcase.validation.FieldValidatorsExampleAction" method="input">
<result name="input" type="dispatcher">/WEB-INF/validation/fieldValidatorsExample.jsp</result>
</action>
<action name="submitFieldValidatorsExamples" class="org.apache.struts2.showcase.validation.FieldValidatorsExampleAction" method="submit">
<result name="input" type="dispatcher">/WEB-INF/validation/fieldValidatorsExample.jsp</result>
<result type="dispatcher">/WEB-INF/validation/successFieldValidatorsExample.jsp</result>
</action>
<!-- ============================ -->
<!-- === Non Field Validators === -->
<!-- ============================ -->
<action name="showNonFieldValidatorsExamples" class="org.apache.struts2.showcase.validation.NonFieldValidatorsExampleAction" method="input">
<result name="input">/WEB-INF/validation/nonFieldValidatorsExample.jsp</result>
</action>
<action name="submitNonFieldValidatorsExamples" class="org.apache.struts2.showcase.validation.NonFieldValidatorsExampleAction" method="submit">
<result name="input">/WEB-INF/validation/nonFieldValidatorsExample.jsp</result>
<result>/WEB-INF/validation/successNonFieldValidatorsExample.jsp</result>
</action>
<!-- ========================== -->
<!-- === Visitor Validators === -->
<!-- ========================== -->
<action name="showVisitorValidatorsExamples" class="org.apache.struts2.showcase.validation.VisitorValidatorsExampleAction" method="input">
<result name="input">/WEB-INF/validation/visitorValidatorsExample.jsp</result>
</action>
<action name="submitVisitorValidatorsExamples" class="org.apache.struts2.showcase.validation.VisitorValidatorsExampleAction" method="submit">
<result name="input">/WEB-INF/validation/visitorValidatorsExample.jsp</result>
<result>/WEB-INF/validation/successVisitorValidatorsExample.jsp</result>
@@ -146,7 +146,7 @@
<!-- =========================================== -->
<!-- === Store Error Messages Across Request === -->
<!-- =========================================== -->
<action name="submitApplication" class="org.apache.struts2.showcase.validation.SubmitApplication" method="submitApplication">
<interceptor-ref name="store">
<param name="operationMode">STORE</param>
@@ -173,7 +173,7 @@
<action name="cancelApplication" class="org.apache.struts2.showcase.validation.SubmitApplication" method="cancelApplication">
<result>/WEB-INF/validation/storeErrorsAcrossRequestCancel.jsp</result>
</action>
</package>
</struts>
@@ -21,10 +21,17 @@
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<bean type="org.apache.struts2.interceptor.exec.ExecutorProvider" class="org.apache.struts2.showcase.wait.ThreadPoolExecutorProvider"/>
<package name="wait" extends="struts-default" namespace="/wait">
<default-action-ref name="index"/>
<action name="index">
<result>/WEB-INF/wait/index.jsp</result>
</action>
<action name="example1">
<result>/WEB-INF/wait/example1.jsp</result>
@@ -21,7 +21,7 @@
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="xslt" extends="struts-default" namespace="/xslt">
@@ -36,7 +36,7 @@
<param name="exposedValue">info.classpath</param>
</result>
</action>
<action name="jvmInfoRaw" class="org.apache.struts2.showcase.xslt.JVMAction">
<result type="xslt">
<param name="exposedValue">info</param>
+1 -1
View File
@@ -21,7 +21,7 @@
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
"https://struts.apache.org/dtds/struts-2.5.dtd">
<!-- START SNIPPET: xworkSample -->
<struts>
@@ -64,14 +64,14 @@
<title><decorator:title default="Struts2 Showcase"/></title>
<s:url var="bootstrapCss" value='/styles/bootstrap.css' encode='false' includeParams='none'/>
<s:link href="%{bootstrapCss}" rel="stylesheet" type="text/css" media="all"></s:link>
<s:link href="%{bootstrapCss}" rel="stylesheet" type="text/css" media="all"/>
<s:url var="mainCss" value='/styles/main.css' encode='false' includeParams='none'/>
<s:link href="%{mainCss}" rel="stylesheet" type="text/css" media="all"></s:link>
<s:link href="%{mainCss}" rel="stylesheet" type="text/css" media="all"/>
<s:url var="jqueryJs" value='/js/jquery-2.1.4.min.js' encode='false' includeParams='none'/>
<s:script src="%{jqueryJs}"></s:script>
<s:script src="%{jqueryJs}"/>
<s:url var="bootstrapJs" value='/js/bootstrap.min.js' encode='false' includeParams='none'/>
<s:script src="%{bootstrapJs}"></s:script>
<s:script src="%{bootstrapJs}"/>
<s:script type="text/javascript">
$(function () {
var alerts = $('ul.alert').wrap('<div />');
@@ -82,19 +82,22 @@
<!-- Prettify -->
<s:url var="prettifyCss" value='/styles/prettify.css' encode='false' includeParams='none'/>
<s:link href="%{prettifyCss}" rel="stylesheet"></s:link>
<s:link href="%{prettifyCss}" rel="stylesheet"/>
<s:url var="prettifyJs" value='/js/prettify.js' encode='false' includeParams='none'/>
<s:script src="%{prettifyJs}"></s:script>
<s:script src="%{prettifyJs}"/>
<!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
<!--[if lt IE 9]>
<s:script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></s:script>
<s:script src="http://html5shim.googlecode.com/svn/trunk/html5.js"/>
<![endif]-->
<s:script>
jQuery(document).ready(function() { prettyPrint(); } );
</s:script>
<decorator:head/>
</head>
<body id="page-home" onload="prettyPrint();">
<body id="page-home">
<nav class="navbar navbar-default navbar-fixed-top">
<div class="container-fluid">
@@ -239,7 +242,7 @@
</li>
<li><s:a value="/person/index.html">Person Manager</s:a></li>
<li><s:a value="/skill/index.html">CRUD</s:a></li>
<li><s:a value="/wait/index.html">Execute &amp; Wait</s:a></li>
<li><s:a value="/wait/index">Execute &amp; Wait</s:a></li>
<li><s:a value="/token/index.html">Token</s:a></li>
<li><s:url var="url" namespace="/modelDriven" action="modelDriven"/><s:a
href="%{url}">Model Driven</s:a></li>
@@ -1,19 +1,19 @@
<!--
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
@@ -21,25 +21,26 @@
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Struts2 Showcase - Execute and Wait Examples - Complete</title>
<title>Struts2 Showcase - Execute and Wait Examples - Complete</title>
</head>
<body>
<div class="page-header">
<h1>The process is complete</h1>
<h1>The process is complete</h1>
</div>
<div class="container-fluid">
<div class="row">
<div class="col-md-12" style="text-align: center;">
<div class="row">
<div class="col-md-12" style="text-align: center;">
<b>We have processed your request.</b>
<p/>
Click here to <s:url var="back" value="/wait/index.html"/><s:a href="%{back}" cssClass="btn btn-link">return</s:a>.
<div class="panel">We have processed your request.</div>
</div>
</div>
<s:url var="back" value="/wait/index"/>
Click here to <s:a href="%{back}" cssClass="btn btn-primary">return</s:a>
</div>
</div>
</div>
</body>
</html>
@@ -0,0 +1,53 @@
<!--
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
-->
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Struts2 Showcase - Execute and Wait Examples</title>
</head>
<body>
<div class="page-header">
<h1>Execute and Wait Examples</h1>
</div>
<div class="container-fluid">
<div class="row">
<div class="col-md-12" style="text-align: center;">
<p>
These examples illustrate Struts build in support for execute and wait.
<p/>
<p>
When you have a process that takes a long time your users can be impatient and starts to submit/click
again.
<br/> A good solution is to show the user a progress page (wait page) while the process takes it time.
<p/>
<br/>
<br/><a href='<s:url value="example1"/>'>Example 1 (no delay)</a>
<br/><a href='<s:url value="example2"/>'>Example 2 (with delay)</a>
<br/><a href='<s:url value="example3"/>'>Example 3 (with longer check delay)</a>
</div>
</div>
</div>
</body>
</html>
@@ -1,19 +1,19 @@
<!--
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
@@ -21,53 +21,33 @@
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Struts2 Showcase - Execute and Wait Examples - Wait</title>
</head>
<body>
<div class="page-header">
<h1>Execute and Wait Examples - Wait</h1>
</div>
<div class="container-fluid">
<div class="row">
<div class="col-md-12" style="text-align: center;">
<p class="well">
We are processing your request. Please wait.
</p>
<div class="progress progress-striped active">
<div class="bar" style="width: 50%;"></div>
</div>
<p/>
You can click this link to <a href="<s:url includeParams="all"/>">refresh</a>.
<b>We have processed your request.</b>
<p/>
Click here to <s:url var="back" value="/wait/index.html"/><s:a href="%{back}" cssClass="btn btn-link">return</s:a>.
</div>
</div>
</div>
</body>
</html>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Struts2 Showcase - Execute and Wait Examples - Wait</title>
<meta http-equiv="refresh" content="5;url=<s:url includeParams="all"/>"/>
</head>
<body>
<p style="border: 1px solid silver; padding: 5px; background: #ffd; text-align: center;">
We are processing your request. Please wait.
</p>
<div class="page-header">
<h1>Execute and Wait Examples - Wait</h1>
</div>
<p/>
You can click this link to <a href="<s:url includeParams="all"/>">refresh</a>.
<div class="container-fluid">
<div class="row">
<div class="col-md-12" style="text-align: center;">
<p class="well">
We are processing your request. Please wait.
</p>
<div class="progress progress-striped active">
<div class="bar progress-bar progress-bar-success" style="width: 50%;"></div>
</div>
<div class="panel">
You can click this link to <a href="<s:url includeParams="all"/>">refresh</a>.
</div>
</div>
</div>
</div>
</body>
</html>
@@ -1,53 +0,0 @@
<!--
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
-->
<html>
<head>
<title>Struts2 Showcase - Execute and Wait Examples</title>
</head>
<body>
<div class="page-header">
<h1>Execute and Wait Examples</h1>
</div>
<div class="container-fluid">
<div class="row">
<div class="col-md-12" style="text-align: center;">
<p>
These examples illustrate Struts build in support for execute and wait.
<p/>
<p>
When you have a process that takes a long time your users can be impatient and starts to submit/click again.
<br/> A good solution is to show the user a progress page (wait page) while the process takes it time.
<p/>
<br/>
<br/><a href="example1.action">Example 1 (no delay)</a>
<br/><a href="example2.action">Example 2 (with delay)</a>
<br/><a href="example3.action">Example 3 (with longer check delay)</a>
</div>
</div>
</div>
</body>
</html>
+3
View File
@@ -0,0 +1,3 @@
# Struts 2 Assemblies
This module is used to prepare ZIP archives with different set of JARs, like code source, Javadocs, etc.
It's a part of the release process, it shouldn't be used directly by users.
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>6.0.3</version>
<version>6.1.2.3-SNAPSHOT</version>
</parent>
<artifactId>struts2-assembly</artifactId>
+1
View File
@@ -36,6 +36,7 @@
<include>org.apache.struts:struts2-core</include>
<include>org.freemarker:freemarker</include>
<include>org.apache.commons:commons-lang3</include>
<include>org.apache.commons:commons-text</include>
<include>org.apache.logging.log4j:log4j-api</include>
<include>ognl:ognl</include>
<include>commons-fileupload:commons-fileupload</include>
+20
View File
@@ -0,0 +1,20 @@
# Struts 2 BOM
This is a Bill-Of-Materials to be used with Maven based project. It allows to import all the Struts 2
dependencies at once and used them in your project where needed.
## Installation
You must add a proper import statement into your `pom.xml` as presented below:
```xml
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-bom</artifactId>
<version>${struts2.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
```
+3 -3
View File
@@ -29,7 +29,7 @@
</parent>
<artifactId>struts2-bom</artifactId>
<version>6.0.3</version>
<version>6.1.2.3-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Struts 2 Bill of Materials</name>
@@ -44,7 +44,7 @@
</licenses>
<properties>
<struts-version.version>6.0.3</struts-version.version>
<struts-version.version>6.1.2.3-SNAPSHOT</struts-version.version>
<maven.site.skip>true</maven.site.skip>
<maven.site.deploy.skip>true</maven.site.deploy.skip>
</properties>
@@ -185,7 +185,7 @@
</dependencyManagement>
<scm>
<tag>STRUTS_6_0_3</tag>
<tag>STRUTS_6_1_2_1</tag>
<connection>scm:git:https://gitbox.apache.org/repos/asf/struts.git</connection>
<developerConnection>scm:git:https://gitbox.apache.org/repos/asf/struts.git</developerConnection>
<url>https://github.com/apache/struts/</url>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-osgi-bundles</artifactId>
<version>6.0.3</version>
<version>6.1.2.3-SNAPSHOT</version>
</parent>
<artifactId>struts2-osgi-admin-bundle</artifactId>
+2 -2
View File
@@ -22,13 +22,13 @@
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<constant name="struts.enable.DynamicMethodInvocation" value="false" />
<package name="bundle-admin" namespace="/osgi/admin" extends="osgi-default" strict-method-invocation="true">
<default-action-ref name="bundles" />
<action name="bundle_*!*" class="org.apache.struts2.osgi.admin.actions.BundlesAction" method="{2}">
<param name="id">{1}</param>
<result type="freemarker">viewBundle.ftl</result>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-osgi-bundles</artifactId>
<version>6.0.3</version>
<version>6.1.2.3-SNAPSHOT</version>
</parent>
<artifactId>struts2-osgi-demo-bundle</artifactId>
+4 -4
View File
@@ -21,11 +21,11 @@
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<!-- Set some Struts 2 constants relevant to the OSGi Plugin.
Note: The constant definitions specified here (within the demo bundle) may also need to be
<!-- Set some Struts 2 constants relevant to the OSGi Plugin.
Note: The constant definitions specified here (within the demo bundle) may also need to be
duplicated in the struts.xml configuration of the outer project that packages the demo
bundle JAR file within it.
-->
@@ -52,4 +52,4 @@
</action>
</package>
</struts>
</struts>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>6.0.3</version>
<version>6.1.2.3-SNAPSHOT</version>
</parent>
<artifactId>struts2-osgi-bundles</artifactId>
+6
View File
@@ -0,0 +1,6 @@
# Struts 2 Core
This is a core of the Apache Struts framework and all other modules depend on it.
It requires Java 8 at minimum and a Servlet container supporting Java Servlet API 3.1 at least.
## Installation
Just drop this plugin into `WEB-INF/lib` folder or add it as Maven dependency
+3 -6
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>6.0.3</version>
<version>6.1.2.3-SNAPSHOT</version>
</parent>
<artifactId>struts2-core</artifactId>
<packaging>jar</packaging>
@@ -41,6 +41,7 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>@{argLine}</argLine>
<properties>
<property>
<name>maven.testng.output.dir</name>
@@ -54,9 +55,6 @@
<element>${project.build.testOutputDirectory}/xwork - jar.jar</element>
<element>${project.build.testOutputDirectory}/xwork - zip.zip</element>
</additionalClasspathElements>
<includes>
<include>**/*Test.java</include>
</includes>
<excludes>
<exclude>**/XWorkTestCase.java</exclude>
<exclude>**/TestBean.java</exclude>
@@ -339,8 +337,7 @@
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<scope>compile</scope>
<optional>true</optional>
<scope>test</scope>
</dependency>
<!-- SLF4J support -->
@@ -24,6 +24,7 @@ import com.opensymphony.xwork2.config.entities.InterceptorMapping;
import com.opensymphony.xwork2.config.entities.ResultConfig;
import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.interceptor.ConditionalInterceptor;
import com.opensymphony.xwork2.interceptor.Interceptor;
import com.opensymphony.xwork2.interceptor.PreResultListener;
import com.opensymphony.xwork2.interceptor.WithLazyParams;
@@ -73,7 +74,7 @@ public class DefaultActionInvocation implements ActionInvocation {
protected UnknownHandlerManager unknownHandlerManager;
protected OgnlUtil ognlUtil;
protected AsyncManager asyncManager;
protected Callable asyncAction;
protected Callable<?> asyncAction;
protected WithLazyParams.LazyParamInjector lazyParamInjector;
public DefaultActionInvocation(final Map<String, Object> extraContext, final boolean pushAction) {
@@ -101,7 +102,7 @@ public class DefaultActionInvocation implements ActionInvocation {
this.container = cont;
}
@Inject(required=false)
@Inject(required = false)
public void setActionEventListener(ActionEventListener listener) {
this.actionEventListener = listener;
}
@@ -111,7 +112,7 @@ public class DefaultActionInvocation implements ActionInvocation {
this.ognlUtil = ognlUtil;
}
@Inject(required=false)
@Inject(required = false)
public void setAsyncManager(AsyncManager asyncManager) {
this.asyncManager = asyncManager;
}
@@ -214,7 +215,7 @@ public class DefaultActionInvocation implements ActionInvocation {
} catch (NullPointerException e) {
LOG.debug("Got NPE trying to read result configuration for resultCode [{}]", resultCode);
}
if (resultConfig == null) {
// If no result is found for the given resultCode, try to get a wildcard '*' match.
resultConfig = results.get("*");
@@ -248,7 +249,12 @@ public class DefaultActionInvocation implements ActionInvocation {
if (interceptor instanceof WithLazyParams) {
interceptor = lazyParamInjector.injectParams(interceptor, interceptorMapping.getParams(), invocationContext);
}
resultCode = interceptor.intercept(DefaultActionInvocation.this);
if (interceptor instanceof ConditionalInterceptor) {
resultCode = executeConditional((ConditionalInterceptor) interceptor);
} else {
LOG.debug("Executing normal interceptor: {}", interceptorMapping.getName());
resultCode = interceptor.intercept(this);
}
} else {
resultCode = invokeActionOnly();
}
@@ -268,9 +274,7 @@ public class DefaultActionInvocation implements ActionInvocation {
if (preResultListeners != null) {
LOG.trace("Executing PreResultListeners for result [{}]", result);
for (Object preResultListener : preResultListeners) {
PreResultListener listener = (PreResultListener) preResultListener;
for (PreResultListener listener : preResultListeners) {
listener.beforeResult(this, resultCode);
}
}
@@ -289,6 +293,16 @@ public class DefaultActionInvocation implements ActionInvocation {
return resultCode;
}
protected String executeConditional(ConditionalInterceptor conditionalInterceptor) throws Exception {
if (conditionalInterceptor.shouldIntercept(this)) {
LOG.debug("Executing conditional interceptor: {}", conditionalInterceptor.getClass().getSimpleName());
return conditionalInterceptor.intercept(this);
} else {
LOG.debug("Interceptor: {} is disabled, skipping to next", conditionalInterceptor.getClass().getSimpleName());
return this.invoke();
}
}
public String invokeActionOnly() throws Exception {
return invokeAction(getAction(), proxy.getConfig());
}
@@ -314,7 +328,7 @@ public class DefaultActionInvocation implements ActionInvocation {
gripe = "Unable to instantiate Action, " + proxy.getConfig().getClassName() + ", defined for '" + proxy.getActionName() + "' in namespace '" + proxy.getNamespace() + "'";
}
gripe += (((" -- " + e.getMessage()) != null) ? e.getMessage() : " [no message in exception]");
gripe += e.getMessage();
throw new StrutsException(gripe, e, proxy.getConfig());
}
@@ -363,7 +377,7 @@ public class DefaultActionInvocation implements ActionInvocation {
result.execute(this);
} else if (resultCode != null && !Action.NONE.equals(resultCode)) {
throw new ConfigurationException("No result defined for action " + getAction().getClass().getName()
+ " and result " + getResultCode(), proxy.getConfig());
+ " and result " + getResultCode(), proxy.getConfig());
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("No result returned for action {} at {}", getAction().getClass().getName(), proxy.getConfig().getLocation());
@@ -464,6 +478,7 @@ public class DefaultActionInvocation implements ActionInvocation {
/**
* Save the result to be used later.
*
* @param actionConfig current ActionConfig
* @param methodResult the result of the action.
* @return the result code to process.
@@ -476,7 +491,7 @@ public class DefaultActionInvocation implements ActionInvocation {
container.inject(explicitResult);
return null;
} else if (methodResult instanceof Callable) {
asyncAction = (Callable) methodResult;
asyncAction = (Callable<?>) methodResult;
return null;
} else {
return (String) methodResult;
@@ -34,22 +34,22 @@ import java.util.Locale;
import java.util.Map;
/**
* Base JUnit TestCase to extend for XWork specific JUnit tests. Uses
* Base JUnit TestCase to extend for XWork specific JUnit tests. Uses
* the generic test setup for logic.
*
* @author plightbo
*/
public abstract class XWorkTestCase extends TestCase {
protected ConfigurationManager configurationManager;
protected Configuration configuration;
protected Container container;
protected ActionProxyFactory actionProxyFactory;
public XWorkTestCase() {
super();
}
@Override
protected void setUp() throws Exception {
configurationManager = XWorkTestCaseHelper.setUp();
@@ -57,7 +57,7 @@ public abstract class XWorkTestCase extends TestCase {
container = configuration.getContainer();
actionProxyFactory = container.getInstance(ActionProxyFactory.class);
}
@Override
protected void tearDown() throws Exception {
XWorkTestCaseHelper.tearDown(configurationManager);
@@ -66,34 +66,33 @@ public abstract class XWorkTestCase extends TestCase {
container = null;
actionProxyFactory = null;
}
protected void loadConfigurationProviders(ConfigurationProvider... providers) {
configurationManager = XWorkTestCaseHelper.loadConfigurationProviders(configurationManager, providers);
configuration = configurationManager.getConfiguration();
container = configuration.getContainer();
actionProxyFactory = container.getInstance(ActionProxyFactory.class);
}
protected void loadButAdd(final Class<?> type, final Object impl) {
protected <T> void loadButAdd(final Class<T> type, final T impl) {
loadButAdd(type, Container.DEFAULT_NAME, impl);
}
protected void loadButAdd(final Class<?> type, final String name, final Object impl) {
protected <T> void loadButAdd(final Class<T> type, final String name, final T impl) {
loadConfigurationProviders(new StubConfigurationProvider() {
@Override
public void register(ContainerBuilder builder,
LocatableProperties props) throws ConfigurationException {
public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException {
if (impl instanceof String || ClassUtils.isPrimitiveOrWrapper(impl.getClass())) {
props.setProperty(name, "" + impl);
} else {
builder.factory(type, name, new Factory() {
public Object create(Context context) throws Exception {
builder.factory(type, name, new Factory<T>() {
public T create(Context context) throws Exception {
return impl;
}
@Override
public Class type() {
return impl.getClass();
public Class<T> type() {
return (Class<T>) impl.getClass();
}
}, Scope.SINGLETON);
}
@@ -20,26 +20,18 @@ package com.opensymphony.xwork2.config.entities;
import com.opensymphony.xwork2.util.location.Located;
import com.opensymphony.xwork2.util.location.Location;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.Serializable;
import java.util.*;
/**
* Configuration for Package.
*
* <p>
* In the xml configuration file this is defined as the <code>package</code> tag.
* </p>
*
* @author Rainer Hermanns
* @version $Revision$
*/
public class PackageConfig extends Located implements Comparable, Serializable, InterceptorLocator {
private static final Logger LOG = LogManager.getLogger(PackageConfig.class);
public class PackageConfig extends Located implements Comparable<PackageConfig>, Serializable, InterceptorLocator {
protected Map<String, ActionConfig> actionConfigs;
protected Map<String, ResultConfig> globalResultConfigs;
@@ -422,8 +414,7 @@ public class PackageConfig extends Located implements Comparable, Serializable,
return "PackageConfig: [" + name + "] for namespace [" + namespace + "] with parents [" + parents + "]";
}
public int compareTo(Object o) {
PackageConfig other = (PackageConfig) o;
public int compareTo(PackageConfig other) {
String full = namespace + "!" + name;
String otherFull = other.namespace + "!" + other.name;
@@ -443,7 +434,6 @@ public class PackageConfig extends Located implements Comparable, Serializable,
public static class Builder implements InterceptorLocator {
protected PackageConfig target;
private boolean strictDMI = true;
public Builder(String name) {
target = new PackageConfig(name);
@@ -21,26 +21,16 @@ package com.opensymphony.xwork2.config.providers;
import com.opensymphony.xwork2.ActionProxyFactory;
import com.opensymphony.xwork2.DefaultActionProxyFactory;
import com.opensymphony.xwork2.DefaultLocaleProviderFactory;
import com.opensymphony.xwork2.LocaleProviderFactory;
import com.opensymphony.xwork2.StrutsTextProviderFactory;
import com.opensymphony.xwork2.TextProviderFactory;
import com.opensymphony.xwork2.factory.DefaultUnknownHandlerFactory;
import com.opensymphony.xwork2.factory.UnknownHandlerFactory;
import com.opensymphony.xwork2.ognl.BeanInfoCacheFactory;
import com.opensymphony.xwork2.ognl.ExpressionCacheFactory;
import com.opensymphony.xwork2.ognl.accessor.HttpParametersPropertyAccessor;
import com.opensymphony.xwork2.ognl.accessor.ParameterPropertyAccessor;
import com.opensymphony.xwork2.security.AcceptedPatternsChecker;
import com.opensymphony.xwork2.security.DefaultAcceptedPatternsChecker;
import com.opensymphony.xwork2.security.DefaultExcludedPatternsChecker;
import com.opensymphony.xwork2.DefaultTextProvider;
import com.opensymphony.xwork2.DefaultUnknownHandlerManager;
import com.opensymphony.xwork2.security.DefaultNotExcludedAcceptedPatternsChecker;
import com.opensymphony.xwork2.security.ExcludedPatternsChecker;
import com.opensymphony.xwork2.FileManager;
import com.opensymphony.xwork2.FileManagerFactory;
import com.opensymphony.xwork2.LocaleProviderFactory;
import com.opensymphony.xwork2.LocalizedTextProvider;
import com.opensymphony.xwork2.ObjectFactory;
import com.opensymphony.xwork2.StrutsTextProviderFactory;
import com.opensymphony.xwork2.TextProvider;
import com.opensymphony.xwork2.TextProviderFactory;
import com.opensymphony.xwork2.UnknownHandlerManager;
import com.opensymphony.xwork2.config.Configuration;
import com.opensymphony.xwork2.config.ConfigurationException;
@@ -57,14 +47,7 @@ import com.opensymphony.xwork2.conversion.impl.CollectionConverter;
import com.opensymphony.xwork2.conversion.impl.DateConverter;
import com.opensymphony.xwork2.conversion.impl.DefaultConversionAnnotationProcessor;
import com.opensymphony.xwork2.conversion.impl.DefaultConversionFileProcessor;
import com.opensymphony.xwork2.security.NotExcludedAcceptedPatternsChecker;
import org.apache.struts2.components.date.DateFormatter;
import org.apache.struts2.components.date.DateTimeFormatterAdapter;
import org.apache.struts2.components.date.SimpleDateFormatAdapter;
import org.apache.struts2.conversion.StrutsConversionPropertiesProcessor;
import com.opensymphony.xwork2.conversion.impl.DefaultObjectTypeDeterminer;
import org.apache.struts2.conversion.StrutsTypeConverterCreator;
import org.apache.struts2.conversion.StrutsTypeConverterHolder;
import com.opensymphony.xwork2.conversion.impl.InstantiatingNullHandler;
import com.opensymphony.xwork2.conversion.impl.NumberConverter;
import com.opensymphony.xwork2.conversion.impl.StringConverter;
@@ -73,34 +56,45 @@ import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
import com.opensymphony.xwork2.factory.ActionFactory;
import com.opensymphony.xwork2.factory.ConverterFactory;
import com.opensymphony.xwork2.factory.DefaultActionFactory;
import com.opensymphony.xwork2.factory.StrutsConverterFactory;
import com.opensymphony.xwork2.factory.DefaultInterceptorFactory;
import com.opensymphony.xwork2.factory.DefaultResultFactory;
import com.opensymphony.xwork2.factory.DefaultUnknownHandlerFactory;
import com.opensymphony.xwork2.factory.InterceptorFactory;
import com.opensymphony.xwork2.factory.ResultFactory;
import com.opensymphony.xwork2.factory.StrutsConverterFactory;
import com.opensymphony.xwork2.factory.UnknownHandlerFactory;
import com.opensymphony.xwork2.inject.ContainerBuilder;
import com.opensymphony.xwork2.inject.Scope;
import com.opensymphony.xwork2.ognl.BeanInfoCacheFactory;
import com.opensymphony.xwork2.ognl.DefaultOgnlBeanInfoCacheFactory;
import com.opensymphony.xwork2.ognl.DefaultOgnlExpressionCacheFactory;
import com.opensymphony.xwork2.ognl.ExpressionCacheFactory;
import com.opensymphony.xwork2.ognl.ObjectProxy;
import com.opensymphony.xwork2.ognl.OgnlReflectionContextFactory;
import com.opensymphony.xwork2.ognl.OgnlReflectionProvider;
import com.opensymphony.xwork2.ognl.OgnlUtil;
import com.opensymphony.xwork2.ognl.OgnlValueStackFactory;
import com.opensymphony.xwork2.ognl.accessor.CompoundRootAccessor;
import com.opensymphony.xwork2.ognl.accessor.HttpParametersPropertyAccessor;
import com.opensymphony.xwork2.ognl.accessor.ObjectAccessor;
import com.opensymphony.xwork2.ognl.accessor.ObjectProxyPropertyAccessor;
import com.opensymphony.xwork2.ognl.accessor.ParameterPropertyAccessor;
import com.opensymphony.xwork2.ognl.accessor.XWorkCollectionPropertyAccessor;
import com.opensymphony.xwork2.ognl.accessor.XWorkEnumerationAccessor;
import com.opensymphony.xwork2.ognl.accessor.XWorkIteratorPropertyAccessor;
import com.opensymphony.xwork2.ognl.accessor.XWorkListPropertyAccessor;
import com.opensymphony.xwork2.ognl.accessor.XWorkMapPropertyAccessor;
import com.opensymphony.xwork2.ognl.accessor.XWorkMethodAccessor;
import com.opensymphony.xwork2.security.AcceptedPatternsChecker;
import com.opensymphony.xwork2.security.DefaultAcceptedPatternsChecker;
import com.opensymphony.xwork2.security.DefaultExcludedPatternsChecker;
import com.opensymphony.xwork2.security.DefaultNotExcludedAcceptedPatternsChecker;
import com.opensymphony.xwork2.security.ExcludedPatternsChecker;
import com.opensymphony.xwork2.security.NotExcludedAcceptedPatternsChecker;
import com.opensymphony.xwork2.util.CompoundRoot;
import com.opensymphony.xwork2.LocalizedTextProvider;
import com.opensymphony.xwork2.ognl.DefaultOgnlBeanInfoCacheFactory;
import com.opensymphony.xwork2.ognl.DefaultOgnlExpressionCacheFactory;
import com.opensymphony.xwork2.util.StrutsLocalizedTextProvider;
import com.opensymphony.xwork2.util.OgnlTextParser;
import com.opensymphony.xwork2.util.PatternMatcher;
import com.opensymphony.xwork2.util.StrutsLocalizedTextProvider;
import com.opensymphony.xwork2.util.TextParser;
import com.opensymphony.xwork2.util.ValueStackFactory;
import com.opensymphony.xwork2.util.WildcardHelper;
@@ -119,8 +113,19 @@ import com.opensymphony.xwork2.validator.ValidatorFileParser;
import ognl.MethodAccessor;
import ognl.PropertyAccessor;
import org.apache.struts2.StrutsConstants;
import org.apache.struts2.conversion.StrutsConversionPropertiesProcessor;
import org.apache.struts2.conversion.StrutsTypeConverterCreator;
import org.apache.struts2.conversion.StrutsTypeConverterHolder;
import org.apache.struts2.dispatcher.HttpParameters;
import org.apache.struts2.dispatcher.Parameter;
import org.apache.struts2.url.QueryStringBuilder;
import org.apache.struts2.url.QueryStringParser;
import org.apache.struts2.url.StrutsQueryStringBuilder;
import org.apache.struts2.url.StrutsQueryStringParser;
import org.apache.struts2.url.StrutsUrlDecoder;
import org.apache.struts2.url.StrutsUrlEncoder;
import org.apache.struts2.url.UrlDecoder;
import org.apache.struts2.url.UrlEncoder;
import java.util.ArrayList;
import java.util.Collection;
@@ -153,88 +158,90 @@ public class StrutsDefaultConfigurationProvider implements ConfigurationProvider
@Override
public void register(ContainerBuilder builder, LocatableProperties props)
throws ConfigurationException {
throws ConfigurationException {
builder
.factory(ObjectFactory.class)
.factory(ActionFactory.class, DefaultActionFactory.class)
.factory(ResultFactory.class, DefaultResultFactory.class)
.factory(InterceptorFactory.class, DefaultInterceptorFactory.class)
.factory(com.opensymphony.xwork2.factory.ValidatorFactory.class, com.opensymphony.xwork2.factory.DefaultValidatorFactory.class)
.factory(ConverterFactory.class, StrutsConverterFactory.class)
.factory(UnknownHandlerFactory.class, DefaultUnknownHandlerFactory.class)
.factory(ObjectFactory.class)
.factory(ActionFactory.class, DefaultActionFactory.class)
.factory(ResultFactory.class, DefaultResultFactory.class)
.factory(InterceptorFactory.class, DefaultInterceptorFactory.class)
.factory(com.opensymphony.xwork2.factory.ValidatorFactory.class, com.opensymphony.xwork2.factory.DefaultValidatorFactory.class)
.factory(ConverterFactory.class, StrutsConverterFactory.class)
.factory(UnknownHandlerFactory.class, DefaultUnknownHandlerFactory.class)
.factory(ActionProxyFactory.class, DefaultActionProxyFactory.class, Scope.SINGLETON)
.factory(ObjectTypeDeterminer.class, DefaultObjectTypeDeterminer.class, Scope.SINGLETON)
.factory(ActionProxyFactory.class, DefaultActionProxyFactory.class, Scope.SINGLETON)
.factory(ObjectTypeDeterminer.class, DefaultObjectTypeDeterminer.class, Scope.SINGLETON)
.factory(XWorkConverter.class, Scope.SINGLETON)
.factory(XWorkBasicConverter.class, Scope.SINGLETON)
.factory(ConversionPropertiesProcessor.class, StrutsConversionPropertiesProcessor.class, Scope.SINGLETON)
.factory(ConversionFileProcessor.class, DefaultConversionFileProcessor.class, Scope.SINGLETON)
.factory(ConversionAnnotationProcessor.class, DefaultConversionAnnotationProcessor.class, Scope.SINGLETON)
.factory(TypeConverterCreator.class, StrutsTypeConverterCreator.class, Scope.SINGLETON)
.factory(TypeConverterHolder.class, StrutsTypeConverterHolder.class, Scope.SINGLETON)
.factory(XWorkConverter.class, Scope.SINGLETON)
.factory(XWorkBasicConverter.class, Scope.SINGLETON)
.factory(ConversionPropertiesProcessor.class, StrutsConversionPropertiesProcessor.class, Scope.SINGLETON)
.factory(ConversionFileProcessor.class, DefaultConversionFileProcessor.class, Scope.SINGLETON)
.factory(ConversionAnnotationProcessor.class, DefaultConversionAnnotationProcessor.class, Scope.SINGLETON)
.factory(TypeConverterCreator.class, StrutsTypeConverterCreator.class, Scope.SINGLETON)
.factory(TypeConverterHolder.class, StrutsTypeConverterHolder.class, Scope.SINGLETON)
.factory(FileManager.class, "system", DefaultFileManager.class, Scope.SINGLETON)
.factory(FileManagerFactory.class, DefaultFileManagerFactory.class, Scope.SINGLETON)
.factory(ValueStackFactory.class, OgnlValueStackFactory.class, Scope.SINGLETON)
.factory(ValidatorFactory.class, DefaultValidatorFactory.class, Scope.SINGLETON)
.factory(ValidatorFileParser.class, DefaultValidatorFileParser.class, Scope.SINGLETON)
.factory(PatternMatcher.class, WildcardHelper.class, Scope.SINGLETON)
.factory(ReflectionProvider.class, OgnlReflectionProvider.class, Scope.SINGLETON)
.factory(ReflectionContextFactory.class, OgnlReflectionContextFactory.class, Scope.SINGLETON)
.factory(FileManager.class, "system", DefaultFileManager.class, Scope.SINGLETON)
.factory(FileManagerFactory.class, DefaultFileManagerFactory.class, Scope.SINGLETON)
.factory(ValueStackFactory.class, OgnlValueStackFactory.class, Scope.SINGLETON)
.factory(ValidatorFactory.class, DefaultValidatorFactory.class, Scope.SINGLETON)
.factory(ValidatorFileParser.class, DefaultValidatorFileParser.class, Scope.SINGLETON)
.factory(PatternMatcher.class, WildcardHelper.class, Scope.SINGLETON)
.factory(ReflectionProvider.class, OgnlReflectionProvider.class, Scope.SINGLETON)
.factory(ReflectionContextFactory.class, OgnlReflectionContextFactory.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, CompoundRoot.class.getName(), CompoundRootAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, Object.class.getName(), ObjectAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, Iterator.class.getName(), XWorkIteratorPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, Enumeration.class.getName(), XWorkEnumerationAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, CompoundRoot.class.getName(), CompoundRootAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, Object.class.getName(), ObjectAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, Iterator.class.getName(), XWorkIteratorPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, Enumeration.class.getName(), XWorkEnumerationAccessor.class, Scope.SINGLETON)
.factory(UnknownHandlerManager.class, DefaultUnknownHandlerManager.class, Scope.SINGLETON)
.factory(UnknownHandlerManager.class, DefaultUnknownHandlerManager.class, Scope.SINGLETON)
// silly workarounds for ognl since there is no way to flush its caches
.factory(PropertyAccessor.class, List.class.getName(), XWorkListPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, ArrayList.class.getName(), XWorkListPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, HashSet.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, Set.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, HashMap.class.getName(), XWorkMapPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, Map.class.getName(), XWorkMapPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, Collection.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, ObjectProxy.class.getName(), ObjectProxyPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, HttpParameters.class.getName(), HttpParametersPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, Parameter.class.getName(), ParameterPropertyAccessor.class, Scope.SINGLETON)
// silly workarounds for ognl since there is no way to flush its caches
.factory(PropertyAccessor.class, List.class.getName(), XWorkListPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, ArrayList.class.getName(), XWorkListPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, HashSet.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, Set.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, HashMap.class.getName(), XWorkMapPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, Map.class.getName(), XWorkMapPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, Collection.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, ObjectProxy.class.getName(), ObjectProxyPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, HttpParameters.class.getName(), HttpParametersPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, Parameter.class.getName(), ParameterPropertyAccessor.class, Scope.SINGLETON)
.factory(MethodAccessor.class, Object.class.getName(), XWorkMethodAccessor.class, Scope.SINGLETON)
.factory(MethodAccessor.class, CompoundRoot.class.getName(), CompoundRootAccessor.class, Scope.SINGLETON)
.factory(MethodAccessor.class, Object.class.getName(), XWorkMethodAccessor.class, Scope.SINGLETON)
.factory(MethodAccessor.class, CompoundRoot.class.getName(), CompoundRootAccessor.class, Scope.SINGLETON)
.factory(TextParser.class, OgnlTextParser.class, Scope.SINGLETON)
.factory(TextParser.class, OgnlTextParser.class, Scope.SINGLETON)
.factory(NullHandler.class, Object.class.getName(), InstantiatingNullHandler.class, Scope.SINGLETON)
.factory(ActionValidatorManager.class, AnnotationActionValidatorManager.class, Scope.SINGLETON)
.factory(ActionValidatorManager.class, "no-annotations", DefaultActionValidatorManager.class, Scope.SINGLETON)
.factory(NullHandler.class, Object.class.getName(), InstantiatingNullHandler.class, Scope.SINGLETON)
.factory(ActionValidatorManager.class, AnnotationActionValidatorManager.class, Scope.SINGLETON)
.factory(ActionValidatorManager.class, "no-annotations", DefaultActionValidatorManager.class, Scope.SINGLETON)
.factory(TextProvider.class, "system", DefaultTextProvider.class, Scope.SINGLETON)
.factory(LocalizedTextProvider.class, StrutsLocalizedTextProvider.class, Scope.SINGLETON)
.factory(TextProviderFactory.class, StrutsTextProviderFactory.class, Scope.SINGLETON)
.factory(LocaleProviderFactory.class, DefaultLocaleProviderFactory.class, Scope.SINGLETON)
.factory(TextProvider.class, "system", DefaultTextProvider.class, Scope.SINGLETON)
.factory(LocalizedTextProvider.class, StrutsLocalizedTextProvider.class, Scope.SINGLETON)
.factory(TextProviderFactory.class, StrutsTextProviderFactory.class, Scope.SINGLETON)
.factory(LocaleProviderFactory.class, DefaultLocaleProviderFactory.class, Scope.SINGLETON)
.factory(ExpressionCacheFactory.class, DefaultOgnlExpressionCacheFactory.class, Scope.SINGLETON)
.factory(BeanInfoCacheFactory.class, DefaultOgnlBeanInfoCacheFactory.class, Scope.SINGLETON)
.factory(OgnlUtil.class, Scope.SINGLETON)
.factory(CollectionConverter.class, Scope.SINGLETON)
.factory(ArrayConverter.class, Scope.SINGLETON)
.factory(DateConverter.class, Scope.SINGLETON)
.factory(NumberConverter.class, Scope.SINGLETON)
.factory(StringConverter.class, Scope.SINGLETON)
.factory(ExpressionCacheFactory.class, DefaultOgnlExpressionCacheFactory.class, Scope.SINGLETON)
.factory(BeanInfoCacheFactory.class, DefaultOgnlBeanInfoCacheFactory.class, Scope.SINGLETON)
.factory(OgnlUtil.class, Scope.SINGLETON)
.factory(CollectionConverter.class, Scope.SINGLETON)
.factory(ArrayConverter.class, Scope.SINGLETON)
.factory(DateConverter.class, Scope.SINGLETON)
.factory(NumberConverter.class, Scope.SINGLETON)
.factory(StringConverter.class, Scope.SINGLETON)
.factory(ExcludedPatternsChecker.class, DefaultExcludedPatternsChecker.class, Scope.PROTOTYPE)
.factory(AcceptedPatternsChecker.class, DefaultAcceptedPatternsChecker.class, Scope.PROTOTYPE)
.factory(NotExcludedAcceptedPatternsChecker.class, DefaultNotExcludedAcceptedPatternsChecker.class
, Scope.SINGLETON)
.factory(ExcludedPatternsChecker.class, DefaultExcludedPatternsChecker.class, Scope.PROTOTYPE)
.factory(AcceptedPatternsChecker.class, DefaultAcceptedPatternsChecker.class, Scope.PROTOTYPE)
.factory(NotExcludedAcceptedPatternsChecker.class, DefaultNotExcludedAcceptedPatternsChecker.class
, Scope.SINGLETON)
.factory(ValueSubstitutor.class, EnvsValueSubstitutor.class, Scope.SINGLETON)
.factory(ValueSubstitutor.class, EnvsValueSubstitutor.class, Scope.SINGLETON)
.factory(DateFormatter.class, "simpleDateFormatter", SimpleDateFormatAdapter.class, Scope.SINGLETON)
.factory(DateFormatter.class, "dateTimeFormatter", DateTimeFormatterAdapter.class, Scope.SINGLETON)
.factory(QueryStringBuilder.class, StrutsQueryStringBuilder.class, Scope.SINGLETON)
.factory(QueryStringParser.class, StrutsQueryStringParser.class, Scope.SINGLETON)
.factory(UrlEncoder.class, StrutsUrlEncoder.class, Scope.SINGLETON)
.factory(UrlDecoder.class, StrutsUrlDecoder.class, Scope.SINGLETON)
;
props.setProperty(StrutsConstants.STRUTS_ENABLE_DYNAMIC_METHOD_INVOCATION, Boolean.FALSE.toString());
@@ -19,10 +19,26 @@ import com.opensymphony.xwork2.inject.util.ReferenceCache;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ReflectPermission;
import java.security.AccessControlException;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/**
* Default {@link Container} implementation.
@@ -39,11 +55,7 @@ class ContainerImpl implements Container {
this.factories = factories;
final Map<Class<?>, Set<String>> map = new HashMap<>();
for (Key<?> key : factories.keySet()) {
Set<String> names = map.get(key.getType());
if (names == null) {
names = new HashSet<>();
map.put(key.getType(), names);
}
Set<String> names = map.computeIfAbsent(key.getType(), k -> new HashSet<>());
names.add(key.getName());
}
@@ -63,20 +75,20 @@ class ContainerImpl implements Container {
* Field and method injectors.
*/
final Map<Class<?>, List<Injector>> injectors =
new ReferenceCache<Class<?>, List<Injector>>() {
@Override
protected List<Injector> create(Class<?> key) {
List<Injector> injectors = new ArrayList<>();
addInjectors(key, injectors);
return injectors;
}
};
new ReferenceCache<Class<?>, List<Injector>>() {
@Override
protected List<Injector> create(Class<?> key) {
List<Injector> injectors = new ArrayList<>();
addInjectors(key, injectors);
return injectors;
}
};
/**
* Recursively adds injectors for fields and methods from the given class to the given list. Injects parent classes
* before sub classes.
*/
void addInjectors(Class clazz, List<Injector> injectors) {
void addInjectors(Class<?> clazz, List<Injector> injectors) {
if (clazz == Object.class) {
return;
}
@@ -97,38 +109,24 @@ class ContainerImpl implements Container {
addInjectorsForMethods(clazz.getDeclaredMethods(), true, injectors);
}
callInContext(new ContextualCallable<Void>() {
public Void call(InternalContext context) {
for (Injector injector : injectors) {
injector.inject(context, null);
}
return null;
callInContext((ContextualCallable<Void>) context -> {
for (Injector injector : injectors) {
injector.inject(context, null);
}
return null;
});
}
void addInjectorsForMethods(Method[] methods, boolean statics, List<Injector> injectors) {
addInjectorsForMembers(Arrays.asList(methods), statics, injectors,
new InjectorFactory<Method>() {
public Injector create(ContainerImpl container, Method method,
String name) throws MissingDependencyException {
return new MethodInjector(container, method, name);
}
});
addInjectorsForMembers(Arrays.asList(methods), statics, injectors, MethodInjector::new);
}
void addInjectorsForFields(Field[] fields, boolean statics, List<Injector> injectors) {
addInjectorsForMembers(Arrays.asList(fields), statics, injectors,
new InjectorFactory<Field>() {
public Injector create(ContainerImpl container, Field field,
String name) throws MissingDependencyException {
return new FieldInjector(container, field, name);
}
});
addInjectorsForMembers(Arrays.asList(fields), statics, injectors, FieldInjector::new);
}
<M extends Member & AnnotatedElement> void addInjectorsForMembers(
List<M> members, boolean statics, List<Injector> injectors, InjectorFactory<M> injectorFactory) {
List<M> members, boolean statics, List<Injector> injectors, InjectorFactory<M> injectorFactory) {
for (M member : members) {
if (isStatic(member) == statics) {
Inject inject = member.getAnnotation(Inject.class);
@@ -148,12 +146,12 @@ class ContainerImpl implements Container {
interface InjectorFactory<M extends Member & AnnotatedElement> {
Injector create(ContainerImpl container, M member, String name)
throws MissingDependencyException;
throws MissingDependencyException;
}
/**
* Determines if a given {@link Member} is static or not.
*
*
* @param member checked for the static modifier.
* @return true if member is static, false otherwise.
*/
@@ -163,13 +161,13 @@ class ContainerImpl implements Container {
/**
* Determines if a given {@link Member} is considered to be public for reflection usage or not.
*
*
* @param member checked to see if it is public for reflection usage.
* @return true if member is public for reflection usage, false otherwise.
*/
private static boolean isPublicForReflection(Member member) {
return Modifier.isPublic(member.getModifiers()) &&
Modifier.isPublic(member.getDeclaringClass().getModifiers());
Modifier.isPublic(member.getDeclaringClass().getModifiers());
}
static class FieldInjector implements Injector {
@@ -179,7 +177,7 @@ class ContainerImpl implements Container {
final ExternalContext<?> externalContext;
public FieldInjector(ContainerImpl container, Field field, String name)
throws MissingDependencyException {
throws MissingDependencyException {
this.field = field;
if (!isPublicForReflection(field) && !field.isAccessible()) {
SecurityManager sm = System.getSecurityManager();
@@ -190,7 +188,7 @@ class ContainerImpl implements Container {
field.setAccessible(true);
} catch (AccessControlException e) {
throw new DependencyException("Security manager in use, could not access field: "
+ field.getDeclaringClass().getName() + "(" + field.getName() + ")", e);
+ field.getDeclaringClass().getName() + "(" + field.getName() + ")", e);
}
}
@@ -225,8 +223,12 @@ class ContainerImpl implements Container {
* @param parameterTypes parameter types
* @return injections
*/
<M extends AccessibleObject & Member> ParameterInjector<?>[]
getParametersInjectors(M member, Annotation[][] annotations, Class[] parameterTypes, String defaultName) throws MissingDependencyException {
<M extends AccessibleObject & Member> ParameterInjector<?>[] getParametersInjectors(
M member,
Annotation[][] annotations,
Class<?>[] parameterTypes,
String defaultName
) throws MissingDependencyException {
final List<ParameterInjector<?>> parameterInjectors = new ArrayList<>();
final Iterator<Annotation[]> annotationsIterator = Arrays.asList(annotations).iterator();
@@ -247,12 +249,11 @@ class ContainerImpl implements Container {
}
final ExternalContext<T> externalContext = ExternalContext.newInstance(member, key, this);
return new ParameterInjector<T>(externalContext, factory);
return new ParameterInjector<>(externalContext, factory);
}
@SuppressWarnings("unchecked")
private ParameterInjector<?>[] toArray(List<ParameterInjector<?>> parameterInjections) {
return parameterInjections.toArray(new ParameterInjector[parameterInjections.size()]);
return parameterInjections.toArray(new ParameterInjector[0]);
}
/**
@@ -261,7 +262,7 @@ class ContainerImpl implements Container {
Inject findInject(Annotation[] annotations) {
for (Annotation annotation : annotations) {
if (annotation.annotationType() == Inject.class) {
return Inject.class.cast(annotation);
return (Inject) annotation;
}
}
return null;
@@ -283,7 +284,7 @@ class ContainerImpl implements Container {
method.setAccessible(true);
} catch (AccessControlException e) {
throw new DependencyException("Security manager in use, could not access method: "
+ name + "(" + method.getName() + ")", e);
+ name + "(" + method.getName() + ")", e);
}
}
@@ -292,7 +293,7 @@ class ContainerImpl implements Container {
throw new DependencyException(method + " has no parameters to inject.");
}
parameterInjectors = container.getParametersInjectors(
method, method.getParameterAnnotations(), parameterTypes, name);
method, method.getParameterAnnotations(), parameterTypes, name);
}
@Override
@@ -305,14 +306,12 @@ class ContainerImpl implements Container {
}
}
Map<Class<?>, ConstructorInjector> constructors =
new ReferenceCache<Class<?>, ConstructorInjector>() {
@Override
@SuppressWarnings("unchecked")
protected ConstructorInjector<?> create(Class<?> implementation) {
return new ConstructorInjector(ContainerImpl.this, implementation);
}
};
Map<Class<?>, ConstructorInjector<?>> constructors = new ReferenceCache<Class<?>, ConstructorInjector<?>>() {
@Override
protected ConstructorInjector<?> create(Class<?> implementation) {
return new ConstructorInjector<>(ContainerImpl.this, implementation);
}
};
static class ConstructorInjector<T> {
@@ -334,7 +333,7 @@ class ContainerImpl implements Container {
constructor.setAccessible(true);
} catch (AccessControlException e) {
throw new DependencyException("Security manager in use, could not access constructor: "
+ implementation.getName() + "(" + constructor.getName() + ")", e);
+ implementation.getName() + "(" + constructor.getName() + ")", e);
}
}
@@ -359,14 +358,14 @@ class ContainerImpl implements Container {
}
ParameterInjector<?>[] constructParameterInjector(
Inject inject, ContainerImpl container, Constructor<T> constructor) throws MissingDependencyException {
Inject inject, ContainerImpl container, Constructor<T> constructor) throws MissingDependencyException {
return constructor.getParameterTypes().length == 0
? null // default constructor.
: container.getParametersInjectors(
constructor,
constructor.getParameterAnnotations(),
constructor.getParameterTypes(),
inject.value()
? null // default constructor.
: container.getParametersInjectors(
constructor,
constructor.getParameterAnnotations(),
constructor.getParameterTypes(),
inject.value()
);
}
@@ -378,7 +377,7 @@ class ContainerImpl implements Container {
if (constructor.getAnnotation(Inject.class) != null) {
if (found != null) {
throw new DependencyException("More than one constructor annotated"
+ " with @Inject found in " + implementation + ".");
+ " with @Inject found in " + implementation + ".");
}
found = constructor;
}
@@ -466,7 +465,7 @@ class ContainerImpl implements Container {
}
}
private static Object[] getParameters(Member member, InternalContext context, ParameterInjector[] parameterInjectors) {
private static Object[] getParameters(Member member, InternalContext context, ParameterInjector<?>[] parameterInjectors) {
if (parameterInjectors == null) {
return null;
}
@@ -494,13 +493,12 @@ class ContainerImpl implements Container {
}
}
@SuppressWarnings("unchecked")
<T> T getInstance(Class<T> type, String name, InternalContext context) {
final ExternalContext<?> previous = context.getExternalContext();
final Key<T> key = Key.newInstance(type, name);
context.setExternalContext(ExternalContext.newInstance(null, key, this));
try {
final InternalFactory o = getFactory(key);
final InternalFactory<? extends T> o = getFactory(key);
if (o != null) {
return getFactory(key).create(context);
} else {
@@ -517,39 +515,25 @@ class ContainerImpl implements Container {
@Override
public void inject(final Object o) {
callInContext(new ContextualCallable<Void>() {
public Void call(InternalContext context) {
inject(o, context);
return null;
}
callInContext((ContextualCallable<Void>) context -> {
inject(o, context);
return null;
});
}
@Override
public <T> T inject(final Class<T> implementation) {
return callInContext(new ContextualCallable<T>() {
public T call(InternalContext context) {
return inject(implementation, context);
}
});
return callInContext(context -> inject(implementation, context));
}
@Override
public <T> T getInstance(final Class<T> type, final String name) {
return callInContext(new ContextualCallable<T>() {
public T call(InternalContext context) {
return getInstance(type, name, context);
}
});
return callInContext(context -> getInstance(type, name, context));
}
@Override
public <T> T getInstance(final Class<T> type) {
return callInContext(new ContextualCallable<T>() {
public T call(InternalContext context) {
return getInstance(type, context);
}
});
return callInContext(context -> getInstance(type, context));
}
@Override
@@ -561,12 +545,7 @@ class ContainerImpl implements Container {
return names;
}
ThreadLocal<Object[]> localContext = new ThreadLocal<Object[]>() {
@Override
protected Object[] initialValue() {
return new Object[1];
}
};
ThreadLocal<Object[]> localContext = ThreadLocal.withInitial(() -> new Object[1]);
/**
* Looks up thread local context. Creates (and removes) a new context if necessary.
@@ -598,7 +577,7 @@ class ContainerImpl implements Container {
*/
@SuppressWarnings("unchecked")
<T> ConstructorInjector<T> getConstructor(Class<T> implementation) {
return constructors.get(implementation);
return (ConstructorInjector<T>) constructors.get(implementation);
}
final ThreadLocal<Object> localScopeStrategy = new ThreadLocal<>();
@@ -23,23 +23,39 @@ import com.opensymphony.xwork2.ActionInvocation;
/**
* Provides default implementations of optional lifecycle methods
*/
public abstract class AbstractInterceptor implements Interceptor {
public abstract class AbstractInterceptor implements ConditionalInterceptor {
private boolean disabled;
/**
* Does nothing
*/
public void init() {
}
/**
* Does nothing
*/
public void destroy() {
}
/**
* Override to handle interception
*/
public abstract String intercept(ActionInvocation invocation) throws Exception;
/**
* Allows to skip executing a given interceptor, just define {@code <param name="disabled">true</param>}
* or use other way to override interceptor's parameters, see
* <a href="https://struts.apache.org/core-developers/interceptors#interceptor-parameter-overriding">docs</a>.
* @param disable if set to true, execution of a given interceptor will be skipped.
*/
public void setDisabled(String disable) {
this.disabled = Boolean.parseBoolean(disable);
}
@Override
public boolean shouldIntercept(ActionInvocation invocation) {
return !this.disabled;
}
}
@@ -0,0 +1,39 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package com.opensymphony.xwork2.interceptor;
import com.opensymphony.xwork2.ActionInvocation;
/**
* A marking interface, when implemented allows to conditionally execute a given interceptor
* within the current action invocation.
*
* @since Struts 6.1.1
*/
public interface ConditionalInterceptor extends Interceptor {
/**
* Determines if a given interceptor should be executed in the current processing of action invocation.
*
* @param invocation current {@link ActionInvocation} to determine if the interceptor should be executed
* @return true if the given interceptor should be included in the current action invocation
* @since 6.1.1
*/
boolean shouldIntercept(ActionInvocation invocation);
}
@@ -0,0 +1,37 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package com.opensymphony.xwork2.interceptor;
/**
* This interface is implemented by actions that want to declare acceptable parameter value. Works in conjunction with {@link
* ParametersInterceptor}. For example, actions may want to create a white list of parameter values they will accept or a
* blacklist of parameter values they will reject to prevent clients from setting other unexpected (and possibly dangerous)
* parameter values.
*/
public interface ParameterValueAware {
/**
* Tests if the the action will accept the parameter with the given value.
*
* @param parameterValue the parameter value
* @return <tt>true</tt> if accepted, <tt>false</tt> otherwise
*/
boolean acceptableParameterValue(String parameterValue);
}
@@ -26,6 +26,7 @@ import com.opensymphony.xwork2.security.AcceptedPatternsChecker;
import com.opensymphony.xwork2.security.ExcludedPatternsChecker;
import com.opensymphony.xwork2.util.ClearableValueStack;
import com.opensymphony.xwork2.util.MemberAccessValueStack;
import com.opensymphony.xwork2.util.TextParseUtil;
import com.opensymphony.xwork2.util.ValueStack;
import com.opensymphony.xwork2.util.ValueStackFactory;
import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
@@ -33,12 +34,15 @@ import org.apache.commons.lang3.BooleanUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.StrutsConstants;
import org.apache.struts2.dispatcher.Parameter;
import org.apache.struts2.dispatcher.HttpParameters;
import org.apache.struts2.dispatcher.Parameter;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.regex.Pattern;
@@ -62,6 +66,8 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
private ValueStackFactory valueStackFactory;
private ExcludedPatternsChecker excludedPatterns;
private AcceptedPatternsChecker acceptedPatterns;
private Set<Pattern> excludedValuePatterns = null;
private Set<Pattern> acceptedValuePatterns = null;
@Inject
public void setValueStackFactory(ValueStackFactory valueStackFactory) {
@@ -183,8 +189,10 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
for (Map.Entry<String, Parameter> entry : params.entrySet()) {
String parameterName = entry.getKey();
boolean isAcceptableParameter = isAcceptableParameter(parameterName, action);
isAcceptableParameter &= isAcceptableParameterValue(entry.getValue(), action);
if (isAcceptableParameter(parameterName, action)) {
if (isAcceptableParameter) {
acceptableParameters.put(parameterName, entry.getValue());
}
}
@@ -237,8 +245,8 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
if (action instanceof TextProvider) {
TextProvider tp = (TextProvider) action;
developerNotification = tp.getText("devmode.notification",
"Developer Notification:\n{0}",
new String[]{developerNotification}
"Developer Notification:\n{0}",
new String[]{developerNotification}
);
}
@@ -264,6 +272,23 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
return acceptableName(name) && (parameterNameAware == null || parameterNameAware.acceptableParameterName(name));
}
/**
* Checks if parameter value can be accepted or thrown away
*
* @param param the parameter
* @param action current action
* @return true if parameter is accepted
*/
protected boolean isAcceptableParameterValue(Parameter param, Object action) {
ParameterValueAware parameterValueAware = (action instanceof ParameterValueAware) ? (ParameterValueAware) action : null;
boolean acceptableParamValue = (parameterValueAware == null || parameterValueAware.acceptableParameterValue(param.getValue()));
if (hasParamValuesToExclude() || hasParamValuesToAccept()) {
// Additional validations to process
acceptableParamValue &= acceptableValue(param.getName(), param.getValue());
}
return acceptableParamValue;
}
/**
* Gets an instance of the comparator to use for the ordered sorting. Override this
* method to customize the ordering of the parameters as they are set to the
@@ -291,6 +316,15 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
return logEntry.toString();
}
/**
* Validates the name passed is:
* * Within the max length of a parameter name
* * Is not excluded
* * Is accepted
*
* @param name - Name to check
* @return true if accepted
*/
protected boolean acceptableName(String name) {
if (isIgnoredDMI(name)) {
LOG.trace("DMI is enabled, ignoring DMI method: {}", name);
@@ -311,14 +345,37 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
}
}
/**
* Validates:
* * Value is null/blank
* * Value is not excluded
* * Value is accepted
*
* @param name - Param name (for logging)
* @param value - value to check
* @return true if accepted
*/
protected boolean acceptableValue(String name, String value) {
boolean accepted = (value == null || value.isEmpty() || (!isParamValueExcluded(value) && isParamValueAccepted(value)));
if (!accepted) {
String message = "Value [{}] of parameter [{}] was not accepted and will be dropped!";
if (devMode) {
LOG.warn(message, value, name);
} else {
LOG.debug(message, value, name);
}
}
return accepted;
}
protected boolean isWithinLengthLimit(String name) {
boolean matchLength = name.length() <= paramNameMaxLength;
if (!matchLength) {
if (devMode) { // warn only when in devMode
LOG.warn("Parameter [{}] is too long, allowed length is [{}]. Use Interceptor Parameter Overriding " +
"to override the limit, see more at\n" +
"https://struts.apache.org/core-developers/interceptors.html#interceptor-parameter-overriding",
name, paramNameMaxLength);
"to override the limit, see more at\n" +
"https://struts.apache.org/core-developers/interceptors.html#interceptor-parameter-overriding",
name, paramNameMaxLength);
} else {
LOG.warn("Parameter [{}] is too long, allowed length is [{}]", name, paramNameMaxLength);
}
@@ -332,8 +389,8 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
return true;
} else if (devMode) { // warn only when in devMode
LOG.warn("Parameter [{}] didn't match accepted pattern [{}]! See Accepted / Excluded patterns at\n" +
"https://struts.apache.org/security/#accepted--excluded-patterns",
paramName, result.getAcceptedPattern());
"https://struts.apache.org/security/#accepted--excluded-patterns",
paramName, result.getAcceptedPattern());
} else {
LOG.debug("Parameter [{}] didn't match accepted pattern [{}]!", paramName, result.getAcceptedPattern());
}
@@ -345,8 +402,8 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
if (result.isExcluded()) {
if (devMode) { // warn only when in devMode
LOG.warn("Parameter [{}] matches excluded pattern [{}]! See Accepted / Excluded patterns at\n" +
"https://struts.apache.org/security/#accepted--excluded-patterns",
paramName, result.getExcludedPattern());
"https://struts.apache.org/security/#accepted--excluded-patterns",
paramName, result.getExcludedPattern());
} else {
LOG.debug("Parameter [{}] matches excluded pattern [{}]!", paramName, result.getExcludedPattern());
}
@@ -355,6 +412,54 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
return false;
}
protected boolean isParamValueExcluded(String value) {
if (!hasParamValuesToExclude()) {
LOG.debug("'excludedValuePatterns' not defined so anything is allowed");
return false;
}
for (Pattern excludedValuePattern : excludedValuePatterns) {
if (excludedValuePattern.matcher(value).matches()) {
if (devMode) {
LOG.warn("Parameter value [{}] matches excluded pattern [{}]! See Accepting/Excluding parameter values at\n" +
"https://struts.apache.org/core-developers/parameters-interceptor#excluding-parameter-values",
value, excludedValuePatterns);
} else {
LOG.debug("Parameter value [{}] matches excluded pattern [{}]", value, excludedValuePattern);
}
return true;
}
}
return false;
}
protected boolean isParamValueAccepted(String value) {
if (!hasParamValuesToAccept()) {
LOG.debug("'acceptedValuePatterns' not defined so anything is allowed");
return true;
}
for (Pattern acceptedValuePattern : acceptedValuePatterns) {
if (acceptedValuePattern.matcher(value).matches()) {
return true;
}
}
if (devMode) {
LOG.warn("Parameter value [{}] didn't match accepted pattern [{}]! See Accepting/Excluding parameter values at\n" +
"https://struts.apache.org/core-developers/parameters-interceptor#excluding-parameter-values",
value, acceptedValuePatterns);
} else {
LOG.debug("Parameter value [{}] was not accepted!", value);
}
return false;
}
private boolean hasParamValuesToExclude() {
return excludedValuePatterns != null && excludedValuePatterns.size() > 0;
}
private boolean hasParamValuesToAccept() {
return acceptedValuePatterns != null && acceptedValuePatterns.size() > 0;
}
/**
* Whether to order the parameters or not
*
@@ -397,4 +502,53 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
excludedPatterns.setExcludedPatterns(commaDelim);
}
/**
* Sets a comma-delimited list of regular expressions to match
* values of parameters that should be accepted and included in the parameter map.
*
* @param commaDelimitedPatterns A comma-delimited set of regular expressions
*/
public void setAcceptedValuePatterns(String commaDelimitedPatterns) {
Set<String> patterns = TextParseUtil.commaDelimitedStringToSet(commaDelimitedPatterns);
if (acceptedValuePatterns == null) {
// Limit unwanted log entries (for 1st call, acceptedValuePatterns null)
LOG.debug("Sets accepted value patterns to [{}], note this may impact the safety of your application!", patterns);
} else {
LOG.warn("Replacing accepted patterns [{}] with [{}], be aware that this may impact safety of your application!",
acceptedValuePatterns, patterns);
}
acceptedValuePatterns = new HashSet<>(patterns.size());
try {
for (String pattern : patterns) {
acceptedValuePatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
}
} finally {
acceptedValuePatterns = Collections.unmodifiableSet(acceptedValuePatterns);
}
}
/**
* Sets a comma-delimited list of regular expressions to match
* values of parameters that should be removed from the parameter map.
*
* @param commaDelimitedPatterns A comma-delimited set of regular expressions
*/
public void setExcludedValuePatterns(String commaDelimitedPatterns) {
Set<String> patterns = TextParseUtil.commaDelimitedStringToSet(commaDelimitedPatterns);
if (excludedValuePatterns == null) {
// Limit unwanted log entries (for 1st call, excludedValuePatterns null)
LOG.debug("Setting excluded value patterns to [{}]", patterns);
} else {
LOG.warn("Replacing excluded value patterns [{}] with [{}], be aware that this may impact safety of your application!",
excludedValuePatterns, patterns);
}
excludedValuePatterns = new HashSet<>(patterns.size());
try {
for (String pattern : patterns) {
excludedValuePatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
}
} finally {
excludedValuePatterns = Collections.unmodifiableSet(excludedValuePatterns);
}
}
}
@@ -268,10 +268,10 @@ public class OgnlUtil {
try {
if (maxLength == null || maxLength.isEmpty()) {
Ognl.applyExpressionMaxLength(null);
LOG.info("OGNL Expression Max Length disabled.");
LOG.warn("OGNL Expression Max Length disabled.");
} else {
Ognl.applyExpressionMaxLength(Integer.parseInt(maxLength));
LOG.info("OGNL Expression Max Length enabled with {}.", maxLength);
LOG.debug("OGNL Expression Max Length enabled with {}.", maxLength);
}
} catch (Exception ex) {
LOG.error("Unable to set OGNL Expression Max Length {}.", maxLength); // Help configuration debugging.
@@ -109,6 +109,11 @@ public class XWorkListPropertyAccessor extends ListPropertyAccessor {
if (listSize <= index) {
Object result;
if (index > autoGrowCollectionLimit) {
throw new OgnlException("Error auto growing collection size to " + index + " which limited to "
+ autoGrowCollectionLimit);
}
for (int i = listSize; i < index; i++) {
list.add(null);
}
@@ -28,9 +28,17 @@ import org.apache.struts2.StrutsException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.*;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.TransformerFactory;
@@ -48,28 +56,24 @@ import java.util.Map;
public class DomHelper {
private static final Logger LOG = LogManager.getLogger(DomHelper.class);
public static final String XMLNS_URI = "http://www.w3.org/2000/xmlns/";
public static Location getLocationObject(Element element) {
return LocationAttributes.getLocation(element);
}
/**
* Creates a W3C Document that remembers the location of each element in
* the source file. The location of element nodes can then be retrieved
* using the {@link #getLocationObject(Element)} method.
*
* @param inputSource the inputSource to read the document from
*
* @return the W3C Document
*/
public static Document parse(InputSource inputSource) {
return parse(inputSource, null);
}
/**
* Creates a W3C Document that remembers the location of each element in
* the source file. The location of element nodes can then be retrieved
@@ -77,17 +81,16 @@ public class DomHelper {
*
* @param inputSource the inputSource to read the document from
* @param dtdMappings a map of DTD names and public ids
*
* @return the W3C Document
*/
public static Document parse(InputSource inputSource, Map<String, String> dtdMappings) {
SAXParserFactory factory = null;
String parserProp = System.getProperty("xwork.saxParserFactory");
if (parserProp != null) {
try {
ObjectFactory objectFactory = ActionContext.getContext().getContainer().getInstance(ObjectFactory.class);
Class clazz = objectFactory.getClassInstance(parserProp);
Class<?> clazz = objectFactory.getClassInstance(parserProp);
factory = (SAXParserFactory) clazz.newInstance();
} catch (Exception e) {
LOG.error("Unable to load saxParserFactory set by system property 'xwork.saxParserFactory': {}", parserProp, e);
@@ -98,6 +101,13 @@ public class DomHelper {
factory = SAXParserFactory.newInstance();
}
try {
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
} catch (ParserConfigurationException | SAXNotRecognizedException | SAXNotSupportedException e) {
throw new StrutsException("Unable to disable resolving external entities!", e);
}
factory.setValidating((dtdMappings != null));
factory.setNamespaceAware(true);
@@ -107,22 +117,22 @@ public class DomHelper {
} catch (Exception ex) {
throw new StrutsException("Unable to create SAX parser", ex);
}
DOMBuilder builder = new DOMBuilder();
// Enhance the sax stream with location information
ContentHandler locationHandler = new LocationAttributes.Pipe(builder);
try {
parser.parse(inputSource, new StartHandler(locationHandler, dtdMappings));
} catch (Exception ex) {
throw new StrutsException(ex);
}
return builder.getDocument();
}
/**
* The <code>DOMBuilder</code> is a utility class that will generate a W3C
* DOM Document from SAX events.
@@ -130,27 +140,35 @@ public class DomHelper {
* @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
*/
static public class DOMBuilder implements ContentHandler {
/** The default transformer factory shared by all instances */
/**
* The default transformer factory shared by all instances
*/
protected static SAXTransformerFactory FACTORY;
/** The transformer factory */
/**
* The transformer factory
*/
protected SAXTransformerFactory factory;
/** The result */
/**
* The result
*/
protected DOMResult result;
/** The parentNode */
/**
* The parentNode
*/
protected Node parentNode;
protected ContentHandler nextHandler;
static {
String parserProp = System.getProperty("xwork.saxTransformerFactory");
if (parserProp != null) {
try {
ObjectFactory objectFactory = ActionContext.getContext().getContainer().getInstance(ObjectFactory.class);
Class clazz = objectFactory.getClassInstance(parserProp);
Class<?> clazz = objectFactory.getClassInstance(parserProp);
FACTORY = (SAXTransformerFactory) clazz.newInstance();
} catch (Exception e) {
LOG.error("Unable to load SAXTransformerFactory set by system property 'xwork.saxTransformerFactory': {}", parserProp, e);
@@ -158,7 +176,7 @@ public class DomHelper {
}
if (FACTORY == null) {
FACTORY = (SAXTransformerFactory) TransformerFactory.newInstance();
FACTORY = (SAXTransformerFactory) TransformerFactory.newInstance();
}
}
@@ -168,15 +186,16 @@ public class DomHelper {
public DOMBuilder() {
this((Node) null);
}
/**
* Construct a new instance of this DOMBuilder.
*
* @param factory the SAX transformer factory
*/
public DOMBuilder(SAXTransformerFactory factory) {
this(factory, null);
}
/**
* Constructs a new instance that appends nodes to the given parent node.
*
@@ -185,19 +204,19 @@ public class DomHelper {
public DOMBuilder(Node parentNode) {
this(null, parentNode);
}
/**
* Construct a new instance of this DOMBuilder.
*
* @param factory the SAX transformer factory
* @param factory the SAX transformer factory
* @param parentNode the parent node
*/
public DOMBuilder(SAXTransformerFactory factory, Node parentNode) {
this.factory = factory == null? FACTORY: factory;
this.factory = factory == null ? FACTORY : factory;
this.parentNode = parentNode;
setup();
}
/**
* Setup this instance transformer and result objects.
*/
@@ -215,7 +234,7 @@ public class DomHelper {
throw new StrutsException("Fatal-Error: Unable to get transformer handler", local);
}
}
/**
* Return the newly built Document.
*
@@ -230,60 +249,61 @@ public class DomHelper {
return this.result.getNode().getOwnerDocument();
}
}
public void setDocumentLocator(Locator locator) {
nextHandler.setDocumentLocator(locator);
}
public void startDocument() throws SAXException {
nextHandler.startDocument();
}
public void endDocument() throws SAXException {
nextHandler.endDocument();
}
public void startElement(String uri, String loc, String raw, Attributes attrs) throws SAXException {
nextHandler.startElement(uri, loc, raw, attrs);
}
public void endElement(String arg0, String arg1, String arg2) throws SAXException {
nextHandler.endElement(arg0, arg1, arg2);
}
public void startPrefixMapping(String arg0, String arg1) throws SAXException {
nextHandler.startPrefixMapping(arg0, arg1);
}
public void endPrefixMapping(String arg0) throws SAXException {
nextHandler.endPrefixMapping(arg0);
}
public void characters(char[] arg0, int arg1, int arg2) throws SAXException {
nextHandler.characters(arg0, arg1, arg2);
}
public void ignorableWhitespace(char[] arg0, int arg1, int arg2) throws SAXException {
nextHandler.ignorableWhitespace(arg0, arg1, arg2);
}
public void processingInstruction(String arg0, String arg1) throws SAXException {
nextHandler.processingInstruction(arg0, arg1);
}
public void skippedEntity(String arg0) throws SAXException {
nextHandler.skippedEntity(arg0);
}
}
public static class StartHandler extends DefaultHandler {
private ContentHandler nextHandler;
private Map<String, String> dtdMappings;
private final ContentHandler nextHandler;
private final Map<String, String> dtdMappings;
/**
* Create a filter that is chained to another handler.
* @param next the next handler in the chain.
*
* @param next the next handler in the chain.
* @param dtdMappings map of DTD mappings
*/
public StartHandler(ContentHandler next, Map<String, String> dtdMappings) {
@@ -295,12 +315,12 @@ public class DomHelper {
public void setDocumentLocator(Locator locator) {
nextHandler.setDocumentLocator(locator);
}
@Override
public void startDocument() throws SAXException {
nextHandler.startDocument();
}
@Override
public void endDocument() throws SAXException {
nextHandler.endDocument();
@@ -345,7 +365,7 @@ public class DomHelper {
public void skippedEntity(String arg0) throws SAXException {
nextHandler.skippedEntity(arg0);
}
@Override
public InputSource resolveEntity(String publicId, String systemId) {
if (dtdMappings != null && dtdMappings.containsKey(publicId)) {
@@ -356,7 +376,7 @@ public class DomHelper {
}
return null;
}
@Override
public void warning(SAXParseException exception) {
}
@@ -18,6 +18,8 @@
*/
package com.opensymphony.xwork2.util;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -57,7 +59,7 @@ import java.util.regex.Pattern;
* </table>
*
* <p>
* Excaping hasn't been implemented since the intended use of these patterns will be in matching URLs.
* Escaping hasn't been implemented since the intended use of these patterns will be in matching URLs.
* </p>
*
* @since 2.1
@@ -75,38 +77,47 @@ public class NamedVariablePatternMatcher implements PatternMatcher<NamedVariable
* @return The compiled pattern, null if the pattern was null or empty
*/
public CompiledPattern compilePattern(String data) {
StringBuilder regex = new StringBuilder();
if (data != null && data.length() > 0) {
List<String> varNames = new ArrayList<>();
StringBuilder varName = null;
for (int x=0; x<data.length(); x++) {
char c = data.charAt(x);
switch (c) {
case '{' : varName = new StringBuilder(); break;
case '}' : if (varName == null) {
throw new IllegalArgumentException("Mismatched braces in pattern");
}
varNames.add(varName.toString());
regex.append("([^/]+)");
varName = null;
break;
default : if (varName == null) {
regex.append(c);
} else {
varName.append(c);
}
}
}
return new CompiledPattern(Pattern.compile(regex.toString()), varNames);
if (StringUtils.isEmpty(data)) {
return null;
}
return null;
int len = data.length();
StringBuilder regex = new StringBuilder();
List<String> varNames = new ArrayList<>();
int s = 0;
while (s < len) {
int e = data.indexOf('{', s);
if (e < 0 && data.indexOf('}') > -1) {
throw new IllegalArgumentException("Missing openning '{' in [" + data + "]!");
}
if (e < 0) {
regex.append(Pattern.quote(data.substring(s)));
break;
}
if (e > s) {
regex.append(Pattern.quote(data.substring(s, e)));
}
s = e + 1;
e = data.indexOf('}', s);
if (e < 0) {
return null;
}
String varName = data.substring(s, e);
if (StringUtils.isEmpty(varName)) {
throw new IllegalArgumentException("Missing variable name in [" + data + "]!");
}
varNames.add(varName);
regex.append("([^/]+)");
s = e + 1;
}
return new CompiledPattern(Pattern.compile(regex.toString()), varNames);
}
/**
* Tries to process the data against the compiled expression. If successful, the map will contain
* the matched data, using the specified variable names in the original pattern.
*
* @param map The map of variables
* @param map The map of variables
* @param data The data to match
* @param expr The compiled pattern
* @return True if matched, false if not matched, the data was null, or the data was an empty string
@@ -116,8 +127,8 @@ public class NamedVariablePatternMatcher implements PatternMatcher<NamedVariable
if (data != null && data.length() > 0) {
Matcher matcher = expr.getPattern().matcher(data);
if (matcher.matches()) {
for (int x=0; x<expr.getVariableNames().size(); x++) {
map.put(expr.getVariableNames().get(x), matcher.group(x+1));
for (int x = 0; x < expr.getVariableNames().size(); x++) {
map.put(expr.getVariableNames().get(x), matcher.group(x + 1));
}
return true;
}
@@ -129,8 +140,8 @@ public class NamedVariablePatternMatcher implements PatternMatcher<NamedVariable
* Stores the compiled pattern and the variable names matches will correspond to.
*/
public static class CompiledPattern {
private Pattern pattern;
private List<String> variableNames;
private final Pattern pattern;
private final List<String> variableNames;
public CompiledPattern(Pattern pattern, List<String> variableNames) {
@@ -222,7 +222,7 @@ import com.opensymphony.xwork2.util.ValueStack;
* &lt;!-- START SNIPPET: exShortCircuitingValidators --&gt;
* &lt;!DOCTYPE validators PUBLIC
* &quot;-//Apache Struts//XWork Validator 1.0.3//EN&quot;
* &quot;http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd&quot;&gt;
* &quot;https://struts.apache.org/dtds/xwork-validator-1.0.3.dtd&quot;&gt;
* &lt;validators&gt;
* &lt;!-- Field Validators for email field --&gt;
* &lt;field name=&quot;email&quot;&gt;
@@ -115,7 +115,7 @@ package com.opensymphony.xwork2.validator;
* <pre>
* <!-- START SNIPPET: exValidationRules1 -->
* &lt;!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator 1.0.3//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd"&gt;
"https://struts.apache.org/dtds/xwork-validator-1.0.3.dtd"&gt;
* &lt;validators&gt;
* &lt;field name="bar"&gt;
* &lt;field-validator type="required"&gt;
@@ -56,6 +56,9 @@ public final class StrutsConstants {
/** Comma separated list of patterns (java.util.regex.Pattern) to be excluded from Struts2-processing */
public static final String STRUTS_ACTION_EXCLUDE_PATTERN = "struts.action.excludePattern";
/** A custom separator used to split list of patterns (java.util.regex.Pattern) to be excluded from Struts2-processing */
public static final String STRUTS_ACTION_EXCLUDE_PATTERN_SEPARATOR = "struts.action.excludePattern.separator";
/** Whether to use the response encoding (JSP page encoding) for s:include tag processing (false - use STRUTS_I18N_ENCODING - by default) */
public static final String STRUTS_TAG_INCLUDETAG_USERESPONSEENCODING = "struts.tag.includetag.useResponseEncoding";
@@ -139,6 +142,12 @@ public final class StrutsConstants {
/** The maximize size of a multipart request (file upload) */
public static final String STRUTS_MULTIPART_MAXSIZE = "struts.multipart.maxSize";
/** The maximized number of files allowed to upload */
public static final String STRUTS_MULTIPART_MAXFILES = "struts.multipart.maxFiles";
/** The maximum length of a string parameter in a multipart request. */
public static final String STRUTS_MULTIPART_MAX_STRING_LENGTH = "struts.multipart.maxStringLength";
/** The directory to use for storing uploaded files */
public static final String STRUTS_MULTIPART_SAVEDIR = "struts.multipart.saveDir";
@@ -376,7 +385,7 @@ public final class StrutsConstants {
public static final String STRUTS_CONVERTER_FILE_PROCESSOR = "struts.converter.file.processor";
public static final String STRUTS_CONVERTER_ANNOTATION_PROCESSOR = "struts.converter.annotation.processor";
public static final String STRUTS_CONVERTER_CREATOR = "struts.converter.creator";
public static final String STRUTS_CONVERTER_HOLDER = "struts..converter.holder";
public static final String STRUTS_CONVERTER_HOLDER = "struts.converter.holder";
public static final String STRUTS_EXPRESSION_PARSER = "struts.expression.parser";
@@ -398,7 +407,11 @@ public final class StrutsConstants {
/** Enables action: prefix */
public static final String STRUTS_MAPPER_ACTION_PREFIX_ENABLED = "struts.mapper.action.prefix.enabled";
/** Enables access to actions in other namespaces than current with action: prefix */
/**
* Enables access to actions in other namespaces than current with action: prefix
* @deprecated it will be removed soon, please refactor your application
*/
@Deprecated
public static final String STRUTS_MAPPER_ACTION_PREFIX_CROSSNAMESPACES = "struts.mapper.action.prefix.crossNamespaces";
public static final String DEFAULT_TEMPLATE_TYPE_CONFIG_KEY = "struts.ui.templateSuffix";
@@ -450,4 +463,12 @@ public final class StrutsConstants {
/** See {@link org.apache.struts2.components.Date#setDateFormatter(DateFormatter)} */
public static final String STRUTS_DATE_FORMATTER = "struts.date.formatter";
public static final String STRUTS_URL_QUERY_STRING_BUILDER = "struts.url.queryStringBuilder";
public static final String STRUTS_URL_QUERY_STRING_PARSER = "struts.url.queryStringParser";
public static final String STRUTS_URL_ENCODER = "struts.url.encoder";
public static final String STRUTS_URL_DECODER = "struts.url.decoder";
/** A global flag to set property {@link org.apache.struts2.components.Checkbox#setSubmitUnchecked(String)} */
public static final String STRUTS_UI_CHECKBOX_SUBMIT_UNCHECKED = "struts.ui.checkbox.submitUnchecked";
}
@@ -18,13 +18,14 @@
*/
package org.apache.struts2.components;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.ValueStack;
import org.apache.struts2.StrutsConstants;
import org.apache.struts2.views.annotations.StrutsTag;
import org.apache.struts2.views.annotations.StrutsTagAttribute;
import com.opensymphony.xwork2.util.ValueStack;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* <!-- START SNIPPET: javadoc -->
@@ -46,15 +47,19 @@ import com.opensymphony.xwork2.util.ValueStack;
*
* <!-- END SNIPPET: example -->
* </pre>
*
*/
@StrutsTag(
name="checkbox",
tldTagClass="org.apache.struts2.views.jsp.ui.CheckboxTag",
description="Render a checkbox input field",
allowDynamicAttributes=true)
name = "checkbox",
tldTagClass = "org.apache.struts2.views.jsp.ui.CheckboxTag",
description = "Render a checkbox input field",
allowDynamicAttributes = true)
public class Checkbox extends UIBean {
final public static String TEMPLATE = "checkbox";
private static final String ATTR_SUBMIT_UNCHECKED = "submitUnchecked";
public static final String TEMPLATE = "checkbox";
private String submitUncheckedGlobal;
protected String fieldValue;
protected String submitUnchecked;
@@ -69,46 +74,45 @@ public class Checkbox extends UIBean {
protected void evaluateExtraParams() {
if (fieldValue != null) {
addParameter("fieldValue", findString(fieldValue));
addParameter(ATTR_FIELD_VALUE, findString(fieldValue));
} else {
addParameter("fieldValue", "true");
addParameter(ATTR_FIELD_VALUE, "true");
}
if (submitUnchecked != null) {
Object parsedValue = findValue(submitUnchecked, Boolean.class);
addParameter("submitUnchecked", parsedValue == null ? Boolean.valueOf(submitUnchecked) : parsedValue);
addParameter(ATTR_SUBMIT_UNCHECKED, parsedValue == null ? Boolean.valueOf(submitUnchecked) : parsedValue);
} else if (submitUncheckedGlobal != null) {
addParameter(ATTR_SUBMIT_UNCHECKED, Boolean.parseBoolean(submitUncheckedGlobal));
} else {
addParameter("submitUnchecked", false);
addParameter(ATTR_SUBMIT_UNCHECKED, false);
}
}
protected Class getValueClassType() {
@Override
protected Class<?> getValueClassType() {
return Boolean.class; // for checkboxes, everything needs to end up as a Boolean
}
@StrutsTagAttribute(description="The actual HTML value attribute of the checkbox.", defaultValue="true")
@Inject(value = StrutsConstants.STRUTS_UI_CHECKBOX_SUBMIT_UNCHECKED, required = false)
public void setSubmitUncheckedGlobal(String submitUncheckedGlobal) {
this.submitUncheckedGlobal = submitUncheckedGlobal;
}
@StrutsTagAttribute(description = "The actual HTML value attribute of the checkbox.", defaultValue = "true")
public void setFieldValue(String fieldValue) {
this.fieldValue = fieldValue;
}
@StrutsTagAttribute(description="If set to true, unchecked elements will be submitted with the form.", type="Boolean", defaultValue="false")
@StrutsTagAttribute(description = "If set to true, unchecked elements will be submitted with the form. " +
"Since Struts 6.1.1 you can use a constant \"" + StrutsConstants.STRUTS_UI_CHECKBOX_SUBMIT_UNCHECKED + "\" to set this attribute globally",
type = "Boolean", defaultValue = "false")
public void setSubmitUnchecked(String submitUnchecked) {
this.submitUnchecked = submitUnchecked;
}
/**
* Deprecated since 2.5.27
* @deprecated use {@link #setLabelPosition(String)} instead
*/
@Deprecated
@Override
@StrutsTagAttribute(description="(Deprecated) Define label position of form element (top/left), also 'right' is supported when using 'xhtml' theme")
public void setLabelposition(String labelPosition) {
super.setLabelPosition(labelPosition);
}
@Override
@StrutsTagAttribute(description="Define label position of form element (top/left), also 'right' is supported when using 'xhtml' theme")
@StrutsTagAttribute(description = "Define label position of form element (top/left), also 'right' is supported when using 'xhtml' theme")
public void setLabelPosition(String labelPosition) {
super.setLabelPosition(labelPosition);
}
@@ -21,5 +21,7 @@ package org.apache.struts2.components;
import java.util.Map;
public interface ExtraParameterProvider {
public Map getExtraParameters();
Map<String, Object> getExtraParameters();
}
@@ -29,6 +29,7 @@ import org.apache.logging.log4j.Logger;
import org.apache.struts2.StrutsException;
import org.apache.struts2.dispatcher.mapper.ActionMapper;
import org.apache.struts2.dispatcher.mapper.ActionMapping;
import org.apache.struts2.url.QueryStringParser;
import org.apache.struts2.views.util.UrlHelper;
import java.io.IOException;
@@ -48,6 +49,7 @@ public class ServletUrlRenderer implements UrlRenderer {
private ActionMapper actionMapper;
private UrlHelper urlHelper;
private QueryStringParser queryStringParser;
@Override
@Inject
@@ -60,6 +62,11 @@ public class ServletUrlRenderer implements UrlRenderer {
this.urlHelper = urlHelper;
}
@Inject
public void setQueryStringParser(QueryStringParser queryStringParser) {
this.queryStringParser = queryStringParser;
}
/**
* {@inheritDoc}
*/
@@ -152,10 +159,10 @@ public class ServletUrlRenderer implements UrlRenderer {
}
}
Map actionParams = null;
Map<String, Object> actionParams = null;
if (action != null && action.indexOf('?') > 0) {
String queryString = action.substring(action.indexOf('?') + 1);
actionParams = urlHelper.parseQueryString(queryString, false);
actionParams = queryStringParser.parse(queryString, false);
action = action.substring(0, action.indexOf('?'));
}
@@ -164,19 +171,19 @@ public class ServletUrlRenderer implements UrlRenderer {
String actionMethod = nameMapping.getMethod();
final ActionConfig actionConfig = formComponent.configuration.getRuntimeConfiguration().getActionConfig(
namespace, actionName);
namespace, actionName);
if (actionConfig != null) {
ActionMapping mapping = new ActionMapping(actionName, namespace, actionMethod, formComponent.parameters);
String result = urlHelper.buildUrl(formComponent.actionMapper.getUriFromActionMapping(mapping),
formComponent.request, formComponent.response, actionParams, scheme, formComponent.includeContext, true, false, false);
formComponent.request, formComponent.response, actionParams, scheme, formComponent.includeContext, true, false, false);
formComponent.addParameter("action", result);
// let's try to get the actual action class and name
// this can be used for getting the list of validators
formComponent.addParameter("actionName", actionName);
try {
Class clazz = formComponent.objectFactory.getClassInstance(actionConfig.getClassName());
Class<?> clazz = formComponent.objectFactory.getClassInstance(actionConfig.getClassName());
formComponent.addParameter("actionClass", clazz);
} catch (ClassNotFoundException e) {
// this is OK, we'll just move on
@@ -258,7 +265,7 @@ public class ServletUrlRenderer implements UrlRenderer {
}
if (UrlProvider.NONE.equalsIgnoreCase(includeParams)) {
mergeRequestParameters(urlComponent.getValue(), urlComponent.getParameters(), Collections.<String, Object>emptyMap());
mergeRequestParameters(urlComponent.getValue(), urlComponent.getParameters(), Collections.emptyMap());
} else if (UrlProvider.ALL.equalsIgnoreCase(includeParams)) {
mergeRequestParameters(urlComponent.getValue(), urlComponent.getParameters(), urlComponent.getHttpServletRequest().getParameterMap());
@@ -284,7 +291,7 @@ public class ServletUrlRenderer implements UrlRenderer {
private void includeGetParameters(UrlProvider urlComponent) {
String query = extractQueryString(urlComponent);
mergeRequestParameters(urlComponent.getValue(), urlComponent.getParameters(), urlHelper.parseQueryString(query, false));
mergeRequestParameters(urlComponent.getValue(), urlComponent.getParameters(), queryStringParser.parse(query, false));
}
private String extractQueryString(UrlProvider urlComponent) {
@@ -309,7 +316,7 @@ public class ServletUrlRenderer implements UrlRenderer {
* Merge request parameters into current parameters. If a parameter is
* already present, than the request parameter in the current request and value attribute
* will not override its value.
*
* <p>
* The priority is as follows:-
* <ul>
* <li>parameter from the current request (least priority)</li>
@@ -317,8 +324,8 @@ public class ServletUrlRenderer implements UrlRenderer {
* <li>parameter from the param tag (most priority)</li>
* </ul>
*
* @param value the value attribute (URL to be generated by this component)
* @param parameters component parameters
* @param value the value attribute (URL to be generated by this component)
* @param parameters component parameters
* @param contextParameters request parameters
*/
protected void mergeRequestParameters(String value, Map<String, Object> parameters, Map<String, ?> contextParameters) {
@@ -332,7 +339,7 @@ public class ServletUrlRenderer implements UrlRenderer {
if (StringUtils.contains(value, "?")) {
String queryString = value.substring(value.indexOf('?') + 1);
mergedParams = urlHelper.parseQueryString(queryString, false);
mergedParams = queryStringParser.parse(queryString, false);
for (Map.Entry<String, ?> entry : contextParameters.entrySet()) {
if (!mergedParams.containsKey(entry.getKey())) {
mergedParams.put(entry.getKey(), entry.getValue());
@@ -45,6 +45,7 @@ import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
/**
* <p>
@@ -435,8 +436,13 @@ import java.util.Map;
*
*/
public abstract class UIBean extends Component {
private static final Logger LOG = LogManager.getLogger(UIBean.class);
protected static final String ATTR_FIELD_VALUE = "fieldValue";
protected static final String ATTR_NAME_VALUE = "nameValue";
protected static final String ATTR_VALUE = "value";
protected HttpServletRequest request;
protected HttpServletResponse response;
@@ -689,7 +695,6 @@ public abstract class UIBean extends Component {
if (labelPosition != null) {
String labelPosition = findString(this.labelPosition);
addParameter("labelposition", labelPosition);
addParameter("labelPosition", labelPosition);
}
@@ -795,37 +800,7 @@ public abstract class UIBean extends Component {
addParameter("title", findString(title));
}
// see if the value was specified as a parameter already
final String NAME_VALUE = "nameValue";
if (parameters.containsKey("value")) {
parameters.put(NAME_VALUE, parameters.get("value"));
} else {
if (evaluateNameValue()) {
final Class<?> valueClazz = getValueClassType();
if (valueClazz != null) {
if (value != null) {
addParameter(NAME_VALUE, findValue(value, valueClazz));
} else if (translatedName != null) {
boolean evaluated = !translatedName.equals(this.name);
boolean reevaluate = !evaluated || isAcceptableExpression(translatedName);
if (!reevaluate) {
addParameter(NAME_VALUE, translatedName);
} else {
String expr = completeExpression(translatedName);
addParameter(NAME_VALUE, findValue(expr, valueClazz));
}
}
} else {
if (value != null) {
addParameter(NAME_VALUE, findValue(value));
} else if (translatedName != null) {
addParameter(NAME_VALUE, findValue(translatedName));
}
}
}
}
applyValueParameter(translatedName);
final Form form = (Form) findAncestor(Form.class);
@@ -909,6 +884,46 @@ public abstract class UIBean extends Component {
evaluateExtraParams();
}
/**
* Tries to calculate the "value" parameter based either on the provided {@link #value} or {@link #name}
* @param translatedName the already evaluated {@link #name}
*/
protected void applyValueParameter(String translatedName) {
// see if the value has been specified as a parameter already
if (parameters.containsKey(ATTR_VALUE)) {
parameters.put(ATTR_NAME_VALUE, parameters.get(ATTR_VALUE));
} else {
if (evaluateNameValue()) {
final Class<?> valueClazz = getValueClassType();
if (valueClazz != null) {
if (value != null) {
addParameter(ATTR_NAME_VALUE, findValue(value, valueClazz));
} else if (translatedName != null) {
processTranslatedName(translatedName, (expr) -> findValue(expr, valueClazz));
}
} else {
if (value != null) {
addParameter(ATTR_NAME_VALUE, findValue(value));
} else if (translatedName != null) {
processTranslatedName(translatedName, this::findValue);
}
}
}
}
}
private void processTranslatedName(String translatedName, Function<String, Object> evaluator) {
boolean evaluated = !translatedName.equals(this.name);
boolean reevaluate = !evaluated || isAcceptableExpression(translatedName);
if (!reevaluate) {
addParameter(ATTR_NAME_VALUE, translatedName);
} else {
String expr = completeExpression(translatedName);
addParameter(ATTR_NAME_VALUE, evaluator.apply(expr));
}
}
protected String escape(String name) {
// escape any possible values that can make the ID painful to work with in JavaScript
if (name != null) {
@@ -1073,12 +1088,6 @@ public abstract class UIBean extends Component {
this.cssClass = cssClass;
}
@Deprecated
@StrutsTagAttribute(description="(Deprecated) The css class to use for element - it's an alias of cssClass attribute.")
public void setClass(String cssClass) {
this.cssClass = cssClass;
}
@StrutsTagAttribute(description="The css style definitions for element to use")
public void setCssStyle(String cssStyle) {
this.cssStyle = cssStyle;
@@ -1119,16 +1128,6 @@ public abstract class UIBean extends Component {
this.labelSeparator = labelseparator;
}
/**
* Deprecated since 2.5.27
* @deprecated use {@link #setLabelPosition(String)} instead
*/
@StrutsTagAttribute(description="(Deprecated) Define label position of form element (top/left)")
@Deprecated
public void setLabelposition(String labelPosition) {
this.labelPosition = labelPosition;
}
@StrutsTagAttribute(description="Define label position of form element (top/left)")
public void setLabelPosition(String labelPosition) {
this.labelPosition = labelPosition;
@@ -25,7 +25,7 @@ import javax.servlet.http.HttpServletResponse;
import java.util.Map;
/**
* Implemntations of this interface can be used to build a URL
* Implementations of this interface can be used to build a URL
*/
public interface UrlProvider {
/**
@@ -37,9 +37,9 @@ public interface UrlProvider {
* get - include only GET parameters in the URL (default)
* all - include both GET and POST parameters in the URL
*/
public static final String NONE = "none";
public static final String GET = "get";
public static final String ALL = "all";
String NONE = "none";
String GET = "get";
String ALL = "all";
boolean isPutInContext();
@@ -55,7 +55,7 @@ public interface UrlProvider {
String getIncludeParams();
Map getParameters();
Map<String, Object> getParameters();
HttpServletRequest getHttpServletRequest();
@@ -78,19 +78,19 @@ public interface UrlProvider {
boolean isForceAddSchemeHostAndPort();
boolean isEscapeAmp();
String getPortletMode();
String getWindowState();
String determineActionURL(String action, String namespace, String method, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Map parameters, String scheme, boolean includeContext, boolean encode, boolean forceAddSchemeHostAndPort, boolean escapeAmp);
String determineActionURL(String action, String namespace, String method, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Map<String, ?> parameters, String scheme, boolean includeContext, boolean encode, boolean forceAddSchemeHostAndPort, boolean escapeAmp);
String determineNamespace(String namespace, ValueStack stack, HttpServletRequest req);
String getAnchor();
String getPortletUrlType();
ValueStack getStack();
void setUrlIncludeParams(String urlIncludeParams);
@@ -24,7 +24,6 @@ import com.opensymphony.xwork2.config.Configuration;
import com.opensymphony.xwork2.config.ConfigurationException;
import com.opensymphony.xwork2.inject.*;
import com.opensymphony.xwork2.util.ClassLoaderUtil;
import com.opensymphony.xwork2.util.location.LocatableProperties;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -66,6 +66,10 @@ import org.apache.struts2.dispatcher.DispatcherErrorHandler;
import org.apache.struts2.dispatcher.StaticContentLoader;
import org.apache.struts2.dispatcher.mapper.ActionMapper;
import org.apache.struts2.dispatcher.multipart.MultiPartRequest;
import org.apache.struts2.url.QueryStringBuilder;
import org.apache.struts2.url.QueryStringParser;
import org.apache.struts2.url.UrlDecoder;
import org.apache.struts2.url.UrlEncoder;
import org.apache.struts2.util.ContentTypeMatcher;
import org.apache.struts2.views.freemarker.FreemarkerManager;
import org.apache.struts2.views.util.UrlHelper;
@@ -429,6 +433,11 @@ public class StrutsBeanSelectionProvider extends AbstractBeanSelectionProvider {
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);
alias(QueryStringBuilder.class, StrutsConstants.STRUTS_URL_QUERY_STRING_BUILDER, builder, props, Scope.SINGLETON);
alias(QueryStringParser.class, StrutsConstants.STRUTS_URL_QUERY_STRING_PARSER, builder, props, Scope.SINGLETON);
alias(UrlEncoder.class, StrutsConstants.STRUTS_URL_ENCODER, builder, props, Scope.SINGLETON);
alias(UrlDecoder.class, StrutsConstants.STRUTS_URL_DECODER, builder, props, Scope.SINGLETON);
switchDevMode(props);
}
@@ -64,6 +64,8 @@ public class ConstantConfig {
private String uiTheme;
private String uiThemeExpansionToken;
private Long multipartMaxSize;
private Long multipartMaxFiles;
private Long multipartMaxStringLength;
private String multipartSaveDir;
private Integer multipartBufferSize;
private BeanConfig multipartParser;
@@ -195,6 +197,8 @@ public class ConstantConfig {
map.put(StrutsConstants.STRUTS_UI_THEME, uiTheme);
map.put(StrutsConstants.STRUTS_UI_THEME_EXPANSION_TOKEN, uiThemeExpansionToken);
map.put(StrutsConstants.STRUTS_MULTIPART_MAXSIZE, Objects.toString(multipartMaxSize, null));
map.put(StrutsConstants.STRUTS_MULTIPART_MAXFILES, Objects.toString(multipartMaxFiles, null));
map.put(StrutsConstants.STRUTS_MULTIPART_MAX_STRING_LENGTH, Objects.toString(multipartMaxStringLength, null));
map.put(StrutsConstants.STRUTS_MULTIPART_SAVEDIR, multipartSaveDir);
map.put(StrutsConstants.STRUTS_MULTIPART_BUFFERSIZE, Objects.toString(multipartBufferSize, null));
map.put(StrutsConstants.STRUTS_MULTIPART_PARSER, beanConfToString(multipartParser));
@@ -580,6 +584,22 @@ public class ConstantConfig {
this.multipartMaxSize = multipartMaxSize;
}
public Long getMultipartMaxFiles() {
return multipartMaxFiles;
}
public void setMultipartMaxFiles(Long multipartMaxFiles) {
this.multipartMaxFiles = multipartMaxFiles;
}
public Long getMultipartMaxStringLength() {
return multipartMaxStringLength;
}
public void setMultipartMaxStringLength(Long multipartMaxStringLength) {
this.multipartMaxStringLength = multipartMaxStringLength;
}
public String getMultipartSaveDir() {
return multipartSaveDir;
}
@@ -21,7 +21,12 @@ package org.apache.struts2.dispatcher;
import com.opensymphony.xwork2.ActionContext;
import org.apache.struts2.StrutsConstants;
import java.util.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
/**
@@ -36,10 +41,9 @@ public class InitOperations {
* Creates and initializes the dispatcher
*
* @param filterConfig host configuration
*
* @return the dispatcher
*/
public Dispatcher initDispatcher( HostConfig filterConfig ) {
public Dispatcher initDispatcher(HostConfig filterConfig) {
Dispatcher dispatcher = createDispatcher(filterConfig);
dispatcher.init();
return dispatcher;
@@ -49,10 +53,10 @@ public class InitOperations {
* Initializes the static content loader with the filter configuration
*
* @param filterConfig host configuration
* @param dispatcher the dispatcher
* @param dispatcher the dispatcher
* @return the static content loader
*/
public StaticContentLoader initStaticContentLoader( HostConfig filterConfig, Dispatcher dispatcher ) {
public StaticContentLoader initStaticContentLoader(HostConfig filterConfig, Dispatcher dispatcher) {
StaticContentLoader loader = dispatcher.getContainer().getInstance(StaticContentLoader.class);
loader.setHostConfig(filterConfig);
return loader;
@@ -60,7 +64,6 @@ public class InitOperations {
/**
* @return The dispatcher on the thread.
*
* @throws IllegalStateException If there is no dispatcher available
*/
public Dispatcher findDispatcherOnThread() {
@@ -75,12 +78,11 @@ public class InitOperations {
* Create a {@link Dispatcher}
*
* @param filterConfig host configuration
*
* @return The dispatcher on the thread.
*/
protected Dispatcher createDispatcher(HostConfig filterConfig) {
Map<String, String> params = new HashMap<>();
for ( Iterator<String> parameterNames = filterConfig.getInitParameterNames(); parameterNames.hasNext(); ) {
for (Iterator<String> parameterNames = filterConfig.getInitParameterNames(); parameterNames.hasNext(); ) {
String name = parameterNames.next();
String value = filterConfig.getInitParameter(name);
params.put(name, value);
@@ -96,20 +98,23 @@ public class InitOperations {
* Extract a list of patterns to exclude from request filtering
*
* @param dispatcher The dispatcher to check for exclude pattern configuration
*
* @return a List of Patterns for request to exclude if apply, or <tt>null</tt>
*
* @see org.apache.struts2.StrutsConstants#STRUTS_ACTION_EXCLUDE_PATTERN
*/
public List<Pattern> buildExcludedPatternsList( Dispatcher dispatcher ) {
return buildExcludedPatternsList(dispatcher.getContainer().getInstance(String.class, StrutsConstants.STRUTS_ACTION_EXCLUDE_PATTERN));
public List<Pattern> buildExcludedPatternsList(Dispatcher dispatcher) {
String excludePatterns = dispatcher.getContainer().getInstance(String.class, StrutsConstants.STRUTS_ACTION_EXCLUDE_PATTERN);
String separator = dispatcher.getContainer().getInstance(String.class, StrutsConstants.STRUTS_ACTION_EXCLUDE_PATTERN_SEPARATOR);
if (separator == null) {
separator = ",";
}
return buildExcludedPatternsList(excludePatterns, separator);
}
private List<Pattern> buildExcludedPatternsList( String patterns ) {
private List<Pattern> buildExcludedPatternsList(String patterns, String separator) {
if (null != patterns && patterns.trim().length() != 0) {
List<Pattern> list = new ArrayList<>();
String[] tokens = patterns.split(",");
for ( String token : tokens ) {
String[] tokens = patterns.split(separator);
for (String token : tokens) {
list.add(Pattern.compile(token.trim()));
}
return Collections.unmodifiableList(list);
@@ -34,7 +34,12 @@ import org.apache.struts2.StrutsConstants;
import org.apache.struts2.util.PrefixTrie;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
/**
@@ -236,6 +241,11 @@ public class DefaultActionMapper implements ActionMapper {
this.allowActionPrefix = BooleanUtils.toBoolean(allowActionPrefix);
}
/**
* @deprecated since 6.1.0 - please refactor your application to avoid using this functionality
* @param allowActionCrossNamespaceAccess true to enable cross namespace action access
*/
@Deprecated
@Inject(value = StrutsConstants.STRUTS_MAPPER_ACTION_PREFIX_CROSSNAMESPACES)
public void setAllowActionCrossNamespaceAccess(String allowActionCrossNamespaceAccess) {
this.allowActionCrossNamespaceAccess = BooleanUtils.toBoolean(allowActionCrossNamespaceAccess);
@@ -289,8 +299,8 @@ public class DefaultActionMapper implements ActionMapper {
}
parseNameAndNamespace(uri, mapping, configManager);
extractMethodName(mapping, configManager);
handleSpecialParameters(request, mapping);
extractMethodName(mapping, configManager);
return parseActionName(mapping);
}
@@ -344,8 +354,8 @@ public class DefaultActionMapper implements ActionMapper {
/**
* Parses the name and namespace from the uri
*
* @param uri The uri
* @param mapping The action mapping to populate
* @param uri The uri
* @param mapping The action mapping to populate
* @param configManager configuration manager
*/
protected void parseNameAndNamespace(String uri, ActionMapping mapping, ConfigurationManager configManager) {
@@ -453,10 +463,14 @@ public class DefaultActionMapper implements ActionMapper {
/**
* Reads defined method name for a given action from configuration
*
* @param mapping current instance of {@link ActionMapping}
* @param mapping current instance of {@link ActionMapping}
* @param configurationManager current instance of {@link ConfigurationManager}
*/
protected void extractMethodName(ActionMapping mapping, ConfigurationManager configurationManager) {
if (mapping.getMethod() != null && allowDynamicMethodCalls) {
LOG.debug("DMI is enabled and method has been already mapped based on bang operator");
return;
}
String methodName = null;
for (PackageConfig cfg : configurationManager.getConfiguration().getPackageConfigs().values()) {
if (cfg.getNamespace().equals(mapping.getNamespace())) {
@@ -507,7 +521,7 @@ public class DefaultActionMapper implements ActionMapper {
}
/**
* @return null if no extension is specified.
* @return null if no extension is specified.
*/
protected String getDefaultExtension() {
if (extensions == null) {
@@ -552,17 +566,19 @@ public class DefaultActionMapper implements ActionMapper {
}
protected void handleDynamicMethod(ActionMapping mapping, StringBuilder uri) {
if (!allowDynamicMethodCalls) {
LOG.debug("DMI is disabled, ignoring appending !method to the URI");
return;
}
// See WW-3965
if (StringUtils.isNotEmpty(mapping.getMethod())) {
if (allowDynamicMethodCalls) {
// handle "name!method" convention.
String name = mapping.getName();
if (!name.contains("!")) {
// Append the method as no bang found
uri.append("!").append(mapping.getMethod());
}
} else {
// handle "name!method" convention.
String name = mapping.getName();
if (!name.contains("!")) {
// Append the method as no bang found
uri.append("!").append(mapping.getMethod());
} else if (name.endsWith("!")) {
uri.append(mapping.getMethod());
}
}
}
@@ -24,7 +24,7 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.StrutsConstants;
import org.apache.struts2.util.URLDecoderUtil;
import org.apache.struts2.url.UrlDecoder;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
@@ -32,18 +32,26 @@ import java.util.StringTokenizer;
/**
* Extended version of {@link RestfulActionMapper}, see documentation for more details
* https://struts.apache.org/core-developers/restful-action-mapper.html
* <a href="https://struts.apache.org/core-developers/restful-action-mapper.html">Restful2ActionMapper</a>
*/
public class Restful2ActionMapper extends DefaultActionMapper {
protected static final Logger LOG = LogManager.getLogger(Restful2ActionMapper.class);
private static final Logger LOG = LogManager.getLogger(Restful2ActionMapper.class);
public static final String HTTP_METHOD_PARAM = "__http_method";
private String idParameterName = null;
private UrlDecoder decoder;
public Restful2ActionMapper() {
setSlashesInActionNames("true");
}
@Inject
public void setDecoder(UrlDecoder decoder) {
this.decoder = decoder;
}
/*
* (non-Javadoc)
*
@@ -54,7 +62,7 @@ public class Restful2ActionMapper extends DefaultActionMapper {
throw new IllegalStateException("This action mapper requires the setting 'slashesInActionNames' to be set to 'true'");
}
ActionMapping mapping = super.getMapping(request, configManager);
if (mapping == null) {
return null;
}
@@ -78,7 +86,7 @@ public class Restful2ActionMapper extends DefaultActionMapper {
// Index e.g. foo/
if (isGet(request)) {
mapping.setMethod("index");
// Creating a new entry on POST e.g. foo/
} else if (isPost(request)) {
mapping.setMethod("create");
@@ -96,14 +104,14 @@ public class Restful2ActionMapper extends DefaultActionMapper {
// Removing an item e.g. foo/1
} else if (isDelete(request)) {
mapping.setMethod("remove");
// Updating an item e.g. foo/1
// Updating an item e.g. foo/1
} else if (isPut(request)) {
mapping.setMethod("update");
}
}
if (idParameterName != null && lastSlashPos > -1) {
actionName = actionName.substring(0, lastSlashPos);
}
@@ -129,10 +137,10 @@ public class Restful2ActionMapper extends DefaultActionMapper {
while (st.hasMoreTokens()) {
if (isNameTok) {
paramName = URLDecoderUtil.decode(st.nextToken(), "UTF-8");
paramName = decoder.decode(st.nextToken(), "UTF-8", false);
isNameTok = false;
} else {
paramValue = URLDecoderUtil.decode(st.nextToken(), "UTF-8");
paramValue = decoder.decode(st.nextToken(), "UTF-8", false);
if ((paramName != null) && (paramName.length() > 0)) {
parameters.put(paramName, paramValue);
@@ -143,7 +151,7 @@ public class Restful2ActionMapper extends DefaultActionMapper {
}
if (parameters.size() > 0) {
if (mapping.getParams() == null) {
mapping.setParams(new HashMap<String, Object>());
mapping.setParams(new HashMap<>());
}
mapping.getParams().putAll(parameters);
}
@@ -19,10 +19,11 @@
package org.apache.struts2.dispatcher.mapper;
import com.opensymphony.xwork2.config.ConfigurationManager;
import com.opensymphony.xwork2.inject.Inject;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.RequestUtils;
import org.apache.struts2.util.URLDecoderUtil;
import org.apache.struts2.url.UrlDecoder;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
@@ -32,11 +33,19 @@ import java.util.StringTokenizer;
/**
* Simple Restfull Action Mapper to support REST application
* See docs for more information
* https://struts.apache.org/core-developers/restful-action-mapper.html
* <a href="https://struts.apache.org/core-developers/restful-action-mapper.html">RestfulActionMapper</a>
*/
public class RestfulActionMapper implements ActionMapper {
protected static final Logger LOG = LogManager.getLogger(RestfulActionMapper.class);
private UrlDecoder decoder;
@Inject
public void setDecoder(UrlDecoder decoder) {
this.decoder = decoder;
}
/* (non-Javadoc)
* @see org.apache.struts2.dispatcher.mapper.ActionMapper#getMapping(javax.servlet.http.HttpServletRequest)
*/
@@ -64,10 +73,10 @@ public class RestfulActionMapper implements ActionMapper {
while (st.hasMoreTokens()) {
if (isNameTok) {
paramName = URLDecoderUtil.decode(st.nextToken(), "UTF-8");
paramName = decoder.decode(st.nextToken(), "UTF-8", false);
isNameTok = false;
} else {
paramValue = URLDecoderUtil.decode(st.nextToken(), "UTF-8");
paramValue = decoder.decode(st.nextToken(), "UTF-8", false);
if ((paramName != null) && (paramName.length() > 0)) {
parameters.put(paramName, paramValue);
@@ -98,7 +107,7 @@ public class RestfulActionMapper implements ActionMapper {
if (value != null) {
retVal.append("/");
retVal.append(value);
}
}
return retVal.toString();
}
@@ -51,8 +51,17 @@ public abstract class AbstractMultiPartRequest implements MultiPartRequest {
/**
* Specifies the maximum size of the entire request.
*/
protected long maxSize;
protected boolean maxSizeProvided;
protected Long maxSize;
/**
* Specifies the maximum number of files in one request.
*/
protected Long maxFiles;
/**
* Specifies the maximum length of a string parameter in a multipart request.
*/
protected Long maxStringLength;
/**
* Specifies the buffer size to use during streaming.
@@ -84,10 +93,19 @@ public abstract class AbstractMultiPartRequest implements MultiPartRequest {
*/
@Inject(StrutsConstants.STRUTS_MULTIPART_MAXSIZE)
public void setMaxSize(String maxSize) {
this.maxSizeProvided = true;
this.maxSize = Long.parseLong(maxSize);
}
@Inject(StrutsConstants.STRUTS_MULTIPART_MAXFILES)
public void setMaxFiles(String maxFiles) {
this.maxFiles = Long.parseLong(maxFiles);
}
@Inject(StrutsConstants.STRUTS_MULTIPART_MAX_STRING_LENGTH)
public void setMaxStringLength(String maxStringLength) {
this.maxStringLength = Long.parseLong(maxStringLength);
}
@Inject
public void setLocaleProviderFactory(LocaleProviderFactory localeProviderFactory) {
defaultLocale = localeProviderFactory.createLocaleProvider().getLocale();
@@ -134,9 +152,9 @@ public abstract class AbstractMultiPartRequest implements MultiPartRequest {
int forwardSlash = fileName.lastIndexOf('/');
int backwardSlash = fileName.lastIndexOf('\\');
if (forwardSlash != -1 && forwardSlash > backwardSlash) {
fileName = fileName.substring(forwardSlash + 1, fileName.length());
fileName = fileName.substring(forwardSlash + 1);
} else {
fileName = fileName.substring(backwardSlash + 1, fileName.length());
fileName = fileName.substring(backwardSlash + 1);
}
return fileName;
}
@@ -18,6 +18,7 @@
*/
package org.apache.struts2.dispatcher.multipart;
import org.apache.commons.fileupload.FileCountLimitExceededException;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadBase;
import org.apache.commons.fileupload.FileUploadException;
@@ -35,7 +36,13 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Multipart form data request adapter for Jakarta Commons Fileupload package.
@@ -65,9 +72,12 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
} catch (FileUploadException e) {
LOG.warn("Request exceeded size limit!", e);
LocalizedMessage errorMessage;
if(e instanceof FileUploadBase.SizeLimitExceededException) {
if (e instanceof FileUploadBase.SizeLimitExceededException) {
FileUploadBase.SizeLimitExceededException ex = (FileUploadBase.SizeLimitExceededException) e;
errorMessage = buildErrorMessage(e, new Object[]{ex.getPermittedSize(), ex.getActualSize()});
} else if (e instanceof FileCountLimitExceededException) {
FileCountLimitExceededException ex = (FileCountLimitExceededException) e;
errorMessage = buildErrorMessage(e, new Object[]{ex.getLimit()});
} else {
errorMessage = buildErrorMessage(e, new Object[]{});
}
@@ -101,7 +111,7 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
LOG.debug("Item is a file upload");
// Skip file uploads that don't have a file name - meaning that no file was selected.
if (item.getName() == null || item.getName().trim().length() < 1) {
if (item.getName() == null || item.getName().trim().isEmpty()) {
LOG.debug("No file has been uploaded for the field: {}", item.getFieldName());
return;
}
@@ -118,28 +128,42 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
}
protected void processNormalFormField(FileItem item, String charset) throws UnsupportedEncodingException {
LOG.debug("Item is a normal form field");
try {
LOG.debug("Item is a normal form field");
List<String> values;
if (params.get(item.getFieldName()) != null) {
values = params.get(item.getFieldName());
} else {
values = new ArrayList<>();
}
List<String> values;
if (params.get(item.getFieldName()) != null) {
values = params.get(item.getFieldName());
} else {
values = new ArrayList<>();
}
if (item.getSize() == 0) {
values.add(StringUtils.EMPTY);
} else if (charset != null) {
values.add(item.getString(charset));
} else {
// note: see https://issues.apache.org/jira/browse/WW-633
// basically, in some cases the charset may be null, so
// we're just going to try to "other" method (no idea if this
// will work)
values.add(item.getString());
long size = item.getSize();
if (size == 0) {
values.add(StringUtils.EMPTY);
} else if (size > maxStringLength) {
String errorKey = "struts.messages.upload.error.parameter.too.long";
LocalizedMessage localizedMessage = new LocalizedMessage(this.getClass(), errorKey, null,
new Object[]{item.getFieldName(), maxStringLength, size});
if (!errors.contains(localizedMessage)) {
errors.add(localizedMessage);
}
return;
} else if (charset != null) {
values.add(item.getString(charset));
} else {
// note: see https://issues.apache.org/jira/browse/WW-633
// basically, in some cases the charset may be null, so
// we're just going to try to "other" method (no idea if this
// will work)
values.add(item.getString());
}
params.put(item.getFieldName(), values);
} finally {
item.delete();
}
params.put(item.getFieldName(), values);
item.delete();
}
protected List<FileItem> parseRequest(HttpServletRequest servletRequest, String saveDir) throws FileUploadException {
@@ -150,7 +174,12 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
protected ServletFileUpload createServletFileUpload(DiskFileItemFactory fac) {
ServletFileUpload upload = new ServletFileUpload(fac);
upload.setSizeMax(maxSize);
if (maxSize != null) {
upload.setSizeMax(maxSize);
}
if (maxFiles != null) {
upload.setFileCountMax(maxFiles);
}
return upload;
}
@@ -186,7 +215,7 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
contentTypes.add(fileItem.getContentType());
}
return contentTypes.toArray(new String[contentTypes.size()]);
return contentTypes.toArray(new String[0]);
}
/* (non-Javadoc)
@@ -215,7 +244,7 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
fileList.add(new StrutsUploadedFile(storeLocation));
}
return fileList.toArray(new UploadedFile[fileList.size()]);
return fileList.toArray(new UploadedFile[0]);
}
/* (non-Javadoc)
@@ -233,7 +262,7 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
fileNames.add(getCanonicalName(fileItem.getName()));
}
return fileNames.toArray(new String[fileNames.size()]);
return fileNames.toArray(new String[0]);
}
/* (non-Javadoc)
@@ -251,7 +280,7 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
fileNames.add(((DiskFileItem) fileItem).getStoreLocation().getName());
}
return fileNames.toArray(new String[fileNames.size()]);
return fileNames.toArray(new String[0]);
}
/* (non-Javadoc)
@@ -259,7 +288,7 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
*/
public String getParameter(String name) {
List<String> v = params.get(name);
if (v != null && v.size() > 0) {
if (v != null && !v.isEmpty()) {
return v.get(0);
}
@@ -278,8 +307,8 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
*/
public String[] getParameterValues(String name) {
List<String> v = params.get(name);
if (v != null && v.size() > 0) {
return v.toArray(new String[v.size()]);
if (v != null && !v.isEmpty()) {
return v.toArray(new String[0]);
}
return null;
@@ -316,14 +345,14 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
}
/* (non-Javadoc)
* @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#cleanUp()
*/
* @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#cleanUp()
*/
public void cleanUp() {
Set<String> names = files.keySet();
for (String name : names) {
List<FileItem> items = files.get(name);
for (FileItem item : items) {
LOG.debug("Removing file {} {}", name, item );
LOG.debug("Removing file {} {}", name, item);
if (!item.isInMemory()) {
item.delete();
}
@@ -30,15 +30,15 @@ import org.apache.struts2.dispatcher.LocalizedMessage;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.nio.file.Files;
import java.util.*;
/**
* Multi-part form data request adapter for Jakarta Commons FileUpload package that
* leverages the streaming API rather than the traditional non-streaming API.
*
* <p>
* For more details see WW-3025
*
* @author Chris Cranford
* @since 2.3.18
*/
public class JakartaStreamMultiPartRequest extends AbstractMultiPartRequest {
@@ -85,7 +85,7 @@ public class JakartaStreamMultiPartRequest extends AbstractMultiPartRequest {
types.add(fileInfo.getContentType());
}
return types.toArray(new String[types.size()]);
return types.toArray(new String[0]);
}
/* (non-Javadoc)
@@ -102,7 +102,7 @@ public class JakartaStreamMultiPartRequest extends AbstractMultiPartRequest {
files.add(new StrutsUploadedFile(fileInfo.getFile()));
}
return files.toArray(new UploadedFile[files.size()]);
return files.toArray(new UploadedFile[0]);
}
/* (non-Javadoc)
@@ -119,7 +119,7 @@ public class JakartaStreamMultiPartRequest extends AbstractMultiPartRequest {
names.add(getCanonicalName(fileInfo.getOriginalName()));
}
return names.toArray(new String[names.size()]);
return names.toArray(new String[0]);
}
/* (non-Javadoc)
@@ -143,7 +143,7 @@ public class JakartaStreamMultiPartRequest extends AbstractMultiPartRequest {
names.add(fileInfo.getFile().getName());
}
return names.toArray(new String[names.size()]);
return names.toArray(new String[0]);
}
/* (non-Javadoc)
@@ -170,7 +170,7 @@ public class JakartaStreamMultiPartRequest extends AbstractMultiPartRequest {
public String[] getParameterValues(String name) {
List<String> values = parameters.get(name);
if (values != null && values.size() > 0) {
return values.toArray(new String[values.size()]);
return values.toArray(new String[0]);
}
return null;
}
@@ -209,9 +209,12 @@ public class JakartaStreamMultiPartRequest extends AbstractMultiPartRequest {
// Interface with Commons FileUpload API
// Using the Streaming API
ServletFileUpload servletFileUpload = new ServletFileUpload();
if (maxSizeProvided) {
if (maxSize != null) {
servletFileUpload.setSizeMax(maxSize);
}
if (maxFiles != null) {
servletFileUpload.setFileCountMax(maxFiles);
}
FileItemIterator i = servletFileUpload.getItemIterator(request);
// Iterate the file items
@@ -258,7 +261,7 @@ public class JakartaStreamMultiPartRequest extends AbstractMultiPartRequest {
// if maxSize is specified as -1, there is no sanity check and it's
// safe to return true for any request, delegating the failure
// checks later in the upload process.
if (maxSize == -1 || request == null) {
if ((maxSize != null && maxSize == -1) || request == null) {
return true;
}
@@ -286,8 +289,9 @@ public class JakartaStreamMultiPartRequest extends AbstractMultiPartRequest {
*/
protected void addFileSkippedError(String fileName, HttpServletRequest request) {
String exceptionMessage = "Skipped file " + fileName + "; request size limit exceeded.";
FileSizeLimitExceededException exception = new FileUploadBase.FileSizeLimitExceededException(exceptionMessage, getRequestSize(request), maxSize);
LocalizedMessage message = buildErrorMessage(exception, new Object[]{fileName, getRequestSize(request), maxSize});
long allowedMaxSize = maxSize != null ? maxSize : -1;
FileSizeLimitExceededException exception = new FileUploadBase.FileSizeLimitExceededException(exceptionMessage, getRequestSize(request), allowedMaxSize);
LocalizedMessage message = buildErrorMessage(exception, new Object[]{fileName, getRequestSize(request), allowedMaxSize});
if (!errors.contains(message)) {
errors.add(message);
}
@@ -386,12 +390,12 @@ public class JakartaStreamMultiPartRequest extends AbstractMultiPartRequest {
* @throws IOException in case of IO errors
*/
protected boolean streamFileToDisk(FileItemStream itemStream, File file) throws IOException {
boolean result = false;
boolean result;
try (InputStream input = itemStream.openStream();
OutputStream output = new BufferedOutputStream(new FileOutputStream(file), bufferSize)) {
OutputStream output = new BufferedOutputStream(Files.newOutputStream(file.toPath()), bufferSize)) {
byte[] buffer = new byte[bufferSize];
LOG.debug("Streaming file using buffer size {}.", bufferSize);
for (int length = 0; ((length = input.read(buffer)) > 0); ) {
for (int length; ((length = input.read(buffer)) > 0); ) {
output.write(buffer, 0, length);
}
result = true;
@@ -433,9 +437,9 @@ public class JakartaStreamMultiPartRequest extends AbstractMultiPartRequest {
private static final long serialVersionUID = 1083158552766906037L;
private File file;
private String contentType;
private String originalName;
private final File file;
private final String contentType;
private final String originalName;
/**
* Default constructor.
@@ -47,25 +47,16 @@ public class CoepInterceptor extends AbstractInterceptor implements PreResultLis
private static final String COEP_REPORT_HEADER = "Cross-Origin-Embedder-Policy-Report-Only";
private final Set<String> exemptedPaths = new HashSet<>();
private boolean disabled = false;
private String header = COEP_ENFORCING_HEADER;
@Override
public String intercept(ActionInvocation invocation) throws Exception {
if (disabled) {
LOG.trace("COEP interceptor has been disabled");
} else {
invocation.addPreResultListener(this);
}
invocation.addPreResultListener(this);
return invocation.invoke();
}
@Override
public void beforeResult(ActionInvocation invocation, String resultCode) {
if (disabled) {
return;
}
HttpServletRequest req = invocation.getInvocationContext().getServletRequest();
final String path = req.getContextPath();
@@ -92,8 +83,4 @@ public class CoepInterceptor extends AbstractInterceptor implements PreResultLis
}
}
public void setDisabled(String value) {
disabled = Boolean.parseBoolean(value);
}
}
@@ -49,24 +49,16 @@ public class CoopInterceptor extends AbstractInterceptor implements PreResultLis
private static final String COOP_HEADER = "Cross-Origin-Opener-Policy";
private final Set<String> exemptedPaths = new HashSet<>();
private boolean disabled = false;
private String mode = SAME_ORIGIN;
@Override
public String intercept(ActionInvocation invocation) throws Exception {
if (disabled) {
LOG.trace("COOP interceptor has been disabled");
} else {
invocation.addPreResultListener(this);
}
invocation.addPreResultListener(this);
return invocation.invoke();
}
@Override
public void beforeResult(ActionInvocation invocation, String resultCode) {
if (disabled) {
return;
}
HttpServletRequest request = invocation.getInvocationContext().getServletRequest();
String path = request.getContextPath();
@@ -95,7 +87,4 @@ public class CoopInterceptor extends AbstractInterceptor implements PreResultLis
this.mode = mode;
}
public void setDisabled(String value) {
this.disabled = Boolean.parseBoolean(value);
}
}
@@ -22,12 +22,17 @@ import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.ActionProxy;
import com.opensymphony.xwork2.config.entities.ResultConfig;
import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.interceptor.exec.BackgroundProcess;
import org.apache.struts2.interceptor.exec.ExecutorProvider;
import org.apache.struts2.interceptor.exec.StrutsBackgroundProcess;
import org.apache.struts2.interceptor.exec.StrutsExecutorProvider;
import org.apache.struts2.util.TokenHelper;
import org.apache.struts2.views.freemarker.FreemarkerResult;
@@ -84,7 +89,7 @@ import java.util.Map;
* <!-- END SNIPPET: description -->
*
* <p><u>Interceptor parameters:</u></p>
*
* <p>
* <!-- START SNIPPET: parameters -->
*
* <ul>
@@ -94,11 +99,11 @@ import java.util.Map;
* <li>delaySleepInterval (optional) - only used with delay. Used for waking up at certain intervals to check if the background process is already done. Default is 100 millis.</li>
*
* </ul>
*
* <p>
* <!-- END SNIPPET: parameters -->
*
* <p><u>Extending the interceptor:</u></p>
*
* <p>
* <!-- START SNIPPET: extending -->
* <p>
* If you wish to make special preparations before and/or after the invocation of the background thread, you can extend
@@ -167,9 +172,8 @@ import java.util.Map;
* &lt;result name="success"&gt;longRunningAction-success.jsp&lt;/result&gt;
* &lt;/action&gt;
* </pre>
*
* <p>
* <!-- END SNIPPET: example -->
*
*/
public class ExecuteAndWaitInterceptor extends MethodFilterInterceptor {
@@ -186,22 +190,28 @@ public class ExecuteAndWaitInterceptor extends MethodFilterInterceptor {
private int threadPriority = Thread.NORM_PRIORITY;
private Container container;
private ExecutorProvider executor;
@Inject
public void setContainer(Container container) {
this.container = container;
}
@Inject(required = false)
public void setExecutorProvider(ExecutorProvider executorProvider) {
this.executor = executorProvider;
}
/**
* Creates a new background process
*
* @param name The process name
* @param name The process name
* @param actionInvocation The action invocation
* @param threadPriority The thread priority
* @param threadPriority The thread priority
* @return The new process
*/
protected BackgroundProcess getNewBackgroundProcess(String name, ActionInvocation actionInvocation, int threadPriority) {
return new BackgroundProcess(name + "BackgroundThread", actionInvocation, threadPriority);
return new StrutsBackgroundProcess(actionInvocation, name + "_background-process", threadPriority);
}
/**
@@ -209,7 +219,6 @@ public class ExecuteAndWaitInterceptor extends MethodFilterInterceptor {
* are mapped to requests.
*
* @param proxy action proxy
*
* @return the name of the background thread
*/
protected String getBackgroundProcessName(ActionProxy proxy) {
@@ -223,10 +232,10 @@ public class ExecuteAndWaitInterceptor extends MethodFilterInterceptor {
ActionProxy proxy = actionInvocation.getProxy();
String name = getBackgroundProcessName(proxy);
ActionContext context = actionInvocation.getInvocationContext();
Map session = context.getSession();
Map<String, Object> session = context.getSession();
HttpSession httpSession = ServletActionContext.getRequest().getSession(true);
Boolean secondTime = true;
Boolean secondTime = true;
if (executeAfterValidationPass) {
secondTime = (Boolean) context.get(KEY);
if (secondTime == null) {
@@ -250,8 +259,13 @@ public class ExecuteAndWaitInterceptor extends MethodFilterInterceptor {
}
if ((!executeAfterValidationPass || secondTime) && bp == null) {
bp = getNewBackgroundProcess(name, actionInvocation, threadPriority);
bp = getNewBackgroundProcess(name, actionInvocation, threadPriority).prepare();
session.put(KEY + name, bp);
if (executor.isShutdown()) {
LOG.warn("Executor is shutting down, cannot execute a new process");
return actionInvocation.invoke();
}
executor.execute(bp);
performInitialDelay(bp); // first time let some time pass before showing wait page
secondTime = false;
}
@@ -259,16 +273,16 @@ public class ExecuteAndWaitInterceptor extends MethodFilterInterceptor {
if ((!executeAfterValidationPass || !secondTime) && bp != null && !bp.isDone()) {
actionInvocation.getStack().push(bp.getAction());
final String token = TokenHelper.getToken();
if (token != null) {
TokenHelper.setSessionToken(TokenHelper.getTokenName(), token);
final String token = TokenHelper.getToken();
if (token != null) {
TokenHelper.setSessionToken(TokenHelper.getTokenName(), token);
}
Map results = proxy.getConfig().getResults();
Map<String, ResultConfig> results = proxy.getConfig().getResults();
if (!results.containsKey(WAIT)) {
LOG.warn("ExecuteAndWait interceptor has detected that no result named 'wait' is available. " +
"Defaulting to a plain built-in wait page. It is highly recommend you " +
"provide an action-specific or global result named '{}'.", WAIT);
LOG.warn("ExecuteAndWait interceptor has detected that no result named 'wait' is available. " +
"Defaulting to a plain built-in wait page. It is highly recommend you " +
"provide an action-specific or global result named '{}'.", WAIT);
// no wait result? hmm -- let's try to do dynamically put it in for you!
//we used to add a fake "wait" result here, since the configuration is unmodifiable, that is no longer
@@ -286,7 +300,7 @@ public class ExecuteAndWaitInterceptor extends MethodFilterInterceptor {
session.remove(KEY + name);
actionInvocation.getStack().push(bp.getAction());
// if an exception occured during action execution, throw it here
// if an exception occurred during action execution, throw it here
if (bp.getException() != null) {
throw bp.getException();
}
@@ -369,5 +383,17 @@ public class ExecuteAndWaitInterceptor extends MethodFilterInterceptor {
this.executeAfterValidationPass = executeAfterValidationPass;
}
@Override
public void init() {
super.init();
if (executor == null) {
executor = new StrutsExecutorProvider();
}
}
@Override
public void destroy() {
super.destroy();
executor.shutdown();
}
}
@@ -55,8 +55,6 @@ public class FetchMetadataInterceptor extends AbstractInterceptor {
private final Set<String> exemptedPaths = new HashSet<>();
private final ResourceIsolationPolicy resourceIsolationPolicy = new StrutsResourceIsolationPolicy();
private boolean disabled = false;
@Inject(required = false)
public void setExemptedPaths(String paths) {
this.exemptedPaths.addAll(TextParseUtil.commaDelimitedStringToSet(paths));
@@ -64,10 +62,6 @@ public class FetchMetadataInterceptor extends AbstractInterceptor {
@Override
public String intercept(ActionInvocation invocation) throws Exception {
if (disabled) {
LOG.trace("Fetch Metadata interceptor has been disabled");
return invocation.invoke();
}
ActionContext context = invocation.getInvocationContext();
HttpServletRequest request = context.getServletRequest();
@@ -111,7 +105,4 @@ public class FetchMetadataInterceptor extends AbstractInterceptor {
response.setHeader(VARY_HEADER, VARY_HEADER_VALUE);
}
public void setDisabled(String value) {
this.disabled = Boolean.parseBoolean(value);
}
}

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