mirror of
https://github.com/apache/struts.git
synced 2026-08-09 16:46:55 +00:00
Compare commits
235 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 96c38b27e4 | |||
| e9108f0feb | |||
| 3653f6e9e1 | |||
| 5a2323a19f | |||
| ea09a8828b | |||
| 17fe42c0f7 | |||
| 87fa5bddf2 | |||
| 45161e88f9 | |||
| 4456620b51 | |||
| f4a8f42ca4 | |||
| a6f8895c90 | |||
| 46dbdbe018 | |||
| 6f176cc5a7 | |||
| db085dbeef | |||
| c3205888b4 | |||
| ce9789c4c9 | |||
| b431755332 | |||
| c694c6fdfb | |||
| 1d49a06272 | |||
| 264e03b7ed | |||
| 2f99110189 | |||
| d33930f3bd | |||
| 360927931c | |||
| bff033e6a3 | |||
| 536d2db4b8 | |||
| 32c8f6f811 | |||
| 06e5dfc2ab | |||
| fe630db5f2 | |||
| b75c0ba5e1 | |||
| 8ab102209f | |||
| d7a7c909ea | |||
| 24e151143e | |||
| c5272d2a0d | |||
| 11de8810e4 | |||
| 75dc4c00d2 | |||
| 415ce3e165 | |||
| fe6257b995 | |||
| 3b81a9edaa | |||
| 478a9b8f1c | |||
| 12c3d23bdf | |||
| 8293add6aa | |||
| ecb7beef38 | |||
| bac0f2953c | |||
| 7bca98205d | |||
| 567fae9dfc | |||
| f4d7dbe88e | |||
| 289782c583 | |||
| 1e33df947a | |||
| de1a42d34a | |||
| b816cc1374 | |||
| b802baa4a9 | |||
| daac47ee62 | |||
| b1709c5208 | |||
| daf6f6f749 | |||
| 029892d656 | |||
| 65fb0d4a44 | |||
| 58d87025e9 | |||
| 83c0f1cd1e | |||
| eacc002334 | |||
| 45effc3822 | |||
| bad9a49060 | |||
| a75eddb515 | |||
| 6e87474f9a | |||
| 9fcbd912bc | |||
| fbc780e779 | |||
| 6eb83016e3 | |||
| 3ec7fa9d80 | |||
| f9806ee4e1 | |||
| d175836757 | |||
| e55ad3fb45 | |||
| 6efaf900d4 | |||
| b3bad5ea44 | |||
| 1fec084542 | |||
| 26babb6fdf | |||
| 6e691ffdbf | |||
| 4b540f1ab4 | |||
| a292920462 | |||
| 10ba97f08f | |||
| 54fa3f7b04 | |||
| a448b8a91b | |||
| 6c5b79c204 | |||
| b806c11ea5 | |||
| a64b8b27fb | |||
| b4ca6ceec9 | |||
| 7619ed3076 | |||
| 8bf6605e6b | |||
| 8d91536b17 | |||
| 02c347afd4 | |||
| ce1ac9d381 | |||
| 9260720568 | |||
| 67ecf3a216 | |||
| 4260bee634 | |||
| f929673a91 | |||
| 4718dccd1c | |||
| 155dff745e | |||
| def4adacd6 | |||
| 2c0facc3aa | |||
| 6ae63d7669 | |||
| eed364fe28 | |||
| 56c6456c61 | |||
| 20a7b6503f | |||
| 2eb809144a | |||
| 8ef8581d0c | |||
| caeea2145f | |||
| b5cfccace3 | |||
| 95a24c87a4 | |||
| 92e931d169 | |||
| e9a4bcd776 | |||
| 9b892cc7fd | |||
| 74460831dc | |||
| 8d4194bbf7 | |||
| cf71e0f34f | |||
| 97012c78a9 | |||
| f34cf78ae3 | |||
| 72058bf4c6 | |||
| 46462b3d00 | |||
| a9899b6622 | |||
| a3180e3069 | |||
| d5926b7a21 | |||
| 9beb940136 | |||
| 17b598433d | |||
| 86baf21098 | |||
| bba17b6496 | |||
| c3b85aef7c | |||
| 355e4b517f | |||
| 1ec1e71d41 | |||
| e23197ab1b | |||
| c21fb94921 | |||
| 9bf89f82dc | |||
| 29e1847f64 | |||
| 02b61b2c05 | |||
| d1df216b32 | |||
| 7518e809dd | |||
| 2b2de35519 | |||
| 87b2bd8138 | |||
| 215eb627d4 | |||
| 29b29a9c64 | |||
| 5e237e0fd3 | |||
| 23077d3ee9 | |||
| 4fbded7147 | |||
| f9f0f36d29 | |||
| f60095bae9 | |||
| e4a3669061 | |||
| 5e4b385984 | |||
| 38450c9a70 | |||
| df4a18107f | |||
| 9739291d79 | |||
| 4394238b7e | |||
| ff903e7754 | |||
| 77cbafb74d | |||
| faf50183e0 | |||
| 67bdf68419 | |||
| cf19d5130c | |||
| a66f92c66d | |||
| 08c058ea65 | |||
| cd0773fb1b | |||
| f803ddb16e | |||
| 0deea3f9e4 | |||
| 2fd2cb5f97 | |||
| 7df1e67bac | |||
| dd76d39224 | |||
| 476697f99a | |||
| 0bfdc5e643 | |||
| a29051c835 | |||
| 7a251ddca9 | |||
| 38e3734930 | |||
| fe65910e0e | |||
| 1d0b375afd | |||
| 0713da431c | |||
| 79951db2aa | |||
| d64365770d | |||
| b04683181c | |||
| fad603c49b | |||
| 87c2746fc7 | |||
| 49ef41c218 | |||
| e1767d9525 | |||
| 67b7fc3f3e | |||
| a4183b63f8 | |||
| 74dd8d6990 | |||
| 4152cbc71c | |||
| dd8ba56425 | |||
| 8eb282f76b | |||
| eabf4eda21 | |||
| 809d8968ca | |||
| 033573e8fb | |||
| 7cdc319b41 | |||
| 7116ac8801 | |||
| a2710a6f04 | |||
| 457f7c2f57 | |||
| 6fc695970f | |||
| 15e6be1d66 | |||
| eddab32e75 | |||
| fb23b719f2 | |||
| c4bd87e510 | |||
| 004b4981a1 | |||
| 8b608e334a | |||
| 031bd1aceb | |||
| 9078422496 | |||
| 278a2112b9 | |||
| 2941416a18 | |||
| 4738d1d802 | |||
| d4b620d06d | |||
| cf8ba17454 | |||
| a8ecd9bd29 | |||
| 6b131550a6 | |||
| dda3facc26 | |||
| d082aaca2a | |||
| 94758b699c | |||
| bfa119c7db | |||
| 7058872723 | |||
| 6c3316a14a | |||
| e717e469a4 | |||
| 80698ed516 | |||
| 9b9bd212ed | |||
| 0856157e27 | |||
| bea5537e9e | |||
| 7c7d5be441 | |||
| a728c74f21 | |||
| 76d0bc5ff4 | |||
| ad98881dd5 | |||
| 0207c09b11 | |||
| 5ef7687259 | |||
| 794cd5e141 | |||
| a2824b7c87 | |||
| 941374ecdb | |||
| 0d42ff50ac | |||
| 00b5f43c8a | |||
| 87967b9f84 | |||
| d36ec579fc | |||
| 0bde78cc36 | |||
| c14dca5be3 | |||
| 7c4f30f619 | |||
| c956d1a40d | |||
| a92eb529a9 | |||
| 882a6e803e |
@@ -62,3 +62,4 @@ bundles/target
|
||||
plugins/target
|
||||
target
|
||||
plugins/testng/test-output
|
||||
test-output
|
||||
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
import java.net.*;
|
||||
import java.io.*;
|
||||
import java.nio.channels.*;
|
||||
import java.util.Properties;
|
||||
|
||||
public class MavenWrapperDownloader {
|
||||
|
||||
/**
|
||||
* Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
|
||||
*/
|
||||
private static final String DEFAULT_DOWNLOAD_URL =
|
||||
"https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.0/maven-wrapper-0.4.0.jar";
|
||||
|
||||
/**
|
||||
* Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
|
||||
* use instead of the default one.
|
||||
*/
|
||||
private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
|
||||
".mvn/wrapper/maven-wrapper.properties";
|
||||
|
||||
/**
|
||||
* Path where the maven-wrapper.jar will be saved to.
|
||||
*/
|
||||
private static final String MAVEN_WRAPPER_JAR_PATH =
|
||||
".mvn/wrapper/maven-wrapper.jar";
|
||||
|
||||
/**
|
||||
* Name of the property which should be used to override the default download url for the wrapper.
|
||||
*/
|
||||
private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
|
||||
|
||||
public static void main(String args[]) {
|
||||
System.out.println("- Downloader started");
|
||||
File baseDirectory = new File(args[0]);
|
||||
System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
|
||||
|
||||
// If the maven-wrapper.properties exists, read it and check if it contains a custom
|
||||
// wrapperUrl parameter.
|
||||
File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
|
||||
String url = DEFAULT_DOWNLOAD_URL;
|
||||
if(mavenWrapperPropertyFile.exists()) {
|
||||
FileInputStream mavenWrapperPropertyFileInputStream = null;
|
||||
try {
|
||||
mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
|
||||
Properties mavenWrapperProperties = new Properties();
|
||||
mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
|
||||
url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
|
||||
} catch (IOException e) {
|
||||
System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
|
||||
} finally {
|
||||
try {
|
||||
if(mavenWrapperPropertyFileInputStream != null) {
|
||||
mavenWrapperPropertyFileInputStream.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// Ignore ...
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println("- Downloading from: : " + url);
|
||||
|
||||
File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
|
||||
if(!outputFile.getParentFile().exists()) {
|
||||
if(!outputFile.getParentFile().mkdirs()) {
|
||||
System.out.println(
|
||||
"- ERROR creating output direcrory '" + outputFile.getParentFile().getAbsolutePath() + "'");
|
||||
}
|
||||
}
|
||||
System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
|
||||
try {
|
||||
downloadFileFromURL(url, outputFile);
|
||||
System.out.println("Done");
|
||||
System.exit(0);
|
||||
} catch (Throwable e) {
|
||||
System.out.println("- Error downloading");
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
private static void downloadFileFromURL(String urlString, File destination) throws Exception {
|
||||
URL website = new URL(urlString);
|
||||
ReadableByteChannel rbc;
|
||||
rbc = Channels.newChannel(website.openStream());
|
||||
FileOutputStream fos = new FileOutputStream(destination);
|
||||
fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
|
||||
fos.close();
|
||||
rbc.close();
|
||||
}
|
||||
|
||||
}
|
||||
BIN
Binary file not shown.
+1
@@ -0,0 +1 @@
|
||||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.5.3/apache-maven-3.5.3-bin.zip
|
||||
+20
-1
@@ -4,6 +4,25 @@ sudo: false
|
||||
jdk:
|
||||
- openjdk7
|
||||
- oraclejdk8
|
||||
- oraclejdk9
|
||||
- oraclejdk11
|
||||
|
||||
install: true
|
||||
script: mvn test -DskipAssembly
|
||||
script: mvn test -DskipAssembly
|
||||
|
||||
after_success:
|
||||
# TODO delete following if statement after fix of https://github.com/cobertura/cobertura/issues/271
|
||||
- if [ "$TRAVIS_JDK_VERSION" == "openjdk8" ] || [ "$TRAVIS_JDK_VERSION" == "oraclejdk8" ]; then
|
||||
mvn cobertura:cobertura org.eluder.coveralls:coveralls-maven-plugin:report com.updateimpact:updateimpact-maven-plugin:submit -Ptravis-coveralls,update-impact -DskipAssembly;
|
||||
else
|
||||
echo "Not reporting coverage for $TRAVIS_JDK_VERSION due to incompatibility or to save performance";
|
||||
fi;
|
||||
|
||||
env:
|
||||
global:
|
||||
secure: DusDFL9cuX+mj64tXQfrYVbUVYnxlXmad4nuIMj0/lgDbwJ6kxYasJmR99bdvwu08gS9D5b7a+m7WJTN0FNATzEiiGDXcM+TiDPlYDh7dcalU4QGiLmMuCCEw1r7hp2XE4BWVgDfFhCsHhStgjE4RXzHIyPowoPXr4ugPf8at6w=
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.m2
|
||||
|
||||
@@ -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
|
||||
-------------------------------
|
||||
|
||||
[](https://builds.apache.org/view/S-Z/view/Struts/job/Struts-master-JDK7/)
|
||||
[](https://travis-ci.org/apache/struts)
|
||||
[](https://maven-badges.herokuapp.com/maven-central/org.apache.struts/struts2-core/)
|
||||
[](https://coveralls.io/github/apache/struts)
|
||||
[](https://app.updateimpact.com/latest/713283867011846144/Struts%202)
|
||||
[](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 framework’s 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 framework’s 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 doesn’t 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 doesn’t 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
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-parent</artifactId>
|
||||
<version>2.5.14</version>
|
||||
<version>2.5.20</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>
|
||||
|
||||
@@ -24,12 +24,12 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-apps</artifactId>
|
||||
<version>2.5.14</version>
|
||||
<version>2.5.20</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-rest-showcase</artifactId>
|
||||
<packaging>war</packaging>
|
||||
<version>2.5.14</version>
|
||||
<version>2.5.20</version>
|
||||
<name>Struts 2 Rest Showcase Webapp</name>
|
||||
<description>Struts 2 Rest Showcase Example</description>
|
||||
|
||||
@@ -47,6 +47,11 @@
|
||||
<artifactId>struts2-config-browser-plugin</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||
<artifactId>jackson-dataformat-xml</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Logging -->
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
|
||||
@@ -32,6 +32,12 @@
|
||||
|
||||
<constant name="struts.convention.package.locators" value="example"/>
|
||||
|
||||
<!-- Uncomment the lines below to use Jackson XML bindings instead of the XStream library to handle XML serialisations -->
|
||||
<!--
|
||||
<bean name="jacksonXml" type="org.apache.struts2.rest.handler.ContentTypeHandler" class="org.apache.struts2.rest.handler.JacksonXmlHandler" />
|
||||
<constant name="struts.rest.handlerOverride.xml" value="jacksonXml"/>
|
||||
-->
|
||||
|
||||
<package name="rest-showcase" extends="rest-default">
|
||||
<global-allowed-methods>index,show,create,update,destroy,deleteConfirm,edit,editNew</global-allowed-methods>
|
||||
</package>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-apps</artifactId>
|
||||
<version>2.5.14</version>
|
||||
<version>2.5.20</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-showcase</artifactId>
|
||||
|
||||
+30
@@ -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>
|
||||
|
||||
+2
-2
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-parent</artifactId>
|
||||
<version>2.5.14</version>
|
||||
<version>2.5.20</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-assembly</artifactId>
|
||||
@@ -111,7 +111,7 @@
|
||||
<id>make-assembly</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>attached</goal>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
http://struts.apache.org/getting-started/
|
||||
http://struts.apache.org/security/
|
||||
http://struts.apache.org/core-developers/
|
||||
http://struts.apache.org/tag-developers/
|
||||
http://struts.apache.org/maven-archetypes/
|
||||
http://struts.apache.org/plugins/
|
||||
https://struts.apache.org/getting-started/
|
||||
https://struts.apache.org/security/
|
||||
https://struts.apache.org/core-developers/
|
||||
https://struts.apache.org/tag-developers/
|
||||
https://struts.apache.org/maven-archetypes/
|
||||
https://struts.apache.org/plugins/
|
||||
|
||||
+3
-3
@@ -30,7 +30,7 @@
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-bom</artifactId>
|
||||
<version>2.5.14</version>
|
||||
<version>2.5.20</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.20</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_20</tag>
|
||||
</scm>
|
||||
</project>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-osgi-bundles</artifactId>
|
||||
<version>2.5.14</version>
|
||||
<version>2.5.20</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-osgi-admin-bundle</artifactId>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-osgi-bundles</artifactId>
|
||||
<version>2.5.14</version>
|
||||
<version>2.5.20</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-osgi-demo-bundle</artifactId>
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-parent</artifactId>
|
||||
<version>2.5.14</version>
|
||||
<version>2.5.20</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-osgi-bundles</artifactId>
|
||||
|
||||
+1
-3
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-parent</artifactId>
|
||||
<version>2.5.14</version>
|
||||
<version>2.5.20</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>
|
||||
|
||||
@@ -201,13 +201,10 @@ public class ActionChainResult implements Result {
|
||||
* @param invocation the DefaultActionInvocation calling the action call stack
|
||||
*/
|
||||
public void execute(ActionInvocation invocation) throws Exception {
|
||||
// if the finalNamespace wasn't explicitly defined, assume the current one
|
||||
if (this.namespace == null) {
|
||||
this.namespace = invocation.getProxy().getNamespace();
|
||||
}
|
||||
|
||||
ValueStack stack = ActionContext.getContext().getValueStack();
|
||||
String finalNamespace = TextParseUtil.translateVariables(namespace, stack);
|
||||
String finalNamespace = this.namespace != null
|
||||
? TextParseUtil.translateVariables(namespace, stack)
|
||||
: invocation.getProxy().getNamespace();
|
||||
String finalActionName = TextParseUtil.translateVariables(actionName, stack);
|
||||
String finalMethodName = this.methodName != null
|
||||
? TextParseUtil.translateVariables(this.methodName, stack)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ public interface FileManager {
|
||||
void setReloadingConfigs(boolean reloadingConfigs);
|
||||
|
||||
/**
|
||||
* Checks if given file changed and must be reloaded if {@link #setReloadingConfigs(boolean)} is true
|
||||
* Checks if given file changed and must be reloaded
|
||||
*
|
||||
* @param fileName to check
|
||||
* @return true if file changed
|
||||
@@ -44,7 +44,7 @@ public interface FileManager {
|
||||
boolean fileNeedsReloading(String fileName);
|
||||
|
||||
/**
|
||||
* Checks if file represented by provided URL should be reloaded
|
||||
* Checks if file represented by provided URL changed and must be reloaded
|
||||
*
|
||||
* @param fileUrl url to a file
|
||||
* @return true if file exists and should be reloaded, if url is null return false
|
||||
@@ -61,7 +61,7 @@ public interface FileManager {
|
||||
InputStream loadFile(URL fileUrl);
|
||||
|
||||
/**
|
||||
* Adds file to list of monitored files if {@link #setReloadingConfigs(boolean)} is true
|
||||
* Adds file to list of monitored files
|
||||
*
|
||||
* @param fileUrl {@link URL} to file to be monitored
|
||||
*/
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-1
@@ -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);
|
||||
|
||||
+2
@@ -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;
|
||||
@@ -225,6 +226,7 @@ public class XWorkConfigurationProvider implements ConfigurationProvider {
|
||||
props.setProperty(XWorkConstants.ENABLE_OGNL_EXPRESSION_CACHE, Boolean.TRUE.toString());
|
||||
props.setProperty(XWorkConstants.ENABLE_OGNL_EVAL_EXPRESSION, Boolean.FALSE.toString());
|
||||
props.setProperty(XWorkConstants.RELOAD_XML_CONFIGURATION, Boolean.FALSE.toString());
|
||||
props.setProperty(StrutsConstants.STRUTS_ALLOW_STATIC_METHOD_ACCESS, Boolean.FALSE.toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-3
@@ -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)
|
||||
|
||||
+7
-8
@@ -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> </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> </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 "";
|
||||
|
||||
/**
|
||||
|
||||
@@ -21,7 +21,6 @@ package com.opensymphony.xwork2.conversion.impl;
|
||||
import com.opensymphony.xwork2.conversion.ObjectTypeDeterminer;
|
||||
import com.opensymphony.xwork2.conversion.TypeConverter;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.util.XWorkList;
|
||||
|
||||
import java.lang.reflect.Member;
|
||||
import java.util.Collection;
|
||||
|
||||
+12
-7
@@ -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());
|
||||
}
|
||||
|
||||
+2
-2
@@ -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);
|
||||
}
|
||||
|
||||
+3
-1
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -16,13 +16,12 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.util;
|
||||
package com.opensymphony.xwork2.conversion.impl;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.ObjectFactory;
|
||||
import com.opensymphony.xwork2.XWorkException;
|
||||
import com.opensymphony.xwork2.conversion.TypeConverter;
|
||||
import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
+4
-2
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ package com.opensymphony.xwork2.ognl;
|
||||
*/
|
||||
public class ErrorMessageBuilder {
|
||||
|
||||
private StringBuilder message = new StringBuilder();
|
||||
private final StringBuilder message = new StringBuilder();
|
||||
|
||||
public static ErrorMessageBuilder create() {
|
||||
return new ErrorMessageBuilder();
|
||||
|
||||
@@ -24,7 +24,7 @@ import java.util.Map;
|
||||
|
||||
public class OgnlNullHandlerWrapper implements ognl.NullHandler {
|
||||
|
||||
private NullHandler wrapped;
|
||||
private final NullHandler wrapped;
|
||||
|
||||
public OgnlNullHandlerWrapper(NullHandler target) {
|
||||
this.wrapped = target;
|
||||
|
||||
@@ -28,7 +28,7 @@ import java.util.Map;
|
||||
*/
|
||||
public class OgnlTypeConverterWrapper implements ognl.TypeConverter {
|
||||
|
||||
private TypeConverter typeConverter;
|
||||
private final TypeConverter typeConverter;
|
||||
|
||||
public OgnlTypeConverterWrapper(TypeConverter converter) {
|
||||
if (converter == null) {
|
||||
|
||||
@@ -58,44 +58,57 @@ public class OgnlUtil {
|
||||
private final ConcurrentMap<Class, BeanInfo> beanInfoCache = new ConcurrentHashMap<>();
|
||||
private TypeConverter defaultConverter;
|
||||
|
||||
private boolean devMode = false;
|
||||
private boolean devMode;
|
||||
private boolean enableExpressionCache = true;
|
||||
private boolean enableEvalExpression;
|
||||
|
||||
private Set<Class<?>> excludedClasses = Collections.emptySet();
|
||||
private Set<Pattern> excludedPackageNamePatterns = Collections.emptySet();
|
||||
private Set<String> excludedPackageNames = Collections.emptySet();
|
||||
private Set<Class<?>> excludedClasses;
|
||||
private Set<Pattern> excludedPackageNamePatterns;
|
||||
private Set<String> excludedPackageNames;
|
||||
|
||||
private Container container;
|
||||
private boolean allowStaticMethodAccess;
|
||||
private boolean disallowProxyMemberAccess;
|
||||
|
||||
public OgnlUtil() {
|
||||
excludedClasses = new HashSet<>();
|
||||
excludedPackageNamePatterns = new HashSet<>();
|
||||
excludedPackageNames = new HashSet<>();
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setXWorkConverter(XWorkConverter conv) {
|
||||
protected void setXWorkConverter(XWorkConverter conv) {
|
||||
this.defaultConverter = new OgnlTypeConverterWrapper(conv);
|
||||
}
|
||||
|
||||
@Inject(XWorkConstants.DEV_MODE)
|
||||
public void setDevMode(String mode) {
|
||||
protected void setDevMode(String mode) {
|
||||
this.devMode = BooleanUtils.toBoolean(mode);
|
||||
}
|
||||
|
||||
@Inject(XWorkConstants.ENABLE_OGNL_EXPRESSION_CACHE)
|
||||
public void setEnableExpressionCache(String cache) {
|
||||
protected void setEnableExpressionCache(String cache) {
|
||||
enableExpressionCache = BooleanUtils.toBoolean(cache);
|
||||
}
|
||||
|
||||
@Inject(value = XWorkConstants.ENABLE_OGNL_EVAL_EXPRESSION, required = false)
|
||||
public void setEnableEvalExpression(String evalExpression) {
|
||||
enableEvalExpression = "true".equals(evalExpression);
|
||||
if(enableEvalExpression){
|
||||
protected void setEnableEvalExpression(String evalExpression) {
|
||||
this.enableEvalExpression = BooleanUtils.toBoolean(evalExpression);
|
||||
if (this.enableEvalExpression) {
|
||||
LOG.warn("Enabling OGNL expression evaluation may introduce security risks " +
|
||||
"(see http://struts.apache.org/release/2.3.x/docs/s2-013.html for further details)");
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(value = XWorkConstants.OGNL_EXCLUDED_CLASSES, required = false)
|
||||
public void setExcludedClasses(String commaDelimitedClasses) {
|
||||
protected void setExcludedClasses(String commaDelimitedClasses) {
|
||||
Set<Class<?>> excludedClasses = new HashSet<>();
|
||||
excludedClasses.addAll(this.excludedClasses);
|
||||
excludedClasses.addAll(parseExcludedClasses(commaDelimitedClasses));
|
||||
this.excludedClasses = Collections.unmodifiableSet(excludedClasses);
|
||||
}
|
||||
|
||||
private Set<Class<?>> parseExcludedClasses(String commaDelimitedClasses) {
|
||||
Set<String> classNames = TextParseUtil.commaDelimitedStringToSet(commaDelimitedClasses);
|
||||
Set<Class<?>> classes = new HashSet<>();
|
||||
|
||||
@@ -107,11 +120,18 @@ public class OgnlUtil {
|
||||
}
|
||||
}
|
||||
|
||||
excludedClasses = Collections.unmodifiableSet(classes);
|
||||
return classes;
|
||||
}
|
||||
|
||||
@Inject(value = XWorkConstants.OGNL_EXCLUDED_PACKAGE_NAME_PATTERNS, required = false)
|
||||
public void setExcludedPackageNamePatterns(String commaDelimitedPackagePatterns) {
|
||||
protected void setExcludedPackageNamePatterns(String commaDelimitedPackagePatterns) {
|
||||
Set<Pattern> excludedPackageNamePatterns = new HashSet<>();
|
||||
excludedPackageNamePatterns.addAll(this.excludedPackageNamePatterns);
|
||||
excludedPackageNamePatterns.addAll(parseExcludedPackageNamePatterns(commaDelimitedPackagePatterns));
|
||||
this.excludedPackageNamePatterns = Collections.unmodifiableSet(excludedPackageNamePatterns);
|
||||
}
|
||||
|
||||
private Set<Pattern> parseExcludedPackageNamePatterns(String commaDelimitedPackagePatterns) {
|
||||
Set<String> packagePatterns = TextParseUtil.commaDelimitedStringToSet(commaDelimitedPackagePatterns);
|
||||
Set<Pattern> packageNamePatterns = new HashSet<>();
|
||||
|
||||
@@ -119,12 +139,19 @@ public class OgnlUtil {
|
||||
packageNamePatterns.add(Pattern.compile(pattern));
|
||||
}
|
||||
|
||||
excludedPackageNamePatterns = Collections.unmodifiableSet(packageNamePatterns);
|
||||
return packageNamePatterns;
|
||||
}
|
||||
|
||||
@Inject(value = XWorkConstants.OGNL_EXCLUDED_PACKAGE_NAMES, required = false)
|
||||
public void setExcludedPackageNames(String commaDelimitedPackageNames) {
|
||||
excludedPackageNames = Collections.unmodifiableSet(TextParseUtil.commaDelimitedStringToSet(commaDelimitedPackageNames));
|
||||
protected void setExcludedPackageNames(String commaDelimitedPackageNames) {
|
||||
Set<String> excludedPackageNames = new HashSet<>();
|
||||
excludedPackageNames.addAll(this.excludedPackageNames);
|
||||
excludedPackageNames.addAll(parseExcludedPackageNames(commaDelimitedPackageNames));
|
||||
this.excludedPackageNames = Collections.unmodifiableSet(excludedPackageNames);
|
||||
}
|
||||
|
||||
private Set<String> parseExcludedPackageNames(String commaDelimitedPackageNames) {
|
||||
return TextParseUtil.commaDelimitedStringToSet(commaDelimitedPackageNames);
|
||||
}
|
||||
|
||||
public Set<Class<?>> getExcludedClasses() {
|
||||
@@ -140,17 +167,17 @@ public class OgnlUtil {
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setContainer(Container container) {
|
||||
protected void setContainer(Container container) {
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
@Inject(value = XWorkConstants.ALLOW_STATIC_METHOD_ACCESS, required = false)
|
||||
public void setAllowStaticMethodAccess(String allowStaticMethodAccess) {
|
||||
this.allowStaticMethodAccess = Boolean.parseBoolean(allowStaticMethodAccess);
|
||||
protected void setAllowStaticMethodAccess(String allowStaticMethodAccess) {
|
||||
this.allowStaticMethodAccess = BooleanUtils.toBoolean(allowStaticMethodAccess);
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_DISALLOW_PROXY_MEMBER_ACCESS, required = false)
|
||||
public void setDisallowProxyMemberAccess(String disallowProxyMemberAccess) {
|
||||
protected void setDisallowProxyMemberAccess(String disallowProxyMemberAccess) {
|
||||
this.disallowProxyMemberAccess = Boolean.parseBoolean(disallowProxyMemberAccess);
|
||||
}
|
||||
|
||||
@@ -404,7 +431,7 @@ public class OgnlUtil {
|
||||
|
||||
final T exec = task.execute(tree);
|
||||
// if cache is enabled and it's a valid expression, puts it in
|
||||
if(enableExpressionCache) {
|
||||
if (enableExpressionCache) {
|
||||
expressions.putIfAbsent(expression, tree);
|
||||
}
|
||||
return exec;
|
||||
@@ -425,7 +452,7 @@ public class OgnlUtil {
|
||||
|
||||
final T exec = task.execute(tree);
|
||||
// if cache is enabled and it's a valid expression, puts it in
|
||||
if(enableExpressionCache) {
|
||||
if (enableExpressionCache) {
|
||||
expressions.putIfAbsent(expression, tree);
|
||||
}
|
||||
return exec;
|
||||
@@ -641,8 +668,7 @@ public class OgnlUtil {
|
||||
*/
|
||||
public BeanInfo getBeanInfo(Class clazz) throws IntrospectionException {
|
||||
synchronized (beanInfoCache) {
|
||||
BeanInfo beanInfo;
|
||||
beanInfo = beanInfoCache.get(clazz);
|
||||
BeanInfo beanInfo = beanInfoCache.get(clazz);
|
||||
if (beanInfo == null) {
|
||||
beanInfo = Introspector.getBeanInfo(clazz, Object.class);
|
||||
beanInfoCache.putIfAbsent(clazz, beanInfo);
|
||||
|
||||
@@ -82,7 +82,7 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setOgnlUtil(OgnlUtil ognlUtil) {
|
||||
protected void setOgnlUtil(OgnlUtil ognlUtil) {
|
||||
this.ognlUtil = ognlUtil;
|
||||
securityMemberAccess.setExcludedClasses(ognlUtil.getExcludedClasses());
|
||||
securityMemberAccess.setExcludedPackageNamePatterns(ognlUtil.getExcludedPackageNamePatterns());
|
||||
@@ -102,12 +102,12 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
|
||||
}
|
||||
|
||||
@Inject(XWorkConstants.DEV_MODE)
|
||||
public void setDevMode(String mode) {
|
||||
protected void setDevMode(String mode) {
|
||||
this.devMode = BooleanUtils.toBoolean(mode);
|
||||
}
|
||||
|
||||
@Inject(value = "logMissingProperties", required = false)
|
||||
public void setLogMissingProperties(String logMissingProperties) {
|
||||
protected void setLogMissingProperties(String logMissingProperties) {
|
||||
this.logMissingProperties = BooleanUtils.toBoolean(logMissingProperties);
|
||||
}
|
||||
|
||||
@@ -157,8 +157,6 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
|
||||
setValue(expr, value, devMode);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
/**
|
||||
* @see com.opensymphony.xwork2.util.ValueStack#setValue(java.lang.String, java.lang.Object)
|
||||
*/
|
||||
@@ -379,7 +377,7 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
|
||||
* @param e The thrown exception.
|
||||
*/
|
||||
private void logLookupFailure(String expr, Exception e) {
|
||||
if (devMode && LOG.isWarnEnabled()) {
|
||||
if (devMode) {
|
||||
LOG.warn("Caught an exception while evaluating expression '{}' against value stack", expr, e);
|
||||
LOG.warn("NOTE: Previous warning message was issued due to devMode set to true.");
|
||||
} else {
|
||||
@@ -475,7 +473,7 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setXWorkConverter(final XWorkConverter converter) {
|
||||
protected void setXWorkConverter(final XWorkConverter converter) {
|
||||
this.converter = converter;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,8 @@ import ognl.MethodAccessor;
|
||||
import ognl.OgnlRuntime;
|
||||
import ognl.PropertyAccessor;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -41,24 +43,26 @@ import java.util.Set;
|
||||
*/
|
||||
public class OgnlValueStackFactory implements ValueStackFactory {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(OgnlValueStackFactory.class);
|
||||
|
||||
protected XWorkConverter xworkConverter;
|
||||
protected CompoundRootAccessor compoundRootAccessor;
|
||||
protected TextProvider textProvider;
|
||||
protected Container container;
|
||||
protected boolean allowStaticMethodAccess;
|
||||
private boolean allowStaticMethodAccess;
|
||||
|
||||
@Inject
|
||||
public void setXWorkConverter(XWorkConverter converter) {
|
||||
protected void setXWorkConverter(XWorkConverter converter) {
|
||||
this.xworkConverter = converter;
|
||||
}
|
||||
|
||||
@Inject("system")
|
||||
public void setTextProvider(TextProvider textProvider) {
|
||||
protected void setTextProvider(TextProvider textProvider) {
|
||||
this.textProvider = textProvider;
|
||||
}
|
||||
|
||||
@Inject(value="allowStaticMethodAccess", required=false)
|
||||
public void setAllowStaticMethodAccess(String allowStaticMethodAccess) {
|
||||
protected void setAllowStaticMethodAccess(String allowStaticMethodAccess) {
|
||||
this.allowStaticMethodAccess = BooleanUtils.toBoolean(allowStaticMethodAccess);
|
||||
}
|
||||
|
||||
@@ -77,7 +81,7 @@ public class OgnlValueStackFactory implements ValueStackFactory {
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setContainer(Container container) throws ClassNotFoundException {
|
||||
protected void setContainer(Container container) throws ClassNotFoundException {
|
||||
Set<String> names = container.getInstanceNames(PropertyAccessor.class);
|
||||
for (String name : names) {
|
||||
Class cls = Class.forName(name);
|
||||
|
||||
@@ -46,9 +46,16 @@ public class SecurityMemberAccess extends DefaultMemberAccess {
|
||||
private Set<String> excludedPackageNames = Collections.emptySet();
|
||||
private boolean disallowProxyMemberAccess;
|
||||
|
||||
public SecurityMemberAccess(boolean method) {
|
||||
/**
|
||||
* SecurityMemberAccess
|
||||
* - access decisions based on whether member is static (or not)
|
||||
* - block or allow access to properties (configureable-after-construction)
|
||||
*
|
||||
* @param allowStaticMethodAccess
|
||||
*/
|
||||
public SecurityMemberAccess(boolean allowStaticMethodAccess) {
|
||||
super(false);
|
||||
allowStaticMethodAccess = method;
|
||||
this.allowStaticMethodAccess = allowStaticMethodAccess;
|
||||
}
|
||||
|
||||
public boolean getAllowStaticMethodAccess() {
|
||||
@@ -59,28 +66,30 @@ public class SecurityMemberAccess extends DefaultMemberAccess {
|
||||
public boolean isAccessible(Map context, Object target, Member member, String propertyName) {
|
||||
LOG.debug("Checking access for [target: {}, member: {}, property: {}]", target, member, propertyName);
|
||||
|
||||
final Class memberClass = member.getDeclaringClass();
|
||||
Class targetClass = (target != null ? target.getClass() : memberClass); // Note: target,propertyName may be null (static field checks OGNL 3.1.19+)
|
||||
|
||||
if (checkEnumAccess(target, member)) {
|
||||
LOG.trace("Allowing access to enum: {}", target);
|
||||
LOG.trace("Allowing access to enum: target class [{}] of target [{}], member [{}]", targetClass, target, member);
|
||||
return true;
|
||||
}
|
||||
|
||||
Class targetClass = target.getClass();
|
||||
Class memberClass = member.getDeclaringClass();
|
||||
|
||||
if (Modifier.isStatic(member.getModifiers()) && allowStaticMethodAccess) {
|
||||
LOG.debug("Support for accessing static methods [target: {}, member: {}, property: {}] is deprecated!", target, member, propertyName);
|
||||
if (!isClassExcluded(member.getDeclaringClass())) {
|
||||
targetClass = member.getDeclaringClass();
|
||||
LOG.debug("Support for accessing static methods [target: {}, targetClass: {}, member: {}, property: {}] is deprecated!",
|
||||
target, targetClass, member, propertyName);
|
||||
if (!isClassExcluded(memberClass)) {
|
||||
targetClass = memberClass;
|
||||
}
|
||||
}
|
||||
|
||||
if (isPackageExcluded(targetClass.getPackage(), memberClass.getPackage())) {
|
||||
LOG.warn("Package of target [{}] or package of member [{}] are excluded!", target, member);
|
||||
LOG.warn("Package [{}] of target class [{}] of target [{}] or package [{}] of member [{}] are excluded!", targetClass.getPackage(), targetClass,
|
||||
target, memberClass.getPackage(), member);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isClassExcluded(targetClass)) {
|
||||
LOG.warn("Target class [{}] is excluded!", target);
|
||||
LOG.warn("Target class [{}] of target [{}] is excluded!", targetClass, target);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -90,7 +99,7 @@ public class SecurityMemberAccess extends DefaultMemberAccess {
|
||||
}
|
||||
|
||||
if (disallowProxyMemberAccess && ProxyUtil.isProxyMember(member, target)) {
|
||||
LOG.warn("Access to proxy [{}] is blocked!", member);
|
||||
LOG.warn("Access to proxy is blocked! Target class [{}] of target [{}], member [{}]", targetClass, target, member);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -133,8 +142,8 @@ public class SecurityMemberAccess extends DefaultMemberAccess {
|
||||
LOG.warn("The use of the default (unnamed) package is discouraged!");
|
||||
}
|
||||
|
||||
final String targetPackageName = targetPackage == null ? "" : targetPackage.getName();
|
||||
final String memberPackageName = memberPackage == null ? "" : memberPackage.getName();
|
||||
String targetPackageName = targetPackage == null ? "" : targetPackage.getName();
|
||||
String memberPackageName = memberPackage == null ? "" : memberPackage.getName();
|
||||
|
||||
for (Pattern pattern : excludedPackageNamePatterns) {
|
||||
if (pattern.matcher(targetPackageName).matches() || pattern.matcher(memberPackageName).matches()) {
|
||||
@@ -142,9 +151,11 @@ public class SecurityMemberAccess extends DefaultMemberAccess {
|
||||
}
|
||||
}
|
||||
|
||||
targetPackageName = targetPackageName + ".";
|
||||
memberPackageName = memberPackageName + ".";
|
||||
|
||||
for (String packageName: excludedPackageNames) {
|
||||
if (targetPackageName.startsWith(packageName) || targetPackageName.equals(packageName)
|
||||
|| memberPackageName.startsWith(packageName) || memberPackageName.equals(packageName)) {
|
||||
if (targetPackageName.startsWith(packageName) || memberPackageName.startsWith(packageName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ import java.util.Map;
|
||||
*/
|
||||
public class XWorkTypeConverterWrapper implements TypeConverter {
|
||||
|
||||
private ognl.TypeConverter typeConverter;
|
||||
private final ognl.TypeConverter typeConverter;
|
||||
|
||||
public XWorkTypeConverterWrapper(ognl.TypeConverter conv) {
|
||||
this.typeConverter = conv;
|
||||
|
||||
@@ -63,10 +63,10 @@ public class CompoundRootAccessor implements PropertyAccessor, MethodAccessor, C
|
||||
private final static Logger LOG = LogManager.getLogger(CompoundRootAccessor.class);
|
||||
private final static Class[] EMPTY_CLASS_ARRAY = new Class[0];
|
||||
private static Map<MethodCall, Boolean> invalidMethods = new ConcurrentHashMap<>();
|
||||
private boolean devMode = false;
|
||||
private boolean devMode;
|
||||
|
||||
@Inject(XWorkConstants.DEV_MODE)
|
||||
public void setDevMode(String mode) {
|
||||
protected void setDevMode(String mode) {
|
||||
this.devMode = BooleanUtils.toBoolean(mode);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@ public class XWorkCollectionPropertyAccessor extends SetPropertyAccessor {
|
||||
//use a basic object Ognl property accessor here
|
||||
//to access properties of the objects in the Set
|
||||
//so that nothing is put in the context to screw things up
|
||||
private ObjectPropertyAccessor _accessor = new ObjectPropertyAccessor();
|
||||
private final ObjectPropertyAccessor _accessor = new ObjectPropertyAccessor();
|
||||
|
||||
private XWorkConverter xworkConverter;
|
||||
private ObjectFactory objectFactory;
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ import java.util.Map;
|
||||
*/
|
||||
public class XWorkEnumerationAccessor extends EnumerationPropertyAccessor {
|
||||
|
||||
ObjectPropertyAccessor opa = new ObjectPropertyAccessor();
|
||||
private final ObjectPropertyAccessor opa = new ObjectPropertyAccessor();
|
||||
|
||||
@Override
|
||||
public void setProperty(Map context, Object target, Object name, Object value) throws OgnlException {
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ import java.util.Map;
|
||||
*/
|
||||
public class XWorkIteratorPropertyAccessor extends IteratorPropertyAccessor {
|
||||
|
||||
ObjectPropertyAccessor opa = new ObjectPropertyAccessor();
|
||||
private final ObjectPropertyAccessor opa = new ObjectPropertyAccessor();
|
||||
|
||||
@Override
|
||||
public void setProperty(Map context, Object target, Object name, Object value) throws OgnlException {
|
||||
|
||||
+11
-4
@@ -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;
|
||||
|
||||
+10
-4
@@ -44,9 +44,9 @@ public class DefaultAcceptedPatternsChecker implements AcceptedPatternsChecker {
|
||||
}
|
||||
|
||||
@Inject(value = XWorkConstants.OVERRIDE_ACCEPTED_PATTERNS, required = false)
|
||||
public void setOverrideAcceptedPatterns(String acceptablePatterns) {
|
||||
protected void setOverrideAcceptedPatterns(String acceptablePatterns) {
|
||||
LOG.warn("Overriding accepted patterns [{}] with [{}], be aware that this affects all instances and safety of your application!",
|
||||
XWorkConstants.OVERRIDE_ACCEPTED_PATTERNS, acceptablePatterns);
|
||||
acceptedPatterns, acceptablePatterns);
|
||||
acceptedPatterns = new HashSet<>();
|
||||
for (String pattern : TextParseUtil.commaDelimitedStringToSet(acceptablePatterns)) {
|
||||
acceptedPatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
|
||||
@@ -54,7 +54,7 @@ public class DefaultAcceptedPatternsChecker implements AcceptedPatternsChecker {
|
||||
}
|
||||
|
||||
@Inject(value = XWorkConstants.ADDITIONAL_ACCEPTED_PATTERNS, required = false)
|
||||
public void setAdditionalAcceptedPatterns(String acceptablePatterns) {
|
||||
protected void setAdditionalAcceptedPatterns(String acceptablePatterns) {
|
||||
LOG.warn("Adding additional global patterns [{}] to accepted patterns!", acceptablePatterns);
|
||||
for (String pattern : TextParseUtil.commaDelimitedStringToSet(acceptablePatterns)) {
|
||||
acceptedPatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
|
||||
@@ -70,7 +70,13 @@ public class DefaultAcceptedPatternsChecker implements AcceptedPatternsChecker {
|
||||
}
|
||||
|
||||
public void setAcceptedPatterns(Set<String> patterns) {
|
||||
LOG.trace("Sets accepted patterns [{}]", patterns);
|
||||
if (acceptedPatterns == null) {
|
||||
// Limit unwanted log entries (for 1st call, acceptedPatterns null)
|
||||
LOG.debug("Sets accepted patterns to [{}], note this impacts the safety of your application!", patterns);
|
||||
} else {
|
||||
LOG.warn("Replacing accepted patterns [{}] with [{}], be aware that this affects all instances and safety of your application!",
|
||||
acceptedPatterns, patterns);
|
||||
}
|
||||
acceptedPatterns = new HashSet<>(patterns.size());
|
||||
for (String pattern : patterns) {
|
||||
acceptedPatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
|
||||
|
||||
+17
-6
@@ -47,9 +47,14 @@ public class DefaultExcludedPatternsChecker implements ExcludedPatternsChecker {
|
||||
}
|
||||
|
||||
@Inject(value = XWorkConstants.OVERRIDE_EXCLUDED_PATTERNS, required = false)
|
||||
public void setOverrideExcludePatterns(String excludePatterns) {
|
||||
LOG.warn("Overriding excluded patterns [{}] with [{}], be aware that this affects all instances and safety of your application!",
|
||||
XWorkConstants.OVERRIDE_EXCLUDED_PATTERNS, excludePatterns);
|
||||
protected void setOverrideExcludePatterns(String excludePatterns) {
|
||||
if (excludedPatterns != null && excludedPatterns.size() > 0) {
|
||||
LOG.warn("Overriding excluded patterns [{}] with [{}], be aware that this affects all instances and safety of your application!",
|
||||
excludedPatterns, excludePatterns);
|
||||
} else {
|
||||
// Limit unwanted log entries (when excludedPatterns null/empty - usually 1st call)
|
||||
LOG.debug("Overriding excluded patterns with [{}]", excludePatterns);
|
||||
}
|
||||
excludedPatterns = new HashSet<Pattern>();
|
||||
for (String pattern : TextParseUtil.commaDelimitedStringToSet(excludePatterns)) {
|
||||
excludedPatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
|
||||
@@ -65,8 +70,8 @@ public class DefaultExcludedPatternsChecker implements ExcludedPatternsChecker {
|
||||
}
|
||||
|
||||
@Inject(StrutsConstants.STRUTS_ENABLE_DYNAMIC_METHOD_INVOCATION)
|
||||
public void setDynamicMethodInvocation(String dmiValue) {
|
||||
if (!BooleanUtils.toBoolean(dmiValue)) {
|
||||
protected void setDynamicMethodInvocation(String dmiValue) {
|
||||
if (BooleanUtils.toBoolean(dmiValue) == false) {
|
||||
LOG.debug("DMI is disabled, adding DMI related excluded patterns");
|
||||
setAdditionalExcludePatterns("^(action|method):.*");
|
||||
}
|
||||
@@ -81,7 +86,13 @@ public class DefaultExcludedPatternsChecker implements ExcludedPatternsChecker {
|
||||
}
|
||||
|
||||
public void setExcludedPatterns(Set<String> patterns) {
|
||||
LOG.trace("Sets excluded patterns [{}]", patterns);
|
||||
if (excludedPatterns != null && excludedPatterns.size() > 0) {
|
||||
LOG.warn("Replacing excluded patterns [{}] with [{}], be aware that this affects all instances and safety of your application!",
|
||||
excludedPatterns, patterns);
|
||||
} else {
|
||||
// Limit unwanted log entries (when excludedPatterns null/empty - usually 1st call)
|
||||
LOG.debug("Sets excluded patterns to [{}]", patterns);
|
||||
}
|
||||
excludedPatterns = new HashSet<>(patterns.size());
|
||||
for (String pattern : patterns) {
|
||||
excludedPatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Vector;
|
||||
import java.util.zip.ZipEntry;
|
||||
@@ -77,11 +78,7 @@ public class ClassPathFinder {
|
||||
*/
|
||||
public Vector<String> findMatches() {
|
||||
Vector<String> matches = new Vector<>();
|
||||
URLClassLoader cl = getURLClassLoader();
|
||||
if (cl == null ) {
|
||||
throw new XWorkException("unable to attain an URLClassLoader") ;
|
||||
}
|
||||
URL[] parentUrls = cl.getURLs();
|
||||
URL[] parentUrls = getClassLoaderURLs();
|
||||
compiledPattern = patternMatcher.compilePattern(pattern);
|
||||
for (URL url : parentUrls) {
|
||||
if (!"file".equals(url.getProtocol())) {
|
||||
@@ -173,20 +170,24 @@ public class ClassPathFinder {
|
||||
this.patternMatcher = patternMatcher;
|
||||
}
|
||||
|
||||
private URLClassLoader getURLClassLoader() {
|
||||
URLClassLoader ucl = null;
|
||||
private URL[] getClassLoaderURLs() {
|
||||
URL[] urls;
|
||||
ClassLoader loader = Thread.currentThread().getContextClassLoader();
|
||||
|
||||
if(! (loader instanceof URLClassLoader)) {
|
||||
|
||||
if (!(loader instanceof URLClassLoader)) {
|
||||
loader = ClassPathFinder.class.getClassLoader();
|
||||
if (loader instanceof URLClassLoader) {
|
||||
ucl = (URLClassLoader) loader ;
|
||||
}
|
||||
|
||||
if (loader instanceof URLClassLoader) {
|
||||
urls = ((URLClassLoader) loader).getURLs();
|
||||
} else { //jdk9 or later
|
||||
try {
|
||||
urls = Collections.list(loader.getResources("")).toArray(new URL[0]);
|
||||
} catch (IOException e) {
|
||||
throw new XWorkException("unable to get ClassLoader URLs", e);
|
||||
}
|
||||
}
|
||||
else {
|
||||
ucl = (URLClassLoader) loader;
|
||||
}
|
||||
|
||||
return ucl ;
|
||||
|
||||
return urls;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -40,7 +40,8 @@ public class DefaultFileManager implements FileManager {
|
||||
private static final Pattern JAR_PATTERN = Pattern.compile("^(jar:|wsjar:|zip:|vfsfile:|code-source:)?(file:)?(.*?)(\\!/|\\.jar/)(.*)");
|
||||
private static final int JAR_FILE_PATH = 3;
|
||||
|
||||
protected static Map<String, Revision> files = Collections.synchronizedMap(new HashMap<String, Revision>());
|
||||
protected static final Map<String, Revision> files = Collections.synchronizedMap(new HashMap<String, Revision>());
|
||||
private static final List<URL> lazyMonitoredFilesCache = Collections.synchronizedList(new ArrayList<URL>());
|
||||
|
||||
protected boolean reloadingConfigs = false;
|
||||
|
||||
@@ -48,6 +49,16 @@ public class DefaultFileManager implements FileManager {
|
||||
}
|
||||
|
||||
public void setReloadingConfigs(boolean reloadingConfigs) {
|
||||
if (reloadingConfigs && !this.reloadingConfigs) {
|
||||
//starting monitoring cached not-monitored files (lazy monitoring on demand because of performance)
|
||||
this.reloadingConfigs = true;
|
||||
synchronized (lazyMonitoredFilesCache) {
|
||||
for (URL fileUrl : lazyMonitoredFilesCache) {
|
||||
monitorFile(fileUrl);
|
||||
}
|
||||
lazyMonitoredFilesCache.clear();
|
||||
}
|
||||
}
|
||||
this.reloadingConfigs = reloadingConfigs;
|
||||
}
|
||||
|
||||
@@ -88,6 +99,12 @@ public class DefaultFileManager implements FileManager {
|
||||
|
||||
public void monitorFile(URL fileUrl) {
|
||||
String fileName = fileUrl.toString();
|
||||
if (!reloadingConfigs) {
|
||||
//reserve file for monitoring on demand because of performance
|
||||
files.remove(fileName);
|
||||
lazyMonitoredFilesCache.add(fileUrl);
|
||||
return;
|
||||
}
|
||||
Revision revision;
|
||||
LOG.debug("Creating revision for URL: {}", fileName);
|
||||
if (isJarURL(fileUrl)) {
|
||||
|
||||
@@ -37,11 +37,7 @@ 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();
|
||||
try (StrutsJarURLConnection conn = StrutsJarURLConnection.openConnection(fileUrl)) {
|
||||
conn.setUseCaches(false);
|
||||
URL url = fileManager.normalizeToFileProtocol(fileUrl);
|
||||
if (url != null) {
|
||||
@@ -53,14 +49,6 @@ public class JarEntryRevision extends Revision {
|
||||
LOG.warn("Could not create JarEntryRevision for [{}]!", fileUrl, e);
|
||||
return null;
|
||||
}
|
||||
finally {
|
||||
if(null != conn) {
|
||||
try {
|
||||
conn.getInputStream().close();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private JarEntryRevision(URL jarFileURL, long lastModified) {
|
||||
@@ -72,21 +60,12 @@ public class JarEntryRevision extends Revision {
|
||||
}
|
||||
|
||||
public boolean needsReloading() {
|
||||
JarURLConnection conn = null;
|
||||
long lastLastModified = lastModified;
|
||||
try {
|
||||
conn = (JarURLConnection) jarFileURL.openConnection();
|
||||
try (StrutsJarURLConnection conn = StrutsJarURLConnection.openConnection(jarFileURL)) {
|
||||
conn.setUseCaches(false);
|
||||
lastLastModified = conn.getJarEntry().getTime();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
finally {
|
||||
if(null != conn) {
|
||||
try {
|
||||
conn.getInputStream().close();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
LOG.warn("Could not check if needsReloading for [{}]!", jarFileURL, e);
|
||||
}
|
||||
|
||||
return lastModified < lastLastModified;
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* 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.io.UnsupportedEncodingException;
|
||||
import java.net.JarURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.net.URLDecoder;
|
||||
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.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
/**
|
||||
* WW-4901 If was needed, decouples from underlying implementation of {@link URL#openConnection()}
|
||||
* e.g. from IBM WebSphere com.ibm.ws.classloader.Handler$ClassLoaderURLConnection
|
||||
* WW-4920 Also decouples from and fixes {@link JarURLConnection#parseSpecs(URL)} if was needed
|
||||
* e.g. from Oracle WebLogic which may report jar urls like "zip:C:/web-app-lib-path/some-jar.jar"
|
||||
* but {@link JarURLConnection#parseSpecs(URL)} breaks on such urls
|
||||
* While {@link JarURLConnection#parseSpecs(URL)} is private, then we had to extend {@link URLConnection} instead
|
||||
* @since 2.5.15
|
||||
*/
|
||||
class StrutsJarURLConnection extends URLConnection implements AutoCloseable {
|
||||
private static final String FILE_URL_PREFIX = "file:";
|
||||
|
||||
private JarURLConnection jarURLConnection;
|
||||
|
||||
private JarFile jarFile;
|
||||
private String entryName;
|
||||
private URL jarFileURL;
|
||||
|
||||
private StrutsJarURLConnection(URL url) throws IOException {
|
||||
super(url);
|
||||
|
||||
URLConnection conn = this.url.openConnection();
|
||||
if (conn instanceof JarURLConnection) {//decoupling is not needed?
|
||||
jarURLConnection = (JarURLConnection) conn;
|
||||
} else {
|
||||
try {
|
||||
conn.getInputStream().close();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
parseSpecs(url);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A fixed copy of {@link JarURLConnection#parseSpecs(URL)}
|
||||
*/
|
||||
private void parseSpecs(URL url) throws MalformedURLException, UnsupportedEncodingException {
|
||||
String spec = url.getFile();
|
||||
|
||||
int separator = spec.indexOf("!/");
|
||||
/*
|
||||
* REMIND: we don't handle nested JAR URLs
|
||||
*/
|
||||
if (separator == -1) {
|
||||
throw new MalformedURLException("no !/ found in url spec:" + spec);
|
||||
}
|
||||
|
||||
// start of fixing JarURLConnection#parseSpecs(URL) via handling MalformedURLException
|
||||
String jarFileSpec = spec.substring(0, separator++);
|
||||
try {
|
||||
jarFileURL = new URL(jarFileSpec);
|
||||
} catch (MalformedURLException e) {
|
||||
// Probably no protocol in original jar URL, like "jar:C:/mypath/myjar.jar".
|
||||
// This usually indicates that the jar file resides in the file system.
|
||||
if (!jarFileSpec.startsWith("/")) {
|
||||
jarFileSpec = "/" + jarFileSpec;
|
||||
}
|
||||
jarFileURL = new URL(FILE_URL_PREFIX + jarFileSpec);
|
||||
}
|
||||
// end of fix
|
||||
|
||||
entryName = null;
|
||||
|
||||
/* if ! is the last letter of the innerURL, entryName is null */
|
||||
if (++separator != spec.length()) {
|
||||
entryName = spec.substring(separator, spec.length());
|
||||
entryName = URLDecoder.decode (entryName, "UTF-8");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connect() throws IOException {
|
||||
if (connected) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (jarURLConnection != null) {
|
||||
connected = true;
|
||||
return;
|
||||
}
|
||||
|
||||
try (final InputStream in = jarFileURL.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
|
||||
| JarFile.OPEN_DELETE);
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
JarEntry getJarEntry() throws IOException {
|
||||
if (jarURLConnection != null) {
|
||||
return jarURLConnection.getJarEntry();
|
||||
} else {
|
||||
connect();
|
||||
return jarFile.getJarEntry(entryName);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUseCaches(boolean usecaches) {
|
||||
super.setUseCaches(usecaches);
|
||||
|
||||
if (jarURLConnection != null) {
|
||||
jarURLConnection.setUseCaches(usecaches);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() throws IOException {
|
||||
if (jarURLConnection != null) {
|
||||
return jarURLConnection.getInputStream();
|
||||
} else {
|
||||
return jarFile.getInputStream(jarFile.getJarEntry(entryName));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
try {
|
||||
getInputStream().close();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
if (jarURLConnection == null) {
|
||||
try {
|
||||
jarFile.close();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static StrutsJarURLConnection openConnection(URL url) throws IOException {
|
||||
return new StrutsJarURLConnection(url);
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -41,12 +41,15 @@ public final class StrutsConstants {
|
||||
/** The URL extension to use to determine if the request is meant for a Struts action */
|
||||
public static final String STRUTS_ACTION_EXTENSION = "struts.action.extension";
|
||||
|
||||
/** Comma separated list of patterns (java.util.regex.Pattern) to be excluded from Struts2-processing */
|
||||
public static final String STRUTS_ACTION_EXCLUDE_PATTERN = "struts.action.excludePattern";
|
||||
/** Comma separated list of patterns (java.util.regex.Pattern) to be excluded from Struts2-processing */
|
||||
public static final String STRUTS_ACTION_EXCLUDE_PATTERN = "struts.action.excludePattern";
|
||||
|
||||
/** Whether to use the alterative syntax for the tags or not */
|
||||
/** Whether to use the alternative syntax for the tags or not */
|
||||
public static final String STRUTS_TAG_ALTSYNTAX = "struts.tag.altSyntax";
|
||||
|
||||
/** Whether to use the response encoding (JSP page encoding) for s:include tag processing (false - use STRUTS_I18N_ENCODING - by default) */
|
||||
public static final String STRUTS_TAG_INCLUDETAG_USERESPONSEENCODING = "struts.tag.includetag.useResponseEncoding";
|
||||
|
||||
/** The HTTP port used by Struts URLs */
|
||||
public static final String STRUTS_URL_HTTP_PORT = "struts.url.http.port";
|
||||
|
||||
@@ -56,7 +59,7 @@ public final class StrutsConstants {
|
||||
/** The default includeParams method to generate Struts URLs */
|
||||
public static final String STRUTS_URL_INCLUDEPARAMS = "struts.url.includeParams";
|
||||
|
||||
public static final String STRUTS_URL_RENDERER = "struts.urlRenderer";
|
||||
public static final String STRUTS_URL_RENDERER = "struts.urlRenderer";
|
||||
|
||||
/** The com.opensymphony.xwork2.ObjectFactory implementation class */
|
||||
public static final String STRUTS_OBJECTFACTORY = "struts.objectFactory";
|
||||
@@ -91,7 +94,7 @@ public final class StrutsConstants {
|
||||
/** The org.apache.struts2.views.freemarker.FreemarkerManager implementation class */
|
||||
public static final String STRUTS_FREEMARKER_MANAGER_CLASSNAME = "struts.freemarker.manager.classname";
|
||||
|
||||
/** Update freemarker templates cache in seconds*/
|
||||
/** Update freemarker templates cache in seconds */
|
||||
public static final String STRUTS_FREEMARKER_TEMPLATES_CACHE_UPDATE_DELAY = "struts.freemarker.templatesCache.updateDelay";
|
||||
|
||||
/** Cache model instances at BeanWrapper level */
|
||||
@@ -220,12 +223,12 @@ public final class StrutsConstants {
|
||||
public static final String STRUTS_LOCALE_PROVIDER_FACTORY = "struts.localeProviderFactory";
|
||||
|
||||
/** The name of the parameter to create when mapping an id (used by some action mappers) */
|
||||
public static final String STRUTS_ID_PARAMETER_NAME = "struts.mapper.idParameterName";
|
||||
|
||||
/** The name of the parameter to determine whether static method access will be allowed in OGNL expressions or not */
|
||||
public static final String STRUTS_ALLOW_STATIC_METHOD_ACCESS = "struts.ognl.allowStaticMethodAccess";
|
||||
public static final String STRUTS_ID_PARAMETER_NAME = "struts.mapper.idParameterName";
|
||||
|
||||
/** The com.opensymphony.xwork2.validator.ActionValidatorManager implementation class */
|
||||
/** The name of the parameter to determine whether static method access will be allowed in OGNL expressions or not */
|
||||
public static final String STRUTS_ALLOW_STATIC_METHOD_ACCESS = "struts.ognl.allowStaticMethodAccess";
|
||||
|
||||
/** The com.opensymphony.xwork2.validator.ActionValidatorManager implementation class */
|
||||
public static final String STRUTS_ACTIONVALIDATORMANAGER = "struts.actionValidatorManager";
|
||||
|
||||
/** The {@link com.opensymphony.xwork2.util.ValueStackFactory} implementation class */
|
||||
@@ -236,7 +239,7 @@ public final class StrutsConstants {
|
||||
|
||||
/** The {@link com.opensymphony.xwork2.util.reflection.ReflectionContextFactory} implementation class */
|
||||
public static final String STRUTS_REFLECTIONCONTEXTFACTORY = "struts.reflectionContextFactory";
|
||||
|
||||
|
||||
/** The {@link com.opensymphony.xwork2.util.PatternMatcher} implementation class */
|
||||
public static final String STRUTS_PATTERNMATCHER = "struts.patternMatcher";
|
||||
|
||||
@@ -246,32 +249,32 @@ public final class StrutsConstants {
|
||||
/** The {@link com.opensymphony.xwork2.UnknownHandlerManager} implementation class */
|
||||
public static final String STRUTS_UNKNOWN_HANDLER_MANAGER = "struts.unknownHandlerManager";
|
||||
|
||||
/** Throw RuntimeException when a property is not found, or the evaluation of the espression fails*/
|
||||
/** Throw RuntimeException when a property is not found, or the evaluation of the expression fails */
|
||||
public static final String STRUTS_EL_THROW_EXCEPTION = "struts.el.throwExceptionOnFailure";
|
||||
|
||||
/** Logs properties that are not found (very verbose) **/
|
||||
/** Logs properties that are not found (very verbose) */
|
||||
public static final String STRUTS_LOG_MISSING_PROPERTIES = "struts.ognl.logMissingProperties";
|
||||
|
||||
/** Enables caching of parsed OGNL expressions **/
|
||||
/** Enables caching of parsed OGNL expressions */
|
||||
public static final String STRUTS_ENABLE_OGNL_EXPRESSION_CACHE = "struts.ognl.enableExpressionCache";
|
||||
|
||||
/** Enables evaluation of OGNL expressions **/
|
||||
/** Enables evaluation of OGNL expressions */
|
||||
public static final String STRUTS_ENABLE_OGNL_EVAL_EXPRESSION = "struts.ognl.enableOGNLEvalExpression";
|
||||
|
||||
/** Disables {@link org.apache.struts2.dispatcher.StrutsRequestWrapper} request attribute value stack lookup (JSTL accessibility) **/
|
||||
/** Disables {@link org.apache.struts2.dispatcher.StrutsRequestWrapper} request attribute value stack lookup (JSTL accessibility) */
|
||||
public static final String STRUTS_DISABLE_REQUEST_ATTRIBUTE_VALUE_STACK_LOOKUP = "struts.disableRequestAttributeValueStackLookup";
|
||||
|
||||
/** The{@link org.apache.struts2.views.util.UrlHelper} implementation class **/
|
||||
/** The{@link org.apache.struts2.views.util.UrlHelper} implementation class */
|
||||
public static final String STRUTS_URL_HELPER = "struts.view.urlHelper";
|
||||
|
||||
/** {@link com.opensymphony.xwork2.conversion.impl.XWorkBasicConverter} **/
|
||||
/** {@link com.opensymphony.xwork2.conversion.impl.XWorkBasicConverter} */
|
||||
public static final String STRUTS_CONVERTER_COLLECTION = "struts.converter.collection";
|
||||
public static final String STRUTS_CONVERTER_ARRAY = "struts.converter.array";
|
||||
public static final String STRUTS_CONVERTER_DATE = "struts.converter.date";
|
||||
public static final String STRUTS_CONVERTER_NUMBER = "struts.converter.number";
|
||||
public static final String STRUTS_CONVERTER_STRING = "struts.converter.string";
|
||||
|
||||
/** Enable handling exceptions by Dispatcher - true by default **/
|
||||
/** Enable handling exceptions by Dispatcher - true by default */
|
||||
public static final String STRUTS_HANDLE_EXCEPTION = "struts.handle.exception";
|
||||
|
||||
public static final String STRUTS_CONVERTER_PROPERTIES_PROCESSOR = "struts.converter.properties.processor";
|
||||
@@ -282,37 +285,42 @@ public final class StrutsConstants {
|
||||
|
||||
public static final String STRUTS_EXPRESSION_PARSER = "struts.expression.parser";
|
||||
|
||||
/** actions names' whitelist **/
|
||||
/** Namespace names' whitelist */
|
||||
public static final String STRUTS_ALLOWED_NAMESPACE_NAMES = "struts.allowed.namespace.names";
|
||||
/** Default namespace name to use when namespace didn't match the whitelist */
|
||||
public static final String STRUTS_DEFAULT_NAMESPACE_NAME = "struts.default.namespace.name";
|
||||
|
||||
/** Action names' whitelist */
|
||||
public static final String STRUTS_ALLOWED_ACTION_NAMES = "struts.allowed.action.names";
|
||||
/** default action name to use when action didn't match the whitelist **/
|
||||
/** Default action name to use when action didn't match the whitelist */
|
||||
public static final String STRUTS_DEFAULT_ACTION_NAME = "struts.default.action.name";
|
||||
|
||||
/** methods names' whitelist **/
|
||||
/** Method names' whitelist */
|
||||
public static final String STRUTS_ALLOWED_METHOD_NAMES = "struts.allowed.method.names";
|
||||
/** default method name to use when method didn't match the whitelist **/
|
||||
/** Default method name to use when method didn't match the whitelist */
|
||||
public static final String STRUTS_DEFAULT_METHOD_NAME = "struts.default.method.name";
|
||||
|
||||
/** enables action: prefix **/
|
||||
/** Enables action: prefix */
|
||||
public static final String STRUTS_MAPPER_ACTION_PREFIX_ENABLED = "struts.mapper.action.prefix.enabled";
|
||||
|
||||
/** enables access to actions in other namespaces than current with action: prefix **/
|
||||
/** Enables access to actions in other namespaces than current with action: prefix */
|
||||
public static final String STRUTS_MAPPER_ACTION_PREFIX_CROSSNAMESPACES = "struts.mapper.action.prefix.crossNamespaces";
|
||||
|
||||
public static final String DEFAULT_TEMPLATE_TYPE_CONFIG_KEY = "struts.ui.templateSuffix";
|
||||
|
||||
/** Allows override default DispatcherErrorHandler **/
|
||||
/** Allows override default DispatcherErrorHandler */
|
||||
public static final String STRUTS_DISPATCHER_ERROR_HANDLER = "struts.dispatcher.errorHandler";
|
||||
|
||||
/** Comma delimited set of excluded classes and package names which cannot be accessed via expressions **/
|
||||
/** Comma delimited set of excluded classes and package names which cannot be accessed via expressions */
|
||||
public static final String STRUTS_EXCLUDED_CLASSES = "struts.excludedClasses";
|
||||
public static final String STRUTS_EXCLUDED_PACKAGE_NAME_PATTERNS = "struts.excludedPackageNamePatterns";
|
||||
public static final String STRUTS_EXCLUDED_PACKAGE_NAMES = "struts.excludedPackageNames";
|
||||
|
||||
/** Dedicated services to check if passed string is excluded/accepted **/
|
||||
/** Dedicated services to check if passed string is excluded/accepted */
|
||||
public static final String STRUTS_EXCLUDED_PATTERNS_CHECKER = "struts.excludedPatterns.checker";
|
||||
public static final String STRUTS_ACCEPTED_PATTERNS_CHECKER = "struts.acceptedPatterns.checker";
|
||||
|
||||
/** Constant is used to override framework's default excluded patterns **/
|
||||
/** Constant is used to override framework's default excluded patterns */
|
||||
public static final String STRUTS_OVERRIDE_EXCLUDED_PATTERNS = "struts.override.excludedPatterns";
|
||||
public static final String STRUTS_OVERRIDE_ACCEPTED_PATTERNS = "struts.override.acceptedPatterns";
|
||||
|
||||
@@ -328,4 +336,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;
|
||||
|
||||
@@ -20,6 +20,7 @@ package org.apache.struts2.components;
|
||||
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.struts2.RequestUtils;
|
||||
@@ -35,6 +36,7 @@ import javax.servlet.ServletRequest;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpServletResponseWrapper;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.*;
|
||||
@@ -90,17 +92,19 @@ public class Include extends Component {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(Include.class);
|
||||
|
||||
private static String systemEncoding = System.getProperty("file.encoding");
|
||||
private static final String systemEncoding = System.getProperty("file.encoding");
|
||||
|
||||
protected String value;
|
||||
private HttpServletRequest req;
|
||||
private HttpServletResponse res;
|
||||
private static String defaultEncoding;
|
||||
private String defaultEncoding; // Made non-static (during WW-4971 fix)
|
||||
private boolean useResponseEncoding; // Added with WW-4971 fix (allows switch between usage of response or default encoding)
|
||||
|
||||
public Include(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
|
||||
super(stack);
|
||||
this.req = req;
|
||||
this.res = res;
|
||||
useResponseEncoding = false; // By default use defaultEncoding (vs. response/page encoding)
|
||||
}
|
||||
|
||||
@Inject(StrutsConstants.STRUTS_I18N_ENCODING)
|
||||
@@ -108,9 +112,25 @@ public class Include extends Component {
|
||||
defaultEncoding = encoding;
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_TAG_INCLUDETAG_USERESPONSEENCODING, required=false)
|
||||
public void setUseResponseEncoding(String useEncoding) {
|
||||
useResponseEncoding = Boolean.parseBoolean(useEncoding);
|
||||
}
|
||||
|
||||
public boolean end(Writer writer, String body) {
|
||||
String page = findString(value, "value", "You must specify the URL to include. Example: /foo.jsp");
|
||||
StringBuilder urlBuf = new StringBuilder();
|
||||
String encodingForInclude;
|
||||
|
||||
if (useResponseEncoding) {
|
||||
encodingForInclude = res.getCharacterEncoding(); // Use response (page) encoding
|
||||
if (encodingForInclude == null || encodingForInclude.length() == 0) {
|
||||
encodingForInclude = defaultEncoding; // Revert to defaultEncoding when response (page) encoding is invalid
|
||||
}
|
||||
}
|
||||
else {
|
||||
encodingForInclude = defaultEncoding; // Use default encoding (when useResponseEncoding is false)
|
||||
}
|
||||
|
||||
// Add URL
|
||||
urlBuf.append(page);
|
||||
@@ -147,7 +167,7 @@ public class Include extends Component {
|
||||
|
||||
// Include
|
||||
try {
|
||||
include(result, writer, req, res, defaultEncoding);
|
||||
include(result, writer, req, res, encodingForInclude);
|
||||
} catch (ServletException | IOException e) {
|
||||
LOG.warn("Exception thrown during include of {}", result, e);
|
||||
}
|
||||
@@ -209,7 +229,7 @@ public class Include extends Component {
|
||||
}
|
||||
|
||||
public void addParameter(String key, Object value) {
|
||||
// don't use the default implementation of addParameter,
|
||||
// Don't use the default implementation of addParameter,
|
||||
// instead, include tag requires that each parameter be a list of objects,
|
||||
// just like the HTTP servlet interfaces are (String[])
|
||||
if (value != null) {
|
||||
@@ -256,7 +276,7 @@ public class Include extends Component {
|
||||
// Use given encoding
|
||||
pageResponse.getContent().writeTo(writer, encoding);
|
||||
} else {
|
||||
//use the platform specific encoding
|
||||
// Use the platform specific encoding
|
||||
pageResponse.getContent().writeTo(writer, systemEncoding);
|
||||
}
|
||||
}
|
||||
@@ -349,9 +369,9 @@ public class Include extends Component {
|
||||
* @throws IOException
|
||||
*/
|
||||
public FastByteArrayOutputStream getContent() throws IOException {
|
||||
//if we are using a writer, we need to flush the
|
||||
//data to the underlying outputstream.
|
||||
//most containers do this - but it seems Jetty 4.0.5 doesn't
|
||||
// If we are using a writer, we need to flush the
|
||||
// data to the underlying outputstream.
|
||||
// Most containers do this - but it seems Jetty 4.0.5 doesn't
|
||||
if (pagePrintWriter != null) {
|
||||
pagePrintWriter.flush();
|
||||
}
|
||||
|
||||
@@ -140,4 +140,19 @@ public class OptGroup extends Component {
|
||||
public void setListValue(String listValue) {
|
||||
internalUiBean.setListValue(listValue);
|
||||
}
|
||||
|
||||
@StrutsTagAttribute(description = "Property of list objects to get css class from")
|
||||
public void setListCssClass(String listCssClass) {
|
||||
internalUiBean.setListCssClass(listCssClass);
|
||||
}
|
||||
|
||||
@StrutsTagAttribute(description = "Property of list objects to get css style from")
|
||||
public void setListCssStyle(String listCssStyle) {
|
||||
internalUiBean.setListCssStyle(listCssStyle);
|
||||
}
|
||||
|
||||
@StrutsTagAttribute(description = "Property of list objects to get title from")
|
||||
public void setListTitle(String listTitle) {
|
||||
internalUiBean.setListTitle(listTitle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -83,6 +83,7 @@ import com.opensymphony.xwork2.util.ValueStack;
|
||||
public class Set extends ContextBean {
|
||||
protected String scope;
|
||||
protected String value;
|
||||
protected boolean trimBody = true;
|
||||
|
||||
public Set(ValueStack stack) {
|
||||
super(stack);
|
||||
@@ -136,6 +137,11 @@ public class Set extends ContextBean {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@StrutsTagAttribute(description="Set to false to prevent the default whitespace-trim of this tag's body content", type="Boolean", defaultValue="true")
|
||||
public void setTrimBody(boolean trimBody) {
|
||||
this.trimBody = trimBody;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean usesBody() {
|
||||
return true;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -31,7 +31,6 @@ import org.apache.logging.log4j.Logger;
|
||||
import org.apache.struts2.RequestUtils;
|
||||
import org.apache.struts2.ServletActionContext;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
import org.apache.struts2.StrutsException;
|
||||
import org.apache.struts2.util.PrefixTrie;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
@@ -117,6 +116,10 @@ public class DefaultActionMapper implements ActionMapper {
|
||||
protected boolean allowSlashesInActionNames = false;
|
||||
protected boolean alwaysSelectFullNamespace = false;
|
||||
protected PrefixTrie prefixTrie = null;
|
||||
|
||||
protected Pattern allowedNamespaceNames = Pattern.compile("[a-zA-Z0-9._/\\-]*");
|
||||
protected String defaultNamespaceName = "/";
|
||||
|
||||
protected Pattern allowedActionNames = Pattern.compile("[a-zA-Z0-9._!/\\-]*");
|
||||
protected String defaultActionName = "index";
|
||||
|
||||
@@ -163,8 +166,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);
|
||||
@@ -202,6 +205,16 @@ public class DefaultActionMapper implements ActionMapper {
|
||||
this.alwaysSelectFullNamespace = BooleanUtils.toBoolean(alwaysSelectFullNamespace);
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_ALLOWED_NAMESPACE_NAMES, required = false)
|
||||
public void setAllowedNamespaceNames(String allowedNamespaceNames) {
|
||||
this.allowedNamespaceNames = Pattern.compile(allowedNamespaceNames);
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_DEFAULT_NAMESPACE_NAME, required = false)
|
||||
public void setDefaultNamespaceName(String defaultNamespaceName) {
|
||||
this.defaultNamespaceName = defaultNamespaceName;
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_ALLOWED_ACTION_NAMES, required = false)
|
||||
public void setAllowedActionNames(String allowedActionNames) {
|
||||
this.allowedActionNames = Pattern.compile(allowedActionNames);
|
||||
@@ -271,7 +284,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 +304,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 +354,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;
|
||||
@@ -389,10 +402,28 @@ public class DefaultActionMapper implements ActionMapper {
|
||||
}
|
||||
}
|
||||
|
||||
mapping.setNamespace(namespace);
|
||||
mapping.setNamespace(cleanupNamespaceName(namespace));
|
||||
mapping.setName(cleanupActionName(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks namespace name against allowed pattern if not matched returns default namespace
|
||||
*
|
||||
* @param rawNamespace name extracted from URI
|
||||
* @return safe namespace name
|
||||
*/
|
||||
protected String cleanupNamespaceName(final String rawNamespace) {
|
||||
if (allowedNamespaceNames.matcher(rawNamespace).matches()) {
|
||||
return rawNamespace;
|
||||
} else {
|
||||
LOG.warn(
|
||||
"{} did not match allowed namespace names {} - default namespace {} will be used!",
|
||||
rawNamespace, allowedNamespaceNames, defaultNamespaceName
|
||||
);
|
||||
return defaultNamespaceName;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks action name against allowed pattern if not matched returns default action name
|
||||
*
|
||||
|
||||
+2
-2
@@ -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 {
|
||||
|
||||
+2
-2
@@ -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);
|
||||
|
||||
@@ -261,6 +261,7 @@ public class I18nInterceptor extends AbstractInterceptor {
|
||||
super(invocation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locale find() {
|
||||
Locale requestOnlyLocale = super.find();
|
||||
|
||||
@@ -281,7 +282,6 @@ public class I18nInterceptor extends AbstractInterceptor {
|
||||
|
||||
@Override
|
||||
public Locale store(ActionInvocation invocation, Locale locale) {
|
||||
//save it in session
|
||||
Map<String, Object> session = invocation.getInvocationContext().getSession();
|
||||
|
||||
if (session != null) {
|
||||
@@ -290,6 +290,7 @@ public class I18nInterceptor extends AbstractInterceptor {
|
||||
session.put(attributeName, locale);
|
||||
}
|
||||
}
|
||||
|
||||
return locale;
|
||||
}
|
||||
|
||||
@@ -298,19 +299,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,6 +134,7 @@ public class PostbackResult extends StrutsResultSupport {
|
||||
|
||||
if (actionName != null) {
|
||||
actionName = conditionalParse(actionName, invocation);
|
||||
parseLocation = false;
|
||||
if (namespace == null) {
|
||||
namespace = invocation.getProxy().getNamespace();
|
||||
} else {
|
||||
@@ -215,8 +216,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);
|
||||
}
|
||||
|
||||
@@ -159,6 +159,7 @@ public class ServletActionRedirectResult extends ServletRedirectResult implement
|
||||
*/
|
||||
public void execute(ActionInvocation invocation) throws Exception {
|
||||
actionName = conditionalParse(actionName, invocation);
|
||||
parseLocation = false;
|
||||
if (namespace == null) {
|
||||
namespace = invocation.getProxy().getNamespace();
|
||||
} else {
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user