Compare commits

...

139 Commits

Author SHA1 Message Date
Lukasz Lenart 2c0facc3aa [maven-release-plugin] prepare release STRUTS_2_5_15 2018-02-09 09:36:18 +01:00
Aleksandr Mashchenko 6ae63d7669 use NumberUtils.isCreatable instead of deprecated NumberUtils.isNumber 2018-02-06 18:22:40 +02:00
Aleksandr Mashchenko eed364fe28 Use managed surefire version in core 2018-02-05 18:39:12 +02:00
Aleksandr Mashchenko 56c6456c61 WW-4914 Update Log4j2 to 2.10.0 2018-02-05 18:31:56 +02:00
Yasser Zamani 20a7b6503f Merge pull request #207 from lukaszlenart/sessionless
WW-4741: Do not create session
2018-01-31 16:48:17 +03:30
Lukasz Lenart 2eb809144a Merge pull request #3 from yasserzamani/sessionless
test not create when no session
2018-01-31 12:16:30 +01:00
Lukasz Lenart 8ef8581d0c Merge pull request #2 from yasserzamani/sessionless
test no session but with locale parameter
2018-01-31 11:27:59 +01:00
Yasser Zamani caeea2145f test not create when no session
See also WW-4741
2018-01-31 13:57:21 +03:30
Yasser Zamani b5cfccace3 test no session but with locale parameter
See also WW-4741
2018-01-31 13:33:44 +03:30
Lukasz Lenart 95a24c87a4 Adds autogenerated file 2018-01-31 07:22:27 +01:00
Lukasz Lenart 92e931d169 WW-4741 Improves tests to cover the new logic 2018-01-31 07:21:53 +01:00
Lukasz Lenart e9a4bcd776 WW-4741 Does not create session if it doesn't exist 2018-01-31 07:21:35 +01:00
Yasser Zamani 9b892cc7fd Merge pull request #206 from lukaszlenart/scan-deeper
WW-4912: scan package-info and class at the same time
2018-01-30 13:17:09 +03:30
Lukasz Lenart 74460831dc Merge pull request #1 from yasserzamani/scan-deeper
Scan even deeper :)
2018-01-30 10:08:11 +01:00
Yasser Zamani 8d4194bbf7 allow duplicate in AnnotationUtils.findAnnotations result
See also WW-4912
2018-01-30 12:21:33 +03:30
Yasser Zamani cf71e0f34f add support finding annotations on interfaces and their packages
See also WW-4912
2018-01-30 11:35:16 +03:30
Yasser Zamani 97012c78a9 Merge pull request #189 from lukaszlenart/upgrade-spring
WW-4899: upgrade Spring
2018-01-30 10:47:04 +03:30
zalsaeed f34cf78ae3 Initial test dedicated for the DefaultUnknownHandlerManager (#205)
* Initial test dedicated for the DefaultUnknownHandlerManager

* fixed the licensing and authore comments issues that causes build to fail

* follow good practice when initializing objects

* use TestCase to enhance performance
2018-01-29 12:39:18 +03:30
Lukasz Lenart 72058bf4c6 Reverts findAnnotation to its previous state 2018-01-29 09:04:03 +01:00
Lukasz Lenart 46462b3d00 Rephrases JavaDoc to better express function's meaning 2018-01-29 08:51:17 +01:00
Lukasz Lenart a9899b6622 Introduces new method find all annotations 2018-01-26 11:47:10 +01:00
Lukasz Lenart a3180e3069 Adds test to cover searching down the hierarchy 2018-01-25 12:28:09 +01:00
Lukasz Lenart d5926b7a21 Searches hierarchy down to Object class for annotation 2018-01-25 12:27:54 +01:00
Yasser Zamani 9beb940136 Merge pull request #201 from HedjuHor/WW-4891
WW-4891 Debug tag should not display anything when not in dev mode
2018-01-20 09:39:32 +03:30
Hedju Hor 17b598433d WW-4891 PrepareOperations overrideDevMode for explicitly switch on for this action only 2018-01-18 23:10:25 +01:00
Lukasz Lenart 86baf21098 Merge pull request #203 from yasserzamani/init_test_scopes
add tests for initializable to cover more scopes
2018-01-17 10:28:47 +01:00
Yasser Zamani bba17b6496 add tests for initializable to cover more scopes 2018-01-16 14:23:41 +03:30
Yasser Zamani c3b85aef7c Merge pull request #202 from lukaszlenart/limitable-extendable
WW-4909: Renames a constant and adds a test
2018-01-16 10:49:16 +03:30
Lukasz Lenart 355e4b517f Adds additional test to cover deprecated option 2018-01-15 18:00:42 +01:00
Lukasz Lenart 1ec1e71d41 Adds test to cover the new constant 2018-01-15 17:32:34 +01:00
Lukasz Lenart e23197ab1b Uses the new constant and makes an old one deprecated 2018-01-15 17:32:24 +01:00
Lukasz Lenart c21fb94921 Defines a new constant to limit collection auto growth 2018-01-15 17:31:26 +01:00
Lukasz Lenart 9bf89f82dc Merge pull request #200 from HedjuHor/BeanValidation_GroupingConstraints
WW-4907 support JSR 303 Validation Groups in BeanValidation-Plugin
2018-01-15 12:38:27 +01:00
Lukasz Lenart 29e1847f64 Merge pull request #199 from yasserzamani/WW-4906
fix conversion by annotation and WW-4906
2018-01-15 11:50:46 +01:00
Hedju Hor 02b61b2c05 WW-4891 Debug tag should not display anything when not in dev mode 2018-01-14 15:54:41 +01:00
Hedju Hor d1df216b32 change default behavior 2018-01-13 10:59:06 +01:00
Hedju Hor 7518e809dd fixed
getValidationGroups get called if not skipped Validation
removed unnecessary LOG.isDebugEnabled()
2018-01-12 18:36:08 +01:00
Hedju Hor 2b2de35519 Revert "fixed some Logger calls. Hint:https://logging.apache.org/log4j/2.0/manual/api.html"
This reverts commit 87b2bd8
2018-01-12 18:26:27 +01:00
Hedju Hor 87b2bd8138 fixed some Logger calls.
Hint:https://logging.apache.org/log4j/2.0/manual/api.html
2018-01-11 21:04:22 +01:00
Hedju Hor 215eb627d4 refactoring ValidateGroup -> ValidationGroup 2018-01-11 19:47:14 +01:00
Yasser Zamani 29b29a9c64 Merge pull request #196 from lukaszlenart/init-like-a-boss
WW-4905: Allows Initializable interface on implementing class instead of interface
2018-01-10 12:09:14 +03:30
Yasser Zamani 5e237e0fd3 fix and add test for lazy and early initialization of prototypes 2018-01-09 21:39:34 +03:30
Lukasz Lenart 23077d3ee9 Loads classes in advance 2018-01-09 16:02:33 +01:00
Lukasz Lenart 4fbded7147 Fixes test to run under JDK8 2018-01-09 15:39:10 +01:00
Lukasz Lenart f9f0f36d29 Increases class visibility 2018-01-09 14:25:13 +01:00
Lukasz Lenart f60095bae9 Fixes failing tests 2018-01-09 13:53:45 +01:00
Lukasz Lenart e4a3669061 Moves wrapping factories into Scope 2018-01-09 13:50:57 +01:00
Hedju Hor 5e4b385984 support Constraints Group in BeanValidation-Plugin 2018-01-09 12:58:42 +01:00
Yasser Zamani 38450c9a70 improve tests for lazy and early initialization 2018-01-09 11:34:58 +03:30
Yasser Zamani df4a18107f add tests for lazy and early initialization 2018-01-08 14:42:03 +03:30
Yasser Zamani 9739291d79 change tests to cover more
Note: Also will delete CLASS from annotation type because it is already the default value
2018-01-07 17:15:36 +03:30
Yasser Zamani 4394238b7e un-deprecate TypeConversion.converter plus some improvements
See WW-4906
2018-01-07 15:49:33 +03:30
Yasser Zamani ff903e7754 add tests if conversion can get annotation from supers 2018-01-07 13:36:31 +03:30
Yasser Zamani 77cbafb74d fix conversion by annotation 2018-01-07 12:48:28 +03:30
Yasser Zamani faf50183e0 add conversion by annotation missing tests 2018-01-07 12:34:55 +03:30
Lukasz Lenart 67bdf68419 Normalises name of property with Spring version 2018-01-06 10:13:59 +01:00
Lukasz Lenart cf19d5130c Adds missing test dependency 2018-01-06 09:07:16 +01:00
Stefaan Dutry a66f92c66d Merge pull request #198 from sdutry/formatting-changes
add tests to UIBean class
2018-01-05 20:25:24 +01:00
Stefaan Dutry 08c058ea65 add test for setStyle method
Verified that the value is added as the cssStyle parameter.
2018-01-05 18:32:32 +01:00
Stefaan Dutry cd0773fb1b add test for setClass method
Verified that the value is added as the cssClass parameter.
2018-01-05 18:24:19 +01:00
Lukasz Lenart f803ddb16e Merge pull request #197 from sdutry/formatting-changes
Minor Formatting changes
2018-01-05 18:08:41 +01:00
Stefaan Dutry 0deea3f9e4 remove old subversion comments 2018-01-05 17:28:47 +01:00
Stefaan Dutry 2fd2cb5f97 add test for parameter with name "value"
Verified that the value parameter is added as the nameValue parameter.
2018-01-04 23:20:45 +01:00
Stefaan Dutry 7df1e67bac add test for setting acceskey
Verified that the value is added to the parameters after they have
been evaluated.
2018-01-04 23:08:12 +01:00
Stefaan Dutry dd76d39224 add tests for getTemplateDir method
Verified that:
- explicit templateDirectory gets precedence
- attribute templateDir gets picked if no explitit one is given
- defaultTemplateDirectory gets picked if there is no explicit one and
    an attribute templateDid
- "template" is returned as default value
2018-01-04 19:39:48 +01:00
Stefaan Dutry 476697f99a add test for buildTemplateName method
Verified that:
- default template name gets picked when no specific one is given
- specific template gets precedence over default one
2018-01-04 18:39:48 +01:00
Stefaan Dutry 0bfdc5e643 add test for mergeTemplate method
Test that a ConfigurationException is thrown when the
TemplateEngineManager returns null instead of a TemplateEngine
instance.
2018-01-04 16:32:21 +01:00
Stefaan Dutry a29051c835 add missing @Override annotation 2018-01-03 22:59:52 +01:00
Stefaan Dutry 7a251ddca9 add curly brackets (formatting)
- add curly brackets around single line conditional statements
2018-01-03 22:53:02 +01:00
Stefaan Dutry 38e3734930 change tabs into spaces (formatting) 2018-01-03 22:50:23 +01:00
Stefaan Dutry fe65910e0e change tabs into spaces (formatting) 2018-01-03 22:44:07 +01:00
Stefaan Dutry 1d0b375afd add curly brackets (formatting)
- add curly brackets around single line conditional statements
- place finally statement on same line as closing catch bracket
2018-01-03 22:26:36 +01:00
Stefaan Dutry 0713da431c add missing @Override annotations 2018-01-03 22:15:54 +01:00
Stefaan Dutry 79951db2aa change tabs into spaces (formatting) 2018-01-03 22:14:07 +01:00
Lukasz Lenart d64365770d Uses the new interface to initialise object when setting up the framework 2018-01-03 10:45:56 +01:00
Lukasz Lenart b04683181c Adjusts builder to handle the new interfaces 2018-01-03 10:45:30 +01:00
Lukasz Lenart fad603c49b Adds a dedicated interfaces to clearly tell CI which beans must be
instantiated early
2018-01-03 10:45:14 +01:00
Lukasz Lenart 87c2746fc7 Introduces dedicated factory to perform object's initialisation 2018-01-03 10:44:25 +01:00
Lukasz Lenart 49ef41c218 Drops unneeded JavaDoc formatting 2018-01-02 09:33:59 +01:00
Lukasz Lenart e1767d9525 Moves Initializable interface into implementing class instead of interface 2018-01-02 09:27:24 +01:00
Lukasz Lenart 67b7fc3f3e Avoids initialising singletons once marked as Initializable 2018-01-02 09:26:39 +01:00
Lukasz Lenart a4183b63f8 Adjusts class to the new Initializable mechanism 2018-01-01 15:51:39 +01:00
Lukasz Lenart 74dd8d6990 Adds implementation of the type() method 2018-01-01 15:47:48 +01:00
Lukasz Lenart 4152cbc71c Uses factory's type to detect Initilizable interfaces 2018-01-01 15:47:48 +01:00
Lukasz Lenart dd8ba56425 Extends factories to allow return implementing type 2018-01-01 15:47:48 +01:00
Yasser Zamani 8eb282f76b Merge pull request #195 from lukaszlenart/fix-prefix-based-factory
WW-4903: fixes PrefixBasedActionProxyFactory
2018-01-01 17:30:05 +03:30
Lukasz Lenart eabf4eda21 Verifies licenses before building a package 2018-01-01 10:15:03 +01:00
Lukasz Lenart 809d8968ca WW-4903 Adds missing header with license 2018-01-01 09:52:31 +01:00
Lukasz Lenart 033573e8fb WW-4903 Reverts to previous initialise mechanism 2018-01-01 09:50:53 +01:00
Lukasz Lenart 7cdc319b41 WW-4903 Extends test of empty prefix 2017-12-31 15:36:59 +01:00
Lukasz Lenart 7116ac8801 WW-4903 Uses StrutsActionProxyFactory as a default factory 2017-12-28 17:35:27 +01:00
Lukasz Lenart a2710a6f04 WW-4903 Covers fallback to default factory with unit test 2017-12-28 17:34:28 +01:00
Lukasz Lenart 457f7c2f57 WW-4903 Adds a unit test to cover the new functionality 2017-12-28 16:56:30 +01:00
Lukasz Lenart 6fc695970f WW-4903 Uses Initializable to properly initialise the factory 2017-12-28 16:56:09 +01:00
Lukasz Lenart 15e6be1d66 WW-4903 Drops injecting defined factory to avoid mapping exception 2017-12-28 16:24:48 +01:00
Lukasz Lenart eddab32e75 Merge pull request #194 from sdutry/sonar/string-optimize-single-characters
used char instead of a single character string
2017-12-21 07:29:48 +01:00
Stefaan Dutry fb23b719f2 used char instead of a single character string 2017-12-20 22:08:33 +01:00
Lukasz Lenart c4bd87e510 Merge pull request #193 from yasserzamani/defaultActionInvocation_coverage
Increases the coverage of DefaultActionInvocation
2017-12-18 08:24:40 +01:00
Yasser Zamani 004b4981a1 Increases the coverage of DefaultActionInvocation 2017-12-16 19:57:39 +03:30
Lukasz Lenart 8b608e334a Merge pull request #192 from yasserzamani/WW-4873_2
WW-4873 Makes ActionInvocation not serializable and InvocationSessionStore$InvocationContext transient
2017-12-16 10:44:52 +01:00
Lukasz Lenart 031bd1aceb Adds empty line to kick Travis build 2017-12-16 09:07:49 +01:00
Lukasz Lenart 9078422496 Adds cache to speed up build on Travis 2017-12-16 08:50:11 +01:00
Lukasz Lenart 278a2112b9 Merge pull request #190 from yasserzamani/WW-4901
WW-4901 Decouples from URL.openConnection implementation of container
2017-12-14 09:18:28 +01:00
Yasser Zamani 2941416a18 WW-4873 Makes InvocationSessionStore$InvocationContext transient 2017-12-14 11:05:05 +03:30
Yasser Zamani 4738d1d802 WW-4873 Makes ActionInvocation not serializable 2017-12-13 19:34:54 +03:30
Lukasz Lenart d4b620d06d Merge pull request #191 from yasserzamani/WW-4900
WW-4900 Makes BackgroundProcess transient
2017-12-13 14:30:25 +01:00
Lukasz Lenart cf8ba17454 Kicks Coveralls to build on master 2017-12-13 13:40:28 +01:00
Yasser Zamani a8ecd9bd29 WW-4900 Fixes BackgroundProcessTest via synchronization 2017-12-13 14:58:54 +03:30
Yasser Zamani 6b131550a6 WW-4900 Reverts BackgroundProcess access modifiers 2017-12-13 13:11:35 +03:30
Yasser Zamani dda3facc26 WW-4900 Makes BackgroundProcess transient 2017-12-13 12:41:22 +03:30
Yasser Zamani d082aaca2a WW-4901 Falls back if JarURLConnection was present already 2017-12-12 16:24:17 +03:30
Yasser Zamani 94758b699c WW-4901 Decouples Struts from URL.openConnection implementation of container 2017-12-12 00:26:44 +03:30
Lukasz Lenart bfa119c7db Uses proper date format 2017-12-04 08:29:54 +01:00
Lukasz Lenart 7058872723 Adds missing dependency 2017-12-04 08:29:44 +01:00
Lukasz Lenart 6c3316a14a Fixes test 2017-12-04 08:29:29 +01:00
Lukasz Lenart e717e469a4 Uses the latest Spring version 2017-12-04 08:29:12 +01:00
Stefaan Dutry 80698ed516 Merge pull request #186 from sdutry/sonar-entrySet
changed Map iterations to use entrySet when both key and value are used
2017-12-01 20:46:00 +01:00
Lukasz Lenart 9b9bd212ed [maven-release-plugin] prepare for next development iteration 2017-11-29 08:35:58 +01:00
Lukasz Lenart 0856157e27 [maven-release-plugin] prepare release STRUTS_2_5_14_1 2017-11-29 08:35:37 +01:00
Lukasz Lenart bea5537e9e Fixes issue with accessing the Central
relates to https://github.com/jeremylong/DependencyCheck/issues/978
2017-11-28 09:36:21 +01:00
Lukasz Lenart 7c7d5be441 Merge pull request #187 from lukaszlenart/jackson-5
WW-4892 WW-4893: Use Jackson to handle JSON request
2017-11-28 08:15:13 +01:00
Lukasz Lenart a728c74f21 Merge pull request #183 from lukaszlenart/pimp-my-struts
Pimp my struts
2017-11-28 08:14:40 +01:00
Lukasz Lenart 76d0bc5ff4 Adds support to generate a report with outdated dependencies 2017-11-27 15:47:35 +01:00
Lukasz Lenart ad98881dd5 Adds support for UpdateImpact 2017-11-27 15:38:22 +01:00
Lukasz Lenart 0207c09b11 Adds missing license header 2017-11-27 10:28:14 +01:00
Lukasz Lenart 5ef7687259 Suppresses some false positives on OWASP Dependency Check 2017-11-27 08:19:15 +01:00
Lukasz Lenart 794cd5e141 WW-4892 Marks handler as deprecated 2017-11-17 09:34:22 +01:00
Lukasz Lenart a2824b7c87 WW-4892 Makes json-lib optional 2017-11-17 09:33:00 +01:00
Lukasz Lenart 941374ecdb WW-4892 Uses Jackson handler as a default one to handle JSON 2017-11-17 09:32:43 +01:00
Lukasz Lenart 0d42ff50ac WW-4893 Upgrades Jackson to version 2.9.2 2017-11-17 09:32:06 +01:00
Stefaan Dutry 00b5f43c8a changed tabs into spaces 2017-11-15 13:58:15 +01:00
Stefaan Dutry 87967b9f84 changed Map iteration to use entrySet 2017-11-15 13:54:20 +01:00
Lukasz Lenart d36ec579fc [maven-release-plugin] prepare for next development iteration 2017-11-14 07:43:35 +01:00
Stefaan Dutry 0bde78cc36 changed Map iterations to use entrySet when both key and value are used 2017-11-13 21:13:27 +01:00
Lukasz Lenart c14dca5be3 Uses full plugin name 2017-11-11 10:11:06 +01:00
Lukasz Lenart 7c4f30f619 Drops unneeded property 2017-11-11 10:10:54 +01:00
Lukasz Lenart c956d1a40d Adds support for coveralls 2017-11-11 09:42:29 +01:00
Lukasz Lenart a92eb529a9 Adds proper header and some links 2017-11-11 09:28:58 +01:00
Lukasz Lenart 882a6e803e Adds Coverage status 2017-11-11 09:20:28 +01:00
160 changed files with 2845 additions and 529 deletions
+13 -1
View File
@@ -6,4 +6,16 @@ jdk:
- oraclejdk8
install: true
script: mvn test -DskipAssembly
script: mvn test -DskipAssembly
after_success:
- mvn clean cobertura:cobertura org.eluder.coveralls:coveralls-maven-plugin:report com.updateimpact:updateimpact-maven-plugin:submit -Ptravis-coveralls,update-impact -DskipAssembly
env:
global:
secure: DusDFL9cuX+mj64tXQfrYVbUVYnxlXmad4nuIMj0/lgDbwJ6kxYasJmR99bdvwu08gS9D5b7a+m7WJTN0FNATzEiiGDXcM+TiDPlYDh7dcalU4QGiLmMuCCEw1r7hp2XE4BWVgDfFhCsHhStgjE4RXzHIyPowoPXr4ugPf8at6w=
cache:
directories:
- $HOME/.m2
+73 -16
View File
@@ -1,41 +1,98 @@
<!---
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.
-->
The Apache Struts web framework
-------------------------------
[![Build Status @ Jenkins](https://builds.apache.org/buildStatus/icon?job=Struts-master-JDK7)](https://builds.apache.org/view/S-Z/view/Struts/job/Struts-master-JDK7/)
[![Build Status @ Travis](https://travis-ci.org/apache/struts.svg?branch=master)](https://travis-ci.org/apache/struts)
[![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/)
[![Coverage Status](https://coveralls.io/repos/github/apache/struts/badge.svg)](https://coveralls.io/github/apache/struts)
[![Dependencies](https://app.updateimpact.com/badge/713283867011846144/Struts%202.svg?config=test)](https://app.updateimpact.com/latest/713283867011846144/Struts%202)
[![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.
Web applications differ from conventional websites in that web applications can create a dynamic response. Many websites deliver only static pages. A web application can interact with databases and business logic engines to customize a response.
## Documentation
Web applications based on JavaServer Pages sometimes commingle database code, page design code, and control flow code. In practice, we find that unless these concerns are separated, larger applications become difficult to maintain.
More information can be found on the [homepage](https://struts.apache.org/). Please read the [Security Guide](https://struts.apache.org/security/)
and the [JavaDocs](https://struts.apache.org/maven/struts2-core/apidocs/index.html) can be browsed.
Questions related to the usage of the Apache Struts should be posted to the [user mailing list](https://struts.apache.org/mail.html).
One way to separate concerns in a software application is to use a Model-View-Controller (MVC) architecture. The Model represents the business or database code, the View represents the page design code, and the Controller represents the navigational code. The Struts framework is designed to help developers create web applications that utilize a MVC architecture.
## Description
Web applications differ from conventional websites in that web applications can create a dynamic response. Many websites
deliver only static pages. A web application can interact with databases and business logic engines to customize a response.
Web applications based on JavaServer Pages sometimes commingle database code, page design code, and control flow code.
In practice, we find that unless these concerns are separated, larger applications become difficult to maintain.
One way to separate concerns in a software application is to use a Model-View-Controller (MVC) architecture. The Model
represents the business or database code, the View represents the page design code, and the Controller represents
the navigational code. The Struts framework is designed to help developers create web applications that utilize
a MVC architecture.
The framework provides three key components:
A “request” handler provided by the application developer that is mapped to a standard URI. A “response” handler that transfers control to another resource which completes the response. A tag library that helps developers create interactive form-based applications with server pages. The frameworks architecture and tags are buzzword compliant. Struts works well with conventional REST applications and with technologies like SOAP and AJAX.
A “request” handler provided by the application developer that is mapped to a standard URI. A “response” handler that
transfers control to another resource which completes the response. A tag library that helps developers create interactive
form-based applications with server pages. The frameworks architecture and tags are buzzword compliant. Struts works well
with conventional REST applications and with technologies like SOAP and AJAX.
The Apache Struts Project
-------------------------
## The Apache Struts Project
The Apache Struts Project is the open source community that creates and maintains the Apache Struts framework. The project consists of a diverse group of volunteers who share common values regarding collaborative, community-based open source development. The Apache Struts Project is proud to share these values with our parent organization: The Apache Software Foundation.
The Apache Struts Project is the open source community that creates and maintains the Apache Struts framework.
The project consists of a diverse group of volunteers who share common values regarding collaborative, community-based
open source development. The Apache Struts Project is proud to share these values with our parent organization:
The Apache Software Foundation.
The project is called “Struts” because the framework is meant to furnish the “invisible underpinnings” that support professional application development. Struts provides the glue that joins the various elements of the standard Java platform into a coherent whole. Our goal is to leverage existing standards by producing the missing pieces we need to create enterprise-grade applications that are easy to maintain over time.
The project is called “Struts” because the framework is meant to furnish the “invisible underpinnings” that support
professional application development. Struts provides the glue that joins the various elements of the standard Java
platform into a coherent whole. Our goal is to leverage existing standards by producing the missing pieces we need to create
enterprise-grade applications that are easy to maintain over time.
The Apache Struts Project offered two major versions of the Struts framework. Currently we are only maintaining the Struts 2 version. It is recommended to upgrade all Struts 1.x application to Struts 2. Please do not start new application development using Struts 1.x, as we are no longer issuing security patches.
The Apache Struts Project offered two major versions of the Struts framework. Currently we are only maintaining the Struts 2
version. It is recommended to upgrade all Struts 1.x application to Struts 2. Please do not start new application development
using Struts 1.x, as we are no longer issuing security patches.
Struts 2 was originally known as WebWork 2. After working independently for several years, the WebWork and Struts communities joined forces to create Struts 2. The 2.x framework is the best choice for teams who value elegant solutions to difficult problems.
Struts 2 was originally known as WebWork 2. After working independently for several years, the WebWork and Struts
communities joined forces to create Struts 2. The 2.x framework is the best choice for teams who value elegant solutions
to difficult problems.
Why should you use Apache Struts?
---------------------------------
## Why should you use Apache Struts?
Apache Struts is a modern, maintained and full-featured web framework. It has been there for years and give the huge user base it is unlikely it will go away anytime soon in the future. Not only that we have dedicated users and developers on the project. Apache Struts is licensed to the Apache License 2.0 and this will not change. We maintain a clean IP and you are “safe” to use the project. Sometimes you are not “safe” to use a project when a company controls the SCM. Access to Source Code doesnt mean it is free. With Apache Struts, you are not only free to “do what you want with it”, you can even contribute (which is not always the case). And best of it: you can become a part of the core team too.
Apache Struts is a modern, maintained and full-featured web framework. It has been there for years and give the huge user
base it is unlikely it will go away anytime soon in the future. Not only that we have dedicated users and developers
on the project. Apache Struts is licensed to the Apache License 2.0 and this will not change. We maintain a clean IP
and you are “safe” to use the project. Sometimes you are not “safe” to use a project when a company controls the SCM.
Access to Source Code doesnt mean it is free. With Apache Struts, you are not only free to “do what you want with it”,
you can even contribute (which is not always the case). And best of it: you can become a part of the core team too.
It is usually very easy to integrate other technologies with Apache Struts. If you are using an ORM like Apache Cayenne, Hibernate or JDBC you will not have any restrictions. Apache Struts is not even tied too much to a frontend technology. In old days it was JSP, then came Velocity and Freemarker. Nowadays you might build your web application with just static HTML and AngularJS. Or you want to use Sitemesh or Tiles. This all is no problem due to Struts elegant and easy to use extension mechanisms.
It is usually very easy to integrate other technologies with Apache Struts. If you are using an ORM like Apache Cayenne,
Hibernate or JDBC you will not have any restrictions. Apache Struts is not even tied too much to a frontend technology.
In old days it was JSP, then came Velocity and Freemarker. Nowadays you might build your web application with just static
HTML and AngularJS. Or you want to use Sitemesh or Tiles. This all is no problem due to Struts elegant and easy to use
extension mechanisms.
Unlike other, component oriented frameworks, we do not aim to hide the stateless nature of the web. We think it is perfectly acceptable to build upon a Request/Response cycle. We also think the MVC pattern is not so bad, just because it is old. In fact, we believe the Apache Struts architecture is clean and easy to understand.
Unlike other, component oriented frameworks, we do not aim to hide the stateless nature of the web. We think it is
perfectly acceptable to build upon a Request/Response cycle. We also think the MVC pattern is not so bad, just because
it is old. In fact, we believe the Apache Struts architecture is clean and easy to understand.
Of course, if you wish to build components on the server side which render on the front end side, you are most likely wrong with Struts. This is a different approach which promises to reduce the amount of HTML/JavaScript knowledge needed and to create reusable components for the view layer. Projects like Wicket and Tapestry serve this purpose very well. As with every framework you need to decide if it makes sense for you to build components or if you are better with the Struts-approach.
Of course, if you wish to build components on the server side which render on the front end side, you are most likely
wrong with Struts. This is a different approach which promises to reduce the amount of HTML/JavaScript knowledge needed
and to create reusable components for the view layer. Projects like Wicket and Tapestry serve this purpose very well.
As with every framework you need to decide if it makes sense for you to build components or if you are better with
the Struts-approach.
+2 -2
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>2.5.14</version>
<version>2.5.15</version>
</parent>
<artifactId>struts2-apps</artifactId>
<packaging>pom</packaging>
@@ -99,7 +99,7 @@
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${struts2.springPlatformVersion}</version>
<version>${spring.platformVersion}</version>
<scope>test</scope>
</dependency>
</dependencies>
+2 -2
View File
@@ -24,12 +24,12 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-apps</artifactId>
<version>2.5.14</version>
<version>2.5.15</version>
</parent>
<artifactId>struts2-rest-showcase</artifactId>
<packaging>war</packaging>
<version>2.5.14</version>
<version>2.5.15</version>
<name>Struts 2 Rest Showcase Webapp</name>
<description>Struts 2 Rest Showcase Example</description>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-apps</artifactId>
<version>2.5.14</version>
<version>2.5.15</version>
</parent>
<artifactId>struts2-showcase</artifactId>
@@ -0,0 +1,30 @@
/*
* 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.tag.nonui.debugtag;
import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.dispatcher.PrepareOperations;
public class DebugTagAction extends ActionSupport {
public String execute() throws Exception {
PrepareOperations.overrideDevMode(true); // Just for Showcase, explicitly switch on for this action only
return SUCCESS;
}
}
@@ -151,7 +151,7 @@
<action name="date">
<result>/WEB-INF/tags/non-ui/date.jsp</result>
</action>
<action name="debugTagDemo">
<action name="debugTagDemo" class="org.apache.struts2.showcase.tag.nonui.debugtag.DebugTagAction">
<result>/WEB-INF/tags/non-ui/debug.jsp</result>
</action>
</package>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>2.5.14</version>
<version>2.5.15</version>
</parent>
<artifactId>struts2-assembly</artifactId>
+3 -3
View File
@@ -30,7 +30,7 @@
</parent>
<artifactId>struts2-bom</artifactId>
<version>2.5.14</version>
<version>2.5.15</version>
<packaging>pom</packaging>
<name>Struts 2 Bill of Materials</name>
@@ -45,7 +45,7 @@
</licenses>
<properties>
<struts-version.version>2.5.14</struts-version.version>
<struts-version.version>2.5.15</struts-version.version>
<maven.site.skip>true</maven.site.skip>
<maven.site.deploy.skip>true</maven.site.deploy.skip>
</properties>
@@ -181,6 +181,6 @@
</dependencyManagement>
<scm>
<tag>STRUTS_2_5_14</tag>
<tag>STRUTS_2_5_15</tag>
</scm>
</project>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-osgi-bundles</artifactId>
<version>2.5.14</version>
<version>2.5.15</version>
</parent>
<artifactId>struts2-osgi-admin-bundle</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-osgi-bundles</artifactId>
<version>2.5.14</version>
<version>2.5.15</version>
</parent>
<artifactId>struts2-osgi-demo-bundle</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>2.5.14</version>
<version>2.5.15</version>
</parent>
<artifactId>struts2-osgi-bundles</artifactId>
+1 -3
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>2.5.14</version>
<version>2.5.15</version>
</parent>
<artifactId>struts2-core</artifactId>
<packaging>jar</packaging>
@@ -40,7 +40,6 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.6</version>
<configuration>
<argLine>-Duser.language=en -Duser.region=US</argLine>
<properties>
@@ -65,7 +64,6 @@
<exclude>**/TestBean2.java</exclude>
<exclude>**/TestInterceptor.java</exclude>
<exclude>**/AnnotatedTestBean.java</exclude>
<exclude>**/ContainerImplTest.java</exclude>
<exclude>**/DefaultFileManagerTest.java</exclude>
</excludes>
</configuration>
@@ -21,8 +21,6 @@ package com.opensymphony.xwork2;
import com.opensymphony.xwork2.interceptor.PreResultListener;
import com.opensymphony.xwork2.util.ValueStack;
import java.io.Serializable;
/**
* An {@link ActionInvocation} represents the execution state of an {@link Action}. It holds the Interceptors and the Action instance.
* By repeated re-entrant execution of the <code>invoke()</code> method, initially by the {@link ActionProxy}, then by the Interceptors, the
@@ -31,7 +29,7 @@ import java.io.Serializable;
* @author Jason Carreira
* @see com.opensymphony.xwork2.ActionProxy
*/
public interface ActionInvocation extends Serializable {
public interface ActionInvocation {
/**
* Get the Action associated with this ActionInvocation.
@@ -178,20 +176,4 @@ public interface ActionInvocation extends Serializable {
void init(ActionProxy proxy) ;
/**
* Prepares instance of ActionInvocation to be serializable,
* which simple means removing all unserializable fields, eg. Container
*
* @return ActionInvocation which can be serialize (eg. into HttpSession)
*/
ActionInvocation serialize();
/**
* Performs opposite process to restore back ActionInvocation after deserialisation
*
* @param actionContext current {@link ActionContext}
* @return fully operational ActionInvocation
*/
ActionInvocation deserialize(ActionContext actionContext);
}
@@ -500,27 +500,4 @@ public class DefaultActionInvocation implements ActionInvocation {
}
}
/**
* Version ready to be serialize
*
* @return instance without reference to {@link Container}
*/
public ActionInvocation serialize() {
DefaultActionInvocation that = this;
that.container = null;
return that;
}
/**
* Restoring Container
*
* @param actionContext current {@link ActionContext}
* @return instance which can be used to invoke action
*/
public ActionInvocation deserialize(ActionContext actionContext) {
DefaultActionInvocation that = this;
that.container = actionContext.getContainer();
return that;
}
}
@@ -74,6 +74,10 @@ public abstract class XWorkJUnit4TestCase {
return impl;
}
@Override
public Class type() {
return impl.getClass();
}
}, Scope.SINGLETON);
}
});
@@ -82,7 +82,11 @@ public abstract class XWorkTestCase extends TestCase {
public Object create(Context context) throws Exception {
return impl;
}
@Override
public Class type() {
return impl.getClass();
}
}, Scope.SINGLETON);
}
});
@@ -56,7 +56,7 @@ public class AllowedMethods {
if (isPattern && !method.startsWith("regex:") && !strictMethodInvocation) {
allowedMethods.add(new PatternAllowedMethod(methodPattern.toString(), method));
} else if (method.startsWith("regex:")) {
String pattern = method.substring(method.indexOf(":") + 1);
String pattern = method.substring(method.indexOf(':') + 1);
allowedMethods.add(new PatternAllowedMethod(pattern, method));
} else if (method.contains("*") && !method.startsWith("regex:") && !strictMethodInvocation) {
String pattern = method.replace("*", defaultRegex);
@@ -154,15 +154,15 @@ public abstract class AbstractMatcher<E> implements Serializable {
Map<String, String> map = new LinkedHashMap<>();
//this will set the group index references, like {1}
for (String key : orig.keySet()) {
map.put(key, convertParam(orig.get(key), vars));
for (Map.Entry<String,String> entry : orig.entrySet()) {
map.put(entry.getKey(), convertParam(entry.getValue(), vars));
}
//the values map will contain entries like name->"Lex Luthor" and 1->"Lex Luthor"
//now add the non-numeric values
for (String key: vars.keySet()) {
if (!NumberUtils.isNumber(key)) {
map.put(key, vars.get(key));
for (Map.Entry<String,String> entry: vars.entrySet()) {
if (!NumberUtils.isCreatable(entry.getKey())) {
map.put(entry.getKey(), entry.getValue());
}
}
@@ -59,8 +59,8 @@ public class ActionConfigMatcher extends AbstractMatcher<ActionConfig> implement
Map<String, ActionConfig> configs,
boolean looseMatch) {
super(patternMatcher);
for (String name : configs.keySet()) {
addPattern(name, configs.get(name), looseMatch);
for (Map.Entry<String, ActionConfig> entry : configs.entrySet()) {
addPattern(entry.getKey(), entry.getValue(), looseMatch);
}
}
@@ -172,6 +172,11 @@ public class DefaultConfiguration implements Configuration {
public Configuration create(Context context) throws Exception {
return DefaultConfiguration.this;
}
@Override
public Class<? extends Configuration> type() {
return DefaultConfiguration.this.getClass();
}
});
ActionContext oldContext = ActionContext.getContext();
@@ -419,8 +424,8 @@ public class DefaultConfiguration implements Configuration {
this.namespaceActionConfigMatchers = new LinkedHashMap<>();
this.namespaceMatcher = new NamespaceMatcher(matcher, namespaceActionConfigs.keySet());
for (String ns : namespaceActionConfigs.keySet()) {
namespaceActionConfigMatchers.put(ns, new ActionConfigMatcher(matcher, namespaceActionConfigs.get(ns), true));
for (Map.Entry<String, Map<String, ActionConfig>> entry : namespaceActionConfigs.entrySet()) {
namespaceActionConfigMatchers.put(entry.getKey(), new ActionConfigMatcher(matcher, entry.getValue(), true));
}
}
@@ -496,11 +501,11 @@ public class DefaultConfiguration implements Configuration {
public String toString() {
StringBuilder buff = new StringBuilder("RuntimeConfiguration - actions are\n");
for (String namespace : namespaceActionConfigs.keySet()) {
Map<String, ActionConfig> actionConfigs = namespaceActionConfigs.get(namespace);
for (Map.Entry<String, Map<String, ActionConfig>> entry : namespaceActionConfigs.entrySet()) {
Map<String, ActionConfig> actionConfigs = entry.getValue();
for (String s : actionConfigs.keySet()) {
buff.append(namespace).append("/").append(s).append("\n");
buff.append(entry.getKey()).append("/").append(s).append("\n");
}
}
@@ -36,7 +36,12 @@ public class LocatableConstantFactory<T> extends Located implements Factory {
public T create(Context ignored) {
return constant;
}
@Override
public Class type() {
return constant.getClass();
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
@@ -51,6 +51,11 @@ public class LocatableFactory<T> extends Located implements Factory<T> {
return (T) obj;
}
@Override
public Class<? extends T> type() {
return implementation;
}
@Override
public String toString() {
String fields = new LinkedHashMap<String, Object>() {
@@ -130,8 +130,8 @@ public class InterceptorBuilder {
* interceptorStack1 -> [interceptor1.param1 -> someValue, interceptor1.param2 -> anotherValue]
*
*/
for (String key : refParams.keySet()) {
String value = refParams.get(key);
for (Map.Entry<String, String> entry : refParams.entrySet()) {
String key = entry.getKey();
try {
String name = key.substring(0, key.indexOf('.'));
@@ -144,7 +144,7 @@ public class InterceptorBuilder {
map = new LinkedHashMap<>();
}
map.put(key, value);
map.put(key, entry.getValue());
params.put(name, map);
} catch (Exception e) {
@@ -154,9 +154,9 @@ public class InterceptorBuilder {
result = new ArrayList<>(stackConfig.getInterceptors());
for (String key : params.keySet()) {
Map<String, String> map = params.get(key);
for (Map.Entry<String, Map<String, String>> entry : params.entrySet()) {
String key = entry.getKey();
Map<String, String> map = entry.getValue();
Object interceptorCfgObj = interceptorLocator.getInterceptorConfig(key);
@@ -113,6 +113,7 @@ import ognl.PropertyAccessor;
import org.apache.struts2.StrutsConstants;
import org.apache.struts2.dispatcher.HttpParameters;
import org.apache.struts2.dispatcher.Parameter;
import org.apache.struts2.factory.PrefixBasedActionProxyFactory;
import java.util.ArrayList;
import java.util.Collection;
@@ -18,12 +18,10 @@
*/
package com.opensymphony.xwork2.conversion;
import com.opensymphony.xwork2.inject.Initializable;
/**
* Used to read converters from Properties file
*/
public interface ConversionPropertiesProcessor extends Initializable {
public interface ConversionPropertiesProcessor {
/**
* Process given property to load converters as not required (Properties file doesn't have to exist)
@@ -82,15 +82,15 @@ import java.lang.annotation.Target;
* </tr>
* <tr>
* <td>converter</td>
* <td>DEPRECATED: either this or value</td>
* <td>either this or value</td>
* <td>&nbsp;</td>
* <td>The class name of the TypeConverter to be used as converter.</td>
* <td>The class or bean name of the TypeConverter to be used as converter.</td>
* </tr>
* <tr>
* <td>converterClass</td>
* <td>either this or value</td>
* <td>&nbsp;</td>
* <td>The class of the TypeConverter to be used as converter. XWorkBasicConverter by default.</td>
* <td>XWorkBasicConverter</td>
* <td>The class of the TypeConverter to be used as converter.</td>
* </tr>
* <tr>
* <td>value</td>
@@ -181,14 +181,13 @@ public @interface TypeConversion {
ConversionRule rule() default ConversionRule.PROPERTY;
/**
* The class of the TypeConverter to be used as converter.
* The class or bean name of the TypeConverter to be used as converter.
*
* Note: This can not be used with ConversionRule.KEY_PROPERTY!
*
* @return class of the TypeConverter to be used as converter
* @deprecated user {@link #converterClass()} instead
* @return class or bean name of the TypeConverter to be used as converter
* @see {@link #converterClass()}
*/
@Deprecated
String converter() default "";
/**
@@ -26,6 +26,7 @@ import com.opensymphony.xwork2.conversion.annotations.ConversionRule;
import com.opensymphony.xwork2.conversion.annotations.ConversionType;
import com.opensymphony.xwork2.conversion.annotations.TypeConversion;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.ClassLoaderUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -69,7 +70,7 @@ public class DefaultConversionAnnotationProcessor implements ConversionAnnotatio
mapping.put(key, tc.value());
}
//for properties of classes
else if (tc.rule() != ConversionRule.ELEMENT || tc.rule() == ConversionRule.KEY || tc.rule() == ConversionRule.COLLECTION) {
else if (tc.rule() != ConversionRule.ELEMENT && tc.rule() != ConversionRule.KEY && tc.rule() != ConversionRule.COLLECTION) {
if (StringUtils.isNoneEmpty(tc.converter())) {
mapping.put(key, converterCreator.createTypeConverter(tc.converter()));
} else {
@@ -80,18 +81,22 @@ public class DefaultConversionAnnotationProcessor implements ConversionAnnotatio
else if (tc.rule() == ConversionRule.KEY) {
Class<?> converterClass;
if (StringUtils.isNoneEmpty(tc.converter())) {
converterClass = Thread.currentThread().getContextClassLoader().loadClass(tc.converter());
//check if the converter is a type converter if it is one
//then just put it in the map as is. Otherwise
//put a value in for the type converter of the class
converterClass = ClassLoaderUtil.loadClass(tc.converter(), this.getClass());
} else {
converterClass = tc.converterClass();
}
LOG.debug("Converter class: [{}]", converterClass);
//check if the converter is a type converter if it is one
//then just put it in the map as is. Otherwise
//put a value in for the type converter of the class
if (converterClass.isAssignableFrom(TypeConverter.class)) {
mapping.put(key, converterCreator.createTypeConverter(tc.converter()));
if (StringUtils.isNoneEmpty(tc.converter())) {
mapping.put(key, converterCreator.createTypeConverter(tc.converter()));
} else {
mapping.put(key, converterCreator.createTypeConverter(tc.converterClass()));
}
} else {
mapping.put(key, converterClass);
LOG.debug("Object placed in mapping for key [{}] is [{}]", key, mapping.get(key));
@@ -100,7 +105,7 @@ public class DefaultConversionAnnotationProcessor implements ConversionAnnotatio
//elements(values) of maps / lists
else {
if (StringUtils.isNoneEmpty(tc.converter())) {
mapping.put(key, Thread.currentThread().getContextClassLoader().loadClass(tc.converter()));
mapping.put(key, ClassLoaderUtil.loadClass(tc.converter(), this.getClass()));
} else {
mapping.put(key, tc.converterClass());
}
@@ -86,7 +86,7 @@ public class DefaultConversionFileProcessor implements ConversionFileProcessor {
//for keys of Maps
else if (key.startsWith(DefaultObjectTypeDeterminer.KEY_PREFIX)) {
Class converterClass = Thread.currentThread().getContextClassLoader().loadClass((String) entry.getValue());
Class converterClass = ClassLoaderUtil.loadClass((String) entry.getValue(), this.getClass());
//check if the converter is a type converter if it is one
//then just put it in the map as is. Otherwise
@@ -102,7 +102,7 @@ public class DefaultConversionFileProcessor implements ConversionFileProcessor {
}
//elements(values) of maps / lists
else {
Class _c = Thread.currentThread().getContextClassLoader().loadClass((String) entry.getValue());
Class _c = ClassLoaderUtil.loadClass((String) entry.getValue(), this.getClass());
LOG.debug("\t{}:{} [treated as Class {}]", key, entry.getValue(), _c);
mapping.put(key, _c);
}
@@ -23,6 +23,8 @@ import com.opensymphony.xwork2.conversion.ConversionPropertiesProcessor;
import com.opensymphony.xwork2.conversion.TypeConverter;
import com.opensymphony.xwork2.conversion.TypeConverterCreator;
import com.opensymphony.xwork2.conversion.TypeConverterHolder;
import com.opensymphony.xwork2.inject.EarlyInitializable;
import com.opensymphony.xwork2.inject.Initializable;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.ClassLoaderUtil;
import org.apache.logging.log4j.Logger;
@@ -34,7 +36,7 @@ import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
public class DefaultConversionPropertiesProcessor implements ConversionPropertiesProcessor {
public class DefaultConversionPropertiesProcessor implements ConversionPropertiesProcessor, EarlyInitializable {
private static final Logger LOG = LogManager.getLogger(DefaultConversionPropertiesProcessor.class);
@@ -412,8 +412,8 @@ public class XWorkConverter extends DefaultTypeConverter {
Object converter = mapping.get(property);
if (converter == null && LOG.isDebugEnabled()) {
LOG.debug("Converter is null for property [{}]. Mapping size [{}]:", property, mapping.size());
for (String next : mapping.keySet()) {
LOG.debug("{}:{}", next, mapping.get(next));
for (Map.Entry<String, Object> entry : mapping.entrySet()) {
LOG.debug("{}:{}", entry.getKey(), entry.getValue());
}
}
return converter;
@@ -500,15 +500,32 @@ public class XWorkConverter extends DefaultTypeConverter {
for (Annotation annotation : annotations) {
if (annotation instanceof TypeConversion) {
TypeConversion tc = (TypeConversion) annotation;
if (mapping.containsKey(tc.key())) {
break;
}
String key = tc.key();
// Default to the property name
// Default to the property name with prefix
if (StringUtils.isEmpty(key)) {
key = AnnotationUtils.resolvePropertyName(method);
switch (tc.rule()) {
case COLLECTION:
key = DefaultObjectTypeDeterminer.DEPRECATED_ELEMENT_PREFIX + key;
break;
case CREATE_IF_NULL:
key = DefaultObjectTypeDeterminer.CREATE_IF_NULL_PREFIX + key;
break;
case ELEMENT:
key = DefaultObjectTypeDeterminer.ELEMENT_PREFIX + key;
break;
case KEY:
key = DefaultObjectTypeDeterminer.KEY_PREFIX + key;
break;
case KEY_PROPERTY:
key = DefaultObjectTypeDeterminer.KEY_PROPERTY_PREFIX + key;
break;
}
LOG.debug("Retrieved key [{}] from method name [{}]", key, method.getName());
}
if (mapping.containsKey(key)) {
break;
}
annotationProcessor.process(mapping, tc, key);
}
}
@@ -47,7 +47,7 @@ public final class ContainerBuilder {
final Map<Key<?>, InternalFactory<?>> factories = new HashMap<>();
final List<InternalFactory<?>> singletonFactories = new ArrayList<>();
final List<InternalFactory<?>> initializableFactories = new ArrayList<>();
final List<InternalFactory<?>> earlyInitializableFactories = new ArrayList<>();
final List<Class<?>> staticInjections = new ArrayList<>();
boolean created;
boolean allowDuplicates = false;
@@ -57,6 +57,11 @@ public final class ContainerBuilder {
public Container create(InternalContext context) {
return context.getContainer();
}
@Override
public Class<? extends Container> type() {
return Container.class;
}
};
private static final InternalFactory<Logger> LOGGER_FACTORY =
@@ -66,6 +71,11 @@ public final class ContainerBuilder {
return member == null ? Logger.getAnonymousLogger()
: Logger.getLogger(member.getDeclaringClass().getName());
}
@Override
public Class<? extends Logger> type() {
return Logger.class;
}
};
/**
@@ -89,12 +99,14 @@ public final class ContainerBuilder {
checkKey(key);
final InternalFactory<? extends T> scopedFactory = scope.scopeFactory(key.getType(), key.getName(), factory);
factories.put(key, scopedFactory);
if (scope == Scope.SINGLETON) {
singletonFactories.add(createCallableFactory(key, scopedFactory));
}
if (Initializable.class.isAssignableFrom(key.getType())) {
initializableFactories.add(createCallableFactory(key, scopedFactory));
InternalFactory<T> callableFactory = createCallableFactory(key, scopedFactory);
if (EarlyInitializable.class.isAssignableFrom(factory.type())) {
earlyInitializableFactories.add(callableFactory);
} else if (scope == Scope.SINGLETON) {
singletonFactories.add(callableFactory);
}
return this;
}
@@ -108,6 +120,11 @@ public final class ContainerBuilder {
context.setExternalContext(null);
}
}
@Override
public Class<? extends T> type() {
return scopedFactory.type();
}
};
}
@@ -145,6 +162,11 @@ public final class ContainerBuilder {
}
}
@Override
public Class<? extends T> type() {
return factory.type();
}
@Override
public String toString() {
return new LinkedHashMap<String, Object>() {{
@@ -231,6 +253,11 @@ public final class ContainerBuilder {
return (T) constructor.construct(context, type);
}
@Override
public Class<? extends T> type() {
return implementation;
}
@Override
public String toString() {
return new LinkedHashMap<String, Object>() {{
@@ -515,6 +542,11 @@ public final class ContainerBuilder {
return value;
}
@Override
public Class<? extends T> type() {
return (Class<? extends T>) value.getClass();
}
@Override
public String toString() {
return new LinkedHashMap<String, Object>() {
@@ -587,13 +619,13 @@ public final class ContainerBuilder {
}
});
}
container.callInContext(new ContainerImpl.ContextualCallable<Void>() {
public Void call(InternalContext context) {
for (InternalFactory<?> factory : initializableFactories) {
Initializable instance = (Initializable) factory.create(context);
instance.init();
}
return null;
for (InternalFactory<?> factory : earlyInitializableFactories) {
factory.create(context);
}
return null;
}
});
@@ -1,23 +1,18 @@
/**
/*
* Copyright (C) 2006 Google Inc.
*
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* </p>
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* <p>
* 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.
* </p>
*/
package com.opensymphony.xwork2.inject;
import com.opensymphony.xwork2.inject.util.ReferenceCache;
@@ -0,0 +1,22 @@
/*
* Copyright 2002-2006,2009 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.opensymphony.xwork2.inject;
/**
* A marking interface that will tell CI to initialise the bean when instating the whole objects' graph
*/
public interface EarlyInitializable extends Initializable {
}
@@ -1,4 +1,4 @@
/**
/*
* Copyright (C) 2006 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.opensymphony.xwork2.inject;
/**
@@ -23,12 +22,20 @@ package com.opensymphony.xwork2.inject;
*/
public interface Factory<T> {
/**
* Creates an object to be injected.
*
* @param context of this injection
* @return instance to be injected
* @throws Exception if unable to create object
*/
T create(Context context) throws Exception;
/**
* Creates an object to be injected.
*
* @param context of this injection
* @return instance to be injected
* @throws Exception if unable to create object
*/
T create(Context context) throws Exception;
/**
* Returns a class of <T>
*
* @return class of the object
*/
Class<? extends T> type();
}
@@ -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 com.opensymphony.xwork2.inject;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
class InitializableFactory<T> implements InternalFactory<T> {
private static final Logger LOG = LogManager.getLogger(InitializableFactory.class);
private InternalFactory<T> internalFactory;
private InitializableFactory(InternalFactory<T> internalFactory) {
this.internalFactory = internalFactory;
}
public static <T> InternalFactory<T> wrapIfNeeded(InternalFactory<T> internalFactory) {
if (Initializable.class.isAssignableFrom(internalFactory.type())) {
return new InitializableFactory<>(internalFactory);
}
return internalFactory;
}
@Override
public T create(InternalContext context) {
T instance = internalFactory.create(context);
if (Initializable.class.isAssignableFrom(instance.getClass())) {
Initializable.class.cast(instance).init();
} else {
LOG.error("Class {} is not marked as {}!", internalFactory.getClass().getName(), Initializable.class.getName());
}
return instance;
}
@Override
public Class<? extends T> type() {
return internalFactory.type();
}
}
@@ -1,4 +1,4 @@
/**
/*
* Copyright (C) 2006 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.opensymphony.xwork2.inject;
import java.io.Serializable;
@@ -25,11 +24,19 @@ import java.io.Serializable;
*/
interface InternalFactory<T> extends Serializable {
/**
* Creates an object to be injected.
*
* @param context of this injection
* @return instance to be injected
*/
T create(InternalContext context);
/**
* Creates an object to be injected.
*
* @param context of this injection
* @return instance to be injected
*/
T create(InternalContext context);
/**
* Returns a class of <T>
*
* @return class of the object
*/
Class<? extends T> type();
}
@@ -33,7 +33,7 @@ public enum Scope {
@Override
<T> InternalFactory<? extends T> scopeFactory(Class<T> type, String name,
InternalFactory<? extends T> factory) {
return factory;
return InitializableFactory.wrapIfNeeded(factory);
}
},
@@ -49,12 +49,17 @@ public enum Scope {
public T create(InternalContext context) {
synchronized (context.getContainer()) {
if (instance == null) {
instance = factory.create(context);
instance = InitializableFactory.wrapIfNeeded(factory).create(context);
}
return instance;
}
}
@Override
public Class<? extends T> type() {
return factory.type();
}
@Override
public String toString() {
return factory.toString();
@@ -83,12 +88,17 @@ public enum Scope {
public T create(final InternalContext context) {
T t = threadLocal.get();
if (t == null) {
t = factory.create(context);
t = InitializableFactory.wrapIfNeeded(factory).create(context);
threadLocal.set(t);
}
return t;
}
@Override
public Class<? extends T> type() {
return factory.type();
}
@Override
public String toString() {
return factory.toString();
@@ -114,6 +124,11 @@ public enum Scope {
}
}
@Override
public Class<? extends T> type() {
return factory.type();
}
@Override
public String toString() {
return factory.toString();
@@ -139,6 +154,11 @@ public enum Scope {
}
}
@Override
public Class<? extends T> type() {
return factory.type();
}
@Override
public String toString() {
return factory.toString();
@@ -164,6 +184,11 @@ public enum Scope {
}
}
@Override
public Class<? extends T> type() {
return factory.type();
}
@Override
public String toString() {
return factory.toString();
@@ -176,7 +201,7 @@ public enum Scope {
final InternalFactory<? extends T> factory) {
return new Callable<T>() {
public T call() throws Exception {
return factory.create(context);
return InitializableFactory.wrapIfNeeded(factory).create(context);
}
};
}
@@ -118,11 +118,13 @@ public class ParameterFilterInterceptor extends AbstractInterceptor {
for (String param : parameters.keySet()) {
boolean currentAllowed = !isDefaultBlock();
for (String currRule : includesExcludesMap.keySet()) {
for (Map.Entry<String, Boolean> entry : includesExcludesMap.entrySet()) {
String currRule = entry.getKey();
if (param.startsWith(currRule) &&
(param.length() == currRule.length() || isPropertySeparator(param.charAt(currRule.length())))
) {
currentAllowed = includesExcludesMap.get(currRule);
currentAllowed = entry.getValue();
}
}
if (!currentAllowed) {
@@ -172,10 +172,11 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
acceptableParameters = new TreeMap<>();
}
for (String name : params.keySet()) {
Parameter parameter = params.get(name);
if (isAcceptableParameter(name, action)) {
acceptableParameters.put(name, parameter);
for (Map.Entry<String, Parameter> entry : params.entrySet()) {
String parameterName = entry.getKey();
if (isAcceptableParameter(parameterName, action)) {
acceptableParameters.put(parameterName, entry.getValue());
}
}
@@ -270,10 +271,10 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
}
StringBuilder logEntry = new StringBuilder();
for (String name : parameters.keySet()) {
logEntry.append(String.valueOf(name));
for (Map.Entry<String, Parameter> entry : parameters.entrySet()) {
logEntry.append(entry.getKey());
logEntry.append(" => ");
logEntry.append(parameters.get(name).getValue());
logEntry.append(entry.getValue().getValue());
logEntry.append(" ");
}
@@ -124,12 +124,4 @@ public class MockActionInvocation implements ActionInvocation {
public void init(ActionProxy proxy) {
}
public ActionInvocation serialize() {
return this;
}
public ActionInvocation deserialize(ActionContext actionContext) {
return this;
}
}
@@ -28,6 +28,7 @@ import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
import ognl.ListPropertyAccessor;
import ognl.OgnlException;
import ognl.PropertyAccessor;
import org.apache.struts2.StrutsConstants;
import java.util.Collection;
import java.util.List;
@@ -49,12 +50,18 @@ public class XWorkListPropertyAccessor extends ListPropertyAccessor {
private ObjectTypeDeterminer objectTypeDeterminer;
private OgnlUtil ognlUtil;
private int autoGrowCollectionLimit = 255;
@Deprecated()
@Inject(value = "xwork.autoGrowCollectionLimit", required = false)
public void setAutoGrowCollectionLimit(String value) {
this.autoGrowCollectionLimit = Integer.valueOf(value);
}
public void setDeprecatedAutoGrowCollectionLimit(String value) {
this.autoGrowCollectionLimit = Integer.valueOf(value);
}
@Inject(value = StrutsConstants.STRUTS_OGNL_AUTO_GROWTH_COLLECTION_LIMIT, required = false)
public void setAutoGrowCollectionLimit(String value) {
this.autoGrowCollectionLimit = Integer.parseInt(value);
}
@Inject("java.util.Collection")
public void setXWorkCollectionPropertyAccessor(PropertyAccessor acc) {
this._sAcc = (XWorkCollectionPropertyAccessor) acc;
@@ -18,9 +18,12 @@
*/
package com.opensymphony.xwork2.util;
import org.apache.commons.lang3.ClassUtils;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
@@ -129,7 +132,7 @@ public class AnnotationUtils {
}
/**
* Returns the annotation on the given class or the package of the class. This searchs up the
* Returns the annotation on the given class or the package of the class. This searches up the
* class hierarchy and the package hierarchy for the closest match.
*
* @param <T> class type
@@ -154,4 +157,36 @@ public class AnnotationUtils {
return ann;
}
/**
* Returns a list of the annotation on the given class or the package of the class.
* This searches up the class hierarchy and the package hierarchy.
*
* @param <T> class type
* @param clazz The class to search for the annotation.
* @param annotationClass The Class of the annotation.
* @return List of the annotations or an empty list.
*/
public static <T extends Annotation> List<T> findAnnotations(Class<?> clazz, Class<T> annotationClass) {
List<T> anns = new ArrayList<>();
List<Class<?>> classes = new ArrayList<>();
classes.add(clazz);
classes.addAll(ClassUtils.getAllSuperclasses(clazz));
classes.addAll(ClassUtils.getAllInterfaces(clazz));
for (Class<?> aClass : classes) {
T ann = aClass.getAnnotation(annotationClass);
if (ann != null) {
anns.add(ann);
}
ann = aClass.getPackage().getAnnotation(annotationClass);
if (ann != null) {
anns.add(ann);
}
}
return anns;
}
}
@@ -171,8 +171,8 @@ public class GlobalLocalizedTextProvider extends AbstractLocalizedTextProvider {
indexedTextName = aTextName;
while ((i = indexedTextName.indexOf("[", i + 1)) != -1) {
int j = indexedTextName.indexOf("]", i);
while ((i = indexedTextName.indexOf('[', i + 1)) != -1) {
int j = indexedTextName.indexOf(']', i);
String a = indexedTextName.substring(0, i);
String b = indexedTextName.substring(j);
indexedTextName = a + "[*" + b;
@@ -232,8 +232,8 @@ public class StrutsLocalizedTextProvider extends AbstractLocalizedTextProvider {
indexedTextName = aTextName;
while ((i = indexedTextName.indexOf("[", i + 1)) != -1) {
int j = indexedTextName.indexOf("]", i);
while ((i = indexedTextName.indexOf('[', i + 1)) != -1) {
int j = indexedTextName.indexOf(']', i);
String a = indexedTextName.substring(0, i);
String b = indexedTextName.substring(j);
indexedTextName = a + "[*" + b;
@@ -293,14 +293,14 @@ public class StrutsLocalizedTextProvider extends AbstractLocalizedTextProvider {
}
// see if it's a child property
int idx = aTextName.indexOf(".");
int idx = aTextName.indexOf('.');
if (idx != -1) {
String newKey = null;
String prop = null;
if (aTextName.startsWith(XWorkConverter.CONVERSION_ERROR_PROPERTY_PREFIX)) {
idx = aTextName.indexOf(".", XWorkConverter.CONVERSION_ERROR_PROPERTY_PREFIX.length());
idx = aTextName.indexOf('.', XWorkConverter.CONVERSION_ERROR_PROPERTY_PREFIX.length());
if (idx != -1) {
prop = aTextName.substring(XWorkConverter.CONVERSION_ERROR_PROPERTY_PREFIX.length(), idx);
@@ -189,7 +189,7 @@ public interface ClassFinder {
}
public String getPackageName(){
return name.indexOf(".") > 0 ? name.substring(0, name.lastIndexOf(".")) : "" ;
return name.indexOf('.') > 0 ? name.substring(0, name.lastIndexOf('.')) : "" ;
}
public List<MethodInfo> getConstructors() {
@@ -37,11 +37,9 @@ public class JarEntryRevision extends Revision {
private long lastModified;
public static Revision build(URL fileUrl, FileManager fileManager) {
// File within a Jar
// Find separator index of jar filename and filename within jar
JarURLConnection conn = null;
try {
conn = (JarURLConnection) fileUrl.openConnection();
conn = StrutsJarURLConnection.openConnection(fileUrl);
conn.setUseCaches(false);
URL url = fileManager.normalizeToFileProtocol(fileUrl);
if (url != null) {
@@ -75,7 +73,7 @@ public class JarEntryRevision extends Revision {
JarURLConnection conn = null;
long lastLastModified = lastModified;
try {
conn = (JarURLConnection) jarFileURL.openConnection();
conn = StrutsJarURLConnection.openConnection(jarFileURL);
conn.setUseCaches(false);
lastLastModified = conn.getJarEntry().getTime();
} catch (IOException ignored) {
@@ -0,0 +1,102 @@
/*
* 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.util.fs;
import java.io.IOException;
import java.io.InputStream;
import java.net.JarURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.jar.JarFile;
/**
* WW-4901 Decouples from underlying implementation of {@link URL#openConnection()}
* e.g. from IBM WebSphere com.ibm.ws.classloader.Handler$ClassLoaderURLConnection
* @since 2.5.15
*/
class StrutsJarURLConnection extends JarURLConnection {
private JarFile jarFile;
private StrutsJarURLConnection(URL url) throws MalformedURLException {
super(url);
}
@Override
public JarFile getJarFile() throws IOException {
connect();
return jarFile;
}
@Override
public void connect() throws IOException {
if (connected) {
return;
}
try (final InputStream in = getJarFileURL().openConnection().getInputStream()) {
jarFile = AccessController.doPrivileged(
new PrivilegedExceptionAction<JarFile>() {
public JarFile run() throws IOException {
Path tmpFile = Files.createTempFile("jar_cache", null);
try {
Files.copy(in, tmpFile, StandardCopyOption.REPLACE_EXISTING);
JarFile jarFile = new JarFile(tmpFile.toFile(), true, JarFile.OPEN_READ);
tmpFile.toFile().deleteOnExit();
return jarFile;
} catch (Throwable thr) {
try {
Files.delete(tmpFile);
} catch (IOException ioe) {
thr.addSuppressed(ioe);
}
throw thr;
} finally {
in.close();
}
}
});
connected = true;
} catch (PrivilegedActionException pae) {
throw (IOException) pae.getException();
}
}
static JarURLConnection openConnection(URL url) throws IOException {
URLConnection conn = url.openConnection();
if (conn instanceof JarURLConnection) {
return (JarURLConnection) conn;
} else {
try {
conn.getInputStream().close();
} catch (IOException ignored) {
}
}
StrutsJarURLConnection result = new StrutsJarURLConnection(url);
return result;
}
}
@@ -22,6 +22,7 @@ import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ObjectFactory;
import com.opensymphony.xwork2.XWorkException;
import com.opensymphony.xwork2.config.ConfigurationException;
import com.opensymphony.xwork2.inject.Initializable;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.ClassLoaderUtil;
import org.apache.logging.log4j.LogManager;
@@ -44,7 +45,7 @@ import java.util.zip.ZipInputStream;
* @author Jason Carreira
* @author James House
*/
public class DefaultValidatorFactory implements ValidatorFactory {
public class DefaultValidatorFactory implements ValidatorFactory, Initializable {
protected Map<String, String> validators = new HashMap<>();
private static Logger LOG = LogManager.getLogger(DefaultValidatorFactory.class);
@@ -18,8 +18,6 @@
*/
package com.opensymphony.xwork2.validator;
import com.opensymphony.xwork2.inject.Initializable;
/**
* ValidatorFactory
*
@@ -227,7 +225,7 @@ import com.opensymphony.xwork2.inject.Initializable;
* @author Jason Carreira
* @author James House
*/
public interface ValidatorFactory extends Initializable {
public interface ValidatorFactory {
/**
* Get a Validator that matches the given configuration.
@@ -328,4 +328,6 @@ public final class StrutsConstants {
public static final String STRUTS_LOCALIZED_TEXT_PROVIDER = "struts.localizedTextProvider";
public static final String STRUTS_DISALLOW_PROXY_MEMBER_ACCESS = "struts.disallowProxyMemberAccess";
public static final String STRUTS_OGNL_AUTO_GROWTH_COLLECTION_LIMIT = "struts.ognl.autoGrowthCollectionLimit";
}
@@ -80,14 +80,17 @@ public class Anchor extends ClosingUIBean {
urlProvider.setHttpServletResponse(response);
}
@Override
public String getDefaultOpenTemplate() {
return OPEN_TEMPLATE;
}
@Override
protected String getDefaultTemplate() {
return TEMPLATE;
}
@Override
public boolean usesBody() {
return true;
}
@@ -96,16 +99,17 @@ public class Anchor extends ClosingUIBean {
protected void evaluateExtraParams() {
super.evaluateExtraParams();
if (href != null)
if (href != null) {
addParameter("href", ensureAttributeSafelyNotEscaped(findString(href)));
else {
} else {
//no href, build it from URL attributes
StringWriter sw = new StringWriter();
urlRenderer.beforeRenderUrl(urlProvider);
urlRenderer.renderUrl(sw, urlProvider);
String builtHref = sw.toString();
if (StringUtils.isNotEmpty(builtHref))
if (StringUtils.isNotEmpty(builtHref)) {
addParameter("href", ensureAttributeSafelyNotEscaped(builtHref));
}
}
}
@@ -115,10 +119,10 @@ public class Anchor extends ClosingUIBean {
}
@Inject
public void setUrlRenderer(UrlRenderer urlRenderer) {
urlProvider.setUrlRenderer(urlRenderer);
public void setUrlRenderer(UrlRenderer urlRenderer) {
urlProvider.setUrlRenderer(urlRenderer);
this.urlRenderer = urlRenderer;
}
}
@Inject(required=false)
public void setExtraParameterProvider(ExtraParameterProvider provider) {
@@ -135,6 +139,7 @@ public class Anchor extends ClosingUIBean {
/**
* Overrides to be able to render body in a template rather than always before the template
*/
@Override
public boolean end(Writer writer, String body) {
this.processingTagBody = false;
evaluateParams();
@@ -143,15 +148,14 @@ public class Anchor extends ClosingUIBean {
mergeTemplate(writer, buildTemplateName(template, getDefaultTemplate()));
} catch (Exception e) {
LOG.error("error when rendering", e);
}
finally {
} finally {
popComponentStack();
}
return false;
}
@Override
public void addParameter(String key, Object value) {
/*
the parameters added by this method are used in the template. this method is also
@@ -160,8 +164,9 @@ public class Anchor extends ClosingUIBean {
*/
if (processingTagBody) {
this.urlParameters.put(key, value);
} else
} else {
super.addParameter(key, value);
}
}
@Override
@@ -173,8 +178,9 @@ public class Anchor extends ClosingUIBean {
*/
if (processingTagBody) {
this.urlParameters.putAll(params);
} else
} else {
super.addAllParameters(params);
}
}
public UrlProvider getUrlProvider() {
@@ -196,6 +202,7 @@ public class Anchor extends ClosingUIBean {
urlProvider.setScheme(scheme);
}
@Override
@StrutsTagAttribute(description = "The target value to use, if not using action")
public void setValue(String value) {
urlProvider.setValue(value);
@@ -30,17 +30,17 @@ import java.util.Map;
import java.util.ArrayList;
import java.util.List;
import org.apache.struts2.dispatcher.PrepareOperations;
import org.apache.struts2.views.annotations.StrutsTag;
import org.apache.struts2.StrutsException;
@StrutsTag(name="debug", tldTagClass="org.apache.struts2.views.jsp.ui.DebugTag",
description="Prints debugging information")
description="Prints debugging information (Only if 'struts.devMode' is enabled)")
public class Debug extends UIBean {
public static final String TEMPLATE = "debug";
protected ReflectionProvider reflectionProvider;
public Debug(ValueStack stack, HttpServletRequest request, HttpServletResponse response) {
super(stack, request, response);
@@ -50,7 +50,7 @@ public class Debug extends UIBean {
public void setReflectionProvider(ReflectionProvider prov) {
this.reflectionProvider = prov;
}
protected String getDefaultTemplate() {
return TEMPLATE;
}
@@ -58,25 +58,40 @@ public class Debug extends UIBean {
public boolean start(Writer writer) {
boolean result = super.start(writer);
ValueStack stack = getStack();
Iterator iter = stack.getRoot().iterator();
List stackValues = new ArrayList(stack.getRoot().size());
while (iter.hasNext()) {
Object o = iter.next();
Map values;
try {
values = reflectionProvider.getBeanMap(o);
} catch (Exception e) {
throw new StrutsException("Caught an exception while getting the property values of " + o, e);
if (showDebug()) {
ValueStack stack = getStack();
Iterator iter = stack.getRoot().iterator();
List stackValues = new ArrayList(stack.getRoot().size());
while (iter.hasNext()) {
Object o = iter.next();
Map values;
try {
values = reflectionProvider.getBeanMap(o);
} catch (Exception e) {
throw new StrutsException("Caught an exception while getting the property values of " + o, e);
}
stackValues.add(new DebugMapEntry(o.getClass().getName(), values));
}
stackValues.add(new DebugMapEntry(o.getClass().getName(), values));
addParameter("stackValues", stackValues);
}
addParameter("stackValues", stackValues);
return result;
}
@Override
public boolean end(Writer writer, String body) {
if (showDebug()) {
return super.end(writer, body);
} else {
popComponentStack();
return false;
}
}
protected boolean showDebug() {
return (devMode || Boolean.TRUE == PrepareOperations.getDevModeOverride());
}
private static class DebugMapEntry implements Map.Entry {
private Object key;
private Object value;
@@ -89,8 +89,8 @@ public class ServletUrlRenderer implements UrlRenderer {
// We don't include the request parameters cause they would have been
// prioritised before this [in start(Writer) method]
if (_value != null && _value.indexOf("?") > 0) {
_value = _value.substring(0, _value.indexOf("?"));
if (_value != null && _value.indexOf('?') > 0) {
_value = _value.substring(0, _value.indexOf('?'));
}
result = urlHelper.buildUrl(_value, urlComponent.getHttpServletRequest(), urlComponent.getHttpServletResponse(), urlComponent.getParameters(), scheme, urlComponent.isIncludeContext(), urlComponent.isEncode(), urlComponent.isForceAddSchemeHostAndPort(), urlComponent.isEscapeAmp());
}
@@ -150,10 +150,10 @@ public class ServletUrlRenderer implements UrlRenderer {
}
Map actionParams = null;
if (action != null && action.indexOf("?") > 0) {
String queryString = action.substring(action.indexOf("?") + 1);
if (action != null && action.indexOf('?') > 0) {
String queryString = action.substring(action.indexOf('?') + 1);
actionParams = urlHelper.parseQueryString(queryString, false);
action = action.substring(0, action.indexOf("?"));
action = action.substring(0, action.indexOf('?'));
}
ActionMapping nameMapping = actionMapper.getMappingFromActionName(action);
@@ -322,7 +322,7 @@ public class ServletUrlRenderer implements UrlRenderer {
// where the parameters specified in value attribute takes priority.
if (StringUtils.contains(value, "?")) {
String queryString = value.substring(value.indexOf("?") + 1);
String queryString = value.substring(value.indexOf('?') + 1);
mergedParams = urlHelper.parseQueryString(queryString, false);
for (Map.Entry<String, Object> entry : contextParameters.entrySet()) {
@@ -528,6 +528,7 @@ public abstract class UIBean extends Component {
this.templateEngineManager = mgr;
}
@Override
public boolean end(Writer writer, String body) {
evaluateParams();
try {
@@ -883,7 +884,7 @@ public abstract class UIBean extends Component {
evaluateExtraParams();
}
protected String escape(String name) {
protected String escape(String name) {
// escape any possible values that can make the ID painful to work with in JavaScript
if (name != null) {
return name.replaceAll("[\\/\\.\\[\\]]", "_");
@@ -930,7 +931,7 @@ public abstract class UIBean extends Component {
form.addParameter("customOnsubmitEnabled", Boolean.TRUE);
} else {
if (LOG.isWarnEnabled()) {
LOG.warn("Cannot find an Ancestor form, custom onsubmit is NOT enabled");
LOG.warn("Cannot find an Ancestor form, custom onsubmit is NOT enabled");
}
}
}
@@ -1240,27 +1241,30 @@ public abstract class UIBean extends Component {
this.tooltipIconPath = tooltipIconPath;
}
public void setDynamicAttributes(Map<String, Object> tagDynamicAttributes) {
for (String key : tagDynamicAttributes.keySet()) {
public void setDynamicAttributes(Map<String, Object> tagDynamicAttributes) {
for (Map.Entry<String, Object> entry : tagDynamicAttributes.entrySet()) {
String key = entry.getKey();
if (!isValidTagAttribute(key)) {
dynamicAttributes.put(key, tagDynamicAttributes.get(key));
dynamicAttributes.put(key, entry.getValue());
}
}
}
@Override
/**
* supports dynamic attributes for freemarker ui tags
* @see https://issues.apache.org/jira/browse/WW-3174
@Override
/**
* supports dynamic attributes for freemarker ui tags
* @see https://issues.apache.org/jira/browse/WW-3174
* @see https://issues.apache.org/jira/browse/WW-4166
*/
*/
public void copyParams(Map params) {
super.copyParams(params);
for (Object o : params.entrySet()) {
Map.Entry entry = (Map.Entry) o;
String key = (String) entry.getKey();
if(!isValidTagAttribute(key) && !key.equals("dynamicAttributes"))
if (!isValidTagAttribute(key) && !key.equals("dynamicAttributes")) {
dynamicAttributes.put(key, entry.getValue());
}
}
}
@@ -28,26 +28,26 @@ import java.io.Writer;
*
*/
public interface UrlRenderer {
/**
* Preprocessing step
* @param provider The {@link UrlProvider} component that "owns" this renderer.
*/
void beforeRenderUrl(UrlProvider provider);
/**
* Render a URL.
* @param writer A writer that the implementation can use to write the result to.
* @param provider The {@link UrlProvider} component that "owns" this renderer.
*/
void renderUrl(Writer writer, UrlProvider provider);
/**
* Render a Form URL.
* @param formComponent The {@link Form} component that "owns" this renderer.
*/
void renderFormUrl(Form formComponent);
void setActionMapper(ActionMapper actionMapper);
/**
* Preprocessing step
* @param provider The {@link UrlProvider} component that "owns" this renderer.
*/
void beforeRenderUrl(UrlProvider provider);
/**
* Render a URL.
* @param writer A writer that the implementation can use to write the result to.
* @param provider The {@link UrlProvider} component that "owns" this renderer.
*/
void renderUrl(Writer writer, UrlProvider provider);
/**
* Render a Form URL.
* @param formComponent The {@link Form} component that "owns" this renderer.
*/
void renderFormUrl(Form formComponent);
void setActionMapper(ActionMapper actionMapper);
}
@@ -142,7 +142,7 @@ public abstract class BaseTemplateEngine implements TemplateEngine {
protected String getFinalTemplateName(Template template) {
String t = template.toString();
if (t.indexOf(".") <= 0) {
if (t.indexOf('.') <= 0) {
return t + "." + getSuffix();
}
return t;
@@ -113,5 +113,10 @@ public abstract class AbstractBeanSelectionProvider implements BeanSelectionProv
throw new ConfigurationException("Unable to load bean "+type.getName()+" ("+name+")");
}
}
@Override
public Class type() {
return type;
}
}
}
@@ -90,6 +90,9 @@ public class StrutsXmlConfigurationProvider extends XmlConfigurationProvider {
public ServletContext create(Context context) throws Exception {
return servletContext;
}
public Class<? extends ServletContext> type() {
return servletContext.getClass();
}
});
}
super.register(containerBuilder, props);
@@ -163,8 +163,8 @@ public class DefaultActionMapper implements ActionMapper {
}
}
if (!allowSlashesInActionNames && !allowActionCrossNamespaceAccess) {
if (actionName.lastIndexOf("/") != -1) {
actionName = actionName.substring(actionName.lastIndexOf("/") + 1);
if (actionName.lastIndexOf('/') != -1) {
actionName = actionName.substring(actionName.lastIndexOf('/') + 1);
}
}
mapping.setName(actionName);
@@ -271,7 +271,7 @@ public class DefaultActionMapper implements ActionMapper {
ActionMapping mapping = new ActionMapping();
String uri = RequestUtils.getUri(request);
int indexOfSemicolon = uri.indexOf(";");
int indexOfSemicolon = uri.indexOf(';');
uri = (indexOfSemicolon > -1) ? uri.substring(0, indexOfSemicolon) : uri;
uri = dropExtension(uri, mapping);
@@ -291,7 +291,7 @@ public class DefaultActionMapper implements ActionMapper {
if (allowDynamicMethodCalls) {
// handle "name!method" convention.
String name = mapping.getName();
int exclamation = name.lastIndexOf("!");
int exclamation = name.lastIndexOf('!');
if (exclamation != -1) {
mapping.setName(name.substring(0, exclamation));
@@ -341,7 +341,7 @@ public class DefaultActionMapper implements ActionMapper {
*/
protected void parseNameAndNamespace(String uri, ActionMapping mapping, ConfigurationManager configManager) {
String namespace, name;
int lastSlash = uri.lastIndexOf("/");
int lastSlash = uri.lastIndexOf('/');
if (lastSlash == -1) {
namespace = "";
name = uri;
@@ -131,8 +131,8 @@ public abstract class AbstractMultiPartRequest implements MultiPartRequest {
protected String getCanonicalName(final String originalFileName) {
String fileName = originalFileName;
int forwardSlash = fileName.lastIndexOf("/");
int backwardSlash = fileName.lastIndexOf("\\");
int forwardSlash = fileName.lastIndexOf('/');
int backwardSlash = fileName.lastIndexOf('\\');
if (forwardSlash != -1 && forwardSlash > backwardSlash) {
fileName = fileName.substring(forwardSlash + 1, fileName.length());
} else {
@@ -60,8 +60,8 @@ public class JakartaStreamMultiPartRequest extends AbstractMultiPartRequest {
*/
public void cleanUp() {
LOG.debug("Performing File Upload temporary storage cleanup.");
for (String fieldName : fileInfos.keySet()) {
for (FileInfo fileInfo : fileInfos.get(fieldName)) {
for (List<FileInfo> fileInfoList : fileInfos.values()) {
for (FileInfo fileInfo : fileInfoList) {
File file = fileInfo.getFile();
LOG.debug("Deleting file '{}'.", file.getName());
if (!file.delete()) {
@@ -22,13 +22,17 @@ import com.opensymphony.xwork2.ActionProxy;
import com.opensymphony.xwork2.ActionProxyFactory;
import com.opensymphony.xwork2.DefaultActionProxyFactory;
import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.inject.Initializable;
import com.opensymphony.xwork2.inject.Inject;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.StrutsConstants;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* <!-- START SNIPPET: description -->
@@ -55,38 +59,37 @@ import java.util.Map;
* </pre>
* <!-- END SNIPPET: description -->
*/
public class PrefixBasedActionProxyFactory extends DefaultActionProxyFactory {
public class PrefixBasedActionProxyFactory extends StrutsActionProxyFactory implements Initializable {
private static final Logger LOG = LogManager.getLogger(PrefixBasedActionProxyFactory.class);
private Map<String, ActionProxyFactory> actionProxyFactories = new HashMap<>();
private ActionProxyFactory defaultFactory;
private Set<String> prefixes = new HashSet<>();
@Inject
public void setContainer(Container container) {
this.container = container;
}
@Inject(StrutsConstants.STRUTS_ACTIONPROXYFACTORY)
public void setActionProxyFactory(ActionProxyFactory factory) {
this.defaultFactory = factory;
}
@Inject(StrutsConstants.PREFIX_BASED_MAPPER_CONFIGURATION)
public void setPrefixBasedActionProxyFactories(String list) {
if (list != null) {
String[] factories = list.split(",");
for (String factory : factories) {
String[] thisFactory = factory.split(":");
if (thisFactory.length == 2) {
String factoryPrefix = thisFactory[0].trim();
String factoryName = thisFactory[1].trim();
ActionProxyFactory obj = container.getInstance(ActionProxyFactory.class, factoryName);
if (obj != null) {
actionProxyFactories.put(factoryPrefix, obj);
} else {
LOG.warn("Invalid PrefixBasedActionProxyFactory config entry: [{}]", factory);
}
prefixes = new HashSet<>(Arrays.asList(list.split(",")));
}
}
@Override
public void init() {
for (String factory : prefixes) {
String[] thisFactory = factory.split(":");
if (thisFactory.length == 2) {
String factoryPrefix = thisFactory[0].trim();
String factoryName = thisFactory[1].trim();
ActionProxyFactory obj = container.getInstance(ActionProxyFactory.class, factoryName);
if (obj != null) {
actionProxyFactories.put(factoryPrefix, obj);
} else {
LOG.warn("Invalid PrefixBasedActionProxyFactory config entry: [{}]", factory);
}
}
}
@@ -106,8 +109,7 @@ public class PrefixBasedActionProxyFactory extends DefaultActionProxyFactory {
LOG.debug("No ActionProxyFactory defined for [{}]", key);
}
}
LOG.debug("Cannot find any matching ActionProxyFactory, falling back to [{}]", defaultFactory);
return defaultFactory.createActionProxy(namespace, actionName, methodName, extraContext, executeResult, cleanupContext);
LOG.debug("Cannot find any matching ActionProxyFactory, falling back to [{}]", super.getClass().getName());
return super.createActionProxy(namespace, actionName, methodName, extraContext, executeResult, cleanupContext);
}
}
@@ -31,10 +31,11 @@ public class BackgroundProcess implements Serializable {
private static final long serialVersionUID = 3884464776311686443L;
protected Object action;
protected ActionInvocation invocation;
//WW-4900 transient since 2.5.15
transient protected ActionInvocation invocation;
transient protected Exception exception;
protected String result;
protected Exception exception;
protected boolean done;
/**
@@ -46,7 +47,6 @@ public class BackgroundProcess implements Serializable {
*/
public BackgroundProcess(String threadName, final ActionInvocation invocation, int threadPriority) {
this.invocation = invocation;
this.action = invocation.getAction();
try {
final Thread t = new Thread(new Runnable() {
public void run() {
@@ -96,7 +96,7 @@ public class BackgroundProcess implements Serializable {
* @return the action.
*/
public Object getAction() {
return action;
return invocation.getAction();
}
/**
@@ -26,7 +26,9 @@ import org.apache.struts2.dispatcher.Parameter;
import org.apache.struts2.dispatcher.HttpParameters;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* <!-- START SNIPPET: description -->
@@ -60,14 +62,16 @@ public class CheckboxInterceptor extends AbstractInterceptor {
HttpParameters parameters = ai.getInvocationContext().getParameters();
Map<String, Parameter> extraParams = new HashMap<>();
for (String name : parameters.keySet()) {
Set<String> checkboxParameters = new HashSet<>();
for (Map.Entry<String, Parameter> parameter : parameters.entrySet()) {
String name = parameter.getKey();
if (name.startsWith("__checkbox_")) {
String checkboxName = name.substring("__checkbox_".length());
Parameter value = parameters.get(name);
parameters = parameters.remove(name);
Parameter value = parameter.getValue();
checkboxParameters.add(name);
if (value.isMultiple()) {
LOG.debug("Bypassing automatic checkbox detection due to multiple checkboxes of the same name: {}", name);
LOG.debug("Bypassing automatic checkbox detection due to multiple checkboxes of the same name: {}", name);
continue;
}
@@ -78,7 +82,7 @@ public class CheckboxInterceptor extends AbstractInterceptor {
}
}
}
parameters.remove(checkboxParameters);
ai.getInvocationContext().getParameters().appendAll(extraParams);
@@ -243,6 +243,12 @@ public class ExecuteAndWaitInterceptor extends MethodFilterInterceptor {
synchronized (httpSession) {
BackgroundProcess bp = (BackgroundProcess) session.get(KEY + name);
//WW-4900 Checks if from a de-serialized session? so background thread missed, let's start a new one.
if (bp != null && bp.getInvocation() == null) {
session.remove(KEY + name);
bp = null;
}
if ((!executeAfterValidationPass || secondTime) && bp == null) {
bp = getNewBackgroundProcess(name, actionInvocation, threadPriority);
session.put(KEY + name, bp);
@@ -281,15 +281,17 @@ public class I18nInterceptor extends AbstractInterceptor {
@Override
public Locale store(ActionInvocation invocation, Locale locale) {
//save it in session
Map<String, Object> session = invocation.getInvocationContext().getSession();
HttpSession session = ServletActionContext.getRequest().getSession(false);
if (session != null) {
String sessionId = ServletActionContext.getRequest().getSession().getId();
String sessionId = session.getId();
synchronized (sessionId.intern()) {
session.put(attributeName, locale);
invocation.getInvocationContext().getSession().put(attributeName, locale);
}
} else {
LOG.debug("session creation avoided as it doesn't exist already");
}
return locale;
}
@@ -298,19 +300,15 @@ public class I18nInterceptor extends AbstractInterceptor {
Locale locale = null;
LOG.debug("Checks session for saved locale");
Map<String, Object> session = invocation.getInvocationContext().getSession();
HttpSession session = ServletActionContext.getRequest().getSession(false);
if (session != null) {
//[WW-4741] Do not force session creation while this is a read operation
HttpSession httpSession = ServletActionContext.getRequest().getSession(false);
if(null != httpSession) {
String sessionId = httpSession.getId();
synchronized (sessionId.intern()) {
Object sessionLocale = session.get(attributeName);
if (sessionLocale != null && sessionLocale instanceof Locale) {
locale = (Locale) sessionLocale;
LOG.debug("Applied session locale: {}", locale);
}
String sessionId = session.getId();
synchronized (sessionId.intern()) {
Object sessionLocale = invocation.getInvocationContext().getSession().get(attributeName);
if (sessionLocale != null && sessionLocale instanceof Locale) {
locale = (Locale) sessionLocale;
LOG.debug("Applied session locale: {}", locale);
}
}
}
@@ -215,8 +215,9 @@ public class PostbackResult extends StrutsResultSupport {
private void writeFormElements(HttpServletRequest request, PrintWriter pw) throws UnsupportedEncodingException {
Map<String, String[]> params = request.getParameterMap();
for (String name : params.keySet()) {
String[] values = params.get(name);
for (Map.Entry<String, String[]> entry : params.entrySet()) {
String name = entry.getKey();
String[] values = entry.getValue();
if (isElementIncluded(name, values)) {
writeFormElement(pw, name, values);
}
@@ -137,8 +137,8 @@ public class ServletDispatcherResult extends StrutsResultSupport {
//add parameters passed on the location to #parameters
// see WW-2120
if (StringUtils.isNotEmpty(finalLocation) && finalLocation.indexOf("?") > 0) {
String queryString = finalLocation.substring(finalLocation.indexOf("?") + 1);
if (StringUtils.isNotEmpty(finalLocation) && finalLocation.indexOf('?') > 0) {
String queryString = finalLocation.substring(finalLocation.indexOf('?') + 1);
HttpParameters parameters = getParameters(invocation);
Map<String, Object> queryParams = urlHelper.parseQueryString(queryString, true);
if (queryParams != null && !queryParams.isEmpty()) {
@@ -271,7 +271,7 @@ public class ServletRedirectResult extends StrutsResultSupport implements Reflec
try {
String rawUrl = url;
if (url.contains("?")) {
rawUrl = url.substring(0, url.indexOf("?"));
rawUrl = url.substring(0, url.indexOf('?'));
}
URI uri = URI.create(rawUrl.replaceAll(" ", "%20"));
if (uri.isAbsolute()) {
@@ -20,7 +20,6 @@ package org.apache.struts2.util;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.util.ValueStack;
import java.io.Serializable;
import java.util.HashMap;
@@ -56,10 +55,14 @@ public class InvocationSessionStore {
return null;
}
ValueStack stack = invocationContext.invocation.getStack();
ActionContext.getContext().setValueStack(stack);
ActionInvocation savedInvocation = null;
if (invocationContext.invocation != null) {
savedInvocation = invocationContext.invocation;
ActionContext.setContext(savedInvocation.getInvocationContext());
ActionContext.getContext().setValueStack(savedInvocation.getStack());
}
return invocationContext.invocation.deserialize(ActionContext.getContext());
return savedInvocation;
}
/**
@@ -71,7 +74,7 @@ public class InvocationSessionStore {
* @param invocation the action invocation
*/
public static void storeInvocation(String key, String token, ActionInvocation invocation) {
InvocationContext invocationContext = new InvocationContext(invocation.serialize(), token);
InvocationContext invocationContext = new InvocationContext(invocation, token);
Map invocationMap = getInvocationMap();
invocationMap.put(key, invocationContext);
setInvocationMap(invocationMap);
@@ -109,7 +112,9 @@ public class InvocationSessionStore {
private static final long serialVersionUID = -286697666275777888L;
ActionInvocation invocation;
//WW-4873 transient since 2.5.15
transient ActionInvocation invocation;
String token;
public InvocationContext(ActionInvocation invocation, String token) {
@@ -524,8 +524,8 @@ public class FreemarkerManager {
ScopesHashModel model = buildScopesHashModel(servletContext, request, response, wrapper, stack);
populateContext(model, stack, action, request, response);
if (tagLibraries != null) {
for (String prefix : tagLibraries.keySet()) {
model.put(prefix, tagLibraries.get(prefix).getModels(stack, request, response));
for (Map.Entry<String, TagLibraryModelProvider> entry : tagLibraries.entrySet()) {
model.put(entry.getKey(), entry.getValue().getModels(stack, request, response));
}
}
@@ -98,7 +98,7 @@ public class TagUtils {
// last part (/foo/bar/baz.xyz -> /foo/bar)
String path = RequestUtils.getServletPath(request);
return path.substring(0, path.lastIndexOf("/"));
return path.substring(0, path.lastIndexOf('/'));
}
} else {
return invocation.getProxy().getNamespace();
@@ -146,7 +146,7 @@ public abstract class AbstractDirective extends Directive {
// node.value uses the StrutsValueStack to evaluate the directive's value parameter
String param = node.value(contextAdapter).toString();
int idx = param.indexOf("=");
int idx = param.indexOf('=');
if (idx != -1) {
String property = param.substring(0, idx);
+1 -1
View File
@@ -12,7 +12,7 @@ Please do not edit it directly.
<h2>Description</h2>
<p>
<!-- START SNIPPET: tagdescription -->
Prints debugging information
Prints debugging information (Only if 'struts.devMode' is enabled)
<!-- END SNIPPET: tagdescription -->
</p>
@@ -18,6 +18,9 @@
*/
package com.opensymphony.xwork2;
import com.opensymphony.xwork2.conversion.annotations.TypeConversion;
import com.opensymphony.xwork2.conversion.impl.FooBarConverter;
import com.opensymphony.xwork2.util.Bar;
import com.opensymphony.xwork2.validator.annotations.IntRangeFieldValidator;
import com.opensymphony.xwork2.validator.annotations.RequiredStringValidator;
import com.opensymphony.xwork2.validator.annotations.Validations;
@@ -37,6 +40,7 @@ public class AnnotatedTestBean {
private Date birth;
private String name;
private int count;
private Bar bar;
//~ Constructors ///////////////////////////////////////////////////////////
@@ -76,4 +80,13 @@ public class AnnotatedTestBean {
public String getName() {
return name;
}
public Bar getSupperBarObj() {
return bar;
}
@TypeConversion(converter = "com.opensymphony.xwork2.conversion.impl.FooBarConverter")
public void setSupperBarObj(Bar b) {
bar = b;
}
}
@@ -23,9 +23,8 @@ import com.opensymphony.xwork2.config.entities.InterceptorMapping;
import com.opensymphony.xwork2.config.entities.ResultConfig;
import com.opensymphony.xwork2.config.providers.XmlConfigurationProvider;
import com.opensymphony.xwork2.mock.MockActionProxy;
import com.opensymphony.xwork2.mock.MockContainer;
import com.opensymphony.xwork2.mock.MockInterceptor;
import com.opensymphony.xwork2.mock.MockLazyInterceptor;
import com.opensymphony.xwork2.mock.MockResult;
import com.opensymphony.xwork2.ognl.OgnlUtil;
import com.opensymphony.xwork2.util.ValueStack;
import com.opensymphony.xwork2.util.ValueStackFactory;
@@ -34,7 +33,6 @@ import org.apache.struts2.dispatcher.HttpParameters;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
@@ -69,38 +67,22 @@ public class DefaultActionInvocationTest extends XWorkTestCase {
container.inject(defaultActionInvocation);
defaultActionInvocation.stack = container.getInstance(ValueStackFactory.class).createValueStack();
defaultActionInvocation.setResultCode("");//is possible when result is not executed already
defaultActionInvocation.invoke();
assertTrue(mockInterceptor1.isExecuted());
assertTrue(mockInterceptor2.isExecuted());
assertTrue(mockInterceptor3.isExecuted());
}
public void testSerialization() throws Exception {
// given
DefaultActionInvocation actionInvocation = new DefaultActionInvocation(new HashMap<String, Object>(), false);
actionInvocation.setContainer(new MockContainer());
// when
DefaultActionInvocation serializable = (DefaultActionInvocation) actionInvocation.serialize();
// then
assertNull(actionInvocation.container);
assertNull(serializable.container);
}
public void testDeserialization() throws Exception {
// given
DefaultActionInvocation actionInvocation = new DefaultActionInvocation(new HashMap<String, Object>(), false);
MockContainer mockContainer = new MockContainer();
ActionContext.getContext().setContainer(mockContainer);
// when
DefaultActionInvocation deserializable = (DefaultActionInvocation) actionInvocation.deserialize(ActionContext.getContext());
// then
assertNotNull(actionInvocation.container);
assertNotNull(deserializable.container);
assertEquals(mockContainer, deserializable.container);
assertTrue(defaultActionInvocation.isExecuted());
try {
defaultActionInvocation.setResultCode("");
fail("should not possible when result already executed");
} catch (Exception ignored) {
}
try {
defaultActionInvocation.invoke();
fail("should not possible when result already executed");
} catch (Exception ignored) {
}
}
public void testInvokingExistingExecuteMethod() throws Exception {
@@ -340,6 +322,74 @@ public class DefaultActionInvocationTest extends XWorkTestCase {
assertEquals("this is blah", action.getName());
}
public void testActionEventListener() throws Exception {
ActionProxy actionProxy = actionProxyFactory.createActionProxy("",
"ExceptionFoo", "exceptionMethod", new HashMap<String, Object>());
DefaultActionInvocation defaultActionInvocation = (DefaultActionInvocation) actionProxy.getInvocation();
SimpleActionEventListener actionEventListener = new SimpleActionEventListener("prepared", "exceptionHandled");
defaultActionInvocation.setActionEventListener(actionEventListener);
defaultActionInvocation.init(actionProxy);
SimpleAction action = (SimpleAction) defaultActionInvocation.getAction();
action.setThrowException(true);
defaultActionInvocation.unknownHandlerManager = new DefaultUnknownHandlerManager() {
@Override
public boolean hasUnknownHandlers() {
return false;
}
};
String result = defaultActionInvocation.invoke();
// then
assertEquals("prepared", action.getName());
assertEquals("exceptionHandled", result);
}
public void testActionChainResult() throws Exception {
ActionProxy actionProxy = actionProxyFactory.createActionProxy("", "Foo", null,
new HashMap<String, Object>());
DefaultActionInvocation defaultActionInvocation = (DefaultActionInvocation) actionProxy.getInvocation();
defaultActionInvocation.init(actionProxy);
SimpleAction action = (SimpleAction) defaultActionInvocation.getAction();
action.setFoo(1);
action.setBar(2);
defaultActionInvocation.invoke();
// then
assertTrue(defaultActionInvocation.result instanceof ActionChainResult);
Result result = defaultActionInvocation.getResult();
assertTrue(result instanceof MockResult);
}
public void testNoResultDefined() throws Exception {
ActionProxy actionProxy = actionProxyFactory.createActionProxy("", "Foo", null,
new HashMap<String, Object>());
DefaultActionInvocation defaultActionInvocation = (DefaultActionInvocation) actionProxy.getInvocation();
defaultActionInvocation.init(actionProxy);
try {
defaultActionInvocation.invoke();//foo==bar so returns error which is not defined
fail("should not possible when result is not defined");
} catch (Exception ignored) {
}
}
public void testNullResultPossible() throws Exception {
ActionProxy actionProxy = actionProxyFactory.createActionProxy("",
"NullFoo", "nullMethod", new HashMap<String, Object>());
DefaultActionInvocation defaultActionInvocation = (DefaultActionInvocation) actionProxy.getInvocation();
defaultActionInvocation.init(actionProxy);
String result = defaultActionInvocation.invoke();
assertNull(result);
}
@Override
protected void setUp() throws Exception {
super.setUp();
@@ -350,6 +400,29 @@ public class DefaultActionInvocationTest extends XWorkTestCase {
loadConfigurationProviders(configurationProvider);
}
private class SimpleActionEventListener implements ActionEventListener {
private String name;
private String result;
SimpleActionEventListener(String name, String result) {
this.name = name;
this.result = result;
}
@Override
public Object prepare(Object action, ValueStack stack) {
((SimpleAction)action).setName(name);
return action;
}
@Override
public String handleException(Throwable t, ValueStack stack) {
return result;
}
}
}
class DefaultActionInvocationTester extends DefaultActionInvocation {
@@ -0,0 +1,83 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package com.opensymphony.xwork2;
import com.opensymphony.xwork2.config.entities.ActionConfig;
import com.opensymphony.xwork2.config.providers.SomeUnknownHandler;
import junit.framework.TestCase;
import java.util.ArrayList;
/**
* Partial test to the DefaultUnknownHandlerManager to understand the relationship between Manager and Handlers.
*
*/
public class DefaultUnknownHandlerManagerTest extends TestCase {
ActionConfig actionConfig;
SomeUnknownHandler someUnknownHandler;
/**
* Relationshsip when UnknownAction method is called.
*
*/
public void testHandleUnknownAction() {
DefaultUnknownHandlerManager defaultUnknownHandlerManager = new DefaultUnknownHandlerManager();
defaultUnknownHandlerManager.unknownHandlers = new ArrayList<>();
defaultUnknownHandlerManager.unknownHandlers.add(someUnknownHandler);
ActionConfig newActionConfig = defaultUnknownHandlerManager.handleUnknownAction("arbitraryNameSpace", "arbitraryActionName");
assertEquals(newActionConfig, actionConfig);
}
/**
* Relationship when UnknownActionMethod method called.
*
*/
public void testHandelUnknownActionMethod() throws Exception {
DefaultUnknownHandlerManager defaultUnknownHandlerManager = new DefaultUnknownHandlerManager();
defaultUnknownHandlerManager.unknownHandlers = new ArrayList<>();
defaultUnknownHandlerManager.unknownHandlers.add(someUnknownHandler);
String result = null;
for (int i = 0; i < 10 ; i++) {
result = (String) defaultUnknownHandlerManager.handleUnknownMethod(this, "someMethodName");
assertEquals(result, "specialActionMethod");
}
}
@Override
protected void setUp() throws Exception {
super.setUp();
// Make sure we are using the actionConfig we initialized.
ActionConfig.Builder actionConfigBuilder = new ActionConfig.Builder( "com", "someAction", "someClass");
actionConfig = actionConfigBuilder.build();
someUnknownHandler = new SomeUnknownHandler();
someUnknownHandler.setActionConfig(actionConfig);
someUnknownHandler.setActionMethodResult("specialActionMethod");
}
}
@@ -229,6 +229,10 @@ public class SimpleAction extends ActionSupport {
return "OK";
}
public String nullMethod() {
return null;
}
@Override
public String execute() throws Exception {
if (foo == bar) {
@@ -23,6 +23,7 @@ import com.mockobjects.dynamic.Mock;
import com.opensymphony.xwork2.FileManagerFactory;
import com.opensymphony.xwork2.XWorkTestCase;
import com.opensymphony.xwork2.config.providers.XWorkConfigurationProvider;
import com.opensymphony.xwork2.conversion.TypeConverterHolder;
import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.inject.ContainerBuilder;
import com.opensymphony.xwork2.util.location.LocatableProperties;
@@ -152,6 +153,12 @@ public class ConfigurationManagerTest extends XWorkTestCase {
configProviderMock.verify();
}
public void testEarlyInitializable() throws Exception {
TypeConverterHolder converterHolder = container.getInstance(TypeConverterHolder.class);
assertTrue("java.io.File mapping should being putted by DefaultConversionPropertiesProcessor.init()",
converterHolder.containsDefaultMapping("java.io.File"));
}
@Override
protected void setUp() throws Exception {
super.setUp();
@@ -177,7 +177,7 @@ public class AnnotationXWorkConverterTest extends XWorkTestCase {
assertEquals("Invalid field value for field \"foo\".", message);
}
public void testFindConversionMappingForInterface() {
public void testFindConversionMappingForInterfaceAndSuperclass() {
ModelDrivenAnnotationAction2 action = new ModelDrivenAnnotationAction2();
ValueStack stack = ActionContext.getContext().getValueStack();
stack.push(action);
@@ -193,6 +193,14 @@ public class AnnotationXWorkConverterTest extends XWorkTestCase {
Bar b = (Bar) o;
assertEquals(value, b.getTitle() + ":" + b.getSomethingElse());
String value2 = "qwer:456";
Object o2 = converter.convertValue(ognlStackContext, action.getModel(), null, "supperBarObj", value2, Bar.class);
assertNotNull(o2);
assertTrue("class is: " + o.getClass(), o2 instanceof Bar);
Bar b2 = (Bar) o2;
assertEquals(value2, b2.getTitle() + ":" + b2.getSomethingElse());
}
public void testLocalizedDateConversion() throws Exception {
@@ -20,6 +20,9 @@ package com.opensymphony.xwork2.inject;
import junit.framework.TestCase;
import java.security.Permission;
import java.util.concurrent.Callable;
/**
* ContainerImpl Tester.
*
@@ -33,11 +36,25 @@ public class ContainerImplTest extends TestCase {
@Override
protected void setUp() throws Exception {
System.setSecurityManager(null);
super.setUp();
ContainerBuilder cb = new ContainerBuilder();
cb.constant("methodCheck.name", "Lukasz");
cb.constant("fieldCheck.name", "Lukasz");
cb.factory(EarlyInitializable.class, EarlyInitializableBean.class, Scope.SINGLETON);
cb.factory(Initializable.class, InitializableBean.class, Scope.SINGLETON);
cb.factory(EarlyInitializable.class, "prototypeEarlyInitializable", EarlyInitializableBean.class, Scope.PROTOTYPE);
cb.factory(Initializable.class, "prototypeInitializable", InitializableBean.class, Scope.PROTOTYPE);
cb.factory(Initializable.class, "requestInitializable", InitializableBean.class, Scope.REQUEST);
cb.factory(Initializable.class, "sessionInitializable", InitializableBean.class, Scope.SESSION);
cb.factory(Initializable.class, "threadInitializable", InitializableBean.class, Scope.THREAD);
cb.factory(Initializable.class, "wizardInitializable", InitializableBean.class, Scope.WIZARD);
c = cb.create(false);
c.setScopeStrategy(new TestScopeStrategy());
Class.forName(FieldCheck.class.getName());
Class.forName(ContainerImpl.FieldInjector.class.getName());
}
/**
@@ -76,17 +93,15 @@ public class ContainerImplTest extends TestCase {
* Inject values into field under SecurityManager
*/
public void testFieldInjectorWithSecurityEnabled() throws Exception {
System.setSecurityManager(new SecurityManager());
System.setSecurityManager(new TestSecurityManager());
FieldCheck fieldCheck = new FieldCheck();
try {
c.inject(fieldCheck);
assertEquals(fieldCheck.getName(), "Lukasz");
fail("Exception should be thrown!");
} catch (DependencyException expected) {
// that was expected
} catch (Error | DependencyException expected) {
assertTrue(true);
}
}
@@ -94,22 +109,97 @@ public class ContainerImplTest extends TestCase {
* Inject values into method under SecurityManager
*/
public void testMethodInjectorWithSecurityEnabled() throws Exception {
// not needed, already set
//System.setSecurityManager(new SecurityManager());
System.setSecurityManager(new TestSecurityManager());
MethodCheck methodCheck = new MethodCheck();
try {
c.inject(methodCheck);
assertEquals(methodCheck.getName(), "Lukasz");
fail("Exception sould be thrown!");
} catch (DependencyException expected) {
// that was expected
fail("Exception should be thrown!");
} catch (DependencyException | Error expected) {
assertTrue(true);
}
}
class FieldCheck {
public void testEarlyInitializable() throws Exception {
assertTrue("should being initialized already", EarlyInitializableBean.initializedEarly);
EarlyInitializableCheck earlyInitializableCheck = new EarlyInitializableCheck();
c.inject(earlyInitializableCheck);
assertEquals("initialized early", ((EarlyInitializableBean) earlyInitializableCheck.getEarlyInitializable()).getMessage());
assertEquals("initialized early", ((EarlyInitializableBean) earlyInitializableCheck.getPrototypeEarlyInitializable()).getMessage());
EarlyInitializableCheck earlyInitializableCheck2 = new EarlyInitializableCheck();
c.inject(earlyInitializableCheck2);
assertEquals("singletons should not being initialized twice", "initialized early",
((EarlyInitializableBean) earlyInitializableCheck2.getEarlyInitializable()).getMessage());
assertEquals("initialized early", ((EarlyInitializableBean) earlyInitializableCheck2.getPrototypeEarlyInitializable()).getMessage());
assertEquals("singletons should being instantiated once",
earlyInitializableCheck.getEarlyInitializable(), earlyInitializableCheck2.getEarlyInitializable());
assertNotSame("prototypes should being instantiated for each injection",
earlyInitializableCheck.getPrototypeEarlyInitializable(), earlyInitializableCheck2.getPrototypeEarlyInitializable());
}
public void testInitializable() throws Exception {
assertFalse("should not being initialized already", InitializableBean.initialized);
InitializableCheck initializableCheck = new InitializableCheck();
c.inject(initializableCheck);
assertTrue("should being initialized here", InitializableBean.initialized);
assertEquals("initialized", ((InitializableBean) initializableCheck.getInitializable()).getMessage());
assertEquals("initialized", ((InitializableBean) initializableCheck.getPrototypeInitializable()).getMessage());
InitializableCheck initializableCheck2 = new InitializableCheck();
c.inject(initializableCheck2);
assertEquals("singletons should not being initialized twice", "initialized",
((InitializableBean) initializableCheck2.getInitializable()).getMessage());
assertEquals("initialized", ((InitializableBean) initializableCheck2.getPrototypeInitializable()).getMessage());
assertEquals("threads should not being initialized twice", "initialized",
((InitializableBean) initializableCheck2.getThreadInitializable()).getMessage());
assertEquals("singletons should being instantiated once",
initializableCheck.getInitializable(), initializableCheck2.getInitializable());
assertNotSame("prototypes should being instantiated for each injection",
initializableCheck.getPrototypeInitializable(), initializableCheck2.getPrototypeInitializable());
assertEquals("threads should being instantiated once for each thread",
initializableCheck.getThreadInitializable(), initializableCheck2.getThreadInitializable());
final InitializableCheck initializableCheck3 = new InitializableCheck();
final TestScopeStrategy testScopeStrategy = new TestScopeStrategy();
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
ContainerBuilder cb2 = new ContainerBuilder();
cb2.factory(EarlyInitializable.class, EarlyInitializableBean.class, Scope.SINGLETON);
cb2.factory(Initializable.class, InitializableBean.class, Scope.SINGLETON);
cb2.factory(EarlyInitializable.class, "prototypeEarlyInitializable", EarlyInitializableBean.class, Scope.PROTOTYPE);
cb2.factory(Initializable.class, "prototypeInitializable", InitializableBean.class, Scope.PROTOTYPE);
cb2.factory(Initializable.class, "requestInitializable", InitializableBean.class, Scope.REQUEST);
cb2.factory(Initializable.class, "sessionInitializable", InitializableBean.class, Scope.SESSION);
cb2.factory(Initializable.class, "threadInitializable", InitializableBean.class, Scope.THREAD);
cb2.factory(Initializable.class, "wizardInitializable", InitializableBean.class, Scope.WIZARD);
Container c2 = cb2.create(false);
c2.setScopeStrategy(testScopeStrategy);
c2.inject(initializableCheck3);
}
});
thread.run();
thread.join();
assertNotSame("threads should being instantiated in new threads",
initializableCheck.getThreadInitializable(), initializableCheck3.getThreadInitializable());
assertEquals("initialized", ((InitializableBean) initializableCheck3.getThreadInitializable()).getMessage());
assertEquals("initialized", ((InitializableBean) initializableCheck3.getRequestInitializable()).getMessage());
assertEquals("initialized", ((InitializableBean) initializableCheck3.getSessionInitializable()).getMessage());
assertEquals("initialized", ((InitializableBean) initializableCheck3.getWizardInitializable()).getMessage());
assertEquals(testScopeStrategy.requestInitializable, initializableCheck3.getRequestInitializable());
assertEquals(testScopeStrategy.sessionInitializable, initializableCheck3.getSessionInitializable());
assertEquals(testScopeStrategy.wizardInitializable, initializableCheck3.getWizardInitializable());
}
public static class FieldCheck {
@Inject("fieldCheck.name")
private String name;
@@ -119,7 +209,7 @@ public class ContainerImplTest extends TestCase {
}
}
class MethodCheck {
public static class MethodCheck {
private String name;
@@ -134,4 +224,131 @@ public class ContainerImplTest extends TestCase {
}
class InitializableCheck {
private Initializable initializable;
private Initializable prototypeInitializable;
private Initializable requestInitializable;
private Initializable sessionInitializable;
private Initializable threadInitializable;
private Initializable wizardInitializable;
@Inject
public void setInitializable(Initializable initializable) {
this.initializable = initializable;
}
@Inject("prototypeInitializable")
public void setPrototypeInitializable(Initializable prototypeInitializable) {
this.prototypeInitializable = prototypeInitializable;
}
@Inject("requestInitializable")
public void setRequestInitializable(Initializable requestInitializable) {
this.requestInitializable = requestInitializable;
}
@Inject("sessionInitializable")
public void setSessionInitializable(Initializable sessionInitializable) {
this.sessionInitializable = sessionInitializable;
}
@Inject("threadInitializable")
public void setThreadInitializable(Initializable threadInitializable) {
this.threadInitializable = threadInitializable;
}
@Inject("wizardInitializable")
public void setWizardInitializable(Initializable wizardInitializable) {
this.wizardInitializable = wizardInitializable;
}
public Initializable getRequestInitializable() {
return requestInitializable;
}
public Initializable getSessionInitializable() {
return sessionInitializable;
}
public Initializable getThreadInitializable() {
return threadInitializable;
}
public Initializable getWizardInitializable() {
return wizardInitializable;
}
public Initializable getInitializable() {
return initializable;
}
public Initializable getPrototypeInitializable() {
return prototypeInitializable;
}
}
class EarlyInitializableCheck {
private EarlyInitializable earlyInitializable;
private EarlyInitializable prototypeEarlyInitializable;
@Inject
public void setEarlyInitializable(EarlyInitializable earlyInitializable) {
this.earlyInitializable = earlyInitializable;
}
@Inject("prototypeEarlyInitializable")
public void setPrototypeEarlyInitializable(EarlyInitializable prototypeEarlyInitializable) {
this.prototypeEarlyInitializable = prototypeEarlyInitializable;
}
public EarlyInitializable getEarlyInitializable() {
return earlyInitializable;
}
public EarlyInitializable getPrototypeEarlyInitializable() {
return prototypeEarlyInitializable;
}
}
class TestSecurityManager extends SecurityManager {
@Override
public void checkPermission(Permission perm) {
if (!"setSecurityManager".equals(perm.getName())) {
super.checkPermission(perm);
}
}
}
class TestScopeStrategy implements Scope.Strategy {
Initializable requestInitializable;
Initializable sessionInitializable;
Initializable wizardInitializable;
@Override
public <T> T findInRequest(Class<T> type, String name, Callable<? extends T> factory) throws Exception {
if (requestInitializable == null) {
requestInitializable = (Initializable) factory.call();
}
return (T) requestInitializable;
}
@Override
public <T> T findInSession(Class<T> type, String name, Callable<? extends T> factory) throws Exception {
if (sessionInitializable == null) {
sessionInitializable = (Initializable) factory.call();
}
return (T) sessionInitializable;
}
@Override
public <T> T findInWizard(Class<T> type, String name, Callable<? extends T> factory) throws Exception {
if (wizardInitializable == null) {
wizardInitializable = (Initializable) factory.call();
}
return (T) wizardInitializable;
}
}
}
@@ -0,0 +1,34 @@
/*
* 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.inject;
class EarlyInitializableBean implements EarlyInitializable {
private String message = "";
static boolean initializedEarly;
public String getMessage() {
return message;
}
@Override
public void init() {
message += "initialized early";
initializedEarly = true;
}
}
@@ -0,0 +1,34 @@
/*
* 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.inject;
public class InitializableBean implements Initializable {
private String message = "";
static boolean initialized;
public String getMessage() {
return message;
}
@Override
public void init() {
message += "initialized";
initialized = true;
}
}
@@ -582,7 +582,7 @@ public class OgnlUtilTest extends XWorkTestCase {
// just do some of the 15 tests
Map beans = ognlUtil.getBeanMap(foo);
assertNotNull(beans);
assertEquals(19, beans.size());
assertEquals(21, beans.size());
assertEquals("Hello Santa", beans.get("title"));
assertEquals(new Long("123"), beans.get("ALong"));
assertEquals(new Integer("44"), beans.get("number"));
@@ -733,11 +733,24 @@ public class OgnlValueStackTest extends XWorkTestCase {
assertEquals("Cat One", ((Cat) foo.getCats().get(0)).getName());
assertEquals("Cat Two", ((Cat) foo.getCats().get(1)).getName());
vs.setValue("annotatedCats[0].name", "Cat One By Annotation");
vs.setValue("annotatedCats[1].name", "Cat Two By Annotation");
assertNotNull(foo.getAnnotatedCats());
assertEquals(2, foo.getAnnotatedCats().size());
assertEquals("Cat One By Annotation", ((Cat) foo.getAnnotatedCats().get(0)).getName());
assertEquals("Cat Two By Annotation", ((Cat) foo.getAnnotatedCats().get(1)).getName());
vs.setValue("cats[0].foo.cats[1].name", "Deep null cat");
assertNotNull(((Cat) foo.getCats().get(0)).getFoo());
assertNotNull(((Cat) foo.getCats().get(0)).getFoo().getCats());
assertNotNull(((Cat) foo.getCats().get(0)).getFoo().getCats().get(1));
assertEquals("Deep null cat", ((Cat) ((Cat) foo.getCats().get(0)).getFoo().getCats().get(1)).getName());
vs.setValue("annotatedCats[0].foo.annotatedCats[1].name", "Deep null cat by annotation");
assertNotNull(((Cat) foo.getAnnotatedCats().get(0)).getFoo());
assertNotNull(((Cat) foo.getAnnotatedCats().get(0)).getFoo().getAnnotatedCats());
assertNotNull(((Cat) foo.getAnnotatedCats().get(0)).getFoo().getAnnotatedCats().get(1));
assertEquals("Deep null cat by annotation", ((Cat) ((Cat) foo.getAnnotatedCats().get(0)).getFoo().getAnnotatedCats().get(1)).getName());
}
public void testSetMultiple() {
@@ -144,7 +144,11 @@ public class SetPropertiesTest extends XWorkTestCase {
public Object create(Context context) throws Exception {
return new MockObjectTypeDeterminer(null,Cat.class,null,allowAdditions);
}
@Override
public Class type() {
return Cat.class;
}
});
}
});
@@ -244,6 +248,7 @@ public class SetPropertiesTest extends XWorkTestCase {
Bar bar2 = new Bar();
bar2.setId(new Long(22));
barColl.add(bar2);
foo.setAnnotatedBarCollection(barColl);
//try modifying bar1 and bar2
//check the logs here to make sure
//the Map is being created
@@ -262,13 +267,31 @@ public class SetPropertiesTest extends XWorkTestCase {
assertEquals(bar1Title, next.getTitle());
}
}
Bar bar3 = new Bar();
bar3.setId(new Long(33));
barColl.add(bar3);
Bar bar4 = new Bar();
bar4.setId(new Long(44));
barColl.add(bar4);
String bar1TitleByAnnotation = "The Phantom Menace By Annotation";
String bar2TitleByAnnotation = "The Clone Wars By Annotation";
vs.setValue("annotatedBarCollection(44).title", bar2TitleByAnnotation);
vs.setValue("annotatedBarCollection(33).title", bar1TitleByAnnotation);
for (Object aBarColl : barColl) {
Bar next = (Bar) aBarColl;
if (next.getId().intValue() == 44) {
assertEquals(bar2TitleByAnnotation, next.getTitle());
} else if (next.getId().intValue() == 33) {
assertEquals(bar1TitleByAnnotation, next.getTitle());
}
}
//now test adding to a collection
String bar3Title = "Revenge of the Sith";
String bar4Title = "A New Hope";
vs.setValue("barCollection.makeNew[4].title", bar4Title, true);
vs.setValue("barCollection.makeNew[0].title", bar3Title, true);
assertEquals(4, barColl.size());
assertEquals(6, barColl.size());
for (Object aBarColl : barColl) {
Bar next = (Bar) aBarColl;
@@ -279,6 +302,24 @@ public class SetPropertiesTest extends XWorkTestCase {
}
}
//now test adding to a collection by annotation
String bar3TitleByAnnotation = "Revenge of the Sith By Annotation";
String bar4TitleByAnnotation = "A New Hope By Annotation";
vs.setValue("annotatedBarCollection.makeNew[5].title", bar4TitleByAnnotation, true);
vs.setValue("annotatedBarCollection.makeNew[1].title", bar3TitleByAnnotation, true);
assertEquals(8, barColl.size());
for (Object aBarColl : barColl) {
Bar next = (Bar) aBarColl;
if (next.getId() == null) {
assertNotNull(next.getTitle());
assertTrue(next.getTitle().equals(bar4TitleByAnnotation)
|| next.getTitle().equals(bar3TitleByAnnotation)
|| next.getTitle().equals(bar4Title)
|| next.getTitle().equals(bar3Title));
}
}
}
public void testAddingToCollectionBasedOnPermission() {
final MockObjectTypeDeterminer determiner = new MockObjectTypeDeterminer(Long.class,Bar.class,"id",true);
@@ -290,7 +331,11 @@ public class SetPropertiesTest extends XWorkTestCase {
public Object create(Context context) throws Exception {
return determiner;
}
@Override
public Class type() {
return determiner.getClass();
}
}, Scope.SINGLETON);
}
});
@@ -22,6 +22,8 @@ import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.XWorkTestCase;
import com.opensymphony.xwork2.util.ListHolder;
import com.opensymphony.xwork2.util.ValueStack;
import ognl.ListPropertyAccessor;
import ognl.PropertyAccessor;
import java.util.ArrayList;
import java.util.List;
@@ -61,4 +63,43 @@ public class XWorkListPropertyAccessorTest extends XWorkTestCase {
assertEquals(new Integer(myList.size()), vs.findValue("strings.size()"));
assertEquals(new Integer(myList.size()), vs.findValue("strings.size"));
}
public void testAutoGrowthCollectionLimit() {
PropertyAccessor accessor = container.getInstance(PropertyAccessor.class, ArrayList.class.getName());
((XWorkListPropertyAccessor) accessor).setAutoGrowCollectionLimit("2");
List<String> myList = new ArrayList<>();
ListHolder listHolder = new ListHolder();
listHolder.setStrings(myList);
ValueStack vs = ActionContext.getContext().getValueStack();
vs.push(listHolder);
vs.setValue("strings[0]", "a");
vs.setValue("strings[1]", "b");
vs.setValue("strings[2]", "c");
vs.setValue("strings[3]", "d");
assertEquals(3, vs.findValue("strings.size()"));
}
public void testDeprecatedAutoGrowCollectionLimit() {
PropertyAccessor accessor = container.getInstance(PropertyAccessor.class, ArrayList.class.getName());
((XWorkListPropertyAccessor) accessor).setDeprecatedAutoGrowCollectionLimit("2");
List<String> myList = new ArrayList<>();
ListHolder listHolder = new ListHolder();
listHolder.setStrings(myList);
ValueStack vs = ActionContext.getContext().getValueStack();
vs.push(listHolder);
vs.setValue("strings[0]", "a");
vs.setValue("strings[1]", "b");
vs.setValue("strings[2]", "c");
vs.setValue("strings[3]", "d");
assertEquals(3, vs.findValue("strings.size()"));
}
}
@@ -86,12 +86,4 @@ public class TestActionInvocation implements ActionInvocation {
public void init(ActionProxy proxy) {
}
public ActionInvocation serialize() {
return this;
}
public ActionInvocation deserialize(ActionContext actionContext) {
return this;
}
}
@@ -32,7 +32,7 @@ import java.util.List;
converterClass=AddressTypeConverter.class),
@TypeConversion(type=ConversionType.APPLICATION,
key="com.opensymphony.xwork2.test.annotations.Person",
converterClass=PersonTypeConverter.class)})
converter="com.opensymphony.xwork2.test.annotations.PersonTypeConverter")})
public class PersonAction {
List<Person> users;
private List<Address> address;
@@ -19,14 +19,19 @@
package com.opensymphony.xwork2.util;
import com.opensymphony.xwork2.util.annotation.Dummy2Class;
import com.opensymphony.xwork2.util.annotation.Dummy3Class;
import com.opensymphony.xwork2.util.annotation.DummyClass;
import com.opensymphony.xwork2.util.annotation.DummyClassExt;
import com.opensymphony.xwork2.util.annotation.MyAnnotation;
import com.opensymphony.xwork2.util.annotation.MyAnnotation2;
import junit.framework.TestCase;
/**
* @author Dan Oxlade, dan d0t oxlade at gmail d0t c0m
*/
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static org.fest.assertions.Assertions.assertThat;
public class AnnotationUtilsTest extends TestCase {
public void testFindAnnotationOnClass() {
@@ -41,4 +46,25 @@ public class AnnotationUtilsTest extends TestCase {
assertEquals("package-test", ns.value());
}
public void testFindAnnotationOnParents() {
MyAnnotation2 ns = AnnotationUtils.findAnnotation(Dummy3Class.class, MyAnnotation2.class);
assertNotNull(ns);
assertEquals("abstract-abstract", ns.value());
}
public void testFindAnnotationsOnAll() {
List<MyAnnotation> annotations = AnnotationUtils.findAnnotations(DummyClassExt.class, MyAnnotation.class);
assertThat(annotations)
.isNotNull()
.isNotEmpty()
.hasSize(5);
Set<String> values = new HashSet<>();
for (MyAnnotation annotation : annotations) {
values.add(annotation.value());
}
assertThat(values).contains("class-test", "package-test", "interface-test", "package2-test");
}
}
@@ -18,6 +18,10 @@
*/
package com.opensymphony.xwork2.util;
import com.opensymphony.xwork2.conversion.annotations.ConversionRule;
import com.opensymphony.xwork2.conversion.annotations.ConversionType;
import com.opensymphony.xwork2.conversion.annotations.TypeConversion;
import java.util.*;
@@ -34,9 +38,11 @@ public class Foo {
Date meeting;
Foo child;
List cats;
List annotatedCats;
List moreCats;
List strings;
Collection barCollection;
Collection annotatedBarCollection;
Map catMap;
Map anotherCatMap;
String title;
@@ -96,6 +102,15 @@ public class Foo {
return cats;
}
public void setAnnotatedCats(List annotatedCats) {
this.annotatedCats = annotatedCats;
}
@TypeConversion(rule = ConversionRule.ELEMENT, converterClass = Cat.class)
public List getAnnotatedCats() {
return annotatedCats;
}
public void setChild(Foo child) {
this.child = child;
}
@@ -154,6 +169,16 @@ public class Foo {
this.barCollection = barCollection;
}
@TypeConversion(rule = ConversionRule.KEY_PROPERTY, value = "id")
public void setAnnotatedBarCollection(Collection annotatedBarCollection) {
this.annotatedBarCollection = annotatedBarCollection;
}
@TypeConversion(rule = ConversionRule.ELEMENT, converter = "com.opensymphony.xwork2.util.Bar")
public Collection getAnnotatedBarCollection() {
return annotatedBarCollection;
}
public void setPoints(long[] points) {
this.points = points;
}
@@ -19,6 +19,10 @@
package com.opensymphony.xwork2.util;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.conversion.annotations.Conversion;
import com.opensymphony.xwork2.conversion.annotations.ConversionRule;
import com.opensymphony.xwork2.conversion.annotations.ConversionType;
import com.opensymphony.xwork2.conversion.annotations.TypeConversion;
import java.util.ArrayList;
import java.util.HashMap;
@@ -30,10 +34,19 @@ import java.util.Map;
*
* @author Rainer Hermanns
*/
@Conversion(
conversions= {
@TypeConversion(key = "KeyProperty_annotatedBeanMap", rule = ConversionRule.KEY_PROPERTY, value = "id"),
@TypeConversion(key = "Element_annotatedBeanMap", rule = ConversionRule.ELEMENT, converterClass = MyBean.class),
@TypeConversion(key = "KeyProperty_annotatedBeanList", rule = ConversionRule.KEY_PROPERTY, value = "id"),
@TypeConversion(key = "Element_annotatedBeanList", rule = ConversionRule.ELEMENT, converterClass = MyBean.class)
})
public class MyBeanAction implements Action {
private List beanList = new ArrayList();
private Map beanMap = new HashMap();
private Map annotatedBeanMap = new HashMap();
private List annotatedBeanList = new ArrayList();
public List getBeanList() {
return beanList;
@@ -51,6 +64,24 @@ public class MyBeanAction implements Action {
this.beanMap = beanMap;
}
public Map getAnnotatedBeanMap() {
return annotatedBeanMap;
}
@TypeConversion(rule = ConversionRule.KEY, converterClass = Long.class)
public void setAnnotatedBeanMap(Map annotatedBeanMap) {
this.annotatedBeanMap = annotatedBeanMap;
}
public List getAnnotatedBeanList() {
return annotatedBeanList;
}
@TypeConversion(rule = ConversionRule.CREATE_IF_NULL, value = "true")
public void setAnnotatedBeanList(List annotatedBeanList) {
this.annotatedBeanList = annotatedBeanList;
}
public String execute() throws Exception {
return SUCCESS;
}
@@ -39,6 +39,9 @@ public class MyBeanActionTest extends XWorkTestCase {
params.put("beanList(1234567890).name", "This is the bla bean");
params.put("beanList(1234567891).name", "This is the 2nd bla bean");
params.put("annotatedBeanList(1234567890).name", "This is the bla bean by annotation");
params.put("annotatedBeanList(1234567891).name", "This is the 2nd bla bean by annotation");
HashMap<String, Object> extraContext = new HashMap<>();
extraContext.put(ActionContext.PARAMETERS, HttpParameters.create(params).build());
@@ -53,6 +56,15 @@ public class MyBeanActionTest extends XWorkTestCase {
assertEquals(new Long(1234567890), Long.valueOf(proxy.getInvocation().getStack().findValue("beanList.get(0).id").toString()));
assertEquals("This is the 2nd bla bean", proxy.getInvocation().getStack().findValue("beanList.get(1).name"));
assertEquals(new Long(1234567891), Long.valueOf(proxy.getInvocation().getStack().findValue("beanList.get(1).id").toString()));
assertEquals(2, Integer.parseInt(proxy.getInvocation().getStack().findValue("annotatedBeanList.size").toString()));
assertEquals(MyBean.class.getName(), proxy.getInvocation().getStack().findValue("annotatedBeanList.get(0)").getClass().getName());
assertEquals(MyBean.class.getName(), proxy.getInvocation().getStack().findValue("annotatedBeanList.get(1)").getClass().getName());
assertEquals("This is the bla bean by annotation", proxy.getInvocation().getStack().findValue("annotatedBeanList.get(0).name"));
assertEquals(new Long(1234567890), Long.valueOf(proxy.getInvocation().getStack().findValue("annotatedBeanList.get(0).id").toString()));
assertEquals("This is the 2nd bla bean by annotation", proxy.getInvocation().getStack().findValue("annotatedBeanList.get(1).name"));
assertEquals(new Long(1234567891), Long.valueOf(proxy.getInvocation().getStack().findValue("annotatedBeanList.get(1).id").toString()));
} catch (Exception e) {
e.printStackTrace();
fail();
@@ -67,6 +79,12 @@ public class MyBeanActionTest extends XWorkTestCase {
params.put("beanMap[1234567890].name", "This is the bla bean");
params.put("beanMap[1234567891].name", "This is the 2nd bla bean");
params.put("annotatedBeanMap[1234567890].id", "1234567890");
params.put("annotatedBeanMap[1234567891].id", "1234567891");
params.put("annotatedBeanMap[1234567890].name", "This is the bla bean by annotation");
params.put("annotatedBeanMap[1234567891].name", "This is the 2nd bla bean by annotation");
HashMap<String, Object> extraContext = new HashMap<>();
extraContext.put(ActionContext.PARAMETERS, HttpParameters.create(params).build());
@@ -89,6 +107,21 @@ public class MyBeanActionTest extends XWorkTestCase {
assertEquals("1234567890", proxy.getInvocation().getStack().findValue("beanMap.get(1234567890L).id").toString());
assertEquals("1234567891", proxy.getInvocation().getStack().findValue("beanMap.get(1234567891L).id").toString());
assertEquals(2, Integer.parseInt(proxy.getInvocation().getStack().findValue("annotatedBeanMap.size").toString()));
assertEquals(true, action.getAnnotatedBeanMap().containsKey(1234567890L));
assertEquals(true, action.getAnnotatedBeanMap().containsKey(1234567891L));
assertEquals(MyBean.class.getName(), proxy.getInvocation().getStack().findValue("annotatedBeanMap.get(1234567890L)").getClass().getName());
assertEquals(MyBean.class.getName(), proxy.getInvocation().getStack().findValue("annotatedBeanMap.get(1234567891L)").getClass().getName());
assertEquals("This is the bla bean by annotation", proxy.getInvocation().getStack().findValue("annotatedBeanMap.get(1234567890L).name"));
assertEquals("This is the 2nd bla bean by annotation", proxy.getInvocation().getStack().findValue("annotatedBeanMap.get(1234567891L).name"));
assertEquals("1234567890", proxy.getInvocation().getStack().findValue("annotatedBeanMap.get(1234567890L).id").toString());
assertEquals("1234567891", proxy.getInvocation().getStack().findValue("annotatedBeanMap.get(1234567891L).id").toString());
} catch (Exception e) {
e.printStackTrace();
fail();
@@ -0,0 +1,25 @@
/*
* 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.util.annotation;
import com.opensymphony.xwork2.util.annotation.pkg1.AbstractDummyAction;
public class Dummy3Class extends AbstractDummyAction{
}
@@ -18,8 +18,10 @@
*/
package com.opensymphony.xwork2.util.annotation;
import com.opensymphony.xwork2.util.annotation.pkg2.Package2DummyInterface;
@MyAnnotation("class-test")
public class DummyClass {
public class DummyClass implements Package2DummyInterface {
public DummyClass() {
}
@@ -23,4 +23,6 @@ import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation2 {
String value() default "";
}
@@ -0,0 +1,25 @@
/*
* 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.util.annotation.pkg1;
import com.opensymphony.xwork2.util.annotation.MyAnnotation2;
@MyAnnotation2("abstract-abstract")
public class AbstractAbstractDummyAction {
}
@@ -0,0 +1,22 @@
/*
* 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.util.annotation.pkg1;
public class AbstractDummyAction extends AbstractAbstractDummyAction {
}
@@ -0,0 +1,25 @@
/*
* 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.util.annotation.pkg2;
import com.opensymphony.xwork2.util.annotation.MyAnnotation;
@MyAnnotation("interface-test")
public interface Package2DummyInterface {
}

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