mirror of
https://github.com/apache/struts.git
synced 2026-08-09 08:36:54 +00:00
Compare commits
124 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b3a9d82d58 | |||
| 828d02fcbd | |||
| 88b05cdaed | |||
| e60df05c27 | |||
| 1efcc938cf | |||
| 7b1c3b0fca | |||
| 10a834a54c | |||
| abb1ac3c99 | |||
| dcc59937a8 | |||
| 22c88faf38 | |||
| 1191ca3ed0 | |||
| bcfb769005 | |||
| d61be3e4ce | |||
| c7149ab1bc | |||
| 4504e610f1 | |||
| 9b3a23eb28 | |||
| 3220d832d2 | |||
| bd007953ea | |||
| 1b0c743e85 | |||
| 89e02a4fb4 | |||
| 8cb93ad39a | |||
| e8656d5737 | |||
| 7f10ed505f | |||
| 043814b774 | |||
| 0fabde9f97 | |||
| 4457f03c70 | |||
| 3d3512a399 | |||
| 59a6cbf6ca | |||
| 19802b0614 | |||
| e46e662a7a | |||
| 4786fba0d7 | |||
| e32bd7dba9 | |||
| 6e1d2add07 | |||
| 2eeac367fe | |||
| 30b43044a3 | |||
| 5c82f0246e | |||
| fff35cfd9d | |||
| 1526b36dd6 | |||
| 6d6a422db7 | |||
| 5cf57b9132 | |||
| f4c0135878 | |||
| 77cfae3084 | |||
| ec56290056 | |||
| 47c87bc62c | |||
| ce467b7fa5 | |||
| 73eb6ed189 | |||
| 80a91dc75f | |||
| a6edb0d5df | |||
| 3dfc5a4074 | |||
| fdfeb3233a | |||
| ae3ae2be76 | |||
| 8dface4fcb | |||
| 1348971d40 | |||
| dd6d206d78 | |||
| e2b644a4fb | |||
| 13cfba86f8 | |||
| d84d59f53c | |||
| 3651f55869 | |||
| 7ffa750c68 | |||
| 313876aa3c | |||
| 9d7a9f81db | |||
| fe98223724 | |||
| 1de94b2092 | |||
| 706bb560e4 | |||
| 12d4feaf8f | |||
| d88a8382d4 | |||
| 73809eae54 | |||
| 5b4b554d11 | |||
| fcbd29b7f9 | |||
| 220896a0cf | |||
| 632f19eab3 | |||
| 50a145152d | |||
| f7b191f782 | |||
| 4ae1a6a26d | |||
| 79e598f360 | |||
| 9216e8e22c | |||
| 651eac2c57 | |||
| a50af87644 | |||
| 9e01fbd2dd | |||
| d4dd3386cc | |||
| 0999fba8c4 | |||
| 3ac6835c5c | |||
| b0dd7c1c0d | |||
| a63beef1f8 | |||
| e74e5539dd | |||
| 2226fcc168 | |||
| 865b8a20df | |||
| 3c6956116e | |||
| bb9ce7582b | |||
| 11d373f8b4 | |||
| b23bfd42d2 | |||
| f94b681050 | |||
| fb38a919c9 | |||
| e25c826167 | |||
| 4815744eaf | |||
| f5a7776854 | |||
| f45d2752bb | |||
| 0262574095 | |||
| 30cb56a414 | |||
| 63dc85fd7a | |||
| 5b0ec3400d | |||
| d460d71498 | |||
| 53f64cdecf | |||
| aff4d46835 | |||
| a15c12a051 | |||
| e62155c460 | |||
| f7f4fbd47f | |||
| 2d81439553 | |||
| 7afe35cb96 | |||
| fdd1054070 | |||
| 32e88d1a2e | |||
| 6403de4516 | |||
| 13c517fc3c | |||
| 7242a87165 | |||
| 0323795d67 | |||
| 71267a9b4b | |||
| 5524c579d2 | |||
| 873ca8fa20 | |||
| 8988d57644 | |||
| 2e7cf63964 | |||
| 3eb68be674 | |||
| c9f279ec2e | |||
| 881e1b2580 | |||
| cb0ac7c440 |
+4
-3
@@ -1,3 +1,4 @@
|
||||
dist: trusty
|
||||
language: java
|
||||
sudo: false
|
||||
|
||||
@@ -8,12 +9,12 @@ jdk:
|
||||
- oraclejdk11
|
||||
|
||||
install: true
|
||||
script: mvn test -DskipAssembly
|
||||
script: mvn test -DskipAssembly -B
|
||||
|
||||
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;
|
||||
mvn cobertura:cobertura org.eluder.coveralls:coveralls-maven-plugin:report com.updateimpact:updateimpact-maven-plugin:submit -Ptravis-coveralls,update-impact -DskipAssembly -B;
|
||||
else
|
||||
echo "Not reporting coverage for $TRAVIS_JDK_VERSION due to incompatibility or to save performance";
|
||||
fi;
|
||||
@@ -25,4 +26,4 @@ env:
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.m2
|
||||
|
||||
|
||||
|
||||
Vendored
+29
@@ -0,0 +1,29 @@
|
||||
#!groovy
|
||||
pipeline {
|
||||
agent {
|
||||
label 'ubuntu'
|
||||
}
|
||||
options {
|
||||
buildDiscarder logRotator(daysToKeepStr: '14', numToKeepStr: '10')
|
||||
timeout(80)
|
||||
disableConcurrentBuilds()
|
||||
}
|
||||
tools {
|
||||
jdk 'JDK 1.7 (latest)'
|
||||
maven 'Maven 3 (latest)'
|
||||
}
|
||||
triggers {
|
||||
pollSCM 'H/15 * * * *'
|
||||
}
|
||||
environment {
|
||||
MAVEN_OPTS = '-Xmx1024m -XX:MaxPermSize=256m'
|
||||
}
|
||||
stages {
|
||||
stage('Build') {
|
||||
steps {
|
||||
sh 'mvn --version'
|
||||
sh 'mvn clean source:jar javadoc:jar install deploy -DskipWiki -Dhttps.protocols=TLSv1,TLSv1.1,TLSv1.2'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-parent</artifactId>
|
||||
<version>2.5.20</version>
|
||||
<version>2.5.25</version>
|
||||
</parent>
|
||||
<artifactId>struts2-apps</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
@@ -24,12 +24,12 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-apps</artifactId>
|
||||
<version>2.5.20</version>
|
||||
<version>2.5.25</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-rest-showcase</artifactId>
|
||||
<packaging>war</packaging>
|
||||
<version>2.5.20</version>
|
||||
<version>2.5.25</version>
|
||||
<name>Struts 2 Rest Showcase Webapp</name>
|
||||
<description>Struts 2 Rest Showcase Example</description>
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-apps</artifactId>
|
||||
<version>2.5.20</version>
|
||||
<version>2.5.25</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-showcase</artifactId>
|
||||
@@ -167,7 +167,7 @@
|
||||
<dependency>
|
||||
<groupId>org.hibernate</groupId>
|
||||
<artifactId>hibernate-validator</artifactId>
|
||||
<version>5.1.3.Final</version>
|
||||
<version>5.4.3.Final</version>
|
||||
</dependency>
|
||||
|
||||
<!-- The Servlet API mocks in Spring Framework 4.x only supports Servlet 3.0 and higher.
|
||||
|
||||
+8
@@ -84,4 +84,12 @@ public class FileUploadAction extends ActionSupport {
|
||||
public void setCaption(String caption) {
|
||||
this.caption = caption;
|
||||
}
|
||||
|
||||
public long getUploadSize() {
|
||||
if (upload != null) {
|
||||
return upload.length();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@
|
||||
<validators>
|
||||
<field name="upload">
|
||||
<field-validator type="fieldexpression">
|
||||
<param name="expression"><![CDATA[upload.length() > 0]]></param>
|
||||
<param name="expression"><![CDATA[getUploadSize() > 0]]></param>
|
||||
<message>File cannot be empty</message>
|
||||
</field-validator>
|
||||
</field>
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
</action>
|
||||
|
||||
<action name="doMultipleUploadUsingList" class="org.apache.struts2.showcase.fileupload.MultipleFileUploadUsingListAction" method="upload">
|
||||
<result name="input">/WEB-INF/fileupload/multipleUploadUsingList.jsp</result>
|
||||
<result>/WEB-INF/fileupload/multiple-success.jsp</result>
|
||||
</action>
|
||||
|
||||
@@ -49,6 +50,7 @@
|
||||
</action>
|
||||
|
||||
<action name="doMultipleUploadUsingArray" class="org.apache.struts2.showcase.fileupload.MultipleFileUploadUsingArrayAction" method="upload">
|
||||
<result name="input">/WEB-INF/fileupload/multipleUploadUsingArray.jsp</result>
|
||||
<result>/WEB-INF/fileupload/multiple-success.jsp</result>
|
||||
</action>
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<interceptor-ref name="defaultStack"/>
|
||||
<interceptor-ref name="token"/>
|
||||
<result name="invalid.token">/WEB-INF/token/doublePost.jsp</result>
|
||||
<result name="success" type="redirect">/WEB-INF/token/transferDone.jsp</result>
|
||||
<result name="success">/WEB-INF/token/transferDone.jsp</result>
|
||||
</action>
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
<interceptor-ref name="defaultStack"/>
|
||||
<interceptor-ref name="tokenSession"/>
|
||||
<result name="invalid.token">/WEB-INF/token/doublePost.jsp</result>
|
||||
<result name="success" type="redirect">/WEB-INF/token/transferDone.jsp</result>
|
||||
<result name="success">/WEB-INF/token/transferDone.jsp</result>
|
||||
</action>
|
||||
|
||||
|
||||
|
||||
@@ -34,28 +34,28 @@
|
||||
|
||||
<package name="validation" extends="json-default" namespace="/validation">
|
||||
<action name="index">
|
||||
<result>/WEB-INF/validation/index.jsp</result>
|
||||
<result>index.jsp</result>
|
||||
</action>
|
||||
|
||||
<action name="quizBasic" class="org.apache.struts2.showcase.validation.QuizAction">
|
||||
<result name="input">quiz-basic.jsp</result>
|
||||
<result>quiz-success.jsp</result>
|
||||
<result name="input">/WEB-INF/validation/quiz-basic.jsp</result>
|
||||
<result>/WEB-INF/validation/quiz-success.jsp</result>
|
||||
</action>
|
||||
|
||||
<action name="quizClient" class="org.apache.struts2.showcase.validation.QuizAction">
|
||||
<result name="input">quiz-client.jsp</result>
|
||||
<result>quiz-success.jsp</result>
|
||||
<result name="input">/WEB-INF/validation/quiz-client.jsp</result>
|
||||
<result>/WEB-INF/validation/quiz-success.jsp</result>
|
||||
</action>
|
||||
|
||||
<action name="quizClientCss" class="org.apache.struts2.showcase.validation.QuizAction">
|
||||
<result name="input">quiz-client-css.jsp</result>
|
||||
<result>quiz-success.jsp</result>
|
||||
<result name="input">/WEB-INF/validation/quiz-client-css.jsp</result>
|
||||
<result>/WEB-INF/validation/quiz-success.jsp</result>
|
||||
</action>
|
||||
|
||||
<action name="quizAjax" class="org.apache.struts2.showcase.validation.QuizAction">
|
||||
<interceptor-ref name="jsonValidationWorkflowStack"/>
|
||||
<result name="input">quiz-ajax.jsp</result>
|
||||
<result>quiz-success.jsp</result>
|
||||
<result name="input">/WEB-INF/validation/quiz-ajax.jsp</result>
|
||||
<result>/WEB-INF/validation/quiz-success.jsp</result>
|
||||
</action>
|
||||
|
||||
<!-- =========================================== -->
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
<!--
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
-->
|
||||
<%@ page
|
||||
language="java"
|
||||
contentType="text/html; charset=UTF-8"
|
||||
pageEncoding="UTF-8"%>
|
||||
<%@ taglib prefix="s" uri="/struts-tags" %>
|
||||
<html>
|
||||
<head>
|
||||
<title>Struts2 Showcase - Fileupload sample - Multiple fileupload</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="page-header">
|
||||
<h1>Fileupload sample - Multiple fileupload</h1>
|
||||
</div>
|
||||
|
||||
<div class="container-fluid">
|
||||
<s:iterator var="counter" begin="1" end="3">
|
||||
<!-- Mechanism for display to work with upload both as an array and list in a single JSP (a bit ugly).
|
||||
Note: Output "index" doesn't always match if the input is "sparse". -->
|
||||
<s:if test="%{#counter <= upload.length}">
|
||||
<s:set var="displayContent" value="true" />
|
||||
</s:if>
|
||||
<s:elseif test="%{#counter <= upload.size}">
|
||||
<s:set var="displayContent" value="true" />
|
||||
</s:elseif>
|
||||
<s:else>
|
||||
<s:set var="displayContent" value="false" />
|
||||
</s:else>
|
||||
<s:if test="%{#displayContent == true}">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<b>File (<s:property value="#counter" />):</b>
|
||||
<ul>
|
||||
<li>ContentType: <s:property value="uploadContentType[#counter - 1]" /></li>
|
||||
<li>FileName: <s:property value="uploadFileName[#counter -1]" /></li>
|
||||
<li>File: <s:property value="upload[#counter - 1]" /></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</s:if>
|
||||
</s:iterator>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -39,7 +39,7 @@
|
||||
<div class="hero-unit">
|
||||
<h1>Welcome!</h1>
|
||||
<p>The Struts Showcase demonstrates a variety of use cases and tag usages. Essentially, the application exercises various framework features in isolation. The Showcase is not meant as a "best practices" example.</p>
|
||||
<p>For more "by example" solutions, see the <a class="btn btn-primary btn-large">Struts Cookbook »</a> pages.</p>
|
||||
<p>For more "by example" solutions, see the <a href="https://github.com/apache/struts-examples" class="btn btn-primary btn-large">Struts Examples »</a> pages.</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
* under the License.
|
||||
*/
|
||||
-->
|
||||
<%@page import="org.apache.struts2.showcase.hangman.HangmanConstants" %>
|
||||
<%@taglib prefix="s" uri="/struts-tags" %>
|
||||
<html>
|
||||
<head>
|
||||
@@ -635,5 +636,18 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<br>
|
||||
<p>
|
||||
A secondary test for this JSP visually verifies expression access to a public constant.
|
||||
A direct access to the same public constant via scriptlet provides secondary verification.
|
||||
</p>
|
||||
<br>
|
||||
<div>
|
||||
Test public static (constant) access (expression). Value: <s:property default="unavailable" value="@org.apache.struts2.showcase.hangman.HangmanConstants@HANGMAN_SESSION_KEY" />
|
||||
</div>
|
||||
<div>
|
||||
Test public static (constant) access (scriptlet). Value: <%=org.apache.struts2.showcase.hangman.HangmanConstants.HANGMAN_SESSION_KEY%>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
|
||||
<@s.form action="transfer4">
|
||||
<@s.token/>
|
||||
<@s.textfield label="Amount" name="amount" required="true" value="400"/>
|
||||
<@s.textfield label="Amount" name="amount" required=true value="400"/>
|
||||
<@s.submit value="Transfer money" cssClass="btn btn-primary"/>
|
||||
</@s.form>
|
||||
</div>
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-parent</artifactId>
|
||||
<version>2.5.20</version>
|
||||
<version>2.5.25</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-assembly</artifactId>
|
||||
|
||||
+6
-3
@@ -30,7 +30,7 @@
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-bom</artifactId>
|
||||
<version>2.5.20</version>
|
||||
<version>2.5.25</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<name>Struts 2 Bill of Materials</name>
|
||||
@@ -45,7 +45,7 @@
|
||||
</licenses>
|
||||
|
||||
<properties>
|
||||
<struts-version.version>2.5.20</struts-version.version>
|
||||
<struts-version.version>2.5.25</struts-version.version>
|
||||
<maven.site.skip>true</maven.site.skip>
|
||||
<maven.site.deploy.skip>true</maven.site.deploy.skip>
|
||||
</properties>
|
||||
@@ -181,6 +181,9 @@
|
||||
</dependencyManagement>
|
||||
|
||||
<scm>
|
||||
<tag>STRUTS_2_5_20</tag>
|
||||
<tag>STRUTS_2_5_25</tag>
|
||||
<connection>scm:git:https://gitbox.apache.org/repos/asf/struts.git</connection>
|
||||
<developerConnection>scm:git:https://gitbox.apache.org/repos/asf/struts.git</developerConnection>
|
||||
<url>https://github.com/apache/struts/</url>
|
||||
</scm>
|
||||
</project>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-osgi-bundles</artifactId>
|
||||
<version>2.5.20</version>
|
||||
<version>2.5.25</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-osgi-admin-bundle</artifactId>
|
||||
|
||||
+21
-10
@@ -60,6 +60,9 @@ public class BundlesAction extends ActionSupport implements ServletContextAware
|
||||
}
|
||||
|
||||
public String start() throws BundleException {
|
||||
clearErrorsAndMessages();
|
||||
addActionMessage("Start - OSGi Host: " + osgiHost + ", ID: " + id);
|
||||
|
||||
Bundle bundle = osgiHost.getBundles().get(id);
|
||||
try {
|
||||
bundle.start();
|
||||
@@ -69,29 +72,35 @@ public class BundlesAction extends ActionSupport implements ServletContextAware
|
||||
//there no easy way/elegant way to know if the bundle was processed already
|
||||
Thread.sleep(1000);
|
||||
} catch (Exception e) {
|
||||
addActionError(e.toString());
|
||||
addActionError("Exception: " + e.toString() + " (" + e.getMessage() + ")");
|
||||
}
|
||||
|
||||
return view();
|
||||
}
|
||||
|
||||
public String stop() throws BundleException {
|
||||
clearErrorsAndMessages();
|
||||
addActionMessage("Stop - OSGi Host: " + osgiHost + ", ID: " + id);
|
||||
|
||||
Bundle bundle = osgiHost.getBundles().get(id);
|
||||
try {
|
||||
bundle.stop();
|
||||
} catch (Exception e) {
|
||||
addActionError(e.toString());
|
||||
addActionError("Exception: " + e.toString() + " (" + e.getMessage() + ")");
|
||||
}
|
||||
|
||||
return view();
|
||||
}
|
||||
|
||||
public String update() throws BundleException {
|
||||
clearErrorsAndMessages();
|
||||
addActionMessage("Update - OSGi Host: " + osgiHost + ", ID: " + id);
|
||||
|
||||
Bundle bundle = osgiHost.getBundles().get(id);
|
||||
try {
|
||||
bundle.update();
|
||||
} catch (Exception e) {
|
||||
addActionError(e.toString());
|
||||
addActionError("Exception: " + e.toString() + " (" + e.getMessage() + ")");
|
||||
}
|
||||
|
||||
return view();
|
||||
@@ -114,13 +123,14 @@ public class BundlesAction extends ActionSupport implements ServletContextAware
|
||||
}
|
||||
|
||||
public List<PackageConfig> getPackages() {
|
||||
List<PackageConfig> pkgs = new ArrayList<PackageConfig>();
|
||||
List<PackageConfig> pkgs = new ArrayList<>();
|
||||
Bundle bundle = getBundle();
|
||||
if (bundle.getState() == Bundle.ACTIVE) {
|
||||
for (String name : bundleAccessor.getPackagesByBundle(bundle)) {
|
||||
PackageConfig packageConfig = configuration.getPackageConfig(name);
|
||||
if (packageConfig != null)
|
||||
if (packageConfig != null) {
|
||||
pkgs.add(packageConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
return pkgs;
|
||||
@@ -133,13 +143,14 @@ public class BundlesAction extends ActionSupport implements ServletContextAware
|
||||
public Collection<Bundle> getBundles() {
|
||||
List<Bundle> bundles = new ArrayList(osgiHost.getBundles().values());
|
||||
Collections.sort(bundles, new Comparator<Bundle>() {
|
||||
@Override
|
||||
public int compare(Bundle bundle1, Bundle bundle2) {
|
||||
boolean bundle1StrutsEnabled = isStrutsEnabled(bundle1);
|
||||
boolean bundle2StrutsEnabled = isStrutsEnabled(bundle2);
|
||||
|
||||
if ((bundle1StrutsEnabled && bundle2StrutsEnabled) || (!bundle1StrutsEnabled && !bundle2StrutsEnabled))
|
||||
if ((bundle1StrutsEnabled && bundle2StrutsEnabled) || (!bundle1StrutsEnabled && !bundle2StrutsEnabled)) {
|
||||
return bundle1.getSymbolicName().compareTo(bundle2.getSymbolicName());
|
||||
else {
|
||||
} else {
|
||||
return bundle1StrutsEnabled ? -1 : 1;
|
||||
}
|
||||
}
|
||||
@@ -172,7 +183,7 @@ public class BundlesAction extends ActionSupport implements ServletContextAware
|
||||
try {
|
||||
state = bundle.getState();
|
||||
} catch (Exception e) {
|
||||
addActionError("Unable to determine bundle state: " + e.getMessage());
|
||||
addActionError("Unable to determine bundle state. Exception: " + e.toString() + " (" + e.getMessage() + ")");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -181,8 +192,7 @@ public class BundlesAction extends ActionSupport implements ServletContextAware
|
||||
} else if ("stop".equals(val)) {
|
||||
return state == Bundle.ACTIVE;
|
||||
} else if ("update".equals(val)) {
|
||||
return state == Bundle.ACTIVE || state == Bundle.INSTALLED
|
||||
|| state == Bundle.RESOLVED;
|
||||
return state == Bundle.ACTIVE || state == Bundle.INSTALLED || state == Bundle.RESOLVED;
|
||||
}
|
||||
throw new IllegalArgumentException("Invalid state");
|
||||
}
|
||||
@@ -197,6 +207,7 @@ public class BundlesAction extends ActionSupport implements ServletContextAware
|
||||
this.bundleAccessor = bundleAccessor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setServletContext(ServletContext servletContext) {
|
||||
osgiHost = (OsgiHost) servletContext.getAttribute(StrutsOsgiListener.OSGI_HOST);
|
||||
}
|
||||
|
||||
+62
-10
@@ -24,20 +24,29 @@ package org.apache.struts2.osgi.admin.actions;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.PrintStream;
|
||||
|
||||
import org.apache.struts2.osgi.DefaultBundleAccessor;
|
||||
import org.apache.felix.shell.ShellService;
|
||||
import org.apache.struts2.osgi.DefaultBundleAccessor;
|
||||
import org.apache.struts2.osgi.interceptor.BundleContextAware;
|
||||
import org.osgi.framework.Bundle;
|
||||
import org.osgi.framework.BundleContext;
|
||||
import org.osgi.framework.ServiceReference;
|
||||
|
||||
import com.opensymphony.xwork2.Action;
|
||||
import com.opensymphony.xwork2.ActionSupport;
|
||||
|
||||
/**
|
||||
* This action executes commands on the Felix Shell
|
||||
* This action executes commands on the Felix Shell.
|
||||
*
|
||||
* The action is BundleContextAware so that if the OSGi interceptor is used the BundleContext
|
||||
* can be provided for configurations where the DefaultBundleAccessor is insufficient.
|
||||
*
|
||||
*/
|
||||
public class ShellAction extends ActionSupport {
|
||||
public class ShellAction extends ActionSupport implements BundleContextAware {
|
||||
private String command;
|
||||
private String output;
|
||||
private BundleContext bundleContext;
|
||||
|
||||
@Override
|
||||
public String execute() {
|
||||
// get service
|
||||
ByteArrayOutputStream outByteStream = new ByteArrayOutputStream();
|
||||
@@ -52,7 +61,9 @@ public class ShellAction extends ActionSupport {
|
||||
outString = outByteStream.toString().trim();
|
||||
errString = errByteStream.toString().trim();
|
||||
} catch (Exception e) {
|
||||
errString = e.getMessage();
|
||||
outString = outByteStream.toString().trim();
|
||||
errString = "Exception: " + e.toString() + " (" + e.getMessage() + "). Output:" + outString +
|
||||
". Error: " + errByteStream.toString().trim(); // Full details for troubleshooting.
|
||||
} finally {
|
||||
outStream.close();
|
||||
errStream.close();
|
||||
@@ -75,17 +86,58 @@ public class ShellAction extends ActionSupport {
|
||||
}
|
||||
|
||||
public void executeCommand(String commandLine, PrintStream out, PrintStream err) throws Exception {
|
||||
ShellService shellService = getShellService();
|
||||
if (shellService != null)
|
||||
ShellService shellService = getShellService(out);
|
||||
if (shellService != null) {
|
||||
out.println("Attempting to execute command: " + commandLine);
|
||||
shellService.executeCommand(commandLine, out, err);
|
||||
}
|
||||
else
|
||||
err.println("Apache Felix Shell service is not installed");
|
||||
}
|
||||
|
||||
private ShellService getShellService() {
|
||||
private ShellService getShellService(PrintStream out) {
|
||||
//bundle can be de-activated, so keeping a reference aorund is not a good idea
|
||||
DefaultBundleAccessor bundleAcessor = DefaultBundleAccessor.getInstance();
|
||||
ServiceReference ref = bundleAcessor.getServiceReference(ShellService.class.getName());
|
||||
return (ShellService) bundleAcessor.getService(ref);
|
||||
final DefaultBundleAccessor bundleAccessor = DefaultBundleAccessor.getInstance();
|
||||
ServiceReference ref = (bundleAccessor != null ? bundleAccessor.getServiceReference(ShellService.class.getName()) : null);
|
||||
//out.println("DefaultBundleAccessor: " + bundleAcessor + ", ServiceReference [" + ShellService.class.getName() + "]: " + ref); // No logger, for debugging only.
|
||||
|
||||
if (ref == null && this.bundleContext != null) {
|
||||
// Depending on OSGi and Felix bundle configurations, the DefaultBundleAccessor may not be able to locate the ShellService.
|
||||
// In such cases, use the bundleContext (if available) to locate the ShellService in a "brute-force" manner.
|
||||
final Bundle[] bundles = this.bundleContext.getBundles();
|
||||
if (bundles != null && bundles.length > 0) {
|
||||
for (Bundle currentBundle : bundles) {
|
||||
if (currentBundle != null) {
|
||||
//out.println("Bundle [" + index + "], SymbolicName: " + currentBundle.getSymbolicName() + ", Location: " + currentBundle.getLocation() + ", BundleID: " + currentBundle.getBundleId()); // No logger, for debugging only.
|
||||
if (currentBundle.getSymbolicName().startsWith("org.apache.felix.shell")) {
|
||||
BundleContext currentBundleContext = currentBundle.getBundleContext();
|
||||
Object directShellServiceByClass = (currentBundleContext != null ? currentBundleContext.getServiceReference(org.apache.felix.shell.ShellService.class) : null);
|
||||
Object directShellServiceByName = (currentBundleContext != null ? currentBundleContext.getServiceReference("org.apache.felix.shell.ShellService") : null);
|
||||
//out.println(" ShellService reference (via bundle's context) by class: " + directShellServiceByClass); // No logger, for debugging only.
|
||||
//out.println(" ShellService reference (via bundle's context) by name: " + directShellServiceByName); // No logger, for debugging only.
|
||||
if (ref == null) {
|
||||
ref = (directShellServiceByClass != null ? (ServiceReference) directShellServiceByClass : (ServiceReference) directShellServiceByName);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//out.println("Bundle [" + index + "] is null"); // No logger, for debugging only.
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//out.println("OSGi Interceptor-provided BundleContext bundle array is null or empty"); // No logger, for debugging only.
|
||||
}
|
||||
}
|
||||
|
||||
if (ref == null) {
|
||||
out.println("ShellService reference cannot be found (null), service lookup will fail.");
|
||||
}
|
||||
|
||||
return (ShellService) (bundleAccessor != null ? bundleAccessor.getService(ref) : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBundleContext(BundleContext bundleContext) {
|
||||
//System.out.println("ShellAction - setBundleContext called. BundleContext: " + bundleContext); // No logger, for debugging only.
|
||||
this.bundleContext = bundleContext;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
Copyright (c) 2009 Paul Bakaus, http://jqueryui.com/
|
||||
Copyright jQuery Foundation and other contributors, https://jquery.org/
|
||||
|
||||
This software consists of voluntary contributions made by many
|
||||
individuals (AUTHORS.txt, http://jqueryui.com/about) For exact
|
||||
contribution history, see the revision history and logs, available
|
||||
at http://jquery-ui.googlecode.com/svn/
|
||||
individuals. For exact contribution history, see the revision history
|
||||
available at https://github.com/jquery/jquery-ui
|
||||
|
||||
The following license applies to all parts of this software except as
|
||||
documented below:
|
||||
|
||||
====
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
@@ -23,3 +27,17 @@ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
====
|
||||
|
||||
Copyright and related rights for sample code are waived via CC0. Sample
|
||||
code is defined as all source code contained within the demos directory.
|
||||
|
||||
CC0: http://creativecommons.org/publicdomain/zero/1.0/
|
||||
|
||||
====
|
||||
|
||||
All files located in the node_modules and external directories are
|
||||
externally maintained libraries used by this software which have their
|
||||
own licenses; we recommend you read them, as their terms may differ from
|
||||
the terms above.
|
||||
|
||||
@@ -2,7 +2,8 @@ Apache Struts
|
||||
Copyright 2000-2011 The Apache Software Foundation
|
||||
|
||||
This product includes software developed by
|
||||
The Apache Software Foundation (http://www.apache.org/).
|
||||
The Apache Software Foundation (https://www.apache.org/).
|
||||
|
||||
The binary distributions includes the following third party software:
|
||||
JQuery (http://jquery.com/).
|
||||
jQuery (https://jquery.com/).
|
||||
jQuery UI (https://jqueryui.com/).
|
||||
|
||||
@@ -24,11 +24,11 @@
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="<@s.url value="/static/css/shell.css" />" />
|
||||
<link rel="stylesheet" type="text/css" href="<@s.url value="/static/css/main.css" />" />
|
||||
<link rel="stylesheet" type="text/css" href="<@s.url value="/static/css/redmond/jquery-ui-1.7.1.custom.css" />" />
|
||||
<link rel="stylesheet" type="text/css" href="<@s.url value="/static/css/redmond/jquery-ui-1.12.1.redmond.css" />" />
|
||||
|
||||
<script src="<@s.url value="/static/js/shell.js" />"></script>
|
||||
<script src="<@s.url value="/static/js/jquery-1.3.2.min.js" />"></script>
|
||||
<script src="<@s.url value="/static/js/jquery-ui-1.7.1.custom.min.js" />"></script>
|
||||
<script src="<@s.url value="/static/js/jquery-1.12.4.min.js" />"></script>
|
||||
<script src="<@s.url value="/static/js/jquery-ui-1.12.1.min.js" />"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="menu">
|
||||
|
||||
@@ -23,10 +23,10 @@
|
||||
<title>${bundle.symbolicName!}</title>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="<@s.url value="/static/css/main.css" />" />
|
||||
<link rel="stylesheet" type="text/css" href="<@s.url value="/static/css/redmond/jquery-ui-1.7.1.custom.css" />" />
|
||||
<link rel="stylesheet" type="text/css" href="<@s.url value="/static/css/redmond/jquery-ui-1.12.1.redmond.css" />" />
|
||||
|
||||
<script src="<@s.url value="/static/js/jquery-1.3.2.min.js" />"></script>
|
||||
<script src="<@s.url value="/static/js/jquery-ui-1.7.1.custom.min.js" />"></script>
|
||||
<script src="<@s.url value="/static/js/jquery-1.12.4.min.js" />"></script>
|
||||
<script src="<@s.url value="/static/js/jquery-ui-1.12.1.min.js" />"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
$(function() {
|
||||
@@ -163,5 +163,8 @@
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<@s.actionmessage />
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -23,10 +23,10 @@
|
||||
<title>OSGi Bundles</title>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="<@s.url value="/static/css/main.css" />" />
|
||||
<link rel="stylesheet" type="text/css" href="<@s.url value="/static/css/redmond/jquery-ui-1.7.1.custom.css" />" />
|
||||
<link rel="stylesheet" type="text/css" href="<@s.url value="/static/css/redmond/jquery-ui-1.12.1.redmond.css" />" />
|
||||
|
||||
<script src="<@s.url value="/static/js/jquery-1.3.2.min.js" />"></script>
|
||||
<script src="<@s.url value="/static/js/jquery-ui-1.7.1.custom.min.js" />"></script>
|
||||
<script src="<@s.url value="/static/js/jquery-1.12.4.min.js" />"></script>
|
||||
<script src="<@s.url value="/static/js/jquery-ui-1.12.1.min.js" />"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -44,6 +44,9 @@
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<@s.actionerror />
|
||||
|
||||
<table class="properties" style="clear:both; width:700px">
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -87,5 +90,8 @@
|
||||
</#list>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<@s.actionmessage />
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+1311
File diff suppressed because it is too large
Load Diff
-404
@@ -1,404 +0,0 @@
|
||||
/*
|
||||
* jQuery UI CSS Framework
|
||||
* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
|
||||
* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
|
||||
*/
|
||||
|
||||
/* Layout helpers
|
||||
----------------------------------*/
|
||||
.ui-helper-hidden { display: none; }
|
||||
.ui-helper-hidden-accessible { position: absolute; left: -99999999px; }
|
||||
.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; }
|
||||
.ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
|
||||
.ui-helper-clearfix { display: inline-block; }
|
||||
/* required comment for clearfix to work in Opera \*/
|
||||
* html .ui-helper-clearfix { height:1%; }
|
||||
.ui-helper-clearfix { display:block; }
|
||||
/* end clearfix */
|
||||
.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }
|
||||
|
||||
|
||||
/* Interaction Cues
|
||||
----------------------------------*/
|
||||
.ui-state-disabled { cursor: default !important; }
|
||||
|
||||
|
||||
/* Icons
|
||||
----------------------------------*/
|
||||
|
||||
/* states and images */
|
||||
.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }
|
||||
|
||||
|
||||
/* Misc visuals
|
||||
----------------------------------*/
|
||||
|
||||
/* Overlays */
|
||||
.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
|
||||
|
||||
/*
|
||||
* jQuery UI CSS Framework
|
||||
* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
|
||||
* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
|
||||
* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Lucida%20Grande,%20Lucida%20Sans,%20Arial,%20sans-serif&fwDefault=bold&fsDefault=1.1em&cornerRadius=5px&bgColorHeader=5c9ccc&bgTextureHeader=12_gloss_wave.png&bgImgOpacityHeader=55&borderColorHeader=4297d7&fcHeader=ffffff&iconColorHeader=d8e7f3&bgColorContent=fcfdfd&bgTextureContent=06_inset_hard.png&bgImgOpacityContent=100&borderColorContent=a6c9e2&fcContent=222222&iconColorContent=469bdd&bgColorDefault=dfeffc&bgTextureDefault=02_glass.png&bgImgOpacityDefault=85&borderColorDefault=c5dbec&fcDefault=2e6e9e&iconColorDefault=6da8d5&bgColorHover=d0e5f5&bgTextureHover=02_glass.png&bgImgOpacityHover=75&borderColorHover=79b7e7&fcHover=1d5987&iconColorHover=217bc0&bgColorActive=f5f8f9&bgTextureActive=06_inset_hard.png&bgImgOpacityActive=100&borderColorActive=79b7e7&fcActive=e17009&iconColorActive=f9bd01&bgColorHighlight=fbec88&bgTextureHighlight=01_flat.png&bgImgOpacityHighlight=55&borderColorHighlight=fad42e&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=fef1ec&bgTextureError=02_glass.png&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=aaaaaa&bgTextureOverlay=01_flat.png&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=01_flat.png&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px
|
||||
*/
|
||||
|
||||
|
||||
/* Component containers
|
||||
----------------------------------*/
|
||||
.ui-widget { font-family: Lucida Grande, Lucida Sans, Arial, sans-serif; font-size: 1.1em; }
|
||||
.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Lucida Grande, Lucida Sans, Arial, sans-serif; font-size: 1em; }
|
||||
.ui-widget-content { border: 1px solid #a6c9e2; background: #fcfdfd url(images/ui-bg_inset-hard_100_fcfdfd_1x100.png) 50% bottom repeat-x; color: #222222; }
|
||||
.ui-widget-content a { color: #222222; }
|
||||
.ui-widget-header { border: 1px solid #4297d7; background: #5c9ccc url(images/ui-bg_gloss-wave_55_5c9ccc_500x100.png) 50% 50% repeat-x; color: #ffffff; font-weight: bold; }
|
||||
.ui-widget-header a { color: #ffffff; }
|
||||
|
||||
/* Interaction states
|
||||
----------------------------------*/
|
||||
.ui-state-default, .ui-widget-content .ui-state-default { border: 1px solid #c5dbec; background: #dfeffc url(images/ui-bg_glass_85_dfeffc_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #2e6e9e; outline: none; }
|
||||
.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #2e6e9e; text-decoration: none; outline: none; }
|
||||
.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus { border: 1px solid #79b7e7; background: #d0e5f5 url(images/ui-bg_glass_75_d0e5f5_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #1d5987; outline: none; }
|
||||
.ui-state-hover a, .ui-state-hover a:hover { color: #1d5987; text-decoration: none; outline: none; }
|
||||
.ui-state-active, .ui-widget-content .ui-state-active { border: 1px solid #79b7e7; background: #f5f8f9 url(images/ui-bg_inset-hard_100_f5f8f9_1x100.png) 50% 50% repeat-x; font-weight: bold; color: #e17009; outline: none; }
|
||||
.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #e17009; outline: none; text-decoration: none; }
|
||||
|
||||
/* Interaction Cues
|
||||
----------------------------------*/
|
||||
.ui-state-highlight, .ui-widget-content .ui-state-highlight {border: 1px solid #fad42e; background: #fbec88 url(images/ui-bg_flat_55_fbec88_40x100.png) 50% 50% repeat-x; color: #363636; }
|
||||
.ui-state-highlight a, .ui-widget-content .ui-state-highlight a { color: #363636; }
|
||||
.ui-state-error, .ui-widget-content .ui-state-error {border: 1px solid #cd0a0a; background: #fef1ec url(images/ui-bg_glass_95_fef1ec_1x400.png) 50% 50% repeat-x; color: #cd0a0a; }
|
||||
.ui-state-error a, .ui-widget-content .ui-state-error a { color: #cd0a0a; }
|
||||
.ui-state-error-text, .ui-widget-content .ui-state-error-text { color: #cd0a0a; }
|
||||
.ui-state-disabled, .ui-widget-content .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }
|
||||
.ui-priority-primary, .ui-widget-content .ui-priority-primary { font-weight: bold; }
|
||||
.ui-priority-secondary, .ui-widget-content .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }
|
||||
|
||||
/* Icons
|
||||
----------------------------------*/
|
||||
|
||||
/* states and images */
|
||||
.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_469bdd_256x240.png); }
|
||||
.ui-widget-content .ui-icon {background-image: url(images/ui-icons_469bdd_256x240.png); }
|
||||
.ui-widget-header .ui-icon {background-image: url(images/ui-icons_d8e7f3_256x240.png); }
|
||||
.ui-state-default .ui-icon { background-image: url(images/ui-icons_6da8d5_256x240.png); }
|
||||
.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_217bc0_256x240.png); }
|
||||
.ui-state-active .ui-icon {background-image: url(images/ui-icons_f9bd01_256x240.png); }
|
||||
.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_2e83ff_256x240.png); }
|
||||
.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_cd0a0a_256x240.png); }
|
||||
|
||||
/* positioning */
|
||||
.ui-icon-carat-1-n { background-position: 0 0; }
|
||||
.ui-icon-carat-1-ne { background-position: -16px 0; }
|
||||
.ui-icon-carat-1-e { background-position: -32px 0; }
|
||||
.ui-icon-carat-1-se { background-position: -48px 0; }
|
||||
.ui-icon-carat-1-s { background-position: -64px 0; }
|
||||
.ui-icon-carat-1-sw { background-position: -80px 0; }
|
||||
.ui-icon-carat-1-w { background-position: -96px 0; }
|
||||
.ui-icon-carat-1-nw { background-position: -112px 0; }
|
||||
.ui-icon-carat-2-n-s { background-position: -128px 0; }
|
||||
.ui-icon-carat-2-e-w { background-position: -144px 0; }
|
||||
.ui-icon-triangle-1-n { background-position: 0 -16px; }
|
||||
.ui-icon-triangle-1-ne { background-position: -16px -16px; }
|
||||
.ui-icon-triangle-1-e { background-position: -32px -16px; }
|
||||
.ui-icon-triangle-1-se { background-position: -48px -16px; }
|
||||
.ui-icon-triangle-1-s { background-position: -64px -16px; }
|
||||
.ui-icon-triangle-1-sw { background-position: -80px -16px; }
|
||||
.ui-icon-triangle-1-w { background-position: -96px -16px; }
|
||||
.ui-icon-triangle-1-nw { background-position: -112px -16px; }
|
||||
.ui-icon-triangle-2-n-s { background-position: -128px -16px; }
|
||||
.ui-icon-triangle-2-e-w { background-position: -144px -16px; }
|
||||
.ui-icon-arrow-1-n { background-position: 0 -32px; }
|
||||
.ui-icon-arrow-1-ne { background-position: -16px -32px; }
|
||||
.ui-icon-arrow-1-e { background-position: -32px -32px; }
|
||||
.ui-icon-arrow-1-se { background-position: -48px -32px; }
|
||||
.ui-icon-arrow-1-s { background-position: -64px -32px; }
|
||||
.ui-icon-arrow-1-sw { background-position: -80px -32px; }
|
||||
.ui-icon-arrow-1-w { background-position: -96px -32px; }
|
||||
.ui-icon-arrow-1-nw { background-position: -112px -32px; }
|
||||
.ui-icon-arrow-2-n-s { background-position: -128px -32px; }
|
||||
.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
|
||||
.ui-icon-arrow-2-e-w { background-position: -160px -32px; }
|
||||
.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
|
||||
.ui-icon-arrowstop-1-n { background-position: -192px -32px; }
|
||||
.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
|
||||
.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
|
||||
.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
|
||||
.ui-icon-arrowthick-1-n { background-position: 0 -48px; }
|
||||
.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
|
||||
.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
|
||||
.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
|
||||
.ui-icon-arrowthick-1-s { background-position: -64px -48px; }
|
||||
.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
|
||||
.ui-icon-arrowthick-1-w { background-position: -96px -48px; }
|
||||
.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
|
||||
.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
|
||||
.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
|
||||
.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
|
||||
.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
|
||||
.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
|
||||
.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
|
||||
.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
|
||||
.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
|
||||
.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
|
||||
.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
|
||||
.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
|
||||
.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
|
||||
.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
|
||||
.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
|
||||
.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
|
||||
.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
|
||||
.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
|
||||
.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
|
||||
.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
|
||||
.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
|
||||
.ui-icon-arrow-4 { background-position: 0 -80px; }
|
||||
.ui-icon-arrow-4-diag { background-position: -16px -80px; }
|
||||
.ui-icon-extlink { background-position: -32px -80px; }
|
||||
.ui-icon-newwin { background-position: -48px -80px; }
|
||||
.ui-icon-refresh { background-position: -64px -80px; }
|
||||
.ui-icon-shuffle { background-position: -80px -80px; }
|
||||
.ui-icon-transfer-e-w { background-position: -96px -80px; }
|
||||
.ui-icon-transferthick-e-w { background-position: -112px -80px; }
|
||||
.ui-icon-folder-collapsed { background-position: 0 -96px; }
|
||||
.ui-icon-folder-open { background-position: -16px -96px; }
|
||||
.ui-icon-document { background-position: -32px -96px; }
|
||||
.ui-icon-document-b { background-position: -48px -96px; }
|
||||
.ui-icon-note { background-position: -64px -96px; }
|
||||
.ui-icon-mail-closed { background-position: -80px -96px; }
|
||||
.ui-icon-mail-open { background-position: -96px -96px; }
|
||||
.ui-icon-suitcase { background-position: -112px -96px; }
|
||||
.ui-icon-comment { background-position: -128px -96px; }
|
||||
.ui-icon-person { background-position: -144px -96px; }
|
||||
.ui-icon-print { background-position: -160px -96px; }
|
||||
.ui-icon-trash { background-position: -176px -96px; }
|
||||
.ui-icon-locked { background-position: -192px -96px; }
|
||||
.ui-icon-unlocked { background-position: -208px -96px; }
|
||||
.ui-icon-bookmark { background-position: -224px -96px; }
|
||||
.ui-icon-tag { background-position: -240px -96px; }
|
||||
.ui-icon-home { background-position: 0 -112px; }
|
||||
.ui-icon-flag { background-position: -16px -112px; }
|
||||
.ui-icon-calendar { background-position: -32px -112px; }
|
||||
.ui-icon-cart { background-position: -48px -112px; }
|
||||
.ui-icon-pencil { background-position: -64px -112px; }
|
||||
.ui-icon-clock { background-position: -80px -112px; }
|
||||
.ui-icon-disk { background-position: -96px -112px; }
|
||||
.ui-icon-calculator { background-position: -112px -112px; }
|
||||
.ui-icon-zoomin { background-position: -128px -112px; }
|
||||
.ui-icon-zoomout { background-position: -144px -112px; }
|
||||
.ui-icon-search { background-position: -160px -112px; }
|
||||
.ui-icon-wrench { background-position: -176px -112px; }
|
||||
.ui-icon-gear { background-position: -192px -112px; }
|
||||
.ui-icon-heart { background-position: -208px -112px; }
|
||||
.ui-icon-star { background-position: -224px -112px; }
|
||||
.ui-icon-link { background-position: -240px -112px; }
|
||||
.ui-icon-cancel { background-position: 0 -128px; }
|
||||
.ui-icon-plus { background-position: -16px -128px; }
|
||||
.ui-icon-plusthick { background-position: -32px -128px; }
|
||||
.ui-icon-minus { background-position: -48px -128px; }
|
||||
.ui-icon-minusthick { background-position: -64px -128px; }
|
||||
.ui-icon-close { background-position: -80px -128px; }
|
||||
.ui-icon-closethick { background-position: -96px -128px; }
|
||||
.ui-icon-key { background-position: -112px -128px; }
|
||||
.ui-icon-lightbulb { background-position: -128px -128px; }
|
||||
.ui-icon-scissors { background-position: -144px -128px; }
|
||||
.ui-icon-clipboard { background-position: -160px -128px; }
|
||||
.ui-icon-copy { background-position: -176px -128px; }
|
||||
.ui-icon-contact { background-position: -192px -128px; }
|
||||
.ui-icon-image { background-position: -208px -128px; }
|
||||
.ui-icon-video { background-position: -224px -128px; }
|
||||
.ui-icon-script { background-position: -240px -128px; }
|
||||
.ui-icon-alert { background-position: 0 -144px; }
|
||||
.ui-icon-info { background-position: -16px -144px; }
|
||||
.ui-icon-notice { background-position: -32px -144px; }
|
||||
.ui-icon-help { background-position: -48px -144px; }
|
||||
.ui-icon-check { background-position: -64px -144px; }
|
||||
.ui-icon-bullet { background-position: -80px -144px; }
|
||||
.ui-icon-radio-off { background-position: -96px -144px; }
|
||||
.ui-icon-radio-on { background-position: -112px -144px; }
|
||||
.ui-icon-pin-w { background-position: -128px -144px; }
|
||||
.ui-icon-pin-s { background-position: -144px -144px; }
|
||||
.ui-icon-play { background-position: 0 -160px; }
|
||||
.ui-icon-pause { background-position: -16px -160px; }
|
||||
.ui-icon-seek-next { background-position: -32px -160px; }
|
||||
.ui-icon-seek-prev { background-position: -48px -160px; }
|
||||
.ui-icon-seek-end { background-position: -64px -160px; }
|
||||
.ui-icon-seek-first { background-position: -80px -160px; }
|
||||
.ui-icon-stop { background-position: -96px -160px; }
|
||||
.ui-icon-eject { background-position: -112px -160px; }
|
||||
.ui-icon-volume-off { background-position: -128px -160px; }
|
||||
.ui-icon-volume-on { background-position: -144px -160px; }
|
||||
.ui-icon-power { background-position: 0 -176px; }
|
||||
.ui-icon-signal-diag { background-position: -16px -176px; }
|
||||
.ui-icon-signal { background-position: -32px -176px; }
|
||||
.ui-icon-battery-0 { background-position: -48px -176px; }
|
||||
.ui-icon-battery-1 { background-position: -64px -176px; }
|
||||
.ui-icon-battery-2 { background-position: -80px -176px; }
|
||||
.ui-icon-battery-3 { background-position: -96px -176px; }
|
||||
.ui-icon-circle-plus { background-position: 0 -192px; }
|
||||
.ui-icon-circle-minus { background-position: -16px -192px; }
|
||||
.ui-icon-circle-close { background-position: -32px -192px; }
|
||||
.ui-icon-circle-triangle-e { background-position: -48px -192px; }
|
||||
.ui-icon-circle-triangle-s { background-position: -64px -192px; }
|
||||
.ui-icon-circle-triangle-w { background-position: -80px -192px; }
|
||||
.ui-icon-circle-triangle-n { background-position: -96px -192px; }
|
||||
.ui-icon-circle-arrow-e { background-position: -112px -192px; }
|
||||
.ui-icon-circle-arrow-s { background-position: -128px -192px; }
|
||||
.ui-icon-circle-arrow-w { background-position: -144px -192px; }
|
||||
.ui-icon-circle-arrow-n { background-position: -160px -192px; }
|
||||
.ui-icon-circle-zoomin { background-position: -176px -192px; }
|
||||
.ui-icon-circle-zoomout { background-position: -192px -192px; }
|
||||
.ui-icon-circle-check { background-position: -208px -192px; }
|
||||
.ui-icon-circlesmall-plus { background-position: 0 -208px; }
|
||||
.ui-icon-circlesmall-minus { background-position: -16px -208px; }
|
||||
.ui-icon-circlesmall-close { background-position: -32px -208px; }
|
||||
.ui-icon-squaresmall-plus { background-position: -48px -208px; }
|
||||
.ui-icon-squaresmall-minus { background-position: -64px -208px; }
|
||||
.ui-icon-squaresmall-close { background-position: -80px -208px; }
|
||||
.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
|
||||
.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
|
||||
.ui-icon-grip-solid-vertical { background-position: -32px -224px; }
|
||||
.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
|
||||
.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
|
||||
.ui-icon-grip-diagonal-se { background-position: -80px -224px; }
|
||||
|
||||
|
||||
/* Misc visuals
|
||||
----------------------------------*/
|
||||
|
||||
/* Corner radius */
|
||||
.ui-corner-tl { -moz-border-radius-topleft: 5px; -webkit-border-top-left-radius: 5px; }
|
||||
.ui-corner-tr { -moz-border-radius-topright: 5px; -webkit-border-top-right-radius: 5px; }
|
||||
.ui-corner-bl { -moz-border-radius-bottomleft: 5px; -webkit-border-bottom-left-radius: 5px; }
|
||||
.ui-corner-br { -moz-border-radius-bottomright: 5px; -webkit-border-bottom-right-radius: 5px; }
|
||||
.ui-corner-top { -moz-border-radius-topleft: 5px; -webkit-border-top-left-radius: 5px; -moz-border-radius-topright: 5px; -webkit-border-top-right-radius: 5px; }
|
||||
.ui-corner-bottom { -moz-border-radius-bottomleft: 5px; -webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomright: 5px; -webkit-border-bottom-right-radius: 5px; }
|
||||
.ui-corner-right { -moz-border-radius-topright: 5px; -webkit-border-top-right-radius: 5px; -moz-border-radius-bottomright: 5px; -webkit-border-bottom-right-radius: 5px; }
|
||||
.ui-corner-left { -moz-border-radius-topleft: 5px; -webkit-border-top-left-radius: 5px; -moz-border-radius-bottomleft: 5px; -webkit-border-bottom-left-radius: 5px; }
|
||||
.ui-corner-all { -moz-border-radius: 5px; -webkit-border-radius: 5px; }
|
||||
|
||||
/* Overlays */
|
||||
.ui-widget-overlay { background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); }
|
||||
.ui-widget-shadow { margin: -8px 0 0 -8px; padding: 8px; background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); -moz-border-radius: 8px; -webkit-border-radius: 8px; }/* Accordion
|
||||
----------------------------------*/
|
||||
.ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; }
|
||||
.ui-accordion .ui-accordion-li-fix { display: inline; }
|
||||
.ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; }
|
||||
.ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em 2.2em; }
|
||||
.ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; }
|
||||
.ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; }
|
||||
.ui-accordion .ui-accordion-content-active { display: block; }/* Datepicker
|
||||
----------------------------------*/
|
||||
.ui-datepicker { width: 17em; padding: .2em .2em 0; }
|
||||
.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; }
|
||||
.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; }
|
||||
.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; }
|
||||
.ui-datepicker .ui-datepicker-prev { left:2px; }
|
||||
.ui-datepicker .ui-datepicker-next { right:2px; }
|
||||
.ui-datepicker .ui-datepicker-prev-hover { left:1px; }
|
||||
.ui-datepicker .ui-datepicker-next-hover { right:1px; }
|
||||
.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; }
|
||||
.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; }
|
||||
.ui-datepicker .ui-datepicker-title select { float:left; font-size:1em; margin:1px 0; }
|
||||
.ui-datepicker select.ui-datepicker-month-year {width: 100%;}
|
||||
.ui-datepicker select.ui-datepicker-month,
|
||||
.ui-datepicker select.ui-datepicker-year { width: 49%;}
|
||||
.ui-datepicker .ui-datepicker-title select.ui-datepicker-year { float: right; }
|
||||
.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; }
|
||||
.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; }
|
||||
.ui-datepicker td { border: 0; padding: 1px; }
|
||||
.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; }
|
||||
.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; }
|
||||
.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; }
|
||||
.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; }
|
||||
|
||||
/* with multiple calendars */
|
||||
.ui-datepicker.ui-datepicker-multi { width:auto; }
|
||||
.ui-datepicker-multi .ui-datepicker-group { float:left; }
|
||||
.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; }
|
||||
.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; }
|
||||
.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; }
|
||||
.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; }
|
||||
.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; }
|
||||
.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; }
|
||||
.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; }
|
||||
.ui-datepicker-row-break { clear:both; width:100%; }
|
||||
|
||||
/* RTL support */
|
||||
.ui-datepicker-rtl { direction: rtl; }
|
||||
.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; }
|
||||
.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; }
|
||||
.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; }
|
||||
.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; }
|
||||
.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; }
|
||||
.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; }
|
||||
.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; }
|
||||
.ui-datepicker-rtl .ui-datepicker-group { float:right; }
|
||||
.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
|
||||
.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
|
||||
|
||||
/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */
|
||||
.ui-datepicker-cover {
|
||||
display: none; /*sorry for IE5*/
|
||||
display/**/: block; /*sorry for IE5*/
|
||||
position: absolute; /*must have*/
|
||||
z-index: -1; /*must have*/
|
||||
filter: mask(); /*must have*/
|
||||
top: -4px; /*must have*/
|
||||
left: -4px; /*must have*/
|
||||
width: 200px; /*must have*/
|
||||
height: 200px; /*must have*/
|
||||
}/* Dialog
|
||||
----------------------------------*/
|
||||
.ui-dialog { position: relative; padding: .2em; width: 300px; }
|
||||
.ui-dialog .ui-dialog-titlebar { padding: .5em .3em .3em 1em; position: relative; }
|
||||
.ui-dialog .ui-dialog-title { float: left; margin: .1em 0 .2em; }
|
||||
.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; }
|
||||
.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; }
|
||||
.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; }
|
||||
.ui-dialog .ui-dialog-content { border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; }
|
||||
.ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; }
|
||||
.ui-dialog .ui-dialog-buttonpane button { float: right; margin: .5em .4em .5em 0; cursor: pointer; padding: .2em .6em .3em .6em; line-height: 1.4em; width:auto; overflow:visible; }
|
||||
.ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; }
|
||||
.ui-draggable .ui-dialog-titlebar { cursor: move; }
|
||||
/* Progressbar
|
||||
----------------------------------*/
|
||||
.ui-progressbar { height:2em; text-align: left; }
|
||||
.ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; }/* Resizable
|
||||
----------------------------------*/
|
||||
.ui-resizable { position: relative;}
|
||||
.ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block;}
|
||||
.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; }
|
||||
.ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0px; }
|
||||
.ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0px; }
|
||||
.ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0px; height: 100%; }
|
||||
.ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0px; height: 100%; }
|
||||
.ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; }
|
||||
.ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; }
|
||||
.ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; }
|
||||
.ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/* Slider
|
||||
----------------------------------*/
|
||||
.ui-slider { position: relative; text-align: left; }
|
||||
.ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; }
|
||||
.ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; }
|
||||
|
||||
.ui-slider-horizontal { height: .8em; }
|
||||
.ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; }
|
||||
.ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; }
|
||||
.ui-slider-horizontal .ui-slider-range-min { left: 0; }
|
||||
.ui-slider-horizontal .ui-slider-range-max { right: 0; }
|
||||
|
||||
.ui-slider-vertical { width: .8em; height: 100px; }
|
||||
.ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; }
|
||||
.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; }
|
||||
.ui-slider-vertical .ui-slider-range-min { bottom: 0; }
|
||||
.ui-slider-vertical .ui-slider-range-max { top: 0; }/* Tabs
|
||||
----------------------------------*/
|
||||
.ui-tabs { padding: .2em; zoom: 1; }
|
||||
.ui-tabs .ui-tabs-nav { list-style: none; position: relative; padding: .2em .2em 0; }
|
||||
.ui-tabs .ui-tabs-nav li { position: relative; float: left; border-bottom-width: 0 !important; margin: 0 .2em -1px 0; padding: 0; }
|
||||
.ui-tabs .ui-tabs-nav li a { float: left; text-decoration: none; padding: .5em 1em; }
|
||||
.ui-tabs .ui-tabs-nav li.ui-tabs-selected { padding-bottom: 1px; border-bottom-width: 0; }
|
||||
.ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; }
|
||||
.ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */
|
||||
.ui-tabs .ui-tabs-panel { padding: 1em 1.4em; display: block; border-width: 0; background: none; }
|
||||
.ui-tabs .ui-tabs-hide { display: none !important; }
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -29,8 +29,8 @@
|
||||
result_div.appendChild(document.createElement('br'));
|
||||
|
||||
for (var line_index in result_array) {
|
||||
var result_wrap = document.createElement('pre')
|
||||
line = document.createTextNode(result_array[line_index]);
|
||||
var result_wrap = document.createElement('pre');
|
||||
var line = document.createTextNode(result_array[line_index]);
|
||||
result_wrap.appendChild(line);
|
||||
result_div.appendChild(result_wrap);
|
||||
result_div.appendChild(document.createElement('br'));
|
||||
|
||||
@@ -21,28 +21,29 @@
|
||||
-->
|
||||
|
||||
<!DOCTYPE struts PUBLIC
|
||||
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
|
||||
"http://struts.apache.org/dtds/struts-2.0.dtd">
|
||||
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
|
||||
"http://struts.apache.org/dtds/struts-2.5.dtd">
|
||||
|
||||
<struts>
|
||||
<package name="bundle-admin" namespace="/osgi/admin" extends="struts-default">
|
||||
<constant name="struts.enable.DynamicMethodInvocation" value="false" />
|
||||
<package name="bundle-admin" namespace="/osgi/admin" extends="osgi-default" strict-method-invocation="true">
|
||||
<default-action-ref name="bundles" />
|
||||
|
||||
<action name="bundle_*!*" class="org.apache.struts2.osgi.admin.actions.BundlesAction" method="{2}">
|
||||
<param name="id">{1}</param>
|
||||
<result type="freemarker">viewBundle.ftl</result>
|
||||
<allowed-methods>index,view,start,stop,update</allowed-methods>
|
||||
</action>
|
||||
|
||||
<action name="bundles" class="org.apache.struts2.osgi.admin.actions.BundlesAction" method="index">
|
||||
<result type="freemarker">viewBundles.ftl</result>
|
||||
</action>
|
||||
|
||||
|
||||
<action name="execCommand" class="org.apache.struts2.osgi.admin.actions.ShellAction">
|
||||
<result type="freemarker">commandResult.ftl</result>
|
||||
</action>
|
||||
|
||||
<action name="shell">
|
||||
<action name="shell" class="org.apache.struts2.osgi.admin.actions.ShellAction">
|
||||
<result type="freemarker">shell.ftl</result>
|
||||
</action>
|
||||
</package>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-osgi-bundles</artifactId>
|
||||
<version>2.5.20</version>
|
||||
<version>2.5.25</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-osgi-demo-bundle</artifactId>
|
||||
@@ -67,7 +67,6 @@
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>maven-bundle-plugin</artifactId>
|
||||
<extensions>true</extensions>
|
||||
<version>2.1.0</version>
|
||||
<configuration>
|
||||
<instructions>
|
||||
<manifestLocation>META-INF</manifestLocation>
|
||||
|
||||
@@ -22,13 +22,21 @@ package actions.osgi;
|
||||
|
||||
import com.opensymphony.xwork2.ActionSupport;
|
||||
import org.apache.struts2.convention.annotation.Action;
|
||||
import org.apache.struts2.convention.annotation.Actions;
|
||||
import org.apache.struts2.convention.annotation.Namespace;
|
||||
import org.apache.struts2.convention.annotation.Result;
|
||||
import org.apache.struts2.convention.annotation.ResultPath;
|
||||
|
||||
@ResultPath("/content")
|
||||
@Namespace("/osgi")
|
||||
@ResultPath("/content/osgi")
|
||||
public class HelloWorldAction extends ActionSupport {
|
||||
private Message message;
|
||||
private Message message = new Message("Default non-null message");
|
||||
|
||||
@Action("hello-convention")
|
||||
@Override
|
||||
@Actions({
|
||||
@Action(value="hello-convention", results={@Result(name="success", type="freemarker", location="/content/osgi/hello-convention.ftl")}),
|
||||
@Action(value="/osgi/hello-convention", results={@Result(name="success", type="freemarker", location="/content/osgi/hello-convention.ftl")})
|
||||
})
|
||||
public String execute() {
|
||||
return SUCCESS;
|
||||
}
|
||||
@@ -45,6 +53,7 @@ public class HelloWorldAction extends ActionSupport {
|
||||
return "Hello!!!";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder("{message:");
|
||||
sb.append(message != null ? message.getText() : "null");
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<title>Action mapped by the XML configurationn</title>
|
||||
<title>Action mapped by the XML configuration</title>
|
||||
</head>
|
||||
<body>
|
||||
This is an action mapped by XML configuration, using a <b>FreeMarker</b> result.
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
*#
|
||||
<html>
|
||||
<head>
|
||||
<title>Action mapped by the XML configurationn</title>
|
||||
<title>Action mapped by the XML configuration</title>
|
||||
</head>
|
||||
<body>
|
||||
This is an action mapped by XML configuration, using a <b>Velocity</b> result.
|
||||
|
||||
@@ -20,21 +20,36 @@
|
||||
*/
|
||||
-->
|
||||
<!DOCTYPE struts PUBLIC
|
||||
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
|
||||
"http://struts.apache.org/dtds/struts-2.0.dtd">
|
||||
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
|
||||
"http://struts.apache.org/dtds/struts-2.5.dtd">
|
||||
|
||||
<struts>
|
||||
<package name="bundle-demo" namespace="/osgi" extends="osgi-default">
|
||||
<!-- Set some Struts 2 constants relevant to the OSGi Plugin.
|
||||
Note: The constant definitions specified here (within the demo bundle) may also need to be
|
||||
duplicated in the struts.xml configuration of the outer project that packages the demo
|
||||
bundle JAR file within it.
|
||||
-->
|
||||
<constant name="struts.objectFactory" value="osgi" />
|
||||
<constant name="struts.objectFactory.delegate" value="struts" />
|
||||
<constant name="struts.freemarker.manager.classname" value="org.apache.struts2.osgi.BundleFreemarkerManager" />
|
||||
<constant name="struts.velocity.manager.classname" value="org.apache.struts2.osgi.VelocityBundleResourceLoader" />
|
||||
<constant name="struts.staticContentLoader" value="org.apache.struts2.osgi.loaders.StaticContentBundleResourceLoader" />
|
||||
|
||||
<package name="bundle-demo" namespace="/osgi" extends="osgi-default" strict-method-invocation="true">
|
||||
|
||||
<default-action-ref name="home" />
|
||||
<action name="hello-velocity" class="helloWorldAction">
|
||||
|
||||
<action name="hello-velocity" class="actions.osgi.HelloWorldAction">
|
||||
<result type="velocity">/content/osgi/hello.vm</result>
|
||||
</action>
|
||||
<action name="hello-freemarker" class="helloWorldAction">
|
||||
|
||||
<action name="hello-freemarker" class="actions.osgi.HelloWorldAction">
|
||||
<result type="freemarker">/content/osgi/hello.ftl</result>
|
||||
</action>
|
||||
|
||||
<action name="home">
|
||||
<action name="home" class="actions.osgi.HelloWorldAction">
|
||||
<result type="freemarker">/content/osgi/home.ftl</result>
|
||||
</action>
|
||||
|
||||
</package>
|
||||
</struts>
|
||||
+1
-1
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-parent</artifactId>
|
||||
<version>2.5.20</version>
|
||||
<version>2.5.25</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-osgi-bundles</artifactId>
|
||||
|
||||
+3
-2
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-parent</artifactId>
|
||||
<version>2.5.20</version>
|
||||
<version>2.5.25</version>
|
||||
</parent>
|
||||
<artifactId>struts2-core</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
@@ -190,7 +190,8 @@
|
||||
<dependency>
|
||||
<groupId>com.sun</groupId>
|
||||
<artifactId>tools</artifactId>
|
||||
<version>1.5.0</version>
|
||||
<version>1.7.0</version>
|
||||
<!-- Match version of minimum compatible version -->
|
||||
<scope>system</scope>
|
||||
<systemPath>${java.home}/../lib/tools.jar</systemPath>
|
||||
</dependency>
|
||||
|
||||
@@ -27,6 +27,7 @@ import com.opensymphony.xwork2.test.StubConfigurationProvider;
|
||||
import com.opensymphony.xwork2.util.XWorkTestCaseHelper;
|
||||
import com.opensymphony.xwork2.util.location.LocatableProperties;
|
||||
import junit.framework.TestCase;
|
||||
import org.apache.commons.lang3.ClassUtils;
|
||||
|
||||
/**
|
||||
* Base JUnit TestCase to extend for XWork specific JUnit tests. Uses
|
||||
@@ -78,16 +79,20 @@ public abstract class XWorkTestCase extends TestCase {
|
||||
@Override
|
||||
public void register(ContainerBuilder builder,
|
||||
LocatableProperties props) throws ConfigurationException {
|
||||
builder.factory(type, name, new Factory() {
|
||||
public Object create(Context context) throws Exception {
|
||||
return impl;
|
||||
}
|
||||
if (impl instanceof String || ClassUtils.isPrimitiveOrWrapper(impl.getClass())) {
|
||||
props.setProperty(name, "" + impl);
|
||||
} else {
|
||||
builder.factory(type, name, new Factory() {
|
||||
public Object create(Context context) throws Exception {
|
||||
return impl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class type() {
|
||||
return impl.getClass();
|
||||
}
|
||||
}, Scope.SINGLETON);
|
||||
@Override
|
||||
public Class type() {
|
||||
return impl.getClass();
|
||||
}
|
||||
}, Scope.SINGLETON);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -36,10 +36,8 @@ import java.util.*;
|
||||
* @since 2.1
|
||||
*/
|
||||
public abstract class AbstractMatcher<E> implements Serializable {
|
||||
/**
|
||||
* <p> The logging instance </p>
|
||||
*/
|
||||
private static final Logger log = LogManager.getLogger(AbstractMatcher.class);
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(AbstractMatcher.class);
|
||||
|
||||
/**
|
||||
* <p> Handles all wildcard pattern matching. </p>
|
||||
@@ -50,10 +48,34 @@ public abstract class AbstractMatcher<E> implements Serializable {
|
||||
* <p> The compiled patterns and their associated target objects </p>
|
||||
*/
|
||||
List<Mapping<E>> compiledPatterns = new ArrayList<>();
|
||||
;
|
||||
|
||||
/**
|
||||
* This flag controls if passed named params should be appended
|
||||
* to the map in {@link #replaceParameters(Map, Map)}
|
||||
* and will be accessible in {@link com.opensymphony.xwork2.config.entities.ResultConfig}.
|
||||
* If set to false, the named parameters won't be appended.
|
||||
*
|
||||
* This behaviour is controlled by {@link org.apache.struts2.StrutsConstants#STRUTS_MATCHER_APPEND_NAMED_PARAMETERS}
|
||||
*
|
||||
* @since 2.5.23
|
||||
* See WW-5065
|
||||
*/
|
||||
private final boolean appendNamedParameters;
|
||||
|
||||
public AbstractMatcher(PatternMatcher<?> helper) {
|
||||
public AbstractMatcher(PatternMatcher<?> helper, boolean appendNamedParameters) {
|
||||
this.wildcard = (PatternMatcher<Object>) helper;
|
||||
this.appendNamedParameters = appendNamedParameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a matcher with {@link #appendNamedParameters} set to true to keep backward compatibility
|
||||
*
|
||||
* @param helper an instance of {@link PatternMatcher}
|
||||
* @deprecated use @{link {@link AbstractMatcher(PatternMatcher, boolean)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public AbstractMatcher(PatternMatcher<?> helper) {
|
||||
this(helper, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,17 +106,17 @@ public abstract class AbstractMatcher<E> implements Serializable {
|
||||
name = name.substring(1);
|
||||
}
|
||||
|
||||
log.debug("Compiling pattern '{}'", name);
|
||||
LOG.debug("Compiling pattern '{}'", name);
|
||||
|
||||
pattern = wildcard.compilePattern(name);
|
||||
compiledPatterns.add(new Mapping<E>(name, pattern, target));
|
||||
compiledPatterns.add(new Mapping<>(name, pattern, target));
|
||||
|
||||
if (looseMatch) {
|
||||
int lastStar = name.lastIndexOf('*');
|
||||
if (lastStar > 1 && lastStar == name.length() - 1) {
|
||||
if (name.charAt(lastStar - 1) != '*') {
|
||||
pattern = wildcard.compilePattern(name.substring(0, lastStar - 1));
|
||||
compiledPatterns.add(new Mapping<E>(name, pattern, target));
|
||||
compiledPatterns.add(new Mapping<>(name, pattern, target));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -115,12 +137,12 @@ public abstract class AbstractMatcher<E> implements Serializable {
|
||||
E config = null;
|
||||
|
||||
if (compiledPatterns.size() > 0) {
|
||||
log.debug("Attempting to match '{}' to a wildcard pattern, {} available", potentialMatch, compiledPatterns.size());
|
||||
LOG.debug("Attempting to match '{}' to a wildcard pattern, {} available", potentialMatch, compiledPatterns.size());
|
||||
|
||||
Map<String,String> vars = new LinkedHashMap<String,String>();
|
||||
Map<String,String> vars = new LinkedHashMap<>();
|
||||
for (Mapping<E> m : compiledPatterns) {
|
||||
if (wildcard.match(vars, potentialMatch, m.getPattern())) {
|
||||
log.debug("Value matches pattern '{}'", m.getOriginalPattern());
|
||||
LOG.debug("Value matches pattern '{}'", m.getOriginalPattern());
|
||||
config = convert(potentialMatch, m.getTarget(), vars);
|
||||
break;
|
||||
}
|
||||
@@ -152,20 +174,23 @@ public abstract class AbstractMatcher<E> implements Serializable {
|
||||
*/
|
||||
protected Map<String,String> replaceParameters(Map<String, String> orig, Map<String,String> vars) {
|
||||
Map<String, String> map = new LinkedHashMap<>();
|
||||
|
||||
|
||||
//this will set the group index references, like {1}
|
||||
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 (Map.Entry<String,String> entry: vars.entrySet()) {
|
||||
if (!NumberUtils.isCreatable(entry.getKey())) {
|
||||
map.put(entry.getKey(), entry.getValue());
|
||||
|
||||
if (appendNamedParameters) {
|
||||
LOG.debug("Appending named parameters to the result map");
|
||||
//the values map will contain entries like name->"Lex Luthor" and 1->"Lex Luthor"
|
||||
//now add the non-numeric values
|
||||
for (Map.Entry<String,String> entry: vars.entrySet()) {
|
||||
if (!NumberUtils.isCreatable(entry.getKey())) {
|
||||
map.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
@@ -192,7 +217,7 @@ public abstract class AbstractMatcher<E> implements Serializable {
|
||||
c = val.charAt(x);
|
||||
if (x < len - 2 &&
|
||||
c == '{' && '}' == val.charAt(x+2)) {
|
||||
varVal = (String)vars.get(String.valueOf(val.charAt(x + 1)));
|
||||
varVal = vars.get(String.valueOf(val.charAt(x + 1)));
|
||||
if (varVal != null) {
|
||||
ret.append(varVal);
|
||||
}
|
||||
@@ -213,18 +238,18 @@ public abstract class AbstractMatcher<E> implements Serializable {
|
||||
/**
|
||||
* <p> The original pattern. </p>
|
||||
*/
|
||||
private String original;
|
||||
private final String original;
|
||||
|
||||
|
||||
/**
|
||||
* <p> The compiled pattern. </p>
|
||||
*/
|
||||
private Object pattern;
|
||||
private final Object pattern;
|
||||
|
||||
/**
|
||||
* <p> The original object. </p>
|
||||
*/
|
||||
private E config;
|
||||
private final E config;
|
||||
|
||||
/**
|
||||
* <p> Contructs a read-only Mapping instance. </p>
|
||||
|
||||
@@ -58,7 +58,33 @@ public class ActionConfigMatcher extends AbstractMatcher<ActionConfig> implement
|
||||
public ActionConfigMatcher(PatternMatcher<?> patternMatcher,
|
||||
Map<String, ActionConfig> configs,
|
||||
boolean looseMatch) {
|
||||
super(patternMatcher);
|
||||
this(patternMatcher, configs, looseMatch, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p> Finds and precompiles the wildcard patterns from the ActionConfig
|
||||
* "path" attributes. ActionConfig's will be evaluated in the order they
|
||||
* exist in the config file. Only paths that actually contain a
|
||||
* wildcard will be compiled. </p>
|
||||
*
|
||||
* <p>Patterns can optionally be matched "loosely". When
|
||||
* the end of the pattern matches \*[^*]\*$ (wildcard, no wildcard,
|
||||
* wildcard), if the pattern fails, it is also matched as if the
|
||||
* last two characters didn't exist. The goal is to support the
|
||||
* legacy "*!*" syntax, where the "!*" is optional.</p>
|
||||
*
|
||||
* @param patternMatcher pattern matcher
|
||||
* @param configs An array of ActionConfig's to process
|
||||
* @param looseMatch To loosely match wildcards or not
|
||||
* @param appendNamedParameters To append named parameters or not
|
||||
*
|
||||
* @since 2.5.23
|
||||
* See WW-5065
|
||||
*/
|
||||
public ActionConfigMatcher(PatternMatcher<?> patternMatcher,
|
||||
Map<String, ActionConfig> configs,
|
||||
boolean looseMatch, boolean appendNamedParameters) {
|
||||
super(patternMatcher, appendNamedParameters);
|
||||
for (Map.Entry<String, ActionConfig> entry : configs.entrySet()) {
|
||||
addPattern(entry.getKey(), entry.getValue(), looseMatch);
|
||||
}
|
||||
|
||||
@@ -289,6 +289,8 @@ public class DefaultConfiguration implements Configuration {
|
||||
builder.constant(XWorkConstants.RELOAD_XML_CONFIGURATION, "false");
|
||||
builder.constant(StrutsConstants.STRUTS_I18N_RELOAD, "false");
|
||||
|
||||
builder.constant(StrutsConstants.STRUTS_MATCHER_APPEND_NAMED_PARAMETERS, "true");
|
||||
|
||||
return builder.create(true);
|
||||
}
|
||||
|
||||
@@ -338,8 +340,12 @@ public class DefaultConfiguration implements Configuration {
|
||||
}
|
||||
|
||||
PatternMatcher<int[]> matcher = container.getInstance(PatternMatcher.class);
|
||||
boolean appendNamedParameters = Boolean.parseBoolean(
|
||||
container.getInstance(String.class, StrutsConstants.STRUTS_MATCHER_APPEND_NAMED_PARAMETERS)
|
||||
);
|
||||
|
||||
return new RuntimeConfigurationImpl(Collections.unmodifiableMap(namespaceActionConfigs),
|
||||
Collections.unmodifiableMap(namespaceConfigs), matcher);
|
||||
Collections.unmodifiableMap(namespaceConfigs), matcher, appendNamedParameters);
|
||||
}
|
||||
|
||||
private void setDefaultResults(Map<String, ResultConfig> results, PackageConfig packageContext) {
|
||||
@@ -417,15 +423,18 @@ public class DefaultConfiguration implements Configuration {
|
||||
|
||||
public RuntimeConfigurationImpl(Map<String, Map<String, ActionConfig>> namespaceActionConfigs,
|
||||
Map<String, String> namespaceConfigs,
|
||||
PatternMatcher<int[]> matcher) {
|
||||
PatternMatcher<int[]> matcher,
|
||||
boolean appendNamedParameters)
|
||||
{
|
||||
this.namespaceActionConfigs = namespaceActionConfigs;
|
||||
this.namespaceConfigs = namespaceConfigs;
|
||||
|
||||
this.namespaceActionConfigMatchers = new LinkedHashMap<>();
|
||||
this.namespaceMatcher = new NamespaceMatcher(matcher, namespaceActionConfigs.keySet());
|
||||
this.namespaceMatcher = new NamespaceMatcher(matcher, namespaceActionConfigs.keySet(), appendNamedParameters);
|
||||
|
||||
for (Map.Entry<String, Map<String, ActionConfig>> entry : namespaceActionConfigs.entrySet()) {
|
||||
namespaceActionConfigMatchers.put(entry.getKey(), new ActionConfigMatcher(matcher, entry.getValue(), true));
|
||||
ActionConfigMatcher configMatcher = new ActionConfigMatcher(matcher, entry.getValue(), true, appendNamedParameters);
|
||||
namespaceActionConfigMatchers.put(entry.getKey(), configMatcher);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,9 +29,23 @@ import java.util.Set;
|
||||
* @since 2.1
|
||||
*/
|
||||
public class NamespaceMatcher extends AbstractMatcher<NamespaceMatch> {
|
||||
public NamespaceMatcher(PatternMatcher<?> patternMatcher,
|
||||
Set<String> namespaces) {
|
||||
super(patternMatcher);
|
||||
|
||||
public NamespaceMatcher(PatternMatcher<?> patternMatcher, Set<String> namespaces) {
|
||||
this(patternMatcher, namespaces, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches namespace strings against a wildcard pattern matcher
|
||||
*
|
||||
* @param patternMatcher pattern matcher
|
||||
* @param namespaces A set of namespaces to process
|
||||
* @param appendNamedParameters To append named parameters or not
|
||||
*
|
||||
* @since 2.5.23
|
||||
* See WW-5065
|
||||
*/
|
||||
public NamespaceMatcher(PatternMatcher<?> patternMatcher, Set<String> namespaces, boolean appendNamedParameters) {
|
||||
super(patternMatcher, appendNamedParameters);
|
||||
for (String name : namespaces) {
|
||||
if (!patternMatcher.isLiteral(name)) {
|
||||
addPattern(name, new NamespaceMatch(name, null), false);
|
||||
|
||||
+2
@@ -108,6 +108,7 @@ import com.opensymphony.xwork2.validator.DefaultValidatorFactory;
|
||||
import com.opensymphony.xwork2.validator.DefaultValidatorFileParser;
|
||||
import com.opensymphony.xwork2.validator.ValidatorFactory;
|
||||
import com.opensymphony.xwork2.validator.ValidatorFileParser;
|
||||
import com.sun.org.apache.xpath.internal.operations.Bool;
|
||||
import ognl.MethodAccessor;
|
||||
import ognl.PropertyAccessor;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
@@ -227,6 +228,7 @@ public class XWorkConfigurationProvider implements ConfigurationProvider {
|
||||
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());
|
||||
props.setProperty(StrutsConstants.STRUTS_MATCHER_APPEND_NAMED_PARAMETERS, Boolean.TRUE.toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+35
-14
@@ -93,12 +93,12 @@ public class XmlConfigurationProvider implements ConfigurationProvider {
|
||||
private String configFileName;
|
||||
private ObjectFactory objectFactory;
|
||||
|
||||
private Set<String> loadedFileUrls = new HashSet<>();
|
||||
private final Set<String> loadedFileUrls = new HashSet<>();
|
||||
private boolean errorIfMissing;
|
||||
private Map<String, String> dtdMappings;
|
||||
private Configuration configuration;
|
||||
private boolean throwExceptionOnDuplicateBeans = true;
|
||||
private Map<String, Element> declaredPackages = new HashMap<>();
|
||||
private final Map<String, Element> declaredPackages = new HashMap<>();
|
||||
|
||||
private FileManager fileManager;
|
||||
private ValueSubstitutor valueSubstitutor;
|
||||
@@ -874,15 +874,24 @@ public class XmlConfigurationProvider implements ConfigurationProvider {
|
||||
if (allowedMethodsEls.getLength() > 0) {
|
||||
// user defined 'allowed-methods' so used them whatever Strict DMI was enabled or not
|
||||
allowedMethods = new HashSet<>(packageContext.getGlobalAllowedMethods());
|
||||
|
||||
if (allowedMethodsEls.getLength() > 0) {
|
||||
Node n = allowedMethodsEls.item(0).getFirstChild();
|
||||
if (n != null) {
|
||||
String s = n.getNodeValue().trim();
|
||||
if (s.length() > 0) {
|
||||
allowedMethods.addAll(TextParseUtil.commaDelimitedStringToSet(s));
|
||||
// Fix for WW-5029 (concatenate all possible text node children)
|
||||
final Node allowedMethodsNode = allowedMethodsEls.item(0);
|
||||
if (allowedMethodsNode != null) {
|
||||
final NodeList allowedMethodsChildren = allowedMethodsNode.getChildNodes();
|
||||
final StringBuilder allowedMethodsSB = new StringBuilder();
|
||||
for (int i = 0; i < allowedMethodsChildren.getLength(); i++) {
|
||||
Node allowedMethodsChildNode = allowedMethodsChildren.item(i);
|
||||
if (allowedMethodsChildNode != null && allowedMethodsChildNode.getNodeType() == Node.TEXT_NODE) {
|
||||
String childNodeValue = allowedMethodsChildNode.getNodeValue();
|
||||
childNodeValue = (childNodeValue != null ? childNodeValue.trim() : "");
|
||||
if (childNodeValue.length() > 0) {
|
||||
allowedMethodsSB.append(childNodeValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (allowedMethodsSB.length() > 0) {
|
||||
allowedMethods.addAll(TextParseUtil.commaDelimitedStringToSet(allowedMethodsSB.toString()));
|
||||
}
|
||||
}
|
||||
} else if (packageContext.isStrictMethodInvocation()) {
|
||||
// user enabled Strict DMI but didn't defined action specific 'allowed-methods' so we use 'global-allowed-methods' only
|
||||
@@ -937,11 +946,23 @@ public class XmlConfigurationProvider implements ConfigurationProvider {
|
||||
|
||||
if (globalAllowedMethodsElms.getLength() > 0) {
|
||||
Set<String> globalAllowedMethods = new HashSet<>();
|
||||
Node n = globalAllowedMethodsElms.item(0).getFirstChild();
|
||||
if (n != null) {
|
||||
String s = n.getNodeValue().trim();
|
||||
if (s.length() > 0) {
|
||||
globalAllowedMethods = TextParseUtil.commaDelimitedStringToSet(s);
|
||||
// Fix for WW-5029 (concatenate all possible text node children)
|
||||
Node globaAllowedMethodsNode = globalAllowedMethodsElms.item(0);
|
||||
if (globaAllowedMethodsNode != null) {
|
||||
NodeList globaAllowedMethodsChildren = globaAllowedMethodsNode.getChildNodes();
|
||||
final StringBuilder globalAllowedMethodsSB = new StringBuilder();
|
||||
for (int i = 0; i < globaAllowedMethodsChildren.getLength(); i++) {
|
||||
Node globalAllowedMethodsChildNode = globaAllowedMethodsChildren.item(i);
|
||||
if (globalAllowedMethodsChildNode != null && globalAllowedMethodsChildNode.getNodeType() == Node.TEXT_NODE) {
|
||||
String childNodeValue = globalAllowedMethodsChildNode.getNodeValue();
|
||||
childNodeValue = (childNodeValue != null ? childNodeValue.trim() : "");
|
||||
if (childNodeValue.length() > 0) {
|
||||
globalAllowedMethodsSB.append(childNodeValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (globalAllowedMethodsSB.length() > 0) {
|
||||
globalAllowedMethods.addAll(TextParseUtil.commaDelimitedStringToSet(globalAllowedMethodsSB.toString()));
|
||||
}
|
||||
}
|
||||
packageContext.addGlobalAllowedMethods(globalAllowedMethods);
|
||||
|
||||
@@ -164,7 +164,8 @@ class ContainerImpl implements Container {
|
||||
public FieldInjector(ContainerImpl container, Field field, String name)
|
||||
throws MissingDependencyException {
|
||||
this.field = field;
|
||||
if (!field.isAccessible()) {
|
||||
if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()))
|
||||
&& !field.isAccessible()) {
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
try {
|
||||
if (sm != null) {
|
||||
@@ -256,7 +257,8 @@ class ContainerImpl implements Container {
|
||||
|
||||
public MethodInjector(ContainerImpl container, Method method, String name) throws MissingDependencyException {
|
||||
this.method = method;
|
||||
if (!method.isAccessible()) {
|
||||
if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers()))
|
||||
&& !method.isAccessible()) {
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
try {
|
||||
if (sm != null) {
|
||||
@@ -306,7 +308,8 @@ class ContainerImpl implements Container {
|
||||
this.implementation = implementation;
|
||||
|
||||
constructor = findConstructorIn(implementation);
|
||||
if (!constructor.isAccessible()) {
|
||||
if ((!Modifier.isPublic(constructor.getModifiers()) || !Modifier.isPublic(constructor.getDeclaringClass().getModifiers()))
|
||||
&& !constructor.isAccessible()) {
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
try {
|
||||
if (sm != null) {
|
||||
|
||||
@@ -32,6 +32,7 @@ import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.struts2.dispatcher.HttpParameters;
|
||||
import org.apache.struts2.dispatcher.Parameter;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@@ -173,7 +174,10 @@ public class AliasInterceptor extends AbstractInterceptor {
|
||||
HttpParameters contextParameters = ActionContext.getContext().getParameters();
|
||||
|
||||
if (null != contextParameters) {
|
||||
value = new Evaluated(contextParameters.get(name));
|
||||
Parameter param = contextParameters.get(name);
|
||||
if (param.isDefined()) {
|
||||
value = new Evaluated(param.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (value.isDefined()) {
|
||||
|
||||
@@ -25,13 +25,16 @@ import com.opensymphony.xwork2.XWorkConstants;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.security.AcceptedPatternsChecker;
|
||||
import com.opensymphony.xwork2.security.ExcludedPatternsChecker;
|
||||
import com.opensymphony.xwork2.util.*;
|
||||
import com.opensymphony.xwork2.util.ClearableValueStack;
|
||||
import com.opensymphony.xwork2.util.MemberAccessValueStack;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
import com.opensymphony.xwork2.util.ValueStackFactory;
|
||||
import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.struts2.dispatcher.Parameter;
|
||||
import org.apache.struts2.dispatcher.HttpParameters;
|
||||
import org.apache.struts2.dispatcher.Parameter;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
@@ -100,8 +103,8 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
|
||||
*/
|
||||
static final Comparator<String> rbCollator = new Comparator<String>() {
|
||||
public int compare(String s1, String s2) {
|
||||
int l1 = countOGNLCharacters(s1),
|
||||
l2 = countOGNLCharacters(s2);
|
||||
int l1 = countOGNLCharacters(s1);
|
||||
int l2 = countOGNLCharacters(s2);
|
||||
return l1 < l2 ? -1 : (l2 < l1 ? 1 : s1.compareTo(s2));
|
||||
}
|
||||
|
||||
@@ -185,7 +188,7 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
|
||||
if (clearableStack) {
|
||||
//if the stack's context can be cleared, do that to prevent OGNL
|
||||
//from having access to objects in the stack, see XW-641
|
||||
((ClearableValueStack)newStack).clearContextValues();
|
||||
((ClearableValueStack) newStack).clearContextValues();
|
||||
Map<String, Object> context = newStack.getContext();
|
||||
ReflectionContextState.setCreatingNullObjects(context, true);
|
||||
ReflectionContextState.setDenyMethodExecution(context, true);
|
||||
@@ -228,7 +231,7 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
|
||||
TextProvider tp = (TextProvider) action;
|
||||
developerNotification = tp.getText("devmode.notification",
|
||||
"Developer Notification:\n{0}",
|
||||
new String[]{ developerNotification }
|
||||
new String[]{developerNotification}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -245,7 +248,7 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
|
||||
/**
|
||||
* Checks if name of parameter can be accepted or thrown away
|
||||
*
|
||||
* @param name parameter name
|
||||
* @param name parameter name
|
||||
* @param action current action
|
||||
* @return true if parameter is accepted
|
||||
*/
|
||||
@@ -289,27 +292,45 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
|
||||
return accepted;
|
||||
}
|
||||
|
||||
protected boolean isWithinLengthLimit( String name ) {
|
||||
protected boolean isWithinLengthLimit(String name) {
|
||||
boolean matchLength = name.length() <= paramNameMaxLength;
|
||||
if (!matchLength) {
|
||||
LOG.debug("Parameter [{}] is too long, allowed length is [{}]", name, String.valueOf(paramNameMaxLength));
|
||||
if (devMode) { // warn only when in devMode
|
||||
LOG.warn("Parameter [{}] is too long, allowed length is [{}]. Use Interceptor Parameter Overriding " +
|
||||
"to override the limit, see more at\n" +
|
||||
"https://struts.apache.org/core-developers/interceptors.html#interceptor-parameter-overriding",
|
||||
name, paramNameMaxLength);
|
||||
} else {
|
||||
LOG.warn("Parameter [{}] is too long, allowed length is [{}]", name, paramNameMaxLength);
|
||||
}
|
||||
}
|
||||
return matchLength;
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean isAccepted(String paramName) {
|
||||
AcceptedPatternsChecker.IsAccepted result = acceptedPatterns.isAccepted(paramName);
|
||||
if (result.isAccepted()) {
|
||||
return true;
|
||||
} else if (devMode) { // warn only when in devMode
|
||||
LOG.warn("Parameter [{}] didn't match accepted pattern [{}]! See Accepted / Excluded patterns at\n" +
|
||||
"https://struts.apache.org/security/#accepted--excluded-patterns",
|
||||
paramName, result.getAcceptedPattern());
|
||||
} else {
|
||||
LOG.debug("Parameter [{}] didn't match accepted pattern [{}]!", paramName, result.getAcceptedPattern());
|
||||
}
|
||||
LOG.debug("Parameter [{}] didn't match accepted pattern [{}]!", paramName, result.getAcceptedPattern());
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean isExcluded(String paramName) {
|
||||
ExcludedPatternsChecker.IsExcluded result = excludedPatterns.isExcluded(paramName);
|
||||
if (result.isExcluded()) {
|
||||
LOG.debug("Parameter [{}] matches excluded pattern [{}]!", paramName, result.getExcludedPattern());
|
||||
if (devMode) { // warn only when in devMode
|
||||
LOG.warn("Parameter [{}] matches excluded pattern [{}]! See Accepted / Excluded patterns at\n" +
|
||||
"https://struts.apache.org/security/#accepted--excluded-patterns",
|
||||
paramName, result.getExcludedPattern());
|
||||
} else {
|
||||
LOG.debug("Parameter [{}] matches excluded pattern [{}]!", paramName, result.getExcludedPattern());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -54,7 +54,7 @@ public class OgnlUtil {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(OgnlUtil.class);
|
||||
|
||||
private ConcurrentMap<String, Object> expressions = new ConcurrentHashMap<>();
|
||||
private final ConcurrentMap<String, Object> expressions = new ConcurrentHashMap<>();
|
||||
private final ConcurrentMap<Class, BeanInfo> beanInfoCache = new ConcurrentHashMap<>();
|
||||
private TypeConverter defaultConverter;
|
||||
|
||||
@@ -74,6 +74,9 @@ public class OgnlUtil {
|
||||
excludedClasses = new HashSet<>();
|
||||
excludedPackageNamePatterns = new HashSet<>();
|
||||
excludedPackageNames = new HashSet<>();
|
||||
excludedClasses = Collections.unmodifiableSet(excludedClasses);
|
||||
excludedPackageNamePatterns = Collections.unmodifiableSet(excludedPackageNamePatterns);
|
||||
excludedPackageNames = Collections.unmodifiableSet(excludedPackageNames);
|
||||
}
|
||||
|
||||
@Inject
|
||||
@@ -181,10 +184,98 @@ public class OgnlUtil {
|
||||
this.disallowProxyMemberAccess = Boolean.parseBoolean(disallowProxyMemberAccess);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param maxLength Injects the Struts OGNL maximum expression length.
|
||||
*/
|
||||
@Inject(value = StrutsConstants.STRUTS_OGNL_EXPRESSION_MAX_LENGTH, required = false)
|
||||
protected void applyExpressionMaxLength(String maxLength) {
|
||||
try {
|
||||
if (maxLength == null || maxLength.isEmpty()) {
|
||||
// user is going to disable this functionality
|
||||
Ognl.applyExpressionMaxLength(null);
|
||||
} else {
|
||||
Ognl.applyExpressionMaxLength(Integer.parseInt(maxLength));
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
LOG.error("Unable to set OGNL Expression Max Length {}.", maxLength); // Help configuration debugging.
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isDisallowProxyMemberAccess() {
|
||||
return disallowProxyMemberAccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience mechanism to clear the OGNL Runtime Cache via OgnlUtil. May be utilized
|
||||
* by applications that generate many unique OGNL expressions over time.
|
||||
*
|
||||
* Note: This call affects the global OGNL cache, see ({@link ognl.OgnlRuntime#clearCache()} for details.
|
||||
*
|
||||
* Warning: Frequent calling if this method may negatively impact performance, but may be required
|
||||
* to avoid memory exhaustion (resource leak) with too many OGNL expressions being cached.
|
||||
*
|
||||
* @since 2.5.21
|
||||
*/
|
||||
public static void clearRuntimeCache() {
|
||||
OgnlRuntime.clearCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a mechanism to clear the OGNL expression cache. May be utilized by applications
|
||||
* that generate many unique OGNL expressions over time.
|
||||
*
|
||||
* Note: This call affects the current OgnlUtil instance. For Struts this is often a Singleton
|
||||
* instance so it can be "effectively global".
|
||||
*
|
||||
* Warning: Frequent calling if this method may negatively impact performance, but may be required
|
||||
* to avoid memory exhaustion (resource leak) with too many OGNL expressions being cached.
|
||||
*
|
||||
* @since 2.5.21
|
||||
*/
|
||||
public void clearExpressionCache() {
|
||||
expressions.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the size of the expression cache (current number of elements).
|
||||
*
|
||||
* @return current number of elements in the expression cache.
|
||||
*
|
||||
* @since 2.5.21
|
||||
*/
|
||||
public int expressionCacheSize() {
|
||||
return expressions.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a mechanism to clear the BeanInfo cache. May be utilized by applications
|
||||
* that request BeanInfo and/or PropertyDescriptors for many unique classes or objects over time
|
||||
* (especially dynamic objects).
|
||||
*
|
||||
* Note: This call affects the current OgnlUtil instance. For Struts this is often a Singleton
|
||||
* instance so it can be "effectively global".
|
||||
*
|
||||
* Warning: Frequent calling if this method may negatively impact performance, but may be required
|
||||
* to avoid memory exhaustion (resource leak) with too many BeanInfo elements being cached.
|
||||
*
|
||||
* @since 2.5.21
|
||||
*/
|
||||
public void clearBeanInfoCache() {
|
||||
beanInfoCache.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the size of the BeanInfo cache (current number of elements).
|
||||
*
|
||||
* @return current number of elements in the BeanInfo cache.
|
||||
*
|
||||
* @since 2.5.21
|
||||
*/
|
||||
public int beanInfoCacheSize() {
|
||||
return beanInfoCache.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the object's properties using the default type converter, defaulting to not throw
|
||||
* exceptions for problems setting the properties.
|
||||
@@ -682,6 +773,9 @@ public class OgnlUtil {
|
||||
setValue(name, context, o, value);
|
||||
} catch (OgnlException e) {
|
||||
Throwable reason = e.getReason();
|
||||
if (reason instanceof SecurityException) {
|
||||
LOG.warn("Could not evaluate this expression due to security constraints: [{}]", name, e);
|
||||
}
|
||||
String msg = "Caught OgnlException while setting property '" + name + "' on type '" + o.getClass().getName() + "'.";
|
||||
Throwable exception = (reason == null) ? e : reason;
|
||||
|
||||
|
||||
@@ -182,7 +182,7 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
|
||||
|
||||
private void trySetValue(String expr, Object value, boolean throwExceptionOnFailure, Map<String, Object> context) throws OgnlException {
|
||||
context.put(XWorkConverter.CONVERSION_PROPERTY_FULLNAME, expr);
|
||||
context.put(REPORT_ERRORS_ON_NO_PROP, (throwExceptionOnFailure) ? Boolean.TRUE : Boolean.FALSE);
|
||||
context.put(REPORT_ERRORS_ON_NO_PROP, throwExceptionOnFailure || logMissingProperties ? Boolean.TRUE : Boolean.FALSE);
|
||||
ognlUtil.setValue(expr, context, root, value);
|
||||
}
|
||||
|
||||
@@ -204,6 +204,9 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
|
||||
}
|
||||
|
||||
protected void handleOgnlException(String expr, Object value, boolean throwExceptionOnFailure, OgnlException e) {
|
||||
if (e != null && e.getReason() instanceof SecurityException) {
|
||||
LOG.error("Could not evaluate this expression due to security constraints: [{}]", expr, e);
|
||||
}
|
||||
boolean shouldLog = shouldLogMissingPropertyWarning(e);
|
||||
String msg = null;
|
||||
if (throwExceptionOnFailure || shouldLog) {
|
||||
@@ -246,7 +249,7 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
|
||||
}
|
||||
|
||||
protected void setupExceptionOnFailure(boolean throwExceptionOnFailure) {
|
||||
if (throwExceptionOnFailure) {
|
||||
if (throwExceptionOnFailure || logMissingProperties) {
|
||||
context.put(THROW_EXCEPTION_ON_FAILURE, true);
|
||||
}
|
||||
}
|
||||
@@ -326,7 +329,12 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
|
||||
}
|
||||
|
||||
protected Object handleOgnlException(String expr, boolean throwExceptionOnFailure, OgnlException e) {
|
||||
Object ret = findInContext(expr);
|
||||
Object ret = null;
|
||||
if (e != null && e.getReason() instanceof SecurityException) {
|
||||
LOG.error("Could not evaluate this expression due to security constraints: [{}]", expr, e);
|
||||
} else {
|
||||
ret = findInContext(expr);
|
||||
}
|
||||
if (ret == null) {
|
||||
if (shouldLogMissingPropertyWarning(e)) {
|
||||
LOG.warn("Could not find property [{}]!", expr, e);
|
||||
@@ -339,8 +347,9 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
|
||||
}
|
||||
|
||||
protected boolean shouldLogMissingPropertyWarning(OgnlException e) {
|
||||
return (e instanceof NoSuchPropertyException || e instanceof MethodFailedException)
|
||||
&& devMode && logMissingProperties;
|
||||
return (e instanceof NoSuchPropertyException ||
|
||||
(e instanceof MethodFailedException && e.getReason() instanceof NoSuchMethodException))
|
||||
&& logMissingProperties;
|
||||
}
|
||||
|
||||
private Object tryFindValue(String expr, Class asType) throws OgnlException {
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
package com.opensymphony.xwork2.ognl;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.XWorkConstants;
|
||||
import com.opensymphony.xwork2.TextProvider;
|
||||
import com.opensymphony.xwork2.conversion.NullHandler;
|
||||
import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
|
||||
@@ -61,7 +62,7 @@ public class OgnlValueStackFactory implements ValueStackFactory {
|
||||
this.textProvider = textProvider;
|
||||
}
|
||||
|
||||
@Inject(value="allowStaticMethodAccess", required=false)
|
||||
@Inject(value = XWorkConstants.ALLOW_STATIC_METHOD_ACCESS, required = false)
|
||||
protected void setAllowStaticMethodAccess(String allowStaticMethodAccess) {
|
||||
this.allowStaticMethodAccess = BooleanUtils.toBoolean(allowStaticMethodAccess);
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ public class SecurityMemberAccess extends DefaultMemberAccess {
|
||||
/**
|
||||
* SecurityMemberAccess
|
||||
* - access decisions based on whether member is static (or not)
|
||||
* - block or allow access to properties (configureable-after-construction)
|
||||
* - block or allow access to properties (configurable-after-construction)
|
||||
*
|
||||
* @param allowStaticMethodAccess
|
||||
*/
|
||||
|
||||
+16
-12
@@ -217,13 +217,14 @@ public class CompoundRootAccessor implements PropertyAccessor, MethodAccessor, C
|
||||
return null;
|
||||
}
|
||||
|
||||
Throwable reason = null;
|
||||
Class[] argTypes = getArgTypes(objects);
|
||||
for (Object o : root) {
|
||||
if (o == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Class clazz = o.getClass();
|
||||
Class[] argTypes = getArgTypes(objects);
|
||||
|
||||
MethodCall mc = null;
|
||||
|
||||
@@ -233,24 +234,27 @@ public class CompoundRootAccessor implements PropertyAccessor, MethodAccessor, C
|
||||
|
||||
if ((argTypes == null) || !invalidMethods.containsKey(mc)) {
|
||||
try {
|
||||
Object value = OgnlRuntime.callMethod((OgnlContext) context, o, name, objects);
|
||||
|
||||
if (value != null) {
|
||||
return value;
|
||||
}
|
||||
return OgnlRuntime.callMethod((OgnlContext) context, o, name, objects);
|
||||
} catch (OgnlException e) {
|
||||
// try the next one
|
||||
Throwable reason = e.getReason();
|
||||
reason = e.getReason();
|
||||
|
||||
if (!context.containsKey(OgnlValueStack.THROW_EXCEPTION_ON_FAILURE) && (mc != null) && (reason != null) && (reason.getClass() == NoSuchMethodException.class)) {
|
||||
invalidMethods.put(mc, Boolean.TRUE);
|
||||
} else if (reason != null) {
|
||||
throw new MethodFailedException(o, name, e.getReason());
|
||||
if (reason != null && !(reason instanceof NoSuchMethodException)) {
|
||||
// method has found but thrown an exception
|
||||
break;
|
||||
}
|
||||
|
||||
if ((mc != null) && (reason != null)) {
|
||||
invalidMethods.put(mc, Boolean.TRUE);
|
||||
}
|
||||
// continue and try the next one
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (context.containsKey(OgnlValueStack.THROW_EXCEPTION_ON_FAILURE)) {
|
||||
throw new MethodFailedException(target, name, reason);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
+45
-9
@@ -21,10 +21,13 @@ package com.opensymphony.xwork2.security;
|
||||
import com.opensymphony.xwork2.XWorkConstants;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.util.TextParseUtil;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -34,7 +37,11 @@ public class DefaultAcceptedPatternsChecker implements AcceptedPatternsChecker {
|
||||
private static final Logger LOG = LogManager.getLogger(DefaultAcceptedPatternsChecker.class);
|
||||
|
||||
public static final String[] ACCEPTED_PATTERNS = {
|
||||
"\\w+((\\.\\w+)|(\\[\\d+\\])|(\\(\\d+\\))|(\\['(\\w|[\\u4e00-\\u9fa5])+'\\])|(\\('(\\w|[\\u4e00-\\u9fa5])+'\\)))*"
|
||||
"\\w+((\\.\\w+)|(\\[\\d+])|(\\(\\d+\\))|(\\['(\\w|[\\u4e00-\\u9fa5])+'])|(\\('(\\w|[\\u4e00-\\u9fa5])+'\\)))*"
|
||||
};
|
||||
|
||||
public static final String[] DMI_AWARE_ACCEPTED_PATTERNS = {
|
||||
"\\w+([:]?\\w+)?((\\.\\w+)|(\\[\\d+])|(\\(\\d+\\))|(\\['(\\w|[\\u4e00-\\u9fa5])+'])|(\\('(\\w|[\\u4e00-\\u9fa5])+'\\)))*([!]?\\w+)?"
|
||||
};
|
||||
|
||||
private Set<Pattern> acceptedPatterns;
|
||||
@@ -43,46 +50,74 @@ public class DefaultAcceptedPatternsChecker implements AcceptedPatternsChecker {
|
||||
setAcceptedPatterns(ACCEPTED_PATTERNS);
|
||||
}
|
||||
|
||||
public DefaultAcceptedPatternsChecker(
|
||||
@Inject(value = StrutsConstants.STRUTS_ENABLE_DYNAMIC_METHOD_INVOCATION, required = false) String dmiValue
|
||||
) {
|
||||
if (BooleanUtils.toBoolean(dmiValue)) {
|
||||
LOG.debug("DMI is enabled, adding DMI related accepted patterns");
|
||||
setAcceptedPatterns(DMI_AWARE_ACCEPTED_PATTERNS);
|
||||
} else {
|
||||
setAcceptedPatterns(ACCEPTED_PATTERNS);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(value = XWorkConstants.OVERRIDE_ACCEPTED_PATTERNS, required = false)
|
||||
protected void setOverrideAcceptedPatterns(String acceptablePatterns) {
|
||||
LOG.warn("Overriding accepted patterns [{}] with [{}], be aware that this affects all instances and safety of your application!",
|
||||
acceptedPatterns, acceptablePatterns);
|
||||
acceptedPatterns, acceptablePatterns);
|
||||
acceptedPatterns = new HashSet<>();
|
||||
for (String pattern : TextParseUtil.commaDelimitedStringToSet(acceptablePatterns)) {
|
||||
acceptedPatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
|
||||
try {
|
||||
for (String pattern : TextParseUtil.commaDelimitedStringToSet(acceptablePatterns)) {
|
||||
acceptedPatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
|
||||
}
|
||||
} finally {
|
||||
acceptedPatterns = Collections.unmodifiableSet(acceptedPatterns);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(value = XWorkConstants.ADDITIONAL_ACCEPTED_PATTERNS, required = false)
|
||||
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));
|
||||
acceptedPatterns = new HashSet<>(acceptedPatterns); // Make mutable before adding
|
||||
try {
|
||||
for (String pattern : TextParseUtil.commaDelimitedStringToSet(acceptablePatterns)) {
|
||||
acceptedPatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
|
||||
}
|
||||
} finally {
|
||||
acceptedPatterns = Collections.unmodifiableSet(acceptedPatterns);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAcceptedPatterns(String commaDelimitedPatterns) {
|
||||
setAcceptedPatterns(TextParseUtil.commaDelimitedStringToSet(commaDelimitedPatterns));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAcceptedPatterns(String[] additionalPatterns) {
|
||||
setAcceptedPatterns(new HashSet<>(Arrays.asList(additionalPatterns)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAcceptedPatterns(Set<String> 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, patterns);
|
||||
}
|
||||
acceptedPatterns = new HashSet<>(patterns.size());
|
||||
for (String pattern : patterns) {
|
||||
acceptedPatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
|
||||
try {
|
||||
for (String pattern : patterns) {
|
||||
acceptedPatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
|
||||
}
|
||||
} finally {
|
||||
acceptedPatterns = Collections.unmodifiableSet(acceptedPatterns);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IsAccepted isAccepted(String value) {
|
||||
for (Pattern acceptedPattern : acceptedPatterns) {
|
||||
if (acceptedPattern.matcher(value).matches()) {
|
||||
@@ -93,6 +128,7 @@ public class DefaultAcceptedPatternsChecker implements AcceptedPatternsChecker {
|
||||
return IsAccepted.no(acceptedPatterns.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Pattern> getAcceptedPatterns() {
|
||||
return acceptedPatterns;
|
||||
}
|
||||
|
||||
+27
-8
@@ -27,6 +27,7 @@ import org.apache.logging.log4j.Logger;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -55,36 +56,48 @@ public class DefaultExcludedPatternsChecker implements ExcludedPatternsChecker {
|
||||
// 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));
|
||||
excludedPatterns = new HashSet<>();
|
||||
try {
|
||||
for (String pattern : TextParseUtil.commaDelimitedStringToSet(excludePatterns)) {
|
||||
excludedPatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
|
||||
}
|
||||
} finally {
|
||||
excludedPatterns = Collections.unmodifiableSet(excludedPatterns);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(value = XWorkConstants.ADDITIONAL_EXCLUDED_PATTERNS, required = false)
|
||||
public void setAdditionalExcludePatterns(String excludePatterns) {
|
||||
LOG.debug("Adding additional global patterns [{}] to excluded patterns!", excludePatterns);
|
||||
for (String pattern : TextParseUtil.commaDelimitedStringToSet(excludePatterns)) {
|
||||
excludedPatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
|
||||
excludedPatterns = new HashSet<>(excludedPatterns); // Make mutable before adding
|
||||
try {
|
||||
for (String pattern : TextParseUtil.commaDelimitedStringToSet(excludePatterns)) {
|
||||
excludedPatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
|
||||
}
|
||||
} finally {
|
||||
excludedPatterns = Collections.unmodifiableSet(excludedPatterns);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(StrutsConstants.STRUTS_ENABLE_DYNAMIC_METHOD_INVOCATION)
|
||||
protected void setDynamicMethodInvocation(String dmiValue) {
|
||||
if (BooleanUtils.toBoolean(dmiValue) == false) {
|
||||
if (!BooleanUtils.toBoolean(dmiValue)) {
|
||||
LOG.debug("DMI is disabled, adding DMI related excluded patterns");
|
||||
setAdditionalExcludePatterns("^(action|method):.*");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExcludedPatterns(String commaDelimitedPatterns) {
|
||||
setExcludedPatterns(TextParseUtil.commaDelimitedStringToSet(commaDelimitedPatterns));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExcludedPatterns(String[] patterns) {
|
||||
setExcludedPatterns(new HashSet<>(Arrays.asList(patterns)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExcludedPatterns(Set<String> 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!",
|
||||
@@ -94,11 +107,16 @@ public class DefaultExcludedPatternsChecker implements ExcludedPatternsChecker {
|
||||
LOG.debug("Sets excluded patterns to [{}]", patterns);
|
||||
}
|
||||
excludedPatterns = new HashSet<>(patterns.size());
|
||||
for (String pattern : patterns) {
|
||||
excludedPatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
|
||||
try {
|
||||
for (String pattern : patterns) {
|
||||
excludedPatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
|
||||
}
|
||||
} finally {
|
||||
excludedPatterns = Collections.unmodifiableSet(excludedPatterns);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IsExcluded isExcluded(String value) {
|
||||
for (Pattern excludedPattern : excludedPatterns) {
|
||||
if (excludedPattern.matcher(value).matches()) {
|
||||
@@ -109,6 +127,7 @@ public class DefaultExcludedPatternsChecker implements ExcludedPatternsChecker {
|
||||
return IsExcluded.no(excludedPatterns);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Pattern> getExcludedPatterns() {
|
||||
return excludedPatterns;
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ abstract class AbstractLocalizedTextProvider implements LocalizedTextProvider {
|
||||
public static final String STRUTS_MESSAGES_BUNDLE = "org/apache/struts2/struts-messages";
|
||||
|
||||
private static final String TOMCAT_RESOURCE_ENTRIES_FIELD = "resourceEntries";
|
||||
private final String RELOADED = "com.opensymphony.xwork2.util.LocalizedTextProvider.reloaded";
|
||||
private static final String RELOADED = "com.opensymphony.xwork2.util.LocalizedTextProvider.reloaded";
|
||||
|
||||
protected final ConcurrentMap<String, ResourceBundle> bundlesMap = new ConcurrentHashMap<>();
|
||||
protected boolean devMode = false;
|
||||
@@ -225,10 +225,45 @@ abstract class AbstractLocalizedTextProvider implements LocalizedTextProvider {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bundleName Removes the bundle from any cached "misses"
|
||||
* Clear a specific bundle from the <code>bundlesMap</code>
|
||||
*
|
||||
* Warning: This method <b>may be ineffective</b> due to the way the <code>bundlesMap</code>
|
||||
* is used in combination with locale. Descendants should use the method
|
||||
* {@link #clearBundle(java.lang.String, java.util.Locale)} instead.
|
||||
*
|
||||
* @param bundleName The bundle to remove from the bundle map
|
||||
*/
|
||||
public void clearBundle(final String bundleName) {
|
||||
bundlesMap.remove(getCurrentThreadContextClassLoader().hashCode() + bundleName);
|
||||
final ResourceBundle removedBundle = bundlesMap.remove(getCurrentThreadContextClassLoader().hashCode() + bundleName);
|
||||
LOG.debug("Clearing resource bundle [{}], result: [{}].", bundleName, Boolean.valueOf(removedBundle != null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear a specific bundle + locale combination from the <code>bundlesMap</code>.
|
||||
* Intended for descendants to use clear a bundle + locale combination.
|
||||
*
|
||||
* @param bundleName The bundle (combined with locale) to remove from the bundle map
|
||||
* @param locale Provides the locale to combine with the bundle to get the key
|
||||
*/
|
||||
protected void clearBundle(final String bundleName, Locale locale) {
|
||||
final String key = createMissesKey(String.valueOf(getCurrentThreadContextClassLoader().hashCode()), bundleName, locale);
|
||||
final ResourceBundle removedBundle = bundlesMap.remove(key);
|
||||
LOG.debug("Clearing resource bundle [{}], locale [{}], result: [{}].", bundleName, locale, Boolean.valueOf(removedBundle != null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the <code>missingBundles</code> contents. This allows descendants to
|
||||
* clear the <b>"missing bundles cache"</b> when desired (or needed).
|
||||
*
|
||||
* Note: This method may be used when the <code>bundlesMap</code> state has changed
|
||||
* in such a way that bundles that were previously "missing" may now be available
|
||||
* (e.g. after calling {@link #addDefaultResourceBundle(java.lang.String)} when the
|
||||
* {@link AbstractLocalizedTextProvider} has already been used for failed bundle
|
||||
* lookups of a given key, or some transitory state made a bundle lookup fail.
|
||||
*/
|
||||
protected void clearMissingBundlesCache() {
|
||||
missingBundles.clear();
|
||||
LOG.debug("Cleared the missing bundles cache.");
|
||||
}
|
||||
|
||||
protected void reloadBundles() {
|
||||
|
||||
@@ -82,13 +82,14 @@ public class ProxyUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given member is a proxy member of a proxy object.
|
||||
* Check whether the given member is a proxy member of a proxy object or is a static proxy member.
|
||||
* @param member the member to check
|
||||
* @param object the object to check
|
||||
*/
|
||||
public static boolean isProxyMember(Member member, Object object) {
|
||||
if (!isProxy(object))
|
||||
if (!Modifier.isStatic(member.getModifiers()) && !isProxy(object)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Boolean flag = isProxyMemberCache.get(member);
|
||||
if (flag != null) {
|
||||
|
||||
@@ -58,10 +58,25 @@ public class ResourceFinder {
|
||||
this(path, classLoaderInterface, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ResourceFinder instance for looking up resources (via ClassLoader or via specific URLs
|
||||
* specifying resource locations).
|
||||
*
|
||||
* This class was functional in Struts 2.3.x, but broken for Struts 2.5.x (before 2.5.24), when dealing with
|
||||
* JAR resources in certain circumstances. The current logic permits the base path to be "" (empty string),
|
||||
* which is required to also match JAR entries rooted at "" and not just file entries rooted at "/".
|
||||
*
|
||||
* @param path Base path from which to look for resources (typically "xyz/abc/klm" form for file or
|
||||
* jar contents).
|
||||
* @param classLoaderInterface ClassLoader to perform the resource lookup. If null, a default Thread
|
||||
* ClassLoader will be used.
|
||||
* @param urls URLs (typically file: or jar:) within which to search for resources, instead of the
|
||||
* ClassLoader. If null, fallback to a ClassLoader instead.
|
||||
*/
|
||||
public ResourceFinder(String path, ClassLoaderInterface classLoaderInterface, URL... urls) {
|
||||
path = StringUtils.trimToEmpty(path);
|
||||
if (!StringUtils.endsWith(path, "/")) {
|
||||
path += "/";
|
||||
if (!path.isEmpty() && !StringUtils.endsWith(path, "/")) {
|
||||
path += "/"; // Only append terminator to nonempty paths, otherwise JAR entry lookups break.
|
||||
}
|
||||
this.path = path;
|
||||
|
||||
@@ -1015,15 +1030,26 @@ public class ResourceFinder {
|
||||
public Map<URL, Set<String>> findPackagesMap(String uri) throws IOException {
|
||||
String basePath = path + uri;
|
||||
|
||||
LOG.trace(" basePath(initial): " + basePath);
|
||||
|
||||
if (!basePath.endsWith("/")) {
|
||||
basePath += "/";
|
||||
}
|
||||
|
||||
LOG.trace(" basePath(final): " + basePath);
|
||||
|
||||
Enumeration<URL> urls = getResources(basePath);
|
||||
Map<URL, Set<String>> result = new HashMap<>();
|
||||
|
||||
if (! urls.hasMoreElements()) {
|
||||
LOG.debug(" urls enumeration for basePath is empty ?");
|
||||
}
|
||||
|
||||
while (urls.hasMoreElements()) {
|
||||
URL location = urls.nextElement();
|
||||
|
||||
LOG.debug(" url (location): " + location);
|
||||
|
||||
try {
|
||||
if ("jar".equals(location.getProtocol())) {
|
||||
Set<String> resources = new HashSet<>();
|
||||
@@ -1114,12 +1140,20 @@ public class ResourceFinder {
|
||||
jarfile = conn.getJarFile();
|
||||
|
||||
Enumeration<JarEntry> entries = jarfile.entries();
|
||||
|
||||
if (entries == null || ! entries.hasMoreElements()) {
|
||||
LOG.debug(" JAR entries null or empty");
|
||||
}
|
||||
LOG.debug(" Looking for entries matching basePath: " + basePath);
|
||||
|
||||
while (entries != null && entries.hasMoreElements()) {
|
||||
JarEntry entry = entries.nextElement();
|
||||
String name = entry.getName();
|
||||
|
||||
if (entry.isDirectory() && StringUtils.startsWith(name, basePath)) {
|
||||
resources.add(name);
|
||||
} else if (entry.isDirectory()) {
|
||||
LOG.trace(" entry: " + name + " , isDirectory: " + entry.isDirectory() + " but does not start with basepath");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1156,13 +1190,22 @@ public class ResourceFinder {
|
||||
|
||||
private Enumeration<URL> getResources(String fulluri) throws IOException {
|
||||
if (urls == null) {
|
||||
LOG.debug(" urls (member) null, using classLoaderInterface to get resources");
|
||||
|
||||
return classLoaderInterface.getResources(fulluri);
|
||||
}
|
||||
|
||||
LOG.debug(" urls (member) non-null, using findResource to get resources");
|
||||
|
||||
Vector<URL> resources = new Vector<>();
|
||||
for (URL url : urls) {
|
||||
URL resource = findResource(fulluri, url);
|
||||
if (resource != null){
|
||||
|
||||
if (resource != null) {
|
||||
LOG.trace(" resource lookup non-null");
|
||||
resources.add(resource);
|
||||
} else {
|
||||
LOG.trace(" resource lookup is null");
|
||||
}
|
||||
}
|
||||
return resources.elements();
|
||||
|
||||
@@ -261,6 +261,9 @@ public final class StrutsConstants {
|
||||
/** Enables evaluation of OGNL expressions */
|
||||
public static final String STRUTS_ENABLE_OGNL_EVAL_EXPRESSION = "struts.ognl.enableOGNLEvalExpression";
|
||||
|
||||
/** The maximum length of an expression (OGNL) */
|
||||
public static final String STRUTS_OGNL_EXPRESSION_MAX_LENGTH = "struts.ognl.expressionMaxLength";
|
||||
|
||||
/** 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";
|
||||
|
||||
@@ -338,4 +341,7 @@ public final class StrutsConstants {
|
||||
public static final String STRUTS_DISALLOW_PROXY_MEMBER_ACCESS = "struts.disallowProxyMemberAccess";
|
||||
|
||||
public static final String STRUTS_OGNL_AUTO_GROWTH_COLLECTION_LIMIT = "struts.ognl.autoGrowthCollectionLimit";
|
||||
|
||||
/** See {@link com.opensymphony.xwork2.config.impl.AbstractMatcher#appendNamedParameters */
|
||||
public static final String STRUTS_MATCHER_APPEND_NAMED_PARAMETERS = "struts.matcher.appendNamedParameters";
|
||||
}
|
||||
|
||||
@@ -31,8 +31,7 @@ import com.opensymphony.xwork2.util.ValueStack;
|
||||
* complex expression and then simply reference that variable each time rather than the complex expression. This is
|
||||
* useful in both cases: when the complex expression takes time (performance improvement) or is hard to read (code
|
||||
* readability improvement).</p>
|
||||
* <p>If the tag is used with body content, the evaluation of the value parameter is omitted. Instead, the String to
|
||||
* which the body evaluates is set as value for the scoped variable.</p>
|
||||
* <p>If the value parameter is omitted, the String to which the body evaluates is set as value for the scoped variable.</p>
|
||||
*
|
||||
* <p>The scopes available are as follows:</p>
|
||||
* <ul>
|
||||
@@ -94,10 +93,10 @@ public class Set extends ContextBean {
|
||||
|
||||
Object o;
|
||||
if (value == null) {
|
||||
if (body != null && !body.equals("")) {
|
||||
o = body;
|
||||
} else {
|
||||
if (body == null) {
|
||||
o = findValue("top");
|
||||
} else {
|
||||
o = body;
|
||||
}
|
||||
} else {
|
||||
o = findValue(value);
|
||||
|
||||
@@ -97,6 +97,10 @@ public class DefaultDispatcherErrorHandler implements DispatcherErrorHandler {
|
||||
response.sendError(code, e.getMessage());
|
||||
} catch (IOException e1) {
|
||||
// we're already sending an error, not much else we can do if more stuff breaks
|
||||
LOG.warn("Unable to send error response, code: {};", code, e1);
|
||||
} catch (IllegalStateException ise) {
|
||||
// Log illegalstate instead of passing unrecoverable exception to calling thread
|
||||
LOG.warn("Unable to send error response, code: {}; isCommited: {};", code, response.isCommitted(), ise);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,6 +126,10 @@ public class DefaultDispatcherErrorHandler implements DispatcherErrorHandler {
|
||||
response.sendError(code, "Unable to show problem report:\n" + exp + "\n\n" + LocationUtils.getLocation(exp));
|
||||
} catch (IOException ex) {
|
||||
// we're already sending an error, not much else we can do if more stuff breaks
|
||||
LOG.warn("Unable to send error response, code: {};", code, ex);
|
||||
} catch (IllegalStateException ise) {
|
||||
// Log illegalstate instead of passing unrecoverable exception to calling thread
|
||||
LOG.warn("Unable to send error response, code: {}; isCommited: {};", code, response.isCommitted(), ise);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,7 +218,15 @@ public class DefaultStaticContentLoader implements StaticContentLoader {
|
||||
}
|
||||
}
|
||||
|
||||
response.sendError(HttpServletResponse.SC_NOT_FOUND);
|
||||
try {
|
||||
response.sendError(HttpServletResponse.SC_NOT_FOUND);
|
||||
} catch (IOException e1) {
|
||||
// we're already sending an error, not much else we can do if more stuff breaks
|
||||
LOG.warn("Unable to send error response, code: {};", HttpServletResponse.SC_NOT_FOUND, e1);
|
||||
} catch (IllegalStateException ise) {
|
||||
// Log illegalstate instead of passing unrecoverable exception to calling thread
|
||||
LOG.warn("Unable to send error response, code: {}; isCommited: {};", HttpServletResponse.SC_NOT_FOUND, response.isCommitted(), ise);
|
||||
}
|
||||
}
|
||||
|
||||
protected void process(InputStream is, String path, HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||
|
||||
@@ -85,7 +85,7 @@ public class Dispatcher {
|
||||
*/
|
||||
public static final String REQUEST_POST_METHOD = "POST";
|
||||
|
||||
public static final String MULTIPART_FORM_DATA_REGEX = "^multipart/form-data(; boundary=[0-9a-zA-Z'()+_,\\-./:=?]{1,70})?(;charset=[a-zA-Z\\-0-9]{3,14})?";
|
||||
public static final String MULTIPART_FORM_DATA_REGEX = "^multipart/form-data(?:\\s*;\\s*boundary=[0-9a-zA-Z'()+_,\\-./:=?]{1,70})?(?:\\s*;\\s*charset=[a-zA-Z\\-0-9]{3,14})?";
|
||||
|
||||
/**
|
||||
* Provide a thread local instance.
|
||||
@@ -582,8 +582,10 @@ public class Dispatcher {
|
||||
logConfigurationException(request, e);
|
||||
sendError(request, response, HttpServletResponse.SC_NOT_FOUND, e);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
if (handleException || devMode) {
|
||||
if (devMode) {
|
||||
LOG.debug("Dispatcher serviceAction failed", e);
|
||||
}
|
||||
sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
|
||||
} else {
|
||||
throw new ServletException(e);
|
||||
@@ -694,12 +696,23 @@ public class Dispatcher {
|
||||
try {
|
||||
locale = LocaleUtils.toLocale(defaultLocale);
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOG.warn(new ParameterizedMessage("Cannot convert 'struts.locale' = [{}] to proper locale, defaulting to request locale [{}]",
|
||||
defaultLocale, request.getLocale()), e);
|
||||
locale = request.getLocale();
|
||||
try {
|
||||
locale = request.getLocale();
|
||||
LOG.warn(new ParameterizedMessage("Cannot convert 'struts.locale' = [{}] to proper locale, defaulting to request locale [{}]",
|
||||
defaultLocale, locale), e);
|
||||
} catch (RuntimeException rex) {
|
||||
LOG.warn(new ParameterizedMessage("Cannot convert 'struts.locale' = [{}] to proper locale, and cannot get locale from HTTP Request, falling back to system default locale",
|
||||
defaultLocale), rex);
|
||||
locale = Locale.getDefault();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
locale = request.getLocale();
|
||||
try {
|
||||
locale = request.getLocale();
|
||||
} catch (RuntimeException rex) {
|
||||
LOG.warn("Cannot get locale from HTTP Request, falling back to system default locale", rex);
|
||||
locale = Locale.getDefault();
|
||||
}
|
||||
}
|
||||
return locale;
|
||||
}
|
||||
|
||||
@@ -197,5 +197,28 @@ public class HttpParameters implements Map<String, Parameter>, Cloneable {
|
||||
|
||||
return new HttpParameters(parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alternate Builder method which avoids wrapping any parameters that are already
|
||||
* a {@link Parameter} element within another {@link Parameter} wrapper.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public HttpParameters buildNoNestedWrapping() {
|
||||
Map<String, Parameter> parameters = (parent == null)
|
||||
? new HashMap<String, Parameter>()
|
||||
: new HashMap<>(parent.parameters);
|
||||
|
||||
for (Map.Entry<String, Object> entry : requestParameterMap.entrySet()) {
|
||||
String name = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
Parameter parameterValue = (value instanceof Parameter)
|
||||
? (Parameter) value
|
||||
: new Parameter.Request(name, value);
|
||||
parameters.put(name, parameterValue);
|
||||
}
|
||||
|
||||
return new HttpParameters(parameters);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+8
-8
@@ -74,6 +74,8 @@ import java.util.Map;
|
||||
public class ActionMappingParametersInterceptor extends ParametersInterceptor {
|
||||
|
||||
/**
|
||||
* Get the parameter map from ActionMapping associated with the provided ActionContext.
|
||||
*
|
||||
* @param ac The action context
|
||||
* @return the parameters from the action mapping in the context. If none found, returns an empty map.
|
||||
*/
|
||||
@@ -81,27 +83,25 @@ public class ActionMappingParametersInterceptor extends ParametersInterceptor {
|
||||
protected HttpParameters retrieveParameters(ActionContext ac) {
|
||||
ActionMapping mapping = (ActionMapping) ac.get(ServletActionContext.ACTION_MAPPING);
|
||||
if (mapping != null) {
|
||||
return HttpParameters.create(mapping.getParams()).build();
|
||||
return HttpParameters.create(mapping.getParams()).buildNoNestedWrapping();
|
||||
} else {
|
||||
return HttpParameters.create().build();
|
||||
return HttpParameters.create().buildNoNestedWrapping();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the parameters into context's ParameterMap
|
||||
* Adds the parameters into the current ActionContext's parameter map.
|
||||
*
|
||||
* Note: The method should avoid re-wrapping values which are already of type Parameter.
|
||||
*
|
||||
* @param ac The action context
|
||||
* @param newParams The parameter map to apply
|
||||
* <p>
|
||||
* In this class this is a no-op, since the parameters were fetched from the same location.
|
||||
* In subclasses both retrieveParameters() and addParametersToContext() should be overridden.
|
||||
* </p>
|
||||
*/
|
||||
@Override
|
||||
protected void addParametersToContext(ActionContext ac, Map<String, ?> newParams) {
|
||||
HttpParameters previousParams = ac.getParameters();
|
||||
HttpParameters.Builder combinedParams = HttpParameters.create().withParent(previousParams).withExtraParams(newParams);
|
||||
|
||||
ac.setParameters(combinedParams.build());
|
||||
ac.setParameters(combinedParams.buildNoNestedWrapping());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -343,6 +343,14 @@ public class FileUploadInterceptor extends AbstractInterceptor {
|
||||
validation.addFieldError(inputName, errMsg);
|
||||
}
|
||||
|
||||
if (LOG.isWarnEnabled()) {
|
||||
LOG.warn(errMsg);
|
||||
}
|
||||
} else if (file.getContent() == null) {
|
||||
String errMsg = getTextMessage(action, "struts.messages.error.uploading", new String[]{filename});
|
||||
if (validation != null) {
|
||||
validation.addFieldError(inputName, errMsg);
|
||||
}
|
||||
if (LOG.isWarnEnabled()) {
|
||||
LOG.warn(errMsg);
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ import java.util.Map;
|
||||
/**
|
||||
* Just as the CheckboxInterceptor checks that if only the hidden field is present, so too does this interceptor. If
|
||||
* the "__multiselect_" request parameter is present and its visible counterpart is not, set a new request parameter to an
|
||||
* empty Sting.
|
||||
* empty String.
|
||||
*/
|
||||
public class MultiselectInterceptor extends AbstractInterceptor {
|
||||
private static final long serialVersionUID = 1L;
|
||||
@@ -37,7 +37,7 @@ public class MultiselectInterceptor extends AbstractInterceptor {
|
||||
/**
|
||||
* Just as the CheckboxInterceptor checks that if only the hidden field is present, so too does this interceptor.
|
||||
* If the "__multiselect_" request parameter is present and its visible counterpart is not, set a new request parameter
|
||||
* to an empty Sting.
|
||||
* to an empty String.
|
||||
*
|
||||
* @param ai ActionInvocation
|
||||
* @return the result of the action
|
||||
|
||||
+28
-4
@@ -112,6 +112,20 @@ public class TokenSessionStoreInterceptor extends TokenInterceptor {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles processing of invalid tokens. If a previously stored invocation is
|
||||
* available, the method will attempt to return and render its result. Otherwise
|
||||
* it will return INVALID_TOKEN_CODE.
|
||||
*
|
||||
* Note: Because a stored (previously completed) invocation's PageContext will be closed,
|
||||
* this method must replace the stored PageContext with the current invocation's one (or a null).
|
||||
* See {@link org.apache.struts2.util.InvocationSessionStore#loadInvocation(String key, String token)} for details.
|
||||
*
|
||||
* @param invocation
|
||||
*
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
protected String handleInvalidToken(ActionInvocation invocation) throws Exception {
|
||||
ActionContext ac = invocation.getInvocationContext();
|
||||
@@ -126,11 +140,11 @@ public class TokenSessionStoreInterceptor extends TokenInterceptor {
|
||||
params.remove(tokenName);
|
||||
params.remove(TokenHelper.TOKEN_NAME_FIELD);
|
||||
|
||||
String sessionTokenName = TokenHelper.buildTokenSessionAttributeName(tokenName);
|
||||
String sessionTokenName = TokenHelper.buildTokenSessionAttributeName(tokenName);
|
||||
ActionInvocation savedInvocation = InvocationSessionStore.loadInvocation(sessionTokenName, token);
|
||||
|
||||
if (savedInvocation != null) {
|
||||
// set the valuestack to the request scope
|
||||
// set the savedInvocation's valuestack to the request scope
|
||||
ValueStack stack = savedInvocation.getStack();
|
||||
request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);
|
||||
|
||||
@@ -153,13 +167,23 @@ public class TokenSessionStoreInterceptor extends TokenInterceptor {
|
||||
return INVALID_TOKEN_CODE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles processing of valid tokens. Stores the current invocation for
|
||||
* later use by {@link handleInvalidToken}.
|
||||
* See {@link org.apache.struts2.util.InvocationSessionStore#storeInvocation(String key, String token, ActionInvocation invocation)} for details.
|
||||
*
|
||||
* @param invocation
|
||||
*
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
protected String handleValidToken(ActionInvocation invocation) throws Exception {
|
||||
// we know the token name and token must be there
|
||||
String key = TokenHelper.getTokenName();
|
||||
String token = TokenHelper.getToken(key);
|
||||
String sessionTokenName = TokenHelper.buildTokenSessionAttributeName(key);
|
||||
InvocationSessionStore.storeInvocation(sessionTokenName, token, invocation);
|
||||
String sessionTokenName = TokenHelper.buildTokenSessionAttributeName(key);
|
||||
InvocationSessionStore.storeInvocation(sessionTokenName, token, invocation);
|
||||
|
||||
return invocation.invoke();
|
||||
}
|
||||
|
||||
@@ -250,13 +250,24 @@ public class ServletRedirectResult extends StrutsResultSupport implements Reflec
|
||||
* @throws IOException in case of IO errors
|
||||
*/
|
||||
protected void sendRedirect(HttpServletResponse response, String finalLocation) throws IOException {
|
||||
if (SC_FOUND == statusCode) {
|
||||
response.sendRedirect(finalLocation);
|
||||
} else {
|
||||
response.setStatus(statusCode);
|
||||
response.setHeader("Location", finalLocation);
|
||||
response.getWriter().write(finalLocation);
|
||||
response.getWriter().close();
|
||||
try {
|
||||
if (SC_FOUND == statusCode) {
|
||||
response.sendRedirect(finalLocation);
|
||||
} else {
|
||||
response.setStatus(statusCode);
|
||||
response.setHeader("Location", finalLocation);
|
||||
try {
|
||||
response.getWriter().write(finalLocation);
|
||||
} finally {
|
||||
response.getWriter().close();
|
||||
}
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
LOG.warn("Unable to redirect to: {}, code: {}; {}", finalLocation, statusCode, ioe);
|
||||
throw ioe; // Re-throw required to preserve existing default behaviour (no stacktrace in above warn for this reason)
|
||||
} catch (IllegalStateException ise) {
|
||||
LOG.warn("Unable to redirect to: {}, code: {}; isCommited: {}; {}", finalLocation, statusCode, response.isCommitted(), ise);
|
||||
throw ise; // Re-throw required to preserve existing default behaviour (no stacktrace in above warn for this reason)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ package org.apache.struts2.util;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.ActionInvocation;
|
||||
import org.apache.struts2.ServletActionContext;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
@@ -55,11 +56,20 @@ public class InvocationSessionStore {
|
||||
return null;
|
||||
}
|
||||
|
||||
ActionInvocation savedInvocation = null;
|
||||
if (invocationContext.invocation != null) {
|
||||
savedInvocation = invocationContext.invocation;
|
||||
ActionContext.setContext(savedInvocation.getInvocationContext());
|
||||
ActionContext.getContext().setValueStack(savedInvocation.getStack());
|
||||
final ActionInvocation savedInvocation = invocationContext.invocation;
|
||||
if (savedInvocation != null) {
|
||||
// WW-5026 - Preserve the previous PageContext (even if null) and restore it to the
|
||||
// ActionContext after loading the savedInvocation context. The saved context's PageContext
|
||||
// would already be closed at this point (causing failures if used for output).
|
||||
final ActionContext savedActionContext = savedInvocation.getInvocationContext();
|
||||
final ActionContext previousActionContext = ActionContext.getContext();
|
||||
ActionContext.setContext(savedActionContext);
|
||||
savedActionContext.setValueStack(savedInvocation.getStack());
|
||||
if (previousActionContext != null) {
|
||||
savedActionContext.put(ServletActionContext.PAGE_CONTEXT, previousActionContext.get(ServletActionContext.PAGE_CONTEXT));
|
||||
} else {
|
||||
savedActionContext.put(ServletActionContext.PAGE_CONTEXT, null);
|
||||
}
|
||||
}
|
||||
|
||||
return savedInvocation;
|
||||
|
||||
@@ -68,9 +68,13 @@ public class SetTag extends ContextBeanTag {
|
||||
@Override
|
||||
protected String getBody() {
|
||||
if (trimBody) {
|
||||
return super.getBody();
|
||||
if (bodyContent == null) {
|
||||
return null;
|
||||
} else {
|
||||
return bodyContent.getString().trim();
|
||||
}
|
||||
} else {
|
||||
return (bodyContent == null ? "" : bodyContent.getString());
|
||||
return (bodyContent == null ? null : bodyContent.getString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,12 +23,16 @@ import ognl.OgnlException;
|
||||
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.ognl.OgnlUtil;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
/**
|
||||
* FIXME: remove?
|
||||
*/
|
||||
public class OgnlTool {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(OgnlTool.class);
|
||||
|
||||
private OgnlUtil ognlUtil;
|
||||
|
||||
public OgnlTool() {
|
||||
@@ -43,6 +47,9 @@ public class OgnlTool {
|
||||
try {
|
||||
return Ognl.getValue(ognlUtil.compile(expr), context);
|
||||
} catch (OgnlException e) {
|
||||
if (e.getReason() instanceof SecurityException) {
|
||||
LOG.warn("Could not evaluate this expression due to security constraints: [{}]", expr, e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,4 +219,15 @@ struts.ognl.enableExpressionCache=true
|
||||
### Indicates if Dispatcher should handle unexpected exceptions by calling sendError()
|
||||
### or simply rethrow it as a ServletException to allow future processing by other frameworks like Spring Security
|
||||
struts.handle.exception=true
|
||||
|
||||
### Applies maximum length allowed on OGNL expressions for security enhancement (optional)
|
||||
###
|
||||
### **WARNING**: If developers enable this option (by configuration) they should make sure that they understand the implications of setting
|
||||
### struts.ognl.expressionMaxLength. They must choose a value large enough to permit ALL valid OGNL expressions used within the application.
|
||||
### Values larger than the 200-400 range have diminishing security value (at which point it is really only a "style guard" for long OGNL
|
||||
### expressions in an application. Setting a value of null or "" will also disable the feature.
|
||||
###
|
||||
### NOTE: The sample line below is *INTENTIONALLY* commented out, as this feature is disabled by default.
|
||||
# struts.ognl.expressionMaxLength=256
|
||||
|
||||
### END SNIPPET: complete_file
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
java.lang.ClassLoader,
|
||||
java.lang.Shutdown,
|
||||
java.lang.ProcessBuilder,
|
||||
sun.misc.Unsafe,
|
||||
com.opensymphony.xwork2.ActionContext" />
|
||||
|
||||
<!-- this must be valid regex, each '.' in package name must be escaped! -->
|
||||
@@ -55,13 +56,21 @@
|
||||
<constant name="struts.excludedPackageNames"
|
||||
value="
|
||||
ognl.,
|
||||
java.io.,
|
||||
java.net.,
|
||||
java.nio.,
|
||||
javax.,
|
||||
freemarker.core.,
|
||||
freemarker.template.,
|
||||
freemarker.ext.jsp.,
|
||||
freemarker.ext.rhino.,
|
||||
sun.misc.,
|
||||
sun.reflect.,
|
||||
javassist.,
|
||||
org.apache.velocity.,
|
||||
org.objectweb.asm.,
|
||||
org.springframework.context.,
|
||||
com.opensymphony.xwork2.inject.,
|
||||
com.opensymphony.xwork2.ognl.,
|
||||
com.opensymphony.xwork2.security.,
|
||||
com.opensymphony.xwork2.util." />
|
||||
|
||||
@@ -29,9 +29,12 @@
|
||||
<#if parameters.listValueKey??>
|
||||
<#-- checks the valueStack for the 'valueKey.' The valueKey is then looked-up in the locale
|
||||
file for it's localized value. This is then used as a label -->
|
||||
<#assign itemValue = stack.findString(parameters.listValueKey)/>
|
||||
<#-- FIXME: find a better way to get the value than a call to @s.text -->
|
||||
<#assign itemValue><@s.text name="${itemValue}"/></#assign>
|
||||
<#assign valueKey = stack.findString(parameters.listValueKey)!''/>
|
||||
<#if valueKey?has_content>
|
||||
<#assign itemValue = struts.getText(valueKey) />
|
||||
<#else>
|
||||
<#assign itemValue = parameters.listValueKey />
|
||||
</#if>
|
||||
<#elseif parameters.listValue??>
|
||||
<#assign itemValue = stack.findString(parameters.listValue)/>
|
||||
<#else>
|
||||
|
||||
@@ -70,8 +70,8 @@
|
||||
<#if parameters.listValueKey??>
|
||||
<#-- checks the valueStack for the 'valueKey.' The valueKey is then looked-up in the locale file for it's
|
||||
localized value. This is then used as a label -->
|
||||
<#assign valueKey = stack.findString(parameters.listValueKey) />
|
||||
<#if valueKey??>
|
||||
<#assign valueKey = stack.findString(parameters.listValueKey)!'' />
|
||||
<#if valueKey?has_content>
|
||||
<#assign itemValue = struts.getText(valueKey) />
|
||||
<#else>
|
||||
<#assign itemValue = parameters.listValueKey />
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.opensymphony.xwork2.config.entities.ExceptionMappingConfig;
|
||||
import com.opensymphony.xwork2.config.entities.InterceptorMapping;
|
||||
import com.opensymphony.xwork2.config.entities.ResultConfig;
|
||||
import com.opensymphony.xwork2.util.WildcardHelper;
|
||||
import org.apache.struts2.util.RegexPatternMatcher;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@@ -142,6 +143,79 @@ public class ActionConfigMatcherTest extends XWorkTestCase {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to make sure the {@link AbstractMatcher#replaceParameters(Map, Map)} method isn't adding values to the
|
||||
* return value.
|
||||
*/
|
||||
public void testReplaceParametersWithNoAppendingParams() {
|
||||
Map<String, ActionConfig> map = new HashMap<>();
|
||||
|
||||
HashMap<String, String> params = new HashMap<>();
|
||||
params.put("first", "{1}");
|
||||
|
||||
ActionConfig config = new ActionConfig.Builder("package", "foo/{one}/{two}/{three}", "foo.bar.Action")
|
||||
.addParams(params)
|
||||
.addExceptionMapping(new ExceptionMappingConfig.Builder("foo{1}", "java.lang.{2}Exception", "success{1}")
|
||||
.addParams(new HashMap<>(params))
|
||||
.build())
|
||||
.addResultConfig(new ResultConfig.Builder("success{1}", "foo.{2}").addParams(params).build())
|
||||
.setStrictMethodInvocation(false)
|
||||
.build();
|
||||
map.put("foo/{one}/{two}/{three}", config);
|
||||
ActionConfigMatcher replaceMatcher = new ActionConfigMatcher(new RegexPatternMatcher(), map, false, false);
|
||||
ActionConfig matched = replaceMatcher.match("foo/paramOne/paramTwo/paramThree");
|
||||
assertNotNull("ActionConfig should be matched", matched);
|
||||
|
||||
// Verify all The ActionConfig, ExceptionConfig, and ResultConfig have the correct number of params
|
||||
assertEquals("The ActionConfig should have the correct number of params", 1, matched.getParams().size());
|
||||
assertEquals("The ExceptionMappingConfigs should have the correct number of params", 1, matched.getExceptionMappings().get(0).getParams().size());
|
||||
assertEquals("The ResultConfigs should have the correct number of params", 1, matched.getResults().get("successparamOne").getParams().size());
|
||||
|
||||
// Verify the params are still getting their values replaced correctly
|
||||
assertEquals("The ActionConfig params have replaced values", "paramOne", matched.getParams().get("first"));
|
||||
assertEquals("The ActionConfig params have replaced values", "paramOne", matched.getExceptionMappings().get(0).getParams().get("first"));
|
||||
assertEquals("The ActionConfig params have replaced values", "paramOne", matched.getResults().get("successparamOne").getParams().get("first"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to make sure the {@link AbstractMatcher#replaceParameters(Map, Map)} method is adding values to the
|
||||
* return value.
|
||||
*/
|
||||
public void testReplaceParametersWithAppendingParams() {
|
||||
Map<String, ActionConfig> map = new HashMap<>();
|
||||
|
||||
HashMap<String, String> params = new HashMap<>();
|
||||
params.put("first", "{1}");
|
||||
|
||||
ActionConfig config = new ActionConfig.Builder("package", "foo/{one}/{two}/{three}", "foo.bar.Action")
|
||||
.addParams(params)
|
||||
.addExceptionMapping(new ExceptionMappingConfig.Builder("foo{1}", "java.lang.{2}Exception", "success{1}")
|
||||
.addParams(new HashMap<>(params))
|
||||
.build())
|
||||
.addResultConfig(new ResultConfig.Builder("success{1}", "foo.{2}").addParams(params).build())
|
||||
.setStrictMethodInvocation(false)
|
||||
.build();
|
||||
map.put("foo/{one}/{two}/{three}", config);
|
||||
ActionConfigMatcher replaceMatcher = new ActionConfigMatcher(new RegexPatternMatcher(), map, false, true);
|
||||
ActionConfig matched = replaceMatcher.match("foo/paramOne/paramTwo/paramThree");
|
||||
assertNotNull("ActionConfig should be matched", matched);
|
||||
|
||||
assertEquals(4, matched.getParams().size());
|
||||
assertEquals(4, matched.getExceptionMappings().get(0).getParams().size());
|
||||
assertEquals(4, matched.getResults().get("successparamOne").getParams().size());
|
||||
|
||||
// Verify the params are still getting their values replaced correctly
|
||||
assertEquals("paramOne", matched.getParams().get("first"));
|
||||
assertEquals("paramOne", matched.getParams().get("one"));
|
||||
assertEquals("paramTwo", matched.getParams().get("two"));
|
||||
assertEquals("paramThree", matched.getParams().get("three"));
|
||||
assertEquals("paramOne", matched.getExceptionMappings().get(0).getParams().get("first"));
|
||||
assertEquals("paramOne", matched.getExceptionMappings().get(0).getParams().get("one"));
|
||||
assertEquals("paramTwo", matched.getExceptionMappings().get(0).getParams().get("two"));
|
||||
assertEquals("paramThree", matched.getExceptionMappings().get(0).getParams().get("three"));
|
||||
assertEquals("paramOne", matched.getResults().get("successparamOne").getParams().get("first"));
|
||||
}
|
||||
|
||||
private Map<String,ActionConfig> buildActionConfigMap() {
|
||||
Map<String, ActionConfig> map = new HashMap<>();
|
||||
|
||||
|
||||
+802
@@ -23,8 +23,11 @@ import com.opensymphony.xwork2.ObjectFactory;
|
||||
import com.opensymphony.xwork2.config.ConfigurationProvider;
|
||||
import com.opensymphony.xwork2.config.RuntimeConfiguration;
|
||||
import com.opensymphony.xwork2.config.entities.PackageConfig;
|
||||
import com.opensymphony.xwork2.config.entities.ResultConfig;
|
||||
import com.opensymphony.xwork2.config.entities.ResultTypeConfig;
|
||||
import com.opensymphony.xwork2.config.impl.MockConfiguration;
|
||||
import com.opensymphony.xwork2.util.ClassLoaderUtil;
|
||||
import com.sun.org.apache.xerces.internal.dom.ElementImpl;
|
||||
import org.w3c.dom.Document;
|
||||
|
||||
import java.io.File;
|
||||
@@ -35,9 +38,20 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.apache.struts2.result.ServletDispatcherResult;
|
||||
import org.w3c.dom.Attr;
|
||||
import org.w3c.dom.DOMException;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NamedNodeMap;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.w3c.dom.TypeInfo;
|
||||
import org.w3c.dom.UserDataHandler;
|
||||
|
||||
|
||||
public class XmlConfigurationProviderTest extends ConfigurationTestBase {
|
||||
@@ -294,4 +308,792 @@ public class XmlConfigurationProviderTest extends ConfigurationTestBase {
|
||||
assertTrue(loadedFileNames.contains("xwork-test-beans.xml"));
|
||||
assertTrue(loadedFileNames.contains("xwork-test-default.xml"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test buildAllowedMethods() to ensure consistent results for processing
|
||||
* <allowed-methods/> in <action/> XML configuration elements.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public void testBuildAllowedMethods() throws Exception {
|
||||
// Test introduced with WW-5029 fix.
|
||||
// Set up test using two mock DOM Elements:
|
||||
// 1) A mock "action" Element with a single "allowed-methods" child Element that contains a single
|
||||
// TEXT_NODE Node. This simulates a typical result from a SAX parser parsing the allowed-methods
|
||||
// tag body.
|
||||
// 2) A mock "action" Element with a single "allowed-methods" child Element that contains multiple
|
||||
// TEXT_NODE Nodes. This simulates an unusal result from a SAX parser parsing the allowed-methods
|
||||
// tag body (as reported with WW-5029).
|
||||
final String fakeBodyString = "allowedMethod1,allowedMethod2,allowedMethod3";
|
||||
PackageConfig.Builder testPackageConfigBuilder = new PackageConfig.Builder("allowedMethodsPackage");
|
||||
List<String> singleStringList = new ArrayList(1);
|
||||
List<String> multipleStringList = new ArrayList(4);
|
||||
singleStringList.add(fakeBodyString);
|
||||
multipleStringList.add("allowedMethod1,");
|
||||
multipleStringList.add("allowed");
|
||||
multipleStringList.add("Method2,");
|
||||
multipleStringList.add("allowedMethod3");
|
||||
NodeList mockNodeListSingleChild = new MockNodeList(singleStringList);
|
||||
NodeList mockNodeListMultipleChild = new MockNodeList(multipleStringList);
|
||||
Element mockSingleChildAllowedMethodsElement = new MockElement("allowed-methods", fakeBodyString,
|
||||
"allowed-methods", fakeBodyString, Node.TEXT_NODE, mockNodeListSingleChild, null);
|
||||
Element mockMultipleChildAllowedMethodsElement = new MockElement("allowed-methods", fakeBodyString,
|
||||
"allowed-methods", fakeBodyString, Node.TEXT_NODE, mockNodeListMultipleChild, null);
|
||||
MockNodeList mockActionElementChildrenSingle = new MockNodeList();
|
||||
mockActionElementChildrenSingle.addToNodeList(mockSingleChildAllowedMethodsElement);
|
||||
MockNodeList mockActionElementChildrenMultiple = new MockNodeList();
|
||||
mockActionElementChildrenMultiple.addToNodeList(mockMultipleChildAllowedMethodsElement);
|
||||
Element mockActionElementSingle = new MockElement("action", "fakeBody", "action", "fakeValue",
|
||||
Node.TEXT_NODE, mockActionElementChildrenSingle, null);
|
||||
Element mockActionElementMultiple = new MockElement("action", "fakeBody", "action", "fakeValue",
|
||||
Node.TEXT_NODE, mockActionElementChildrenMultiple, null);
|
||||
// Attempt the method using both types of Elements (single child and multiple child) and confirm
|
||||
// the result is the same for both. Also confirm the results are as expected.
|
||||
XmlConfigurationProvider prov = new XmlConfigurationProvider("com/opensymphony/xwork2/config/providers/xwork- test.xml", false);
|
||||
Set<String> singleChildResult = prov.buildAllowedMethods(mockActionElementSingle, testPackageConfigBuilder);
|
||||
Set<String> multipleChildResult = prov.buildAllowedMethods(mockActionElementMultiple, testPackageConfigBuilder);
|
||||
assertNotNull("singleChildResult is null ?", singleChildResult);
|
||||
assertNotNull("multipleChildResult is null ?", multipleChildResult);
|
||||
assertEquals("singleChildResult not equal to multipleChildResult ?", singleChildResult, multipleChildResult);
|
||||
// Since both Sets are equal, only need to test one to confirm contents are correct
|
||||
assertEquals("result Sets not of length 3 ?", 3, multipleChildResult.size());
|
||||
assertTrue("allowedMethod1 not present ?", multipleChildResult.contains("allowedMethod1"));
|
||||
assertTrue("allowedMethod2 not present ?", multipleChildResult.contains("allowedMethod2"));
|
||||
assertTrue("allowedMethod3 not present ?", multipleChildResult.contains("allowedMethod3"));
|
||||
assertFalse("noSuchMethod is present ?", multipleChildResult.contains("noSuchMethod"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test loadGlobalAllowedMethods() to ensure consistent results for processing
|
||||
* <global-allowed-methods/> in <package/> XML configuration elements.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public void testLoadGlobalAllowedMethods() throws Exception {
|
||||
// Test introduced with WW-5029 fix.
|
||||
// Set up test using two mock DOM Elements:
|
||||
// 1) A mock "package" Element with a single "global-allowed-methods" child Element that contains a single
|
||||
// TEXT_NODE Node. This simulates a typical result from a SAX parser parsing the global-allowed-methods
|
||||
// tag body.
|
||||
// 2) A mock "package" Element with a single "global-allowed-methods" child Element that contains multiple
|
||||
// TEXT_NODE Nodes. This simulates an unusal result from a SAX parser parsing the global-allowed-methods
|
||||
// tag body (as reported with WW-5029).
|
||||
final String fakeBodyString = "allowedMethod1,allowedMethod2,allowedMethod3";
|
||||
PackageConfig.Builder testPackageConfigBuilder = new PackageConfig.Builder("globalAllowedMethodsPackage");
|
||||
List<String> singleStringList = new ArrayList(1);
|
||||
List<String> multipleStringList = new ArrayList(4);
|
||||
singleStringList.add(fakeBodyString);
|
||||
multipleStringList.add("allowedMethod4,");
|
||||
multipleStringList.add("allowed");
|
||||
multipleStringList.add("Method5,");
|
||||
multipleStringList.add("allowedMethod6");
|
||||
NodeList mockNodeListSingleChild = new MockNodeList(singleStringList);
|
||||
NodeList mockNodeListMultipleChild = new MockNodeList(multipleStringList);
|
||||
Element mockSingleChildAllowedMethodsElement = new MockElement("global-allowed-methods", fakeBodyString,
|
||||
"global-allowed-methods", fakeBodyString, Node.TEXT_NODE, mockNodeListSingleChild, null);
|
||||
Element mockMultipleChildAllowedMethodsElement = new MockElement("global-allowed-methods", fakeBodyString,
|
||||
"global-allowed-methods", fakeBodyString, Node.TEXT_NODE, mockNodeListMultipleChild, null);
|
||||
MockNodeList mockPackageElementChildrenSingle = new MockNodeList();
|
||||
mockPackageElementChildrenSingle.addToNodeList(mockSingleChildAllowedMethodsElement);
|
||||
MockNodeList mockPackageElementChildrenMultiple = new MockNodeList();
|
||||
mockPackageElementChildrenMultiple.addToNodeList(mockMultipleChildAllowedMethodsElement);
|
||||
Element mockPackageElementSingle = new MockElement("package", "fakeBody", "package", "fakeValue",
|
||||
Node.TEXT_NODE, mockPackageElementChildrenSingle, null);
|
||||
Element mockPackageElementMultiple = new MockElement("package", "fakeBody", "package", "fakeValue",
|
||||
Node.TEXT_NODE, mockPackageElementChildrenMultiple, null);
|
||||
// Attempt the method using the single child Element and confirm the result is as expected.
|
||||
XmlConfigurationProvider prov = new XmlConfigurationProvider("com/opensymphony/xwork2/config/providers/xwork- test.xml", false);
|
||||
prov.loadGlobalAllowedMethods(testPackageConfigBuilder, mockPackageElementSingle);
|
||||
Set<String> currentGlobalResult = testPackageConfigBuilder.getGlobalAllowedMethods();
|
||||
assertNotNull("currentGlobalResult is null ?", currentGlobalResult);
|
||||
assertEquals("currentGlobalResult Sets not of length 3 ?", 3, currentGlobalResult.size());
|
||||
assertTrue("allowedMethod1 not present ?", currentGlobalResult.contains("allowedMethod1"));
|
||||
assertTrue("allowedMethod2 not present ?", currentGlobalResult.contains("allowedMethod2"));
|
||||
assertTrue("allowedMethod3 not present ?", currentGlobalResult.contains("allowedMethod3"));
|
||||
assertFalse("noSuchMethod is present ?", currentGlobalResult.contains("noSuchMethod"));
|
||||
// Attempt the method using the multiple child Element and confirm the result is as expected.
|
||||
prov.loadGlobalAllowedMethods(testPackageConfigBuilder, mockPackageElementMultiple);
|
||||
currentGlobalResult = testPackageConfigBuilder.getGlobalAllowedMethods();
|
||||
assertNotNull("currentGlobalResult is null ?", currentGlobalResult);
|
||||
assertEquals("currentGlobalResult Sets not of length 6 ?", 6, currentGlobalResult.size());
|
||||
assertTrue("allowedMethod4 not present ?", currentGlobalResult.contains("allowedMethod4"));
|
||||
assertTrue("allowedMethod5 not present ?", currentGlobalResult.contains("allowedMethod5"));
|
||||
assertTrue("allowedMethod6 not present ?", currentGlobalResult.contains("allowedMethod6"));
|
||||
assertFalse("noSuchMethod is present ?", currentGlobalResult.contains("snoSUchMethod"));
|
||||
// Confirm the previously added elements are still present
|
||||
assertTrue("allowedMethod1 not present ?", currentGlobalResult.contains("allowedMethod1"));
|
||||
assertTrue("allowedMethod2 not present ?", currentGlobalResult.contains("allowedMethod2"));
|
||||
assertTrue("allowedMethod3 not present ?", currentGlobalResult.contains("allowedMethod3"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test buildResults() to ensure consistent results for processing
|
||||
* <result/> in <action/> XML configuration elements.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public void testBuildResults() throws Exception {
|
||||
// Set up test using two mock DOM Elements:
|
||||
// 1) A mock "action" Element with a two "result" child Elements that each contains a single
|
||||
// TEXT_NODE Node. This simulates a typical result from a SAX parser parsing the result
|
||||
// tag body.
|
||||
// 2) A mock "action" Element with two "result" child Elements that each contain multiple
|
||||
// TEXT_NODE Nodes. This simulates an unusal result from a SAX parser parsing the result
|
||||
// tag body.
|
||||
final String fakeBodyString = "/SomePath/SomePath/SomePath/SomeJSP.jsp";
|
||||
final String fakeBodyString2 = "/SomePath2/SomePath2/SomePath2/SomeJSP2.jsp";
|
||||
final String resultParam = "nonNullDefaultParam";
|
||||
PackageConfig.Builder testPackageConfigBuilder = new PackageConfig.Builder("resultsPackage");
|
||||
ResultTypeConfig.Builder resultTypeConfigBuilder = new ResultTypeConfig.Builder("dispatcher", (String) ServletDispatcherResult.class.getName());
|
||||
resultTypeConfigBuilder.defaultResultParam(resultParam);
|
||||
ResultTypeConfig resultTypeConfig = resultTypeConfigBuilder.build();
|
||||
testPackageConfigBuilder.addResultTypeConfig(resultTypeConfig);
|
||||
List<String> singleStringList = new ArrayList(1);
|
||||
List<String> singleStringList2 = new ArrayList(1);
|
||||
List<String> multipleStringList = new ArrayList(4);
|
||||
List<String> multipleStringList2 = new ArrayList(4);
|
||||
singleStringList.add(fakeBodyString);
|
||||
singleStringList2.add(fakeBodyString2);
|
||||
multipleStringList.add("/SomePath");
|
||||
multipleStringList.add("/SomePath/");
|
||||
multipleStringList.add("SomePath/");
|
||||
multipleStringList.add("SomeJSP.jsp");
|
||||
multipleStringList2.add("/SomePath2");
|
||||
multipleStringList2.add("/SomePath2/");
|
||||
multipleStringList2.add("SomePath2/");
|
||||
multipleStringList2.add("SomeJSP2.jsp");
|
||||
NodeList mockNodeListSingleChild = new MockNodeList(singleStringList);
|
||||
NodeList mockNodeListSingleChild2 = new MockNodeList(singleStringList2);
|
||||
NodeList mockNodeListMultipleChild = new MockNodeList(multipleStringList);
|
||||
NodeList mockNodeListMultipleChild2 = new MockNodeList(multipleStringList2);
|
||||
Element mockSingleChildResultElement = new MockElement("result", fakeBodyString,
|
||||
"result", fakeBodyString, Node.TEXT_NODE, mockNodeListSingleChild, null);
|
||||
mockSingleChildResultElement.setAttribute("name", "input");
|
||||
mockSingleChildResultElement.setAttribute("type", "dispatcher");
|
||||
Element mockSingleChildResultElement2 = new MockElement("result", fakeBodyString2,
|
||||
"result", fakeBodyString2, Node.TEXT_NODE, mockNodeListSingleChild2, null);
|
||||
mockSingleChildResultElement2.setAttribute("name", "success");
|
||||
mockSingleChildResultElement2.setAttribute("type", "dispatcher");
|
||||
Element mockMultipleChildAllowedMethodsElement = new MockElement("result", fakeBodyString,
|
||||
"result", fakeBodyString, Node.TEXT_NODE, mockNodeListMultipleChild, null);
|
||||
mockMultipleChildAllowedMethodsElement.setAttribute("name", "input");
|
||||
mockMultipleChildAllowedMethodsElement.setAttribute("type", "dispatcher");
|
||||
Element mockMultipleChildAllowedMethodsElement2 = new MockElement("result", fakeBodyString2,
|
||||
"result", fakeBodyString2, Node.TEXT_NODE, mockNodeListMultipleChild2, null);
|
||||
mockMultipleChildAllowedMethodsElement2.setAttribute("name", "success");
|
||||
mockMultipleChildAllowedMethodsElement2.setAttribute("type", "dispatcher");
|
||||
MockNodeList mockActionElementChildrenSingle = new MockNodeList();
|
||||
mockActionElementChildrenSingle.addToNodeList(mockSingleChildResultElement);
|
||||
mockActionElementChildrenSingle.addToNodeList(mockSingleChildResultElement2);
|
||||
MockNodeList mockActionElementChildrenMultiple = new MockNodeList();
|
||||
mockActionElementChildrenMultiple.addToNodeList(mockMultipleChildAllowedMethodsElement);
|
||||
mockActionElementChildrenMultiple.addToNodeList(mockMultipleChildAllowedMethodsElement2);
|
||||
Element mockActionElementSingle = new MockElement("action", "fakeBody", "action", "fakeValue",
|
||||
Node.TEXT_NODE, mockActionElementChildrenSingle, null);
|
||||
Element mockActionElementMultiple = new MockElement("action", "fakeBody", "action", "fakeValue",
|
||||
Node.TEXT_NODE, mockActionElementChildrenMultiple, null);
|
||||
// Attempt the method using both types of Elements (single child and multiple child) and confirm
|
||||
// the result is the same for both. Also confirm the results are as expected.
|
||||
XmlConfigurationProvider prov = new XmlConfigurationProvider("com/opensymphony/xwork2/config/providers/xwork- test.xml", false);
|
||||
Map<String, ResultConfig> singleChildResult = prov.buildResults(mockActionElementSingle, testPackageConfigBuilder);
|
||||
Map<String, ResultConfig> multipleChildResult = prov.buildResults(mockActionElementMultiple, testPackageConfigBuilder);
|
||||
assertNotNull("singleChildResult is null ?", singleChildResult);
|
||||
assertNotNull("multipleChildResult is null ?", multipleChildResult);
|
||||
assertEquals("singleChildResult not equal to multipleChildResult ?", singleChildResult, multipleChildResult);
|
||||
// Since both Maps are equal, only need to test one to confirm the contents of ONE are correct.contents are correct
|
||||
// The multipleChildResult (constructed from the multiple child TEXT_NODES) will be checked for correctness.
|
||||
assertEquals("result Maps not of size 2 ?", 2, multipleChildResult.size());
|
||||
ResultConfig inputResult = multipleChildResult.get("input");
|
||||
ResultConfig successResult = multipleChildResult.get("success");
|
||||
assertNotNull("inputResult (multipleChildResult) is null ?", inputResult);
|
||||
assertNotNull("successResult (multipleChildResult) is null ?", successResult);
|
||||
Map<String, String> inputResultParams = inputResult.getParams();
|
||||
Map<String, String> successResultParams = successResult.getParams();
|
||||
assertNotNull("inputResultParams (multipleChildResult) is null ?", inputResultParams);
|
||||
assertNotNull("successResultParams (multipleChildResult) is null ?", successResultParams);
|
||||
assertEquals("inputResult (multipleChildResult) resultParam value not equal to fakeBodyString ?",
|
||||
fakeBodyString, inputResultParams.get(resultParam));
|
||||
assertEquals("successResult (multipleChildResult) resultParam value not equal to fakeBodyString2 ?",
|
||||
fakeBodyString2, successResultParams.get(resultParam));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test loadGlobalResults() to ensure consistent results for processing
|
||||
* <global-results/> in <package/> XML configuration elements.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public void testLoadGlobalResults() throws Exception {
|
||||
// Set up test using two mock DOM Elements:
|
||||
// 1) A mock "package" Element containing a mock "global-results" Element with a two "result"
|
||||
// child Elements that each contains a single TEXT_NODE Node. This simulates a typical result
|
||||
// from a SAX parser parsing the result tag body.
|
||||
// 2) A mock "package" Element containing a mock "global-results" Element with two "result"
|
||||
// child Elements that each contain multiple TEXT_NODE Nodes. This simulates an unusal result
|
||||
// from a SAX parser parsing the result tag body.
|
||||
final String fakeBodyString = "/SomePath/SomePath/SomePath/SomeJSP.jsp";
|
||||
final String fakeBodyString2 = "/SomePath2/SomePath2/SomePath2/SomeJSP2.jsp";
|
||||
final String resultParam = "nonNullDefaultParam";
|
||||
PackageConfig.Builder testPackageConfigBuilder = new PackageConfig.Builder("resultsPackage");
|
||||
ResultTypeConfig.Builder resultTypeConfigBuilder = new ResultTypeConfig.Builder("dispatcher", (String) ServletDispatcherResult.class.getName());
|
||||
resultTypeConfigBuilder.defaultResultParam(resultParam);
|
||||
ResultTypeConfig resultTypeConfig = resultTypeConfigBuilder.build();
|
||||
testPackageConfigBuilder.addResultTypeConfig(resultTypeConfig);
|
||||
List<String> singleStringList = new ArrayList(1);
|
||||
List<String> singleStringList2 = new ArrayList(1);
|
||||
List<String> multipleStringList = new ArrayList(4);
|
||||
List<String> multipleStringList2 = new ArrayList(4);
|
||||
singleStringList.add(fakeBodyString);
|
||||
singleStringList2.add(fakeBodyString2);
|
||||
multipleStringList.add("/SomePath");
|
||||
multipleStringList.add("/SomePath/");
|
||||
multipleStringList.add("SomePath/");
|
||||
multipleStringList.add("SomeJSP.jsp");
|
||||
multipleStringList2.add("/SomePath2");
|
||||
multipleStringList2.add("/SomePath2/");
|
||||
multipleStringList2.add("SomePath2/");
|
||||
multipleStringList2.add("SomeJSP2.jsp");
|
||||
NodeList mockNodeListSingleChild = new MockNodeList(singleStringList);
|
||||
NodeList mockNodeListSingleChild2 = new MockNodeList(singleStringList2);
|
||||
NodeList mockNodeListMultipleChild = new MockNodeList(multipleStringList);
|
||||
NodeList mockNodeListMultipleChild2 = new MockNodeList(multipleStringList2);
|
||||
Element mockSingleChildResultElement = new MockElement("result", fakeBodyString,
|
||||
"result", fakeBodyString, Node.TEXT_NODE, mockNodeListSingleChild, null);
|
||||
mockSingleChildResultElement.setAttribute("name", "input");
|
||||
mockSingleChildResultElement.setAttribute("type", "dispatcher");
|
||||
Element mockSingleChildResultElement2 = new MockElement("result", fakeBodyString2,
|
||||
"result", fakeBodyString2, Node.TEXT_NODE, mockNodeListSingleChild2, null);
|
||||
mockSingleChildResultElement2.setAttribute("name", "success");
|
||||
mockSingleChildResultElement2.setAttribute("type", "dispatcher");
|
||||
Element mockMultipleChildAllowedMethodsElement = new MockElement("result", fakeBodyString,
|
||||
"result", fakeBodyString, Node.TEXT_NODE, mockNodeListMultipleChild, null);
|
||||
mockMultipleChildAllowedMethodsElement.setAttribute("name", "input2");
|
||||
mockMultipleChildAllowedMethodsElement.setAttribute("type", "dispatcher");
|
||||
Element mockMultipleChildAllowedMethodsElement2 = new MockElement("result", fakeBodyString,
|
||||
"result", fakeBodyString2, Node.TEXT_NODE, mockNodeListMultipleChild2, null);
|
||||
mockMultipleChildAllowedMethodsElement2.setAttribute("name", "success2");
|
||||
mockMultipleChildAllowedMethodsElement2.setAttribute("type", "dispatcher");
|
||||
MockNodeList mockGlobalResultsElementChildrenSingle = new MockNodeList();
|
||||
mockGlobalResultsElementChildrenSingle.addToNodeList(mockSingleChildResultElement);
|
||||
mockGlobalResultsElementChildrenSingle.addToNodeList(mockSingleChildResultElement2);
|
||||
MockNodeList mockGlobalResultsElementChildrenMultiple = new MockNodeList();
|
||||
mockGlobalResultsElementChildrenMultiple.addToNodeList(mockMultipleChildAllowedMethodsElement);
|
||||
mockGlobalResultsElementChildrenMultiple.addToNodeList(mockMultipleChildAllowedMethodsElement2);
|
||||
Element mockGlobalResultsElementSingle = new MockElement("global-results", "fakeBody", "global-results", "fakeValue",
|
||||
Node.TEXT_NODE, mockGlobalResultsElementChildrenSingle, null);
|
||||
Element mockGlobalResultsGlobalResultsElementMultiple = new MockElement("global-results", "fakeBody", "global-results", "fakeValue",
|
||||
Node.TEXT_NODE, mockGlobalResultsElementChildrenMultiple, null);
|
||||
MockNodeList mockPackageElementChildrenSingle = new MockNodeList();
|
||||
mockPackageElementChildrenSingle.addToNodeList(mockGlobalResultsElementSingle);
|
||||
MockNodeList mockPackageElementChildrenMultiple = new MockNodeList();
|
||||
mockPackageElementChildrenMultiple.addToNodeList(mockGlobalResultsGlobalResultsElementMultiple);
|
||||
Element mockPackageElementSingle = new MockElement("package", "fakeBody", "package", "fakeValue",
|
||||
Node.TEXT_NODE, mockPackageElementChildrenSingle, null);
|
||||
Element mockPackageElementMultiple = new MockElement("package", "fakeBody", "package", "fakeValue",
|
||||
Node.TEXT_NODE, mockPackageElementChildrenMultiple, null);
|
||||
// Attempt the global laod method using single child Elements first, and confirm the results are as expected.
|
||||
XmlConfigurationProvider prov = new XmlConfigurationProvider("com/opensymphony/xwork2/config/providers/xwork- test.xml", false);
|
||||
prov.loadGlobalResults(testPackageConfigBuilder, mockPackageElementSingle);
|
||||
PackageConfig testPackageConfig = testPackageConfigBuilder.build();
|
||||
Map<String, ResultConfig> currentGlobalResults = testPackageConfig.getAllGlobalResults();
|
||||
assertNotNull("currentGlobalResults is null ?", currentGlobalResults);
|
||||
assertEquals("currentGlobalResults size not 2 ?", 2, currentGlobalResults.size());
|
||||
ResultConfig inputResult = currentGlobalResults.get("input");
|
||||
ResultConfig successResult = currentGlobalResults.get("success");
|
||||
assertNotNull("inputResult (currentGlobalResults - single) is null ?", inputResult);
|
||||
assertNotNull("successResult (currentGlobalResults - single) is null ?", successResult);
|
||||
Map<String, String> inputResultParams = inputResult.getParams();
|
||||
Map<String, String> successResultParams = successResult.getParams();
|
||||
assertNotNull("inputResultParams (currentGlobalResults - single) is null ?", inputResultParams);
|
||||
assertNotNull("successResultParams (currentGlobalResults - single) is null ?", successResultParams);
|
||||
assertEquals("inputResult (currentGlobalResults - single) resultParam value not equal to fakeBodyString ?",
|
||||
fakeBodyString, inputResultParams.get(resultParam));
|
||||
assertEquals("successResult (currentGlobalResults - single) resultParam value not equal to fakeBodyString2 ?",
|
||||
fakeBodyString2, successResultParams.get(resultParam));
|
||||
// Attempt the global laod method using mutliple child Elements next, and confirm the results are as expected.
|
||||
prov.loadGlobalResults(testPackageConfigBuilder, mockPackageElementMultiple);
|
||||
testPackageConfig = testPackageConfigBuilder.build();
|
||||
currentGlobalResults = testPackageConfig.getAllGlobalResults();
|
||||
assertNotNull("currentGlobalResults is null ?", currentGlobalResults);
|
||||
assertEquals("currentGlobalResults size not 4 ?", 4, currentGlobalResults.size());
|
||||
ResultConfig inputResult2 = currentGlobalResults.get("input2");
|
||||
ResultConfig successResult2 = currentGlobalResults.get("success2");
|
||||
assertNotNull("inputResult2 (currentGlobalResults - multiple) is null ?", inputResult2);
|
||||
assertNotNull("successResult2 (currentGlobalResults - multiple) is null ?", successResult2);
|
||||
Map<String, String> inputResultParams2 = inputResult2.getParams();
|
||||
Map<String, String> successResultParams2 = successResult2.getParams();
|
||||
assertNotNull("inputResultParams2 (currentGlobalResults - multiple) is null ?", inputResultParams2);
|
||||
assertNotNull("successResultParams2 (currentGlobalResults - multiple) is null ?", successResultParams2);
|
||||
assertEquals("inputResult2 (currentGlobalResults - multiple) resultParam value not equal to fakeBodyString ?",
|
||||
fakeBodyString, inputResultParams2.get(resultParam));
|
||||
assertEquals("successResult2 (currentGlobalResults - multiple) resultParam value not equal to fakeBodyString2 ?",
|
||||
fakeBodyString2, successResultParams2.get(resultParam));
|
||||
// Confirm the previous global results are still present
|
||||
inputResult = currentGlobalResults.get("input");
|
||||
successResult = currentGlobalResults.get("success");
|
||||
assertNotNull("inputResult (currentGlobalResults - single) is null ?", inputResult);
|
||||
assertNotNull("successResult (currentGlobalResults - single) is null ?", successResult);
|
||||
inputResultParams = inputResult.getParams();
|
||||
successResultParams = successResult.getParams();
|
||||
assertNotNull("inputResultParams (currentGlobalResults - single) is null ?", inputResultParams);
|
||||
assertNotNull("successResultParams (currentGlobalResults - single) is null ?", successResultParams);
|
||||
assertEquals("inputResult (currentGlobalResults - single) resultParam value not equal to fakeBodyString ?",
|
||||
fakeBodyString, inputResultParams.get(resultParam));
|
||||
assertEquals("successResult (currentGlobalResults - single) resultParam value not equal to fakeBodyString2 ?",
|
||||
fakeBodyString2, successResultParams.get(resultParam));
|
||||
inputResult = currentGlobalResults.get("input");
|
||||
successResult = currentGlobalResults.get("success");
|
||||
assertNotNull("inputResult (currentGlobalResults - single) is null ?", inputResult);
|
||||
assertNotNull("successResult (currentGlobalResults - single) is null ?", successResult);
|
||||
inputResultParams = inputResult.getParams();
|
||||
successResultParams = successResult.getParams();
|
||||
assertNotNull("inputResultParams (currentGlobalResults - single) is null ?", inputResultParams);
|
||||
assertNotNull("successResultParams (currentGlobalResults - single) is null ?", successResultParams);
|
||||
assertEquals("inputResult (currentGlobalResults - single) resultParam value not equal to fakeBodyString ?",
|
||||
fakeBodyString, inputResultParams.get(resultParam));
|
||||
assertEquals("successResult (currentGlobalResults - single) resultParam value not equal to fakeBodyString2 ?",
|
||||
fakeBodyString2, successResultParams.get(resultParam));
|
||||
}
|
||||
|
||||
/**
|
||||
* Mock NodeList.
|
||||
*
|
||||
* Provides minimal functionality to permit limited mock DOM testing.
|
||||
*/
|
||||
protected class MockNodeList implements org.w3c.dom.NodeList {
|
||||
List<Node> nodeList;
|
||||
|
||||
public MockNodeList() {
|
||||
this.nodeList = new ArrayList(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Produces TEXT_NODE Nodes based on the input List of Strings. Node names
|
||||
* follow a simple pattern "nodeX" where X is the index.
|
||||
*
|
||||
* @param stringList
|
||||
*/
|
||||
public MockNodeList(List<String> stringList) {
|
||||
if (stringList != null) {
|
||||
final int nodeListLength = stringList.size();
|
||||
this.nodeList = new ArrayList(nodeListLength);
|
||||
for (int index = 0; index < nodeListLength; index++) {
|
||||
this.nodeList.add(new MockNode("node" + index, stringList.get(index), Node.TEXT_NODE, null, null));
|
||||
}
|
||||
} else {
|
||||
this.nodeList = new ArrayList(0);
|
||||
}
|
||||
}
|
||||
|
||||
public MockNodeList(NodeList nodeList) {
|
||||
if (nodeList != null) {
|
||||
final int nodeListLength = nodeList.getLength();
|
||||
this.nodeList = new ArrayList(nodeListLength);
|
||||
for (int index = 0; index < nodeListLength; index++) {
|
||||
this.nodeList.add(nodeList.item(index));
|
||||
}
|
||||
} else {
|
||||
this.nodeList = new ArrayList(0);
|
||||
}
|
||||
}
|
||||
|
||||
public void addToNodeList(List<Node> nodeList) {
|
||||
if (nodeList != null) {
|
||||
final int nodeListLength = nodeList.size();
|
||||
for (int index = 0; index < nodeListLength; index++) {
|
||||
this.nodeList.add(nodeList.get(index));
|
||||
}
|
||||
} else {
|
||||
this.nodeList = new ArrayList(0);
|
||||
}
|
||||
}
|
||||
|
||||
public void addToNodeList(Node node) {
|
||||
nodeList.add(node);
|
||||
}
|
||||
|
||||
public void setParentForListNodes(Node parentNode) {
|
||||
if (nodeList != null) {
|
||||
final int nodeListLength = nodeList.size();
|
||||
for (int index = 0; index < nodeListLength; index++) {
|
||||
Node node = nodeList.get(index);
|
||||
if (node instanceof MockNode) {
|
||||
MockNode mockNode = (MockNode) node;
|
||||
mockNode.setParentNode(parentNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node item(int index) {
|
||||
return (nodeList != null ? nodeList.get(index) : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLength() {
|
||||
return (nodeList != null ? nodeList.size() : 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* MockNode
|
||||
*
|
||||
* Provides minimal functionality to permit limited mock DOM testing.
|
||||
*/
|
||||
protected class MockNode implements org.w3c.dom.Node {
|
||||
final private String nodeName;
|
||||
private String nodeValue;
|
||||
final private short nodeType;
|
||||
private Node parentNode;
|
||||
final private NodeList childNodes;
|
||||
|
||||
public MockNode(String nodeName, String nodeValue, short nodeType, NodeList childNodes, Node parentNode) {
|
||||
this.nodeName = nodeName;
|
||||
this.nodeValue = nodeValue;
|
||||
this.nodeType = nodeType;
|
||||
this.childNodes = childNodes;
|
||||
this.parentNode = parentNode;
|
||||
|
||||
if (childNodes instanceof MockNodeList) {
|
||||
MockNodeList mockNodeList = (MockNodeList) childNodes;
|
||||
mockNodeList.setParentForListNodes(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNodeName() {
|
||||
return nodeName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNodeValue() throws DOMException {
|
||||
return nodeValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNodeValue(String nodeValue) throws DOMException {
|
||||
this.nodeValue = nodeValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getNodeType() {
|
||||
return nodeType;
|
||||
}
|
||||
|
||||
public void setParentNode(Node parentNode) {
|
||||
this.parentNode = parentNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node getParentNode() {
|
||||
return parentNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeList getChildNodes() {
|
||||
return childNodes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node getFirstChild() {
|
||||
if (childNodes != null) {
|
||||
return childNodes.item(0);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node getLastChild() {
|
||||
if (childNodes != null) {
|
||||
return childNodes.item(childNodes.getLength());
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node getPreviousSibling() {
|
||||
throw new UnsupportedOperationException("No mock support.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node getNextSibling() {
|
||||
throw new UnsupportedOperationException("No mock support.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public NamedNodeMap getAttributes() {
|
||||
throw new UnsupportedOperationException("No mock support.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Document getOwnerDocument() {
|
||||
throw new UnsupportedOperationException("No mock support.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node insertBefore(Node newChild, Node refChild) throws DOMException {
|
||||
throw new UnsupportedOperationException("No mock support.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node replaceChild(Node newChild, Node oldChild) throws DOMException {
|
||||
throw new UnsupportedOperationException("No mock support.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node removeChild(Node oldChild) throws DOMException {
|
||||
throw new UnsupportedOperationException("No mock support.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node appendChild(Node newChild) throws DOMException {
|
||||
throw new UnsupportedOperationException("No mock support.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasChildNodes() {
|
||||
return (childNodes != null ? childNodes.getLength() > 0 : false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node cloneNode(boolean deep) {
|
||||
throw new UnsupportedOperationException("No mock support.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void normalize() {
|
||||
throw new UnsupportedOperationException("No mock support.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSupported(String feature, String version) {
|
||||
throw new UnsupportedOperationException("No mock support.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNamespaceURI() {
|
||||
throw new UnsupportedOperationException("No mock support.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefix() {
|
||||
throw new UnsupportedOperationException("No mock support.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPrefix(String prefix) throws DOMException {
|
||||
throw new UnsupportedOperationException("No mock support.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLocalName() {
|
||||
throw new UnsupportedOperationException("No mock support.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAttributes() {
|
||||
throw new UnsupportedOperationException("No mock support.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBaseURI() {
|
||||
throw new UnsupportedOperationException("No mock support.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public short compareDocumentPosition(Node other) throws DOMException {
|
||||
throw new UnsupportedOperationException("No mock support.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTextContent() throws DOMException {
|
||||
return nodeValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTextContent(String textContent) throws DOMException {
|
||||
setNodeValue(textContent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameNode(Node other) {
|
||||
throw new UnsupportedOperationException("No mock support.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String lookupPrefix(String namespaceURI) {
|
||||
throw new UnsupportedOperationException("No mock support.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefaultNamespace(String namespaceURI) {
|
||||
throw new UnsupportedOperationException("No mock support.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String lookupNamespaceURI(String prefix) {
|
||||
throw new UnsupportedOperationException("No mock support.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEqualNode(Node arg) {
|
||||
throw new UnsupportedOperationException("No mock support.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getFeature(String feature, String version) {
|
||||
throw new UnsupportedOperationException("No mock support.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object setUserData(String key, Object data, UserDataHandler handler) {
|
||||
throw new UnsupportedOperationException("No mock support.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getUserData(String key) {
|
||||
throw new UnsupportedOperationException("No mock support.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mock Element.
|
||||
*
|
||||
* Provides minimal functionality to permit limited mock DOM testing.
|
||||
*/
|
||||
protected class MockElement extends MockNode implements org.w3c.dom.Element {
|
||||
final private String tagName;
|
||||
final private String tagBody;
|
||||
final private Map<String, String> attributes;
|
||||
|
||||
public MockElement(String tagName, String tagBody,
|
||||
String nodeName, String nodeValue, short nodeType, NodeList childNodes, Node parentNode) {
|
||||
super(nodeName, nodeValue, nodeType, childNodes, parentNode);
|
||||
this.tagName = nodeName;
|
||||
this.tagBody = nodeValue;
|
||||
attributes = new HashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTagName() {
|
||||
return tagName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAttribute(String name) {
|
||||
return attributes.get(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttribute(String name, String value) throws DOMException {
|
||||
attributes.put(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAttribute(String name) throws DOMException {
|
||||
attributes.remove(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Attr getAttributeNode(String name) {
|
||||
throw new UnsupportedOperationException("No mock support.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Attr setAttributeNode(Attr newAttr) throws DOMException {
|
||||
throw new UnsupportedOperationException("No mock support.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Attr removeAttributeNode(Attr oldAttr) throws DOMException {
|
||||
throw new UnsupportedOperationException("No mock support.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeList getElementsByTagName(String name) {
|
||||
if (name != null) {
|
||||
final NodeList tempChildren = getChildNodes();
|
||||
final MockNodeList result = new MockNodeList();
|
||||
if (tempChildren != null) {
|
||||
final int nodeListLength = tempChildren.getLength();
|
||||
for (int index = 0; index < nodeListLength; index++) {
|
||||
Node node = tempChildren.item(index);
|
||||
if (node instanceof Element) {
|
||||
Element element = (Element) node;
|
||||
if (name.equals(element.getTagName())) {
|
||||
result.addToNodeList(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
return new MockNodeList((NodeList) null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAttributeNS(String namespaceURI, String localName) throws DOMException {
|
||||
throw new UnsupportedOperationException("No mock support.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttributeNS(String namespaceURI, String qualifiedName, String value) throws DOMException {
|
||||
throw new UnsupportedOperationException("No mock support.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAttributeNS(String namespaceURI, String localName) throws DOMException {
|
||||
throw new UnsupportedOperationException("No mock support.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Attr getAttributeNodeNS(String namespaceURI, String localName) throws DOMException {
|
||||
return null; // Sufficient for Result processing
|
||||
}
|
||||
|
||||
@Override
|
||||
public Attr setAttributeNodeNS(Attr newAttr) throws DOMException {
|
||||
throw new UnsupportedOperationException("No mock support.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeList getElementsByTagNameNS(String namespaceURI, String localName) throws DOMException {
|
||||
throw new UnsupportedOperationException("No mock support.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAttribute(String name) {
|
||||
throw new UnsupportedOperationException("No mock support.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAttributeNS(String namespaceURI, String localName) throws DOMException {
|
||||
throw new UnsupportedOperationException("No mock support.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeInfo getSchemaTypeInfo() {
|
||||
throw new UnsupportedOperationException("No mock support.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIdAttribute(String name, boolean isId) throws DOMException {
|
||||
throw new UnsupportedOperationException("No mock support.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIdAttributeNS(String namespaceURI, String localName, boolean isId) throws DOMException {
|
||||
throw new UnsupportedOperationException("No mock support.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIdAttributeNode(Attr idAttr, boolean isId) throws DOMException {
|
||||
throw new UnsupportedOperationException("No mock support.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.opensymphony.xwork2.config.entities.ActionConfig;
|
||||
import com.opensymphony.xwork2.config.providers.XmlConfigurationProvider;
|
||||
import com.opensymphony.xwork2.mock.MockActionInvocation;
|
||||
import com.opensymphony.xwork2.mock.MockActionProxy;
|
||||
import org.apache.struts2.dispatcher.HttpParameters;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@@ -60,7 +61,30 @@ public class AliasInterceptorTest extends XWorkTestCase {
|
||||
actionOne.setFoo(17);
|
||||
actionOne.setBar(23);
|
||||
proxy.execute();
|
||||
assertEquals("name to be copied", actionOne.getAliasSource());
|
||||
assertEquals(actionOne.getAliasSource(), actionOne.getAliasDest());
|
||||
assertNull(actionOne.getBlah()); // WW-5087
|
||||
}
|
||||
|
||||
public void testNotExisting() throws Exception {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
Map<String, Object> httpParams = new HashMap<>();
|
||||
httpParams.put("notExisting", "from http parameter");
|
||||
params.put(ActionContext.PARAMETERS, HttpParameters.create(httpParams).build());
|
||||
|
||||
XmlConfigurationProvider provider = new XmlConfigurationProvider("xwork-sample.xml");
|
||||
container.inject(provider);
|
||||
loadConfigurationProviders(provider);
|
||||
ActionProxy proxy = actionProxyFactory.createActionProxy("", "aliasTest", null, params);
|
||||
SimpleAction actionOne = (SimpleAction) proxy.getAction();
|
||||
|
||||
// prevent ERROR result
|
||||
actionOne.setFoo(-1);
|
||||
actionOne.setBar(1);
|
||||
|
||||
proxy.execute();
|
||||
assertEquals("from http parameter", actionOne.getBlah());
|
||||
assertNull(actionOne.getAliasDest()); // WW-5087
|
||||
}
|
||||
|
||||
public void testInvalidAliasExpression() throws Exception {
|
||||
|
||||
@@ -19,26 +19,39 @@
|
||||
package com.opensymphony.xwork2.ognl;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.ActionProxyFactory;
|
||||
import com.opensymphony.xwork2.XWorkConstants;
|
||||
import com.opensymphony.xwork2.XWorkException;
|
||||
import com.opensymphony.xwork2.XWorkTestCase;
|
||||
import com.opensymphony.xwork2.config.ConfigurationManager;
|
||||
import com.opensymphony.xwork2.config.ConfigurationProvider;
|
||||
import com.opensymphony.xwork2.config.providers.XmlConfigurationProvider;
|
||||
import com.opensymphony.xwork2.config.ConfigurationException;
|
||||
import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
|
||||
import com.opensymphony.xwork2.inject.Container;
|
||||
import com.opensymphony.xwork2.inject.ContainerBuilder;
|
||||
import com.opensymphony.xwork2.interceptor.ChainingInterceptor;
|
||||
import com.opensymphony.xwork2.test.StubConfigurationProvider;
|
||||
import com.opensymphony.xwork2.test.User;
|
||||
import com.opensymphony.xwork2.util.*;
|
||||
import com.opensymphony.xwork2.util.location.LocatableProperties;
|
||||
import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
|
||||
import java.beans.IntrospectionException;
|
||||
import ognl.*;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.text.DateFormat;
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class OgnlUtilTest extends XWorkTestCase {
|
||||
|
||||
|
||||
// Fields for static field access test
|
||||
public static final String STATIC_FINAL_PUBLIC_ATTRIBUTE = "Static_Final_Public_Attribute";
|
||||
static final String STATIC_FINAL_PACKAGE_ATTRIBUTE = "Static_Final_Package_Attribute";
|
||||
protected static final String STATIC_FINAL_PROTECTED_ATTRIBUTE = "Static_Final_Protected_Attribute";
|
||||
private static final String STATIC_FINAL_PRIVATE_ATTRIBUTE = "Static_Final_Private_Attribute";
|
||||
public static String STATIC_PUBLIC_ATTRIBUTE = "Static_Public_Attribute";
|
||||
static String STATIC_PACKAGE_ATTRIBUTE = "Static_Package_Attribute";
|
||||
protected static String STATIC_PROTECTED_ATTRIBUTE = "Static_Protected_Attribute";
|
||||
private static String STATIC_PRIVATE_ATTRIBUTE = "Static_Private_Attribute";
|
||||
|
||||
private OgnlUtil ognlUtil;
|
||||
|
||||
@Override
|
||||
@@ -101,6 +114,84 @@ public class OgnlUtilTest extends XWorkTestCase {
|
||||
assertSame(expr0, expr2);
|
||||
}
|
||||
|
||||
public void testClearExpressionCache() throws OgnlException {
|
||||
ognlUtil.setEnableExpressionCache("true");
|
||||
// Test that the expression cache is functioning as expected.
|
||||
Object expr0 = ognlUtil.compile("test");
|
||||
Object expr1 = ognlUtil.compile("test");
|
||||
Object expr2 = ognlUtil.compile("test");
|
||||
// Cache in effect, so expr0, expr1, expr2 should be the same.
|
||||
assertSame(expr0, expr1);
|
||||
assertSame(expr0, expr2);
|
||||
assertTrue("Expression cache empty before clear ?", ognlUtil.expressionCacheSize() > 0);
|
||||
// Clear the Epxression cache and confirm subsequent requests are new.
|
||||
ognlUtil.clearExpressionCache();
|
||||
assertTrue("Expression cache not empty after clear ?", ognlUtil.expressionCacheSize() == 0);
|
||||
Object expr3 = ognlUtil.compile("test");
|
||||
Object expr4 = ognlUtil.compile("test");
|
||||
Object expr5 = ognlUtil.compile("test");
|
||||
// Cache cleared, expr3 should be a new instance.
|
||||
assertNotSame(expr0, expr3);
|
||||
// Cache still in effect, so expr3, expr4, expr5 should be the same.
|
||||
assertSame(expr3, expr4);
|
||||
assertSame(expr3, expr5);
|
||||
assertTrue("Expression cache empty after usage ?", ognlUtil.expressionCacheSize() > 0);
|
||||
}
|
||||
|
||||
public void testClearBeanInfoCache() throws IntrospectionException {
|
||||
final TestBean1 testBean1 = new TestBean1();
|
||||
final TestBean2 testBean2 = new TestBean2();
|
||||
// Test that the BeanInfo cache is functioning as expected.
|
||||
Object beanInfo1_1 = ognlUtil.getBeanInfo(testBean1);
|
||||
Object beanInfo1_2 = ognlUtil.getBeanInfo(testBean1);
|
||||
Object beanInfo1_3 = ognlUtil.getBeanInfo(testBean1);
|
||||
// Cache in effect, so beanInfo1_1, beanInfo1_2, beanInfo1_3 should be the same.
|
||||
assertSame(beanInfo1_1, beanInfo1_2);
|
||||
assertSame(beanInfo1_1, beanInfo1_3);
|
||||
Object beanInfo2_1 = ognlUtil.getBeanInfo(testBean2);
|
||||
Object beanInfo2_2 = ognlUtil.getBeanInfo(testBean2);
|
||||
Object beanInfo2_3 = ognlUtil.getBeanInfo(testBean2);
|
||||
// Cache in effect, so beanInfo2_1, beanInfo2_2, beanInfo2_3 should be the same.
|
||||
assertSame(beanInfo2_1, beanInfo2_2);
|
||||
assertSame(beanInfo2_1, beanInfo2_3);
|
||||
// BeanInfo for TestBean1 and TestBean2 should always be different.
|
||||
assertNotSame(beanInfo1_1, beanInfo2_1);
|
||||
assertTrue("BeanInfo cache empty before clear ?", ognlUtil.beanInfoCacheSize() > 0);
|
||||
// Clear the BeanInfo cache and confirm subsequent requests are new.
|
||||
ognlUtil.clearBeanInfoCache();
|
||||
assertTrue("BeanInfo cache not empty after clear ?", ognlUtil.beanInfoCacheSize() == 0);
|
||||
Object beanInfo1_4 = ognlUtil.getBeanInfo(testBean1);
|
||||
Object beanInfo1_5 = ognlUtil.getBeanInfo(testBean1);
|
||||
Object beanInfo1_6 = ognlUtil.getBeanInfo(testBean1);
|
||||
// Cache in effect, so beanInfo1_4, beanInfo1_5, beanInfo1_6 should be the same.
|
||||
assertSame(beanInfo1_4, beanInfo1_5);
|
||||
assertSame(beanInfo1_4, beanInfo1_6);
|
||||
// Cache was cleared in-between, so beanInfo1_1/beanInfo1_2/beanInfo1_3 should differ
|
||||
// from beanInfo1_4/beanInfo1_5/beanInfo1_6.
|
||||
assertNotSame(beanInfo1_1, beanInfo1_4);
|
||||
assertNotSame(beanInfo1_2, beanInfo1_5);
|
||||
assertNotSame(beanInfo1_3, beanInfo1_6);
|
||||
Object beanInfo2_4 = ognlUtil.getBeanInfo(testBean2);
|
||||
Object beanInfo2_5 = ognlUtil.getBeanInfo(testBean2);
|
||||
Object beanInfo2_6 = ognlUtil.getBeanInfo(testBean2);
|
||||
// Cache in effect, so beanInfo2_4, beanInfo2_5, beanInfo2_6 should be the same.
|
||||
assertSame(beanInfo2_4, beanInfo2_5);
|
||||
assertSame(beanInfo2_4, beanInfo2_6);
|
||||
// Cache was cleared in-between, so beanInfo2_1/beanInfo2_2/beanInfo2_3 should differ
|
||||
// from beanInfo2_4/beanInfo2_5/beanInfo2_6.
|
||||
assertNotSame(beanInfo2_1, beanInfo2_4);
|
||||
assertNotSame(beanInfo2_2, beanInfo2_5);
|
||||
assertNotSame(beanInfo2_3, beanInfo2_6);
|
||||
// BeanInfo for TestBean1 and TestBean2 should always be different.
|
||||
assertNotSame(beanInfo1_4, beanInfo2_4);
|
||||
assertTrue("BeanInfo cache empty after usage ?", ognlUtil.beanInfoCacheSize() > 0);
|
||||
}
|
||||
|
||||
public void testClearRuntimeCache() {
|
||||
// Confirm that no exceptions or failures arise when calling the convenience global clear method.
|
||||
OgnlUtil.clearRuntimeCache();
|
||||
}
|
||||
|
||||
public void testCacheDisabled() throws OgnlException {
|
||||
ognlUtil.setEnableExpressionCache("false");
|
||||
Object expr0 = ognlUtil.compile("test");
|
||||
@@ -940,37 +1031,358 @@ public class OgnlUtilTest extends XWorkTestCase {
|
||||
assertEquals(expected.getMessage(), "It isn't a simple method which can be called!");
|
||||
}
|
||||
|
||||
private void reloadTestContainerConfiguration(boolean devMode, boolean allowStatic) throws Exception {
|
||||
super.tearDown();
|
||||
public void testXworkTestCaseOgnlUtilExclusions() throws Exception {
|
||||
internalTestInitialEmptyOgnlUtilExclusions(ognlUtil);
|
||||
internalTestOgnlUtilExclusionsImmutable(ognlUtil);
|
||||
}
|
||||
|
||||
ConfigurationProvider configurationProvider;
|
||||
if (devMode == true && allowStatic == true) {
|
||||
configurationProvider = new XmlConfigurationProvider("com/opensymphony/xwork2/config/providers/xwork-test-allowstatic-devmode-true.xml", true);
|
||||
public void testDefaultOgnlUtilExclusions() throws Exception {
|
||||
OgnlUtil basicOgnlUtil = new OgnlUtil();
|
||||
|
||||
internalTestInitialEmptyOgnlUtilExclusions(basicOgnlUtil);
|
||||
internalTestOgnlUtilExclusionsImmutable(basicOgnlUtil);
|
||||
}
|
||||
|
||||
public void testOgnlUtilExcludedAdditivity() throws Exception {
|
||||
Set<Class<?>> excludedClasses;
|
||||
Set<Pattern> excludedPackageNamePatterns;
|
||||
Iterator<Pattern> excludedPackageNamePatternsIterator;
|
||||
Set<String> excludedPackageNames;
|
||||
Set<String> patternStrings = new HashSet<>();
|
||||
|
||||
ognlUtil.setExcludedClasses("java.lang.String,java.lang.Integer");
|
||||
internalTestOgnlUtilExclusionsImmutable(ognlUtil);
|
||||
excludedClasses = ognlUtil.getExcludedClasses();
|
||||
assertNotNull("initial exluded classes null?", excludedClasses);
|
||||
assertTrue("initial exluded classes size not 2 after adds?", excludedClasses.size() == 2);
|
||||
assertTrue("String not in exclusions?", excludedClasses.contains(String.class));
|
||||
assertTrue("Integer not in exclusions?", excludedClasses.contains(Integer.class));
|
||||
ognlUtil.setExcludedClasses("java.lang.Boolean,java.lang.Double");
|
||||
internalTestOgnlUtilExclusionsImmutable(ognlUtil);
|
||||
excludedClasses = ognlUtil.getExcludedClasses();
|
||||
assertNotNull("updated exluded classes null?", excludedClasses);
|
||||
assertTrue("updated exluded classes size not 4 after adds?", excludedClasses.size() == 4);
|
||||
assertTrue("String not in exclusions?", excludedClasses.contains(String.class));
|
||||
assertTrue("Integer not in exclusions?", excludedClasses.contains(Integer.class));
|
||||
assertTrue("String not in exclusions?", excludedClasses.contains(Boolean.class));
|
||||
assertTrue("Integer not in exclusions?", excludedClasses.contains(Double.class));
|
||||
|
||||
ognlUtil.setExcludedPackageNamePatterns("fakepackage1.*,fakepackage2.*");
|
||||
internalTestOgnlUtilExclusionsImmutable(ognlUtil);
|
||||
excludedPackageNamePatterns = ognlUtil.getExcludedPackageNamePatterns();
|
||||
assertNotNull("initial exluded package name patterns null?", excludedPackageNamePatterns);
|
||||
assertTrue("initial exluded package name patterns size not 2 after adds?", excludedPackageNamePatterns.size() == 2);
|
||||
excludedPackageNamePatternsIterator = excludedPackageNamePatterns.iterator();
|
||||
patternStrings.clear();
|
||||
while (excludedPackageNamePatternsIterator.hasNext()) {
|
||||
Pattern pattern = excludedPackageNamePatternsIterator.next();
|
||||
patternStrings.add(pattern.pattern());
|
||||
}
|
||||
else if (devMode == true && allowStatic == false) {
|
||||
configurationProvider = new XmlConfigurationProvider("com/opensymphony/xwork2/config/providers/xwork-test-devmode-true.xml", true);
|
||||
assertTrue("fakepackage1.* not in exclusions?", patternStrings.contains("fakepackage1.*"));
|
||||
assertTrue("fakepackage2.* not in exclusions?", patternStrings.contains("fakepackage2.*"));
|
||||
ognlUtil.setExcludedPackageNamePatterns("fakepackage3.*,fakepackage4.*");
|
||||
internalTestOgnlUtilExclusionsImmutable(ognlUtil);
|
||||
excludedPackageNamePatterns = ognlUtil.getExcludedPackageNamePatterns();
|
||||
assertNotNull("updated exluded package name patterns null?", excludedPackageNamePatterns);
|
||||
assertTrue("updated exluded package name patterns size not 4 after adds?", excludedPackageNamePatterns.size() == 4);
|
||||
excludedPackageNamePatternsIterator = excludedPackageNamePatterns.iterator();
|
||||
patternStrings.clear();
|
||||
while (excludedPackageNamePatternsIterator.hasNext()) {
|
||||
Pattern pattern = excludedPackageNamePatternsIterator.next();
|
||||
patternStrings.add(pattern.pattern());
|
||||
}
|
||||
else if (devMode == false && allowStatic == true) {
|
||||
configurationProvider = new XmlConfigurationProvider("com/opensymphony/xwork2/config/providers/xwork-test-allowstatic-true.xml", true);
|
||||
assertTrue("fakepackage1.* not in exclusions?", patternStrings.contains("fakepackage1.*"));
|
||||
assertTrue("fakepackage2.* not in exclusions?", patternStrings.contains("fakepackage2.*"));
|
||||
assertTrue("fakepackage3.* not in exclusions?", patternStrings.contains("fakepackage3.*"));
|
||||
assertTrue("fakepackage4.* not in exclusions?", patternStrings.contains("fakepackage4.*"));
|
||||
|
||||
ognlUtil.setExcludedPackageNames("fakepackage1.package,fakepackage2.package");
|
||||
internalTestOgnlUtilExclusionsImmutable(ognlUtil);
|
||||
excludedPackageNames = ognlUtil.getExcludedPackageNames();
|
||||
assertNotNull("initial exluded package names null?", excludedPackageNames);
|
||||
assertTrue("initial exluded package names not 2 after adds?", excludedPackageNames.size() == 2);
|
||||
assertTrue("fakepackage1.package not in exclusions?", excludedPackageNames.contains("fakepackage1.package"));
|
||||
assertTrue("fakepackage2.package not in exclusions?", excludedPackageNames.contains("fakepackage2.package"));
|
||||
ognlUtil.setExcludedPackageNames("fakepackage3.package,fakepackage4.package");
|
||||
internalTestOgnlUtilExclusionsImmutable(ognlUtil);
|
||||
excludedPackageNames = ognlUtil.getExcludedPackageNames();
|
||||
assertNotNull("updated exluded package names null?", excludedPackageNames);
|
||||
assertTrue("updated exluded package names not 4 after adds?", excludedPackageNames.size() == 4);
|
||||
assertTrue("fakepackage1.package not in exclusions?", excludedPackageNames.contains("fakepackage1.package"));
|
||||
assertTrue("fakepackage2.package not in exclusions?", excludedPackageNames.contains("fakepackage2.package"));
|
||||
assertTrue("fakepackage3.package not in exclusions?", excludedPackageNames.contains("fakepackage3.package"));
|
||||
assertTrue("fakepackage4.package not in exclusions?", excludedPackageNames.contains("fakepackage4.package"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure getValue permits public static field access, but prevents non-public static field access
|
||||
*/
|
||||
public void testStaticFieldGetValue() {
|
||||
OgnlContext context = null;
|
||||
Object accessedValue;
|
||||
|
||||
try {
|
||||
reloadTestContainerConfiguration(false, false); // Test with allow static methods false
|
||||
context = (OgnlContext) ognlUtil.createDefaultContext(null);
|
||||
} catch (Exception ex) {
|
||||
fail("unable to reload test configuration? Exception: " + ex);
|
||||
}
|
||||
else { // devMode, allowStatic both false
|
||||
configurationProvider = new XmlConfigurationProvider("com/opensymphony/xwork2/config/providers/xwork-test-allowstatic-devmode-false.xml", true);
|
||||
try {
|
||||
accessedValue = ognlUtil.getValue("@com.opensymphony.xwork2.ognl.OgnlUtilTest@STATIC_FINAL_PUBLIC_ATTRIBUTE", context, null);
|
||||
assertEquals("accessed field value not equal to actual?", accessedValue, STATIC_FINAL_PUBLIC_ATTRIBUTE);
|
||||
} catch (Exception ex) {
|
||||
fail("static final public field access failed ? Exception: " + ex);
|
||||
}
|
||||
try {
|
||||
accessedValue = ognlUtil.getValue("@com.opensymphony.xwork2.ognl.OgnlUtilTest@STATIC_PUBLIC_ATTRIBUTE", context, null);
|
||||
assertEquals("accessed field value not equal to actual?", accessedValue, STATIC_PUBLIC_ATTRIBUTE);
|
||||
} catch (Exception ex) {
|
||||
fail("static public field access failed ? Exception: " + ex);
|
||||
}
|
||||
try {
|
||||
accessedValue = ognlUtil.getValue("@com.opensymphony.xwork2.ognl.OgnlUtilTest@STATIC_FINAL_PACKAGE_ATTRIBUTE", context, null);
|
||||
fail("static final package field access succeeded?");
|
||||
} catch (Exception ex) {
|
||||
assertTrue("Exception not an OgnlException?", ex instanceof OgnlException);
|
||||
}
|
||||
try {
|
||||
accessedValue = ognlUtil.getValue("@com.opensymphony.xwork2.ognl.OgnlUtilTest@STATIC_PACKAGE_ATTRIBUTE", context, null);
|
||||
fail("static package field access succeeded?");
|
||||
} catch (Exception ex) {
|
||||
assertTrue("Exception not an OgnlException?", ex instanceof OgnlException);
|
||||
}
|
||||
try {
|
||||
accessedValue = ognlUtil.getValue("@com.opensymphony.xwork2.ognl.OgnlUtilTest@STATIC_FINAL_PROTECTED_ATTRIBUTE", context, null);
|
||||
fail("static final protected field access succeeded?");
|
||||
} catch (Exception ex) {
|
||||
assertTrue("Exception not an OgnlException?", ex instanceof OgnlException);
|
||||
}
|
||||
try {
|
||||
accessedValue = ognlUtil.getValue("@com.opensymphony.xwork2.ognl.OgnlUtilTest@STATIC_PROTECTED_ATTRIBUTE", context, null);
|
||||
fail("static protected field access succeeded?");
|
||||
} catch (Exception ex) {
|
||||
assertTrue("Exception not an OgnlException?", ex instanceof OgnlException);
|
||||
}
|
||||
try {
|
||||
accessedValue = ognlUtil.getValue("@com.opensymphony.xwork2.ognl.OgnlUtilTest@STATIC_FINAL_PRIVATE_ATTRIBUTE", context, null);
|
||||
fail("static final private field access succeeded?");
|
||||
} catch (Exception ex) {
|
||||
assertTrue("Exception not an OgnlException?", ex instanceof OgnlException);
|
||||
}
|
||||
try {
|
||||
accessedValue = ognlUtil.getValue("@com.opensymphony.xwork2.ognl.OgnlUtilTest@STATIC_PRIVATE_ATTRIBUTE", context, null);
|
||||
fail("static private field access succeeded?");
|
||||
} catch (Exception ex) {
|
||||
assertTrue("Exception not an OgnlException?", ex instanceof OgnlException);
|
||||
}
|
||||
|
||||
configurationManager = new ConfigurationManager(Container.DEFAULT_NAME);
|
||||
configurationManager.addContainerProvider(configurationProvider);
|
||||
configuration = configurationManager.getConfiguration();
|
||||
container = configuration.getContainer();
|
||||
container.inject(configurationProvider);
|
||||
configurationProvider.init(configuration);
|
||||
actionProxyFactory = container.getInstance(ActionProxyFactory.class);
|
||||
try {
|
||||
reloadTestContainerConfiguration(false, true); // Re-test with allow static methods true
|
||||
context = (OgnlContext) ognlUtil.createDefaultContext(null);
|
||||
} catch (Exception ex) {
|
||||
fail("unable to reload test configuration? Exception: " + ex);
|
||||
}
|
||||
try {
|
||||
accessedValue = ognlUtil.getValue("@com.opensymphony.xwork2.ognl.OgnlUtilTest@STATIC_FINAL_PUBLIC_ATTRIBUTE", context, null);
|
||||
assertEquals("accessed value not equal to actual?", accessedValue, STATIC_FINAL_PUBLIC_ATTRIBUTE);
|
||||
} catch (Exception ex) {
|
||||
fail("static final public field access failed ? Exception: " + ex);
|
||||
}
|
||||
try {
|
||||
accessedValue = ognlUtil.getValue("@com.opensymphony.xwork2.ognl.OgnlUtilTest@STATIC_PUBLIC_ATTRIBUTE", context, null);
|
||||
assertEquals("accessed value not equal to actual?", accessedValue, STATIC_PUBLIC_ATTRIBUTE);
|
||||
} catch (Exception ex) {
|
||||
fail("static public field access failed ? Exception: " + ex);
|
||||
}
|
||||
try {
|
||||
accessedValue = ognlUtil.getValue("@com.opensymphony.xwork2.ognl.OgnlUtilTest@STATIC_FINAL_PACKAGE_ATTRIBUTE", context, null);
|
||||
fail("static final package field access succeeded?");
|
||||
} catch (Exception ex) {
|
||||
assertTrue("Exception not an OgnlException?", ex instanceof OgnlException);
|
||||
}
|
||||
try {
|
||||
accessedValue = ognlUtil.getValue("@com.opensymphony.xwork2.ognl.OgnlUtilTest@STATIC_PACKAGE_ATTRIBUTE", context, null);
|
||||
fail("static package field access succeeded?");
|
||||
} catch (Exception ex) {
|
||||
assertTrue("Exception not an OgnlException?", ex instanceof OgnlException);
|
||||
}
|
||||
try {
|
||||
accessedValue = ognlUtil.getValue("@com.opensymphony.xwork2.ognl.OgnlUtilTest@STATIC_FINAL_PROTECTED_ATTRIBUTE", context, null);
|
||||
fail("static final protected field access succeeded?");
|
||||
} catch (Exception ex) {
|
||||
assertTrue("Exception not an OgnlException?", ex instanceof OgnlException);
|
||||
}
|
||||
try {
|
||||
accessedValue = ognlUtil.getValue("@com.opensymphony.xwork2.ognl.OgnlUtilTest@STATIC_PROTECTED_ATTRIBUTE", context, null);
|
||||
fail("static protected field access succeeded?");
|
||||
} catch (Exception ex) {
|
||||
assertTrue("Exception not an OgnlException?", ex instanceof OgnlException);
|
||||
}
|
||||
try {
|
||||
accessedValue = ognlUtil.getValue("@com.opensymphony.xwork2.ognl.OgnlUtilTest@STATIC_FINAL_PRIVATE_ATTRIBUTE", context, null);
|
||||
fail("static final private field access succeeded?");
|
||||
} catch (Exception ex) {
|
||||
assertTrue("Exception not an OgnlException?", ex instanceof OgnlException);
|
||||
}
|
||||
try {
|
||||
accessedValue = ognlUtil.getValue("@com.opensymphony.xwork2.ognl.OgnlUtilTest@STATIC_PRIVATE_ATTRIBUTE", context, null);
|
||||
fail("static private field access succeeded?");
|
||||
} catch (Exception ex) {
|
||||
assertTrue("Exception not an OgnlException?", ex instanceof OgnlException);
|
||||
}
|
||||
}
|
||||
|
||||
// Reset the value stack
|
||||
ValueStack stack = container.getInstance(ValueStackFactory.class).createValueStack();
|
||||
stack.getContext().put(ActionContext.CONTAINER, container);
|
||||
ActionContext.setContext(new ActionContext(stack.getContext()));
|
||||
/**
|
||||
* Test OGNL Expression Max Length feature setting via OgnlUtil is disabled by default (in default.properties).
|
||||
*
|
||||
* @since 2.5.21
|
||||
*/
|
||||
public void testDefaultExpressionMaxLengthDisabled() {
|
||||
final String LONG_OGNL_EXPRESSION = "true == ThisIsAReallyLongOGNLExpressionOfRepeatedGarbageText." + new String(new char[65535]).replace('\0', 'A'); // Expression larger than 64KB.
|
||||
try {
|
||||
Object compileResult = ognlUtil.compile(LONG_OGNL_EXPRESSION);
|
||||
assertNotNull("Long OGNL expression compilation produced a null result ?", compileResult);
|
||||
} catch (OgnlException oex) {
|
||||
if (oex.getReason() instanceof SecurityException) {
|
||||
fail ("Unable to compile expression (unexpected). 'struts.ognl.expressionMaxLength' may have accidentally been enabled by default. Exception: " + oex);
|
||||
} else {
|
||||
fail ("Unable to compile expression (unexpected). Exception: " + oex);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
fail ("Unable to compile expression (unexpected). Exception: " + ex);
|
||||
}
|
||||
}
|
||||
|
||||
ognlUtil = container.getInstance(OgnlUtil.class);
|
||||
/**
|
||||
* Test OGNL Expression Max Length feature setting via OgnlUtil.
|
||||
*
|
||||
* @since 2.5.21
|
||||
*/
|
||||
public void testApplyExpressionMaxLength() {
|
||||
try {
|
||||
try {
|
||||
ognlUtil.applyExpressionMaxLength(null);
|
||||
} catch (Exception ex) {
|
||||
fail ("applyExpressionMaxLength did not accept null maxlength string (disable feature) ?");
|
||||
}
|
||||
try {
|
||||
ognlUtil.applyExpressionMaxLength("");
|
||||
} catch (Exception ex) {
|
||||
fail ("applyExpressionMaxLength did not accept empty maxlength string (disable feature) ?");
|
||||
}
|
||||
try {
|
||||
ognlUtil.applyExpressionMaxLength("-1");
|
||||
fail ("applyExpressionMaxLength accepted negative maxlength string ?");
|
||||
} catch (IllegalArgumentException iae) {
|
||||
// Expected rejection of -ive length.
|
||||
}
|
||||
try {
|
||||
ognlUtil.applyExpressionMaxLength("0");
|
||||
} catch (Exception ex) {
|
||||
fail ("applyExpressionMaxLength did not accept maxlength string 0 ?");
|
||||
}
|
||||
try {
|
||||
ognlUtil.applyExpressionMaxLength(Integer.toString(Integer.MAX_VALUE, 10));
|
||||
} catch (Exception ex) {
|
||||
fail ("applyExpressionMaxLength did not accept MAX_VALUE maxlength string ?");
|
||||
}
|
||||
} finally {
|
||||
// Reset expressionMaxLength value to default (disabled)
|
||||
ognlUtil.applyExpressionMaxLength(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void internalTestInitialEmptyOgnlUtilExclusions(OgnlUtil ognlUtilParam) throws Exception {
|
||||
Set<Class<?>> excludedClasses = ognlUtilParam.getExcludedClasses();
|
||||
assertNotNull("parameter (default) exluded classes null?", excludedClasses);
|
||||
assertTrue("parameter (default) exluded classes not empty?", excludedClasses.isEmpty());
|
||||
|
||||
Set<Pattern> excludedPackageNamePatterns = ognlUtilParam.getExcludedPackageNamePatterns();
|
||||
assertNotNull("parameter (default) exluded package name patterns null?", excludedPackageNamePatterns);
|
||||
assertTrue("parameter (default) exluded package name patterns not empty?", excludedPackageNamePatterns.isEmpty());
|
||||
|
||||
Set<String> excludedPackageNames = ognlUtilParam.getExcludedPackageNames();
|
||||
assertNotNull("parameter (default) exluded package names null?", excludedPackageNames);
|
||||
assertTrue("parameter (default) exluded package names not empty?", excludedPackageNames.isEmpty());
|
||||
}
|
||||
|
||||
private void internalTestOgnlUtilExclusionsImmutable(OgnlUtil ognlUtilParam) throws Exception {
|
||||
Pattern somePattern = Pattern.compile("SomeRegexPattern");
|
||||
Set<Class<?>> excludedClasses = ognlUtilParam.getExcludedClasses();
|
||||
assertNotNull("parameter exluded classes null?", excludedClasses);
|
||||
try {
|
||||
excludedClasses.clear();
|
||||
fail("parameter excluded classes modifiable?");
|
||||
} catch (UnsupportedOperationException uoe) {
|
||||
// Expected failure
|
||||
}
|
||||
try {
|
||||
excludedClasses.add(Integer.class);
|
||||
fail("parameter excluded classes modifiable?");
|
||||
} catch (UnsupportedOperationException uoe) {
|
||||
// Expected failure
|
||||
}
|
||||
try {
|
||||
excludedClasses.remove(Integer.class);
|
||||
fail("parameter excluded classes modifiable?");
|
||||
} catch (UnsupportedOperationException uoe) {
|
||||
// Expected failure
|
||||
}
|
||||
|
||||
Set<Pattern> excludedPackageNamePatterns = ognlUtilParam.getExcludedPackageNamePatterns();
|
||||
assertNotNull("parameter exluded package name patterns null?", excludedPackageNamePatterns);
|
||||
try {
|
||||
excludedPackageNamePatterns.clear();
|
||||
fail("parameter excluded package name patterns modifiable?");
|
||||
} catch (UnsupportedOperationException uoe) {
|
||||
// Expected failure
|
||||
}
|
||||
try {
|
||||
excludedPackageNamePatterns.add(somePattern);
|
||||
fail("parameter excluded package name patterns modifiable?");
|
||||
} catch (UnsupportedOperationException uoe) {
|
||||
// Expected failure
|
||||
}
|
||||
try {
|
||||
excludedPackageNamePatterns.remove(somePattern);
|
||||
fail("parameter excluded package name patterns modifiable?");
|
||||
} catch (UnsupportedOperationException uoe) {
|
||||
// Expected failure
|
||||
}
|
||||
|
||||
Set<String> excludedPackageNames = ognlUtilParam.getExcludedPackageNames();
|
||||
assertNotNull("parameter exluded package names null?", excludedPackageNames);
|
||||
try {
|
||||
excludedPackageNames.clear();
|
||||
fail("parameter excluded package names modifiable?");
|
||||
} catch (UnsupportedOperationException uoe) {
|
||||
// Expected failure
|
||||
}
|
||||
try {
|
||||
excludedPackageNames.add("somepackagename");
|
||||
fail("parameter excluded package names modifiable?");
|
||||
} catch (UnsupportedOperationException uoe) {
|
||||
// Expected failure
|
||||
}
|
||||
try {
|
||||
excludedPackageNames.remove("somepackagename");
|
||||
fail("parameter excluded package names modifiable?");
|
||||
} catch (UnsupportedOperationException uoe) {
|
||||
// Expected failure
|
||||
}
|
||||
}
|
||||
|
||||
private void reloadTestContainerConfiguration(final boolean devMode, final boolean allowStatic) throws Exception {
|
||||
loadConfigurationProviders(new StubConfigurationProvider() {
|
||||
@Override
|
||||
public void register(ContainerBuilder builder,
|
||||
LocatableProperties props) throws ConfigurationException {
|
||||
props.setProperty(StrutsConstants.STRUTS_DEVMODE, "" + devMode);
|
||||
props.setProperty(XWorkConstants.DEV_MODE, "" + devMode);
|
||||
props.setProperty(StrutsConstants.STRUTS_ALLOW_STATIC_METHOD_ACCESS, "" + allowStatic);
|
||||
props.setProperty(XWorkConstants.ALLOW_STATIC_METHOD_ACCESS, "" + allowStatic);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static class Email {
|
||||
@@ -1060,4 +1472,37 @@ public class OgnlUtilTest extends XWorkTestCase {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class TestBean1 {
|
||||
private String testBeanProperty;
|
||||
|
||||
public TestBean1() {
|
||||
testBeanProperty = "defaultTestBean1Property";
|
||||
}
|
||||
|
||||
public String getTestBeanProperty() {
|
||||
return testBeanProperty;
|
||||
}
|
||||
|
||||
public void setTestBeanProperty(String testBeanProperty) {
|
||||
this.testBeanProperty = testBeanProperty;
|
||||
}
|
||||
}
|
||||
|
||||
class TestBean2 {
|
||||
private String testBeanProperty;
|
||||
|
||||
public TestBean2() {
|
||||
testBeanProperty = "defaultTestBean2Property";
|
||||
}
|
||||
|
||||
public String getTestBeanProperty() {
|
||||
return testBeanProperty;
|
||||
}
|
||||
|
||||
public void setTestBeanProperty(String testBeanProperty) {
|
||||
this.testBeanProperty = testBeanProperty;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,23 +19,36 @@
|
||||
package com.opensymphony.xwork2.ognl;
|
||||
|
||||
import com.opensymphony.xwork2.*;
|
||||
import com.opensymphony.xwork2.config.ConfigurationManager;
|
||||
import com.opensymphony.xwork2.config.ConfigurationProvider;
|
||||
import com.opensymphony.xwork2.config.providers.XmlConfigurationProvider;
|
||||
import com.opensymphony.xwork2.config.ConfigurationException;
|
||||
import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
|
||||
import com.opensymphony.xwork2.inject.Container;
|
||||
import com.opensymphony.xwork2.inject.ContainerBuilder;
|
||||
import com.opensymphony.xwork2.ognl.accessor.CompoundRootAccessor;
|
||||
import com.opensymphony.xwork2.test.StubConfigurationProvider;
|
||||
import com.opensymphony.xwork2.test.TestBean2;
|
||||
import com.opensymphony.xwork2.util.*;
|
||||
import com.opensymphony.xwork2.util.Foo;
|
||||
import com.opensymphony.xwork2.util.location.LocatableProperties;
|
||||
import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
|
||||
import ognl.OgnlException;
|
||||
import ognl.PropertyAccessor;
|
||||
|
||||
import java.io.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import ognl.ParseException;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.core.LogEvent;
|
||||
import org.apache.logging.log4j.core.Logger;
|
||||
import org.apache.logging.log4j.core.appender.AbstractAppender;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
import org.apache.struts2.config.DefaultPropertiesProvider;
|
||||
|
||||
|
||||
/**
|
||||
@@ -43,10 +56,25 @@ import java.util.Map;
|
||||
*/
|
||||
public class OgnlValueStackTest extends XWorkTestCase {
|
||||
|
||||
// Fields for static field access test
|
||||
public static final String STATIC_FINAL_PUBLIC_ATTRIBUTE = "Static_Final_Public_Attribute";
|
||||
static final String STATIC_FINAL_PACKAGE_ATTRIBUTE = "Static_Final_Package_Attribute";
|
||||
protected static final String STATIC_FINAL_PROTECTED_ATTRIBUTE = "Static_Final_Protected_Attribute";
|
||||
private static final String STATIC_FINAL_PRIVATE_ATTRIBUTE = "Static_Final_Private_Attribute";
|
||||
public static String STATIC_PUBLIC_ATTRIBUTE = "Static_Public_Attribute";
|
||||
static String STATIC_PACKAGE_ATTRIBUTE = "Static_Package_Attribute";
|
||||
protected static String STATIC_PROTECTED_ATTRIBUTE = "Static_Protected_Attribute";
|
||||
private static String STATIC_PRIVATE_ATTRIBUTE = "Static_Private_Attribute";
|
||||
|
||||
|
||||
public static Integer staticNullMethod() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Integer staticInteger100Method() {
|
||||
return 100;
|
||||
}
|
||||
|
||||
private OgnlUtil ognlUtil;
|
||||
|
||||
@Override
|
||||
@@ -69,6 +97,33 @@ public class OgnlValueStackTest extends XWorkTestCase {
|
||||
return stack;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return current OgnlValueStackFactory instance from current container
|
||||
*/
|
||||
private OgnlValueStackFactory getValueStackFactory() {
|
||||
return (OgnlValueStackFactory) container.getInstance(ValueStackFactory.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads container and gets a new OgnlValueStackFactory with specified new configuration.
|
||||
* Intended for testing OgnlValueStack instance(s) that are minimally configured.
|
||||
* This should help ensure no underlying configuration/injection side-effects are responsible
|
||||
* for the behaviour of fundamental access control flags).
|
||||
*
|
||||
* @param allowStaticMethod new allowStaticMethod configuration
|
||||
* @return a new OgnlValueStackFactory with specified new configuration
|
||||
*/
|
||||
private OgnlValueStackFactory reloadValueStackFactory(Boolean allowStaticMethod) {
|
||||
try {
|
||||
reloadTestContainerConfiguration(allowStaticMethod);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
fail("Unable to reload container configuration and configure ognlValueStackFactory - exception: " + ex);
|
||||
}
|
||||
|
||||
return getValueStackFactory();
|
||||
}
|
||||
|
||||
public void testExpOverridesCanStackExpUp() throws Exception {
|
||||
Map expr1 = new LinkedHashMap();
|
||||
expr1.put("expr1", "'expr1value'");
|
||||
@@ -194,6 +249,95 @@ public class OgnlValueStackTest extends XWorkTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* monitors the resolution of WW-4999
|
||||
* @since 2.5.21
|
||||
*/
|
||||
public void testLogMissingProperties() {
|
||||
testLogMissingProperties(true);
|
||||
testLogMissingProperties(false);
|
||||
}
|
||||
|
||||
private void testLogMissingProperties(boolean logMissingProperties) {
|
||||
OgnlValueStack vs = createValueStack();
|
||||
vs.setLogMissingProperties("" + logMissingProperties);
|
||||
|
||||
Dog dog = new Dog();
|
||||
vs.push(dog);
|
||||
|
||||
TestAppender testAppender = new TestAppender();
|
||||
Logger logger = (Logger) LogManager.getLogger(OgnlValueStack.class);
|
||||
logger.addAppender(testAppender);
|
||||
testAppender.start();
|
||||
|
||||
try {
|
||||
vs.setValue("missingProp1", "missingProp1Value", false);
|
||||
vs.findValue("missingProp2", false);
|
||||
vs.findValue("missingProp3", Integer.class, false);
|
||||
|
||||
if (logMissingProperties) {
|
||||
assertEquals(3, testAppender.logEvents.size());
|
||||
assertEquals("Error setting value [missingProp1Value] with expression [missingProp1]",
|
||||
testAppender.logEvents.get(0).getMessage().getFormattedMessage());
|
||||
assertEquals("Could not find property [missingProp2]!",
|
||||
testAppender.logEvents.get(1).getMessage().getFormattedMessage());
|
||||
assertEquals("Could not find property [missingProp3]!",
|
||||
testAppender.logEvents.get(2).getMessage().getFormattedMessage());
|
||||
} else {
|
||||
assertEquals(0, testAppender.logEvents.size());
|
||||
}
|
||||
} finally {
|
||||
testAppender.stop();
|
||||
logger.removeAppender(testAppender);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* tests the correctness of distinguishing between user exception and NoSuchMethodException
|
||||
* @since 2.5.21
|
||||
*/
|
||||
public void testNotLogUserExceptionsAsMissingProperties() {
|
||||
OgnlValueStack vs = createValueStack();
|
||||
vs.setLogMissingProperties("true");
|
||||
|
||||
Dog dog = new Dog();
|
||||
vs.push(dog);
|
||||
|
||||
TestAppender testAppender = new TestAppender();
|
||||
Logger logger = (Logger) LogManager.getLogger(OgnlValueStack.class);
|
||||
logger.addAppender(testAppender);
|
||||
testAppender.start();
|
||||
|
||||
try {
|
||||
vs.setValue("exception", "exceptionValue", false);
|
||||
vs.findValue("exception", false);
|
||||
vs.findValue("exception", String.class, false);
|
||||
vs.findValue("getException()", false);
|
||||
vs.findValue("getException()", String.class, false);
|
||||
vs.findValue("bite", false);
|
||||
vs.findValue("bite", void.class, false);
|
||||
vs.findValue("getBite()", false);
|
||||
vs.findValue("getBite()", void.class, false);
|
||||
|
||||
vs.setLogMissingProperties("false");
|
||||
|
||||
vs.setValue("exception", "exceptionValue", false);
|
||||
vs.findValue("exception", false);
|
||||
vs.findValue("exception", String.class, false);
|
||||
vs.findValue("getException()", false);
|
||||
vs.findValue("getException()", String.class, false);
|
||||
vs.findValue("bite", false);
|
||||
vs.findValue("bite", void.class, false);
|
||||
vs.findValue("getBite()", false);
|
||||
vs.findValue("getBite()", void.class, false);
|
||||
|
||||
assertEquals(0, testAppender.logEvents.size());
|
||||
} finally {
|
||||
testAppender.stop();
|
||||
logger.removeAppender(testAppender);
|
||||
}
|
||||
}
|
||||
|
||||
public void testFailOnMissingMethod() {
|
||||
OgnlValueStack vs = createValueStack();
|
||||
|
||||
@@ -207,6 +351,96 @@ public class OgnlValueStackTest extends XWorkTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
public void testFailOnTooLongExpressionLongerThan192_ViaOverriddenProperty() {
|
||||
try {
|
||||
loadConfigurationProviders(new StubConfigurationProvider() {
|
||||
@Override
|
||||
public void register(ContainerBuilder builder,
|
||||
LocatableProperties props) throws ConfigurationException {
|
||||
props.setProperty(StrutsConstants.STRUTS_OGNL_EXPRESSION_MAX_LENGTH, "192");
|
||||
}
|
||||
});
|
||||
Integer repeat = Integer.parseInt(
|
||||
container.getInstance(String.class, StrutsConstants.STRUTS_OGNL_EXPRESSION_MAX_LENGTH));
|
||||
|
||||
OgnlValueStack vs = createValueStack();
|
||||
try {
|
||||
vs.findValue(StringUtils.repeat('.', repeat + 1), true);
|
||||
fail("Failed to throw exception on too long expression");
|
||||
} catch (Exception ex) {
|
||||
assertTrue(ex.getCause() instanceof OgnlException);
|
||||
assertTrue(((OgnlException) ex.getCause()).getReason() instanceof SecurityException);
|
||||
}
|
||||
} finally {
|
||||
// Reset expressionMaxLength value to default (disabled)
|
||||
ognlUtil.applyExpressionMaxLength(null);
|
||||
}
|
||||
}
|
||||
|
||||
public void testNotFailOnTooLongExpressionWithDefaultProperties() {
|
||||
loadConfigurationProviders(new DefaultPropertiesProvider());
|
||||
|
||||
Object defaultMaxLengthFromConfiguration = container.getInstance(String.class, StrutsConstants.STRUTS_OGNL_EXPRESSION_MAX_LENGTH);
|
||||
if (defaultMaxLengthFromConfiguration != null) {
|
||||
assertTrue("non-null defaultMaxLengthFromConfiguration not a String ?", defaultMaxLengthFromConfiguration instanceof String);
|
||||
assertTrue("non-null defaultMaxLengthFromConfiguration not empty string by default ?", ((String) defaultMaxLengthFromConfiguration).length() == 0);
|
||||
} else {
|
||||
assertNull("defaultMaxLengthFromConfiguration not null ?", defaultMaxLengthFromConfiguration);
|
||||
}
|
||||
// Original test logic was to confirm failure of exceeding the default value. Now the feature should be disabled by default,
|
||||
// so this test's expectations are now changed.
|
||||
Integer repeat = Integer.valueOf(256); // Since maxlength is disabled by default, just choose an arbitrary value for test
|
||||
|
||||
OgnlValueStack vs = createValueStack();
|
||||
try {
|
||||
vs.findValue(StringUtils.repeat('.', repeat + 1), true);
|
||||
fail("findValue did not throw any exception (should either fail as invalid expression syntax or security exception) ?");
|
||||
} catch (Exception ex) {
|
||||
// If STRUTS_OGNL_EXPRESSION_MAX_LENGTH feature is disabled (default), the parse should fail due to a reason of invalid expression syntax
|
||||
// with ParseException. Previously when it was enabled the reason for the failure would have been SecurityException.
|
||||
assertTrue(ex.getCause() instanceof OgnlException);
|
||||
assertTrue(((OgnlException) ex.getCause()).getReason() instanceof ParseException);
|
||||
}
|
||||
}
|
||||
|
||||
public void testNotFailOnTooLongValueWithDefaultProperties() {
|
||||
try {
|
||||
loadConfigurationProviders(new DefaultPropertiesProvider());
|
||||
|
||||
Object defaultMaxLengthFromConfiguration = container.getInstance(String.class, StrutsConstants.STRUTS_OGNL_EXPRESSION_MAX_LENGTH);
|
||||
if (defaultMaxLengthFromConfiguration != null) {
|
||||
assertTrue("non-null defaultMaxLengthFromConfiguration not a String ?", defaultMaxLengthFromConfiguration instanceof String);
|
||||
assertTrue("non-null defaultMaxLengthFromConfiguration not empty string by default ?", ((String) defaultMaxLengthFromConfiguration).length() == 0);
|
||||
} else {
|
||||
assertNull("defaultMaxLengthFromConfiguration not null ?", defaultMaxLengthFromConfiguration);
|
||||
}
|
||||
// Original test logic is unchanged (testing that values can be larger than maximum expression length), but since the feature is disabled by
|
||||
// default we will now have to enable it with an arbitrary value, test, and reset it to disabled.
|
||||
Integer repeat = Integer.valueOf(256); // Since maxlength is disabled by default, just choose an arbitrary value for test
|
||||
|
||||
// Apply a non-default value for expressionMaxLength (as it should be disabled by default)
|
||||
try {
|
||||
ognlUtil.applyExpressionMaxLength(repeat.toString());
|
||||
} catch (Exception ex) {
|
||||
fail ("applyExpressionMaxLength did not accept maxlength string " + repeat.toString() + " ?");
|
||||
}
|
||||
|
||||
OgnlValueStack vs = createValueStack();
|
||||
|
||||
Dog dog = new Dog();
|
||||
vs.push(dog);
|
||||
|
||||
String value = StringUtils.repeat('.', repeat + 1);
|
||||
|
||||
vs.setValue("name", value);
|
||||
|
||||
assertEquals(value, dog.getName());
|
||||
} finally {
|
||||
// Reset expressionMaxLength value to default (disabled)
|
||||
ognlUtil.applyExpressionMaxLength(null);
|
||||
}
|
||||
}
|
||||
|
||||
public void testFailsOnMethodThatThrowsException() {
|
||||
SimpleAction action = new SimpleAction();
|
||||
OgnlValueStack stack = createValueStack();
|
||||
@@ -580,7 +814,7 @@ public class OgnlValueStackTest extends XWorkTestCase {
|
||||
OgnlValueStack stack = createValueStack();
|
||||
stack.push(dog);
|
||||
assertNull(stack.findValue("nullMethod()"));
|
||||
assertNull(stack.findValue("@com.opensymphony.xwork2.util.OgnlValueStackTest@staticNullMethod()"));
|
||||
assertNull(stack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@staticNullMethod()"));
|
||||
}
|
||||
|
||||
public void testPetSoarBug() {
|
||||
@@ -835,6 +1069,49 @@ public class OgnlValueStackTest extends XWorkTestCase {
|
||||
assertNull(vs.findValue("@com.nothing.here.Nothing@BLAH"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Fails on 2.5.20 and earlier - tested on 2.5 (5/5/2016) and failed
|
||||
* @since 2.5.21
|
||||
*/
|
||||
public void testNotThrowExceptionOnTopMissingProperty() {
|
||||
OgnlValueStack vs = createValueStack();
|
||||
|
||||
Dog dog = new Dog();
|
||||
dog.setName("Rover");
|
||||
vs.push(dog);
|
||||
|
||||
Cat cat = new Cat();
|
||||
vs.push(cat);
|
||||
|
||||
vs.setValue("age", 12, true);
|
||||
|
||||
assertEquals(12, vs.findValue("age", true));
|
||||
assertEquals(12, vs.findValue("age", Integer.class, true));
|
||||
assertEquals(12, vs.findValue("getAge()", true));
|
||||
assertEquals(12, vs.findValue("getAge()", Integer.class, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Fails on 2.5.20 and earlier - tested on 2.5 (5/5/2016) and failed
|
||||
* @since 2.5.21
|
||||
*/
|
||||
public void testNotSkipUserReturnedNullValues() {
|
||||
OgnlValueStack vs = createValueStack();
|
||||
|
||||
Dog dog = new Dog();
|
||||
dog.setName("Rover");
|
||||
vs.push(dog);
|
||||
|
||||
Cat cat = new Cat();
|
||||
vs.push(cat);
|
||||
|
||||
// should not skip returned null values from cat.name
|
||||
assertNull(vs.findValue("name", true));
|
||||
assertNull(vs.findValue("name", String.class, true));
|
||||
assertNull(vs.findValue("getName()", true));
|
||||
assertNull(vs.findValue("getName()", String.class, true));
|
||||
}
|
||||
|
||||
public void testTop() {
|
||||
OgnlValueStack vs = createValueStack();
|
||||
|
||||
@@ -972,36 +1249,170 @@ public class OgnlValueStackTest extends XWorkTestCase {
|
||||
assertEquals(null, stack.findValue("address.country.name", String.class));
|
||||
}
|
||||
|
||||
private void reloadTestContainerConfiguration(boolean devMode, boolean allowStatic) throws Exception {
|
||||
super.tearDown();
|
||||
/**
|
||||
* Test a default OgnlValueStackFactory and OgnlValueStack generated by it
|
||||
* when a default configuration is used.
|
||||
*/
|
||||
public void testOgnlValueStackFromOgnlValueStackFactoryDefaultConfig() {
|
||||
OgnlValueStackFactory ognlValueStackFactory = getValueStackFactory();
|
||||
OgnlValueStack ognlValueStack = (OgnlValueStack) ognlValueStackFactory.createValueStack();
|
||||
Object accessedValue;
|
||||
|
||||
ConfigurationProvider configurationProvider;
|
||||
if (devMode == true && allowStatic == true) {
|
||||
configurationProvider = new XmlConfigurationProvider("com/opensymphony/xwork2/config/providers/xwork-test-allowstatic-devmode-true.xml", true);
|
||||
}
|
||||
else if (devMode == true && allowStatic == false) {
|
||||
configurationProvider = new XmlConfigurationProvider("com/opensymphony/xwork2/config/providers/xwork-test-devmode-true.xml", true);
|
||||
}
|
||||
else if (devMode == false && allowStatic == true) {
|
||||
configurationProvider = new XmlConfigurationProvider("com/opensymphony/xwork2/config/providers/xwork-test-allowstatic-true.xml", true);
|
||||
}
|
||||
else { // devMode, allowStatic both false
|
||||
configurationProvider = new XmlConfigurationProvider("com/opensymphony/xwork2/config/providers/xwork-test-allowstatic-devmode-false.xml", true);
|
||||
}
|
||||
// An OgnlValueStackFactory using a container config with default (from XWorkConfigurationProvider)
|
||||
// static method access flag value present should prevent staticMethodAccess but allow staticFieldAccess.
|
||||
assertFalse("OgnlValueStackFactory staticMethodAccess (default flag) not false?", containerAllowsStaticMethodAccess(ognlValueStackFactory.container));
|
||||
// An OgnlValueStack created from the above OgnlValueStackFactory should allow public field access,
|
||||
// but prevent non-public field access. It should also deny static method access.
|
||||
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@staticInteger100Method()");
|
||||
assertNull("able to access static method (result not null) ?", accessedValue);
|
||||
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PUBLIC_ATTRIBUTE");
|
||||
assertEquals("accessed static final public field value not equal to actual?", accessedValue, STATIC_FINAL_PUBLIC_ATTRIBUTE);
|
||||
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PUBLIC_ATTRIBUTE");
|
||||
assertEquals("accessed static public field value not equal to actual?", accessedValue, STATIC_PUBLIC_ATTRIBUTE);
|
||||
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PACKAGE_ATTRIBUTE");
|
||||
assertNull("accessed final package field (result not null) ?", accessedValue);
|
||||
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PACKAGE_ATTRIBUTE");
|
||||
assertNull("accessed package field (result not null) ?", accessedValue);
|
||||
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PROTECTED_ATTRIBUTE");
|
||||
assertNull("accessed final protected field (result not null) ?", accessedValue);
|
||||
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PROTECTED_ATTRIBUTE");
|
||||
assertNull("accessed protected field (result not null) ?", accessedValue);
|
||||
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PRIVATE_ATTRIBUTE");
|
||||
assertNull("accessed final private field (result not null) ?", accessedValue);
|
||||
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PRIVATE_ATTRIBUTE");
|
||||
assertNull("accessed private field (result not null) ?", accessedValue);
|
||||
}
|
||||
|
||||
configurationManager = new ConfigurationManager(Container.DEFAULT_NAME);
|
||||
configurationManager.addContainerProvider(configurationProvider);
|
||||
configuration = configurationManager.getConfiguration();
|
||||
container = configuration.getContainer();
|
||||
container.inject(configurationProvider);
|
||||
configurationProvider.init(configuration);
|
||||
actionProxyFactory = container.getInstance(ActionProxyFactory.class);
|
||||
/**
|
||||
* Test a raw OgnlValueStackFactory and OgnlValueStack generated by it
|
||||
* when no static method access flag is set (not present in configuration).
|
||||
*/
|
||||
public void testOgnlValueStackFromOgnlValueStackFactoryNoFlagsSet() {
|
||||
OgnlValueStackFactory ognlValueStackFactory = reloadValueStackFactory(null);
|
||||
OgnlValueStack ognlValueStack = (OgnlValueStack) ognlValueStackFactory.createValueStack();
|
||||
Object accessedValue;
|
||||
|
||||
// Reset the value stack
|
||||
ValueStack stack = container.getInstance(ValueStackFactory.class).createValueStack();
|
||||
stack.getContext().put(ActionContext.CONTAINER, container);
|
||||
ActionContext.setContext(new ActionContext(stack.getContext()));
|
||||
// An OgnlValueStackFactory using a container config with no static method access flag value present
|
||||
// (such as from a DefaultConfiguration vs. XWorkConfigurationProvider) should
|
||||
// prevent staticMethodAccess and allow staticFieldAccess.
|
||||
// Note: Under normal circumstances, explicit static method access configuration flag should be present,
|
||||
// but this specific check verifies what happens if that configuration flags is not present.
|
||||
assertFalse("OgnlValueStackFactory staticMethodAccess (no flag present) not false?", containerAllowsStaticMethodAccess(ognlValueStackFactory.container));
|
||||
// An OgnlValueStack created from the above OgnlValueStackFactory should allow public field access,
|
||||
// and prevent non-public field access. It should also deny static method access.
|
||||
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@staticInteger100Method()");
|
||||
assertNull("able to access static method (result not null) ?", accessedValue);
|
||||
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PUBLIC_ATTRIBUTE");
|
||||
assertEquals("accessed static final public field value not equal to actual?", accessedValue, STATIC_FINAL_PUBLIC_ATTRIBUTE);
|
||||
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PUBLIC_ATTRIBUTE");
|
||||
assertEquals("accessed static public field value not equal to actual?", accessedValue, STATIC_PUBLIC_ATTRIBUTE);
|
||||
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PACKAGE_ATTRIBUTE");
|
||||
assertNull("accessed final package field (result not null) ?", accessedValue);
|
||||
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PACKAGE_ATTRIBUTE");
|
||||
assertNull("accessed package field (result not null) ?", accessedValue);
|
||||
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PROTECTED_ATTRIBUTE");
|
||||
assertNull("accessed final protected field (result not null) ?", accessedValue);
|
||||
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PROTECTED_ATTRIBUTE");
|
||||
assertNull("accessed protected field (result not null) ?", accessedValue);
|
||||
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PRIVATE_ATTRIBUTE");
|
||||
assertNull("accessed final private field (result not null) ?", accessedValue);
|
||||
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PRIVATE_ATTRIBUTE");
|
||||
assertNull("accessed private field (result not null) ?", accessedValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a raw OgnlValueStackFactory and OgnlValueStack generated by it
|
||||
* when static method access flag is set to false.
|
||||
*/
|
||||
public void testOgnlValueStackFromOgnlValueStackFactoryNoStaticMethodAccess() {
|
||||
OgnlValueStackFactory ognlValueStackFactory = reloadValueStackFactory(false);
|
||||
OgnlValueStack ognlValueStack = (OgnlValueStack) ognlValueStackFactory.createValueStack();
|
||||
Object accessedValue;
|
||||
|
||||
// An OgnlValueStackFactory using a container config with static method access flag set false should
|
||||
// prevent staticMethodAccess and allow staticFieldAccess.
|
||||
assertFalse("OgnlValueStackFactory staticMethodAccess (set false) not false?", containerAllowsStaticMethodAccess(ognlValueStackFactory.container));
|
||||
// An OgnlValueStack created from the above OgnlValueStackFactory should allow public field access,
|
||||
// and prevent non-public field access. It should also deny static method access.
|
||||
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@staticInteger100Method()");
|
||||
assertNull("able to access static method (result not null) ?", accessedValue);
|
||||
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PUBLIC_ATTRIBUTE");
|
||||
assertEquals("accessed static final public field value not equal to actual?", accessedValue, STATIC_FINAL_PUBLIC_ATTRIBUTE);
|
||||
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PUBLIC_ATTRIBUTE");
|
||||
assertEquals("accessed static public field value not equal to actual?", accessedValue, STATIC_PUBLIC_ATTRIBUTE);
|
||||
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PACKAGE_ATTRIBUTE");
|
||||
assertNull("accessed final package field (result not null) ?", accessedValue);
|
||||
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PACKAGE_ATTRIBUTE");
|
||||
assertNull("accessed package field (result not null) ?", accessedValue);
|
||||
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PROTECTED_ATTRIBUTE");
|
||||
assertNull("accessed final protected field (result not null) ?", accessedValue);
|
||||
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PROTECTED_ATTRIBUTE");
|
||||
assertNull("accessed protected field (result not null) ?", accessedValue);
|
||||
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PRIVATE_ATTRIBUTE");
|
||||
assertNull("accessed final private field (result not null) ?", accessedValue);
|
||||
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PRIVATE_ATTRIBUTE");
|
||||
assertNull("accessed private field (result not null) ?", accessedValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a raw OgnlValueStackFactory and OgnlValueStack generated by it
|
||||
* when static method access flag set to true.
|
||||
*/
|
||||
public void testOgnlValueStackFromOgnlValueStackFactoryStaticMethodAccess() {
|
||||
OgnlValueStackFactory ognlValueStackFactory = reloadValueStackFactory(true);
|
||||
OgnlValueStack ognlValueStack = (OgnlValueStack) ognlValueStackFactory.createValueStack();
|
||||
Object accessedValue;
|
||||
|
||||
// An OgnlValueStackFactory using a container config with static method access flag set true should
|
||||
// allow both staticMethodAccess and staticFieldAccess.
|
||||
assertTrue("OgnlValueStackFactory staticMethodAccess (set true) not true?", containerAllowsStaticMethodAccess(ognlValueStackFactory.container));
|
||||
// An OgnlValueStack created from the above OgnlValueStackFactory should allow public field access,
|
||||
// but prevent non-public field access. It should also allow static method access.
|
||||
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@staticInteger100Method()");
|
||||
assertNotNull("unable to access static method (result null) ?", accessedValue);
|
||||
assertEquals("accessed static method result not equal to expected?", accessedValue, staticInteger100Method());
|
||||
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PUBLIC_ATTRIBUTE");
|
||||
assertEquals("accessed static final public field value not equal to actual?", accessedValue, STATIC_FINAL_PUBLIC_ATTRIBUTE);
|
||||
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PUBLIC_ATTRIBUTE");
|
||||
assertEquals("accessed static public field value not equal to actual?", accessedValue, STATIC_PUBLIC_ATTRIBUTE);
|
||||
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PACKAGE_ATTRIBUTE");
|
||||
assertNull("accessed final package field (result not null) ?", accessedValue);
|
||||
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PACKAGE_ATTRIBUTE");
|
||||
assertNull("accessed package field (result not null) ?", accessedValue);
|
||||
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PROTECTED_ATTRIBUTE");
|
||||
assertNull("accessed final protected field (result not null) ?", accessedValue);
|
||||
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PROTECTED_ATTRIBUTE");
|
||||
assertNull("accessed protected field (result not null) ?", accessedValue);
|
||||
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PRIVATE_ATTRIBUTE");
|
||||
assertNull("accessed final private field (result not null) ?", accessedValue);
|
||||
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PRIVATE_ATTRIBUTE");
|
||||
assertNull("accessed private field (result not null) ?", accessedValue);
|
||||
}
|
||||
|
||||
protected boolean containerAllowsStaticMethodAccess(Container checkContainer) {
|
||||
return Boolean.parseBoolean(checkContainer.getInstance(String.class, XWorkConstants.ALLOW_STATIC_METHOD_ACCESS));
|
||||
}
|
||||
|
||||
private void reloadTestContainerConfiguration(final Boolean allowStaticMethod) throws Exception {
|
||||
loadConfigurationProviders(new StubConfigurationProvider() {
|
||||
@Override
|
||||
public void register(ContainerBuilder builder,
|
||||
LocatableProperties props) throws ConfigurationException {
|
||||
// null values simulate undefined (by removing).
|
||||
// undefined values then should be evaluated to false
|
||||
if (props.containsKey(StrutsConstants.STRUTS_ALLOW_STATIC_METHOD_ACCESS)) {
|
||||
props.remove(StrutsConstants.STRUTS_ALLOW_STATIC_METHOD_ACCESS);
|
||||
}
|
||||
if (props.containsKey(XWorkConstants.ALLOW_STATIC_METHOD_ACCESS)) {
|
||||
props.remove(XWorkConstants.ALLOW_STATIC_METHOD_ACCESS);
|
||||
}
|
||||
if (allowStaticMethod != null) {
|
||||
props.setProperty(StrutsConstants.STRUTS_ALLOW_STATIC_METHOD_ACCESS, "" + allowStaticMethod);
|
||||
props.setProperty(XWorkConstants.ALLOW_STATIC_METHOD_ACCESS, "" + allowStaticMethod);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
ognlUtil = container.getInstance(OgnlUtil.class);
|
||||
}
|
||||
|
||||
@@ -1115,6 +1526,19 @@ public class OgnlValueStackTest extends XWorkTestCase {
|
||||
this.displayName = displayName;
|
||||
}
|
||||
}
|
||||
|
||||
class TestAppender extends AbstractAppender {
|
||||
List<LogEvent> logEvents = new ArrayList<>();
|
||||
|
||||
TestAppender() {
|
||||
super("TestAppender", null, null, false, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void append(LogEvent logEvent) {
|
||||
logEvents.add(logEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum MyNumbers {
|
||||
|
||||
+127
-35
@@ -22,44 +22,46 @@ import com.opensymphony.xwork2.XWorkTestCase;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class DefaultAcceptedPatternsCheckerTest extends XWorkTestCase {
|
||||
|
||||
public void testHardcodedAcceptedPatterns() throws Exception {
|
||||
// given
|
||||
List<String> params = new ArrayList<String>() {
|
||||
{
|
||||
add("%{#application['test']}");
|
||||
add("%{#application.test}");
|
||||
add("%{#Application['test']}");
|
||||
add("%{#Application.test}");
|
||||
add("%{#session['test']}");
|
||||
add("%{#session.test}");
|
||||
add("%{#Session['test']}");
|
||||
add("%{#Session.test}");
|
||||
add("%{#struts['test']}");
|
||||
add("%{#struts.test}");
|
||||
add("%{#Struts['test']}");
|
||||
add("%{#Struts.test}");
|
||||
add("%{#request['test']}");
|
||||
add("%{#request.test}");
|
||||
add("%{#Request['test']}");
|
||||
add("%{#Request.test}");
|
||||
add("%{#servletRequest['test']}");
|
||||
add("%{#servletRequest.test}");
|
||||
add("%{#ServletRequest['test']}");
|
||||
add("%{#ServletRequest.test}");
|
||||
add("%{#servletResponse['test']}");
|
||||
add("%{#servletResponse.test}");
|
||||
add("%{#ServletResponse['test']}");
|
||||
add("%{#ServletResponse.test}");
|
||||
add("%{#parameters['test']}");
|
||||
add("%{#parameters.test}");
|
||||
add("%{#Parameters['test']}");
|
||||
add("%{#Parameters.test}");
|
||||
}
|
||||
};
|
||||
private final List<String> params = new ArrayList<String>() {
|
||||
{
|
||||
add("%{#application['test']}");
|
||||
add("%{#application.test}");
|
||||
add("%{#Application['test']}");
|
||||
add("%{#Application.test}");
|
||||
add("%{#session['test']}");
|
||||
add("%{#session.test}");
|
||||
add("%{#Session['test']}");
|
||||
add("%{#Session.test}");
|
||||
add("%{#struts['test']}");
|
||||
add("%{#struts.test}");
|
||||
add("%{#Struts['test']}");
|
||||
add("%{#Struts.test}");
|
||||
add("%{#request['test']}");
|
||||
add("%{#request.test}");
|
||||
add("%{#Request['test']}");
|
||||
add("%{#Request.test}");
|
||||
add("%{#servletRequest['test']}");
|
||||
add("%{#servletRequest.test}");
|
||||
add("%{#ServletRequest['test']}");
|
||||
add("%{#ServletRequest.test}");
|
||||
add("%{#servletResponse['test']}");
|
||||
add("%{#servletResponse.test}");
|
||||
add("%{#ServletResponse['test']}");
|
||||
add("%{#ServletResponse.test}");
|
||||
add("%{#parameters['test']}");
|
||||
add("%{#parameters.test}");
|
||||
add("%{#Parameters['test']}");
|
||||
add("%{#Parameters.test}");
|
||||
}
|
||||
};
|
||||
|
||||
public void testHardcodedAcceptedPatterns() {
|
||||
// given
|
||||
AcceptedPatternsChecker checker = new DefaultAcceptedPatternsChecker();
|
||||
|
||||
for (String param : params) {
|
||||
@@ -71,7 +73,20 @@ public class DefaultAcceptedPatternsCheckerTest extends XWorkTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
public void testUnderscoreInParamName() throws Exception {
|
||||
public void testHardcodedAcceptedPatternsWithDmiEnabled() {
|
||||
// given
|
||||
AcceptedPatternsChecker checker = new DefaultAcceptedPatternsChecker(Boolean.TRUE.toString());
|
||||
|
||||
for (String param : params) {
|
||||
// when
|
||||
AcceptedPatternsChecker.IsAccepted actual = checker.isAccepted(param);
|
||||
|
||||
// then
|
||||
assertFalse("Access to " + param + " is possible!", actual.isAccepted());
|
||||
}
|
||||
}
|
||||
|
||||
public void testUnderscoreInParamName() {
|
||||
// given
|
||||
AcceptedPatternsChecker checker = new DefaultAcceptedPatternsChecker();
|
||||
|
||||
@@ -82,4 +97,81 @@ public class DefaultAcceptedPatternsCheckerTest extends XWorkTestCase {
|
||||
assertTrue("Param with underscore wasn't accepted!", actual.isAccepted());
|
||||
}
|
||||
|
||||
public void testUnderscoreInParamNameWithDmiEnabled() {
|
||||
// given
|
||||
AcceptedPatternsChecker checker = new DefaultAcceptedPatternsChecker(Boolean.TRUE.toString());
|
||||
|
||||
// when
|
||||
AcceptedPatternsChecker.IsAccepted actual = checker.isAccepted("mapParam['param_1']");
|
||||
|
||||
// then
|
||||
assertTrue("Param with underscore wasn't accepted!", actual.isAccepted());
|
||||
}
|
||||
|
||||
public void testAcceptedPatternsImmutable() {
|
||||
AcceptedPatternsChecker checker = new DefaultAcceptedPatternsChecker();
|
||||
|
||||
Set<Pattern> acceptedPatternSet = checker.getAcceptedPatterns();
|
||||
assertNotNull("default accepted patterns null?", acceptedPatternSet);
|
||||
assertFalse("default accepted patterns empty?", acceptedPatternSet.isEmpty());
|
||||
try {
|
||||
acceptedPatternSet.add(Pattern.compile("SomeRegexPattern"));
|
||||
fail("accepted patterns modifiable?");
|
||||
} catch (UnsupportedOperationException uoe) {
|
||||
// Expected result
|
||||
}
|
||||
try {
|
||||
acceptedPatternSet.clear();
|
||||
fail("accepted patterns modifiable?");
|
||||
} catch (UnsupportedOperationException uoe) {
|
||||
// Expected result
|
||||
}
|
||||
|
||||
checker.setAcceptedPatterns(DefaultAcceptedPatternsChecker.ACCEPTED_PATTERNS);
|
||||
acceptedPatternSet = checker.getAcceptedPatterns();
|
||||
assertNotNull("replaced default accepted patterns null?", acceptedPatternSet);
|
||||
assertFalse("replaced default accepted patterns empty?", acceptedPatternSet.isEmpty());
|
||||
try {
|
||||
acceptedPatternSet.add(Pattern.compile("SomeRegexPattern"));
|
||||
fail("replaced accepted patterns modifiable?");
|
||||
} catch (UnsupportedOperationException uoe) {
|
||||
// Expected result
|
||||
}
|
||||
try {
|
||||
acceptedPatternSet.clear();
|
||||
fail("accepted patterns modifiable?");
|
||||
} catch (UnsupportedOperationException uoe) {
|
||||
// Expected result
|
||||
}
|
||||
|
||||
String[] testPatternArray = {"exactmatch1", "exactmatch2", "exactmatch3", "exactmatch4"};
|
||||
checker.setAcceptedPatterns(testPatternArray);
|
||||
acceptedPatternSet = checker.getAcceptedPatterns();
|
||||
assertNotNull("replaced default accepted patterns null?", acceptedPatternSet);
|
||||
assertFalse("replaced default accepted patterns empty?", acceptedPatternSet.isEmpty());
|
||||
assertEquals("replaced default accepted patterns not size " + testPatternArray.length + "?", acceptedPatternSet.size(), testPatternArray.length);
|
||||
for (String testPatternArray1 : testPatternArray) {
|
||||
assertTrue(testPatternArray1 + " not accepted?", checker.isAccepted(testPatternArray1).isAccepted());
|
||||
}
|
||||
try {
|
||||
acceptedPatternSet.add(Pattern.compile("SomeRegexPattern"));
|
||||
fail("replaced accepted patterns modifiable?");
|
||||
} catch (UnsupportedOperationException uoe) {
|
||||
// Expected result
|
||||
}
|
||||
try {
|
||||
acceptedPatternSet.clear();
|
||||
fail("accepted patterns modifiable?");
|
||||
} catch (UnsupportedOperationException uoe) {
|
||||
// Expected result
|
||||
}
|
||||
}
|
||||
|
||||
public void testDmiIsEnabled() {
|
||||
DefaultAcceptedPatternsChecker checker = new DefaultAcceptedPatternsChecker(Boolean.TRUE.toString());
|
||||
|
||||
AcceptedPatternsChecker.IsAccepted accepted = checker.isAccepted("action:myAction!save");
|
||||
|
||||
assertTrue("dmi isn't accepted", accepted.isAccepted());
|
||||
}
|
||||
}
|
||||
+63
@@ -23,6 +23,10 @@ import com.opensymphony.xwork2.XWorkTestCase;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
import static junit.framework.TestCase.assertNotNull;
|
||||
import static junit.framework.TestCase.fail;
|
||||
|
||||
public class DefaultExcludedPatternsCheckerTest extends XWorkTestCase {
|
||||
|
||||
@@ -155,4 +159,63 @@ public class DefaultExcludedPatternsCheckerTest extends XWorkTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
public void testExcludedPatternsImmutable() throws Exception {
|
||||
ExcludedPatternsChecker checker = new DefaultExcludedPatternsChecker();
|
||||
|
||||
Set<Pattern> excludedPatternSet = checker.getExcludedPatterns();
|
||||
assertNotNull("default excluded patterns null?", excludedPatternSet);
|
||||
assertFalse("default excluded patterns empty?", excludedPatternSet.isEmpty());
|
||||
try {
|
||||
excludedPatternSet.add(Pattern.compile("SomeRegexPattern") );
|
||||
fail ("excluded patterns modifiable?");
|
||||
} catch(UnsupportedOperationException uoe) {
|
||||
// Expected result
|
||||
}
|
||||
try {
|
||||
excludedPatternSet.clear();
|
||||
fail ("excluded patterns modifiable?");
|
||||
} catch(UnsupportedOperationException uoe) {
|
||||
// Expected result
|
||||
}
|
||||
|
||||
checker.setExcludedPatterns(DefaultExcludedPatternsChecker.EXCLUDED_PATTERNS);
|
||||
excludedPatternSet = checker.getExcludedPatterns();
|
||||
assertNotNull("default excluded patterns null?", excludedPatternSet);
|
||||
assertFalse("default excluded patterns empty?", excludedPatternSet.isEmpty());
|
||||
try {
|
||||
excludedPatternSet.add(Pattern.compile("SomeRegexPattern") );
|
||||
fail ("excluded patterns modifiable?");
|
||||
} catch(UnsupportedOperationException uoe) {
|
||||
// Expected result
|
||||
}
|
||||
try {
|
||||
excludedPatternSet.clear();
|
||||
fail ("excluded patterns modifiable?");
|
||||
} catch(UnsupportedOperationException uoe) {
|
||||
// Expected result
|
||||
}
|
||||
|
||||
String[] testPatternArray = {"exactmatch1", "exactmatch2", "exactmatch3", "exactmatch4"};
|
||||
checker.setExcludedPatterns(testPatternArray);
|
||||
excludedPatternSet = checker.getExcludedPatterns();
|
||||
assertNotNull("default excluded patterns null?", excludedPatternSet);
|
||||
assertFalse("default excluded patterns empty?", excludedPatternSet.isEmpty());
|
||||
assertTrue("replaced default accepted patterns not size " + testPatternArray.length + "?",
|
||||
excludedPatternSet.size() == testPatternArray.length);
|
||||
for (String testPatternArray1 : testPatternArray) {
|
||||
assertTrue(testPatternArray1 + " not excluded?", checker.isExcluded(testPatternArray1).isExcluded());
|
||||
}
|
||||
try {
|
||||
excludedPatternSet.add(Pattern.compile("SomeRegexPattern") );
|
||||
fail ("excluded patterns modifiable?");
|
||||
} catch(UnsupportedOperationException uoe) {
|
||||
// Expected result
|
||||
}
|
||||
try {
|
||||
excludedPatternSet.clear();
|
||||
fail ("excluded patterns modifiable?");
|
||||
} catch(UnsupportedOperationException uoe) {
|
||||
// Expected result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,6 +249,71 @@ public class StrutsLocalizedTextProviderTest extends XWorkTestCase {
|
||||
assertEquals("Hallo", rbGermany.getString("hello"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Unit test to confirm expected behaviour of "clearing methods" provided to
|
||||
* StrutsLocalizedTextProvider (from AbstractLocalizedTextProvider).
|
||||
*/
|
||||
public void testLocalizedTextProviderClearingMethods() {
|
||||
TestStrutsLocalizedTextProvider testStrutsLocalizedTextProvider = new TestStrutsLocalizedTextProvider();
|
||||
assertTrue("testStrutsLocalizedTextProvider not instance of AbstractLocalizedTextProvider ?",
|
||||
testStrutsLocalizedTextProvider instanceof AbstractLocalizedTextProvider);
|
||||
assertEquals("testStrutsLocalizedTextProvider starting default bundle map size not 0 before any retrievals ?",
|
||||
0, testStrutsLocalizedTextProvider.currentBundlesMapSize());
|
||||
|
||||
// Access the two default bundles to populate their cache entries and test bundle map size
|
||||
ResourceBundle tempBundle = testStrutsLocalizedTextProvider.findResourceBundle(
|
||||
TestStrutsLocalizedTextProvider.XWORK_MESSAGES_BUNDLE, Locale.ENGLISH);
|
||||
assertNotNull("XWORK_MESSAGES_BUNDLE retrieval null ?", tempBundle);
|
||||
tempBundle = testStrutsLocalizedTextProvider.findResourceBundle(
|
||||
TestStrutsLocalizedTextProvider.STRUTS_MESSAGES_BUNDLE, Locale.ENGLISH);
|
||||
assertNotNull("STRUTS_MESSAGES_BUNDLE retrieval null ?", tempBundle);
|
||||
assertEquals("testStrutsLocalizedTextProvider bundle map size not 2 after retrievals ?",
|
||||
2, testStrutsLocalizedTextProvider.currentBundlesMapSize());
|
||||
|
||||
// Add and then access four test bundles to populate their cache entries and test bundle map size
|
||||
testStrutsLocalizedTextProvider.addDefaultResourceBundle("com/opensymphony/xwork2/util/LocalizedTextUtilTest");
|
||||
testStrutsLocalizedTextProvider.addDefaultResourceBundle("com/opensymphony/xwork2/util/FindMe");
|
||||
testStrutsLocalizedTextProvider.addDefaultResourceBundle("com/opensymphony/xwork2/SimpleAction");
|
||||
testStrutsLocalizedTextProvider.addDefaultResourceBundle("com/opensymphony/xwork2/test");
|
||||
tempBundle = testStrutsLocalizedTextProvider.findResourceBundle(
|
||||
"com/opensymphony/xwork2/util/LocalizedTextUtilTest", Locale.ENGLISH);
|
||||
assertNotNull("com/opensymphony/xwork2/util/LocalizedTextUtilTest retrieval null ?", tempBundle);
|
||||
tempBundle = testStrutsLocalizedTextProvider.findResourceBundle(
|
||||
"com/opensymphony/xwork2/util/FindMe", Locale.ENGLISH);
|
||||
assertNotNull("com/opensymphony/xwork2/util/FindMe retrieval null ?", tempBundle);
|
||||
tempBundle = testStrutsLocalizedTextProvider.findResourceBundle(
|
||||
"com/opensymphony/xwork2/SimpleAction", Locale.ENGLISH);
|
||||
assertNotNull("com/opensymphony/xwork2/SimpleAction retrieval null ?", tempBundle);
|
||||
tempBundle = testStrutsLocalizedTextProvider.findResourceBundle(
|
||||
"com/opensymphony/xwork2/test", Locale.ENGLISH);
|
||||
assertNotNull("com/opensymphony/xwork2/test retrieval null ?", tempBundle);
|
||||
assertEquals("testStrutsLocalizedTextProvider bundle map size not 6 after retrievals ?",
|
||||
6, testStrutsLocalizedTextProvider.currentBundlesMapSize());
|
||||
|
||||
// Expect the call to be ineffective due to no locale (but shouldn't throw an Exception or cause failure)
|
||||
testStrutsLocalizedTextProvider.callClearBundleNoLocale("com/opensymphony/xwork2/test");
|
||||
assertEquals("testStrutsLocalizedTextProvider bundle map size not 6 after non-locale clear call ?",
|
||||
6, testStrutsLocalizedTextProvider.currentBundlesMapSize());
|
||||
|
||||
// Expect the call to function with bundle name + locale. Remove all four of the non-default
|
||||
// bundles and confirm the bundle map size changes.
|
||||
testStrutsLocalizedTextProvider.callClearBundleWithLocale("com/opensymphony/xwork2/test", Locale.ENGLISH);
|
||||
assertEquals("testStrutsLocalizedTextProvider bundle map size not 5 after locale clear call ?",
|
||||
5, testStrutsLocalizedTextProvider.currentBundlesMapSize());
|
||||
testStrutsLocalizedTextProvider.callClearBundleWithLocale("com/opensymphony/xwork2/SimpleAction", Locale.ENGLISH);
|
||||
assertEquals("testStrutsLocalizedTextProvider bundle map size not 4 after locale clear call ?",
|
||||
4, testStrutsLocalizedTextProvider.currentBundlesMapSize());
|
||||
testStrutsLocalizedTextProvider.callClearBundleWithLocale("com/opensymphony/xwork2/util/FindMe", Locale.ENGLISH);
|
||||
assertEquals("testStrutsLocalizedTextProvider bundle map size not 3 after locale clear call ?",
|
||||
3, testStrutsLocalizedTextProvider.currentBundlesMapSize());
|
||||
testStrutsLocalizedTextProvider.callClearBundleWithLocale("com/opensymphony/xwork2/util/LocalizedTextUtilTest", Locale.ENGLISH);
|
||||
assertEquals("testStrutsLocalizedTextProvider bundle map size not 2 after locale clear call ?",
|
||||
2, testStrutsLocalizedTextProvider.currentBundlesMapSize());
|
||||
|
||||
// Confirm the missing bundles cache clearing method does not produce any Exceptions or failures.
|
||||
testStrutsLocalizedTextProvider.callClearMissingBundlesCache();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
@@ -267,4 +332,25 @@ public class StrutsLocalizedTextProviderTest extends XWorkTestCase {
|
||||
localizedTextProvider = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic test class to allow specific testing of StrutsLocalizedTextProvider
|
||||
*/
|
||||
class TestStrutsLocalizedTextProvider extends StrutsLocalizedTextProvider {
|
||||
|
||||
public void callClearBundleNoLocale(String bundleName) {
|
||||
super.clearBundle(bundleName);
|
||||
}
|
||||
|
||||
public void callClearBundleWithLocale(String bundleName, Locale locale) {
|
||||
super.clearBundle(bundleName, locale);
|
||||
}
|
||||
|
||||
public void callClearMissingBundlesCache() {
|
||||
super.clearMissingBundlesCache();
|
||||
}
|
||||
|
||||
public int currentBundlesMapSize() {
|
||||
return super.bundlesMap.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ public class DateRangeValidatorTest extends XWorkTestCase {
|
||||
HashMap<String, Object> params = new HashMap<>();
|
||||
params.put("date", date.getTime());
|
||||
context.put(ActionContext.PARAMETERS, HttpParameters.create(params).build());
|
||||
context.put(ActionContext.LOCALE, Locale.US); // Force US Locale for date conversion tests on JDK9+
|
||||
|
||||
ActionProxy proxy = actionProxyFactory.createActionProxy("", MockConfigurationProvider.VALIDATION_ACTION_NAME, null, context);
|
||||
proxy.execute();
|
||||
|
||||
@@ -38,6 +38,7 @@ public class VisitorFieldValidatorTest extends XWorkTestCase {
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
ActionContext.getContext().put(ActionContext.LOCALE, Locale.US); // Force US Locale for date conversion tests on JDK9+
|
||||
action = container.inject(VisitorValidatorTestAction.class);
|
||||
|
||||
TestBean bean = action.getBean();
|
||||
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* 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.dispatcher;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import org.apache.struts2.StrutsInternalTestCase;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import org.apache.struts2.views.freemarker.FreemarkerManager;
|
||||
import static org.easymock.EasyMock.anyInt;
|
||||
import static org.easymock.EasyMock.anyString;
|
||||
import static org.easymock.EasyMock.createMock;
|
||||
import static org.easymock.EasyMock.expect;
|
||||
import static org.easymock.EasyMock.expectLastCall;
|
||||
import static org.easymock.EasyMock.replay;
|
||||
|
||||
public class DefaultDispatcherErrorHandlerTest extends StrutsInternalTestCase {
|
||||
private HttpServletRequest requestMock;
|
||||
private HttpServletResponse responseMock;
|
||||
|
||||
/**
|
||||
* Test to exercise the code path and prove handleError() will output
|
||||
* the desired log warning when an IOException is thrown with devMode false.
|
||||
*/
|
||||
public void testHandleErrorIOException() {
|
||||
DefaultDispatcherErrorHandler defaultDispatcherErrorHandler = new DefaultDispatcherErrorHandler();
|
||||
defaultDispatcherErrorHandler.setDevMode("false");
|
||||
defaultDispatcherErrorHandler.setFreemarkerManager(dispatcher.getContainer().getInstance(FreemarkerManager.class));
|
||||
defaultDispatcherErrorHandler.init(dispatcher.servletContext);
|
||||
Exception fakeException = new Exception("Fake Exception, devMode false");
|
||||
try {
|
||||
requestMock.setAttribute("javax.servlet.error.exception", fakeException);
|
||||
expectLastCall();
|
||||
requestMock.setAttribute("javax.servlet.jsp.jspException", fakeException);
|
||||
expectLastCall();
|
||||
responseMock.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, fakeException.getMessage());
|
||||
expectLastCall().andStubThrow(new IOException("Fake IO Exception (SC_INTERNAL_SERVER_ERROR, devMode false)"));
|
||||
replay(responseMock);
|
||||
} catch (IOException ioe) {
|
||||
fail("Mock sendError call setup failed. Ex: " + ioe);
|
||||
}
|
||||
defaultDispatcherErrorHandler.handleError(requestMock, responseMock, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, fakeException);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to exercise the code path and prove handleError() will output
|
||||
* the desired log warning when an IOException is thrown with devMode true.
|
||||
*/
|
||||
public void testHandleErrorIOExceptionDevMode() {
|
||||
DefaultDispatcherErrorHandler defaultDispatcherErrorHandler = new DefaultDispatcherErrorHandler();
|
||||
defaultDispatcherErrorHandler.setDevMode("true");
|
||||
defaultDispatcherErrorHandler.setFreemarkerManager(dispatcher.getContainer().getInstance(FreemarkerManager.class));
|
||||
defaultDispatcherErrorHandler.init(dispatcher.servletContext);
|
||||
Exception fakeException = new Exception("Fake Exception, devMode true");
|
||||
try {
|
||||
responseMock.setContentType("text/html");
|
||||
expectLastCall().andStubThrow(new IllegalStateException("Fake IllegalState Exception (report write)")); // Fake error during report write
|
||||
responseMock.sendError(anyInt(), anyString());
|
||||
expectLastCall().andStubThrow(new IOException("Fake IO Exception (SC_INTERNAL_SERVER_ERROR, devMode true)"));
|
||||
replay(responseMock);
|
||||
} catch (IOException ioe) {
|
||||
fail("Mock sendError call setup failed. Ex: " + ioe);
|
||||
}
|
||||
defaultDispatcherErrorHandler.handleError(requestMock, responseMock, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, fakeException);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to exercise the code path and prove handleError() will output
|
||||
* the desired log warning when an IllegalStateException is thrown with devMode false.
|
||||
*/
|
||||
public void testHandleErrorIllegalStateException() {
|
||||
DefaultDispatcherErrorHandler defaultDispatcherErrorHandler = new DefaultDispatcherErrorHandler();
|
||||
defaultDispatcherErrorHandler.setDevMode("false");
|
||||
defaultDispatcherErrorHandler.setFreemarkerManager(dispatcher.getContainer().getInstance(FreemarkerManager.class));
|
||||
defaultDispatcherErrorHandler.init(dispatcher.servletContext);
|
||||
Exception fakeException = new Exception("Fake Exception, devMode false");
|
||||
try {
|
||||
requestMock.setAttribute("javax.servlet.error.exception", fakeException);
|
||||
expectLastCall();
|
||||
requestMock.setAttribute("javax.servlet.jsp.jspException", fakeException);
|
||||
expectLastCall();
|
||||
expect(responseMock.isCommitted()).andStubReturn(Boolean.TRUE);
|
||||
responseMock.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, fakeException.getMessage());
|
||||
expectLastCall().andStubThrow(new IllegalStateException("Fake IllegalState Exception (SC_INTERNAL_SERVER_ERROR, devMode false)"));
|
||||
replay(responseMock);
|
||||
} catch (IOException ioe) {
|
||||
fail("Mock sendError call setup failed. Ex: " + ioe);
|
||||
}
|
||||
defaultDispatcherErrorHandler.handleError(requestMock, responseMock, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, fakeException);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to exercise the code path and prove handleError() will output
|
||||
* the desired log warning when an IllegalStateException is thrown with devMode true.
|
||||
*/
|
||||
public void testHandleErrorIllegalStateExceptionDevMode() {
|
||||
DefaultDispatcherErrorHandler defaultDispatcherErrorHandler = new DefaultDispatcherErrorHandler();
|
||||
defaultDispatcherErrorHandler.setDevMode("true");
|
||||
defaultDispatcherErrorHandler.setFreemarkerManager(dispatcher.getContainer().getInstance(FreemarkerManager.class));
|
||||
defaultDispatcherErrorHandler.init(dispatcher.servletContext);
|
||||
Exception fakeException = new Exception("Fake Exception, devMode true");
|
||||
try {
|
||||
expect(responseMock.isCommitted()).andStubReturn(Boolean.TRUE);
|
||||
responseMock.setContentType("text/html");
|
||||
expectLastCall().andStubThrow(new IllegalStateException("Fake IllegalState Exception (report write)")); // Fake error during report write
|
||||
responseMock.sendError(anyInt(), anyString());
|
||||
expectLastCall().andStubThrow(new IllegalStateException("Fake IllegalState Exception (SC_INTERNAL_SERVER_ERROR, devMode true)"));
|
||||
replay(responseMock);
|
||||
} catch (IOException ioe) {
|
||||
fail("Mock sendError call setup failed. Ex: " + ioe);
|
||||
}
|
||||
defaultDispatcherErrorHandler.handleError(requestMock, responseMock, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, fakeException);
|
||||
}
|
||||
|
||||
protected void setUp() {
|
||||
requestMock = (HttpServletRequest) createMock(HttpServletRequest.class);
|
||||
responseMock = (HttpServletResponse) createMock(HttpServletResponse.class);
|
||||
dispatcher = initDispatcher(Collections.<String, String>emptyMap());
|
||||
}
|
||||
}
|
||||
@@ -18,11 +18,22 @@
|
||||
*/
|
||||
package org.apache.struts2.dispatcher;
|
||||
|
||||
import java.io.IOException;
|
||||
import org.apache.struts2.StrutsInternalTestCase;
|
||||
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import static org.easymock.EasyMock.createMock;
|
||||
import static org.easymock.EasyMock.expect;
|
||||
import static org.easymock.EasyMock.expectLastCall;
|
||||
import static org.easymock.EasyMock.replay;
|
||||
|
||||
public class DefaultStaticContentLoaderTest extends StrutsInternalTestCase {
|
||||
private HttpServletRequest requestMock;
|
||||
private HttpServletResponse responseMock;
|
||||
private HostConfig hostConfigMock;
|
||||
private DefaultStaticContentLoader defaultStaticContentLoader;
|
||||
|
||||
public void testParsePackages() throws Exception {
|
||||
|
||||
@@ -50,4 +61,55 @@ public class DefaultStaticContentLoaderTest extends StrutsInternalTestCase {
|
||||
assertEquals(result4.get(3), "foo/bar/package4/");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to exercise the code path and prove findStaticResource() will output
|
||||
* the desired log warning when an IOException is thrown.
|
||||
*/
|
||||
public void testFindStaticResourceIOException() {
|
||||
expect(requestMock.getDateHeader("If-Modified-Since")).andStubReturn(0L);
|
||||
try {
|
||||
responseMock.sendError(HttpServletResponse.SC_NOT_FOUND);
|
||||
expectLastCall().andStubThrow(new IOException("Fake IO Exception (SC_NOT_FOUND)"));
|
||||
replay(responseMock);
|
||||
} catch (IOException ioe) {
|
||||
fail("Mock sendError call setup failed. Ex: " + ioe);
|
||||
}
|
||||
try {
|
||||
defaultStaticContentLoader.findStaticResource("/static/fake.html", requestMock, responseMock);
|
||||
} catch (IOException ioe) {
|
||||
fail("DefaultStaticContentLoader.findStaticResource() call failed. Ex: " + ioe);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to exercise the code path and prove findStaticResource() will output
|
||||
* the desired log warning when an IllegalStateException is thrown.
|
||||
*/
|
||||
public void testFindStaticResourceIllegalStateException() {
|
||||
expect(requestMock.getDateHeader("If-Modified-Since")).andStubReturn(0L);
|
||||
try {
|
||||
expect(responseMock.isCommitted()).andStubReturn(Boolean.TRUE);
|
||||
responseMock.sendError(HttpServletResponse.SC_NOT_FOUND);
|
||||
expectLastCall().andStubThrow(new IllegalStateException("Fake IllegalState Exception (SC_NOT_FOUND)"));
|
||||
replay(responseMock);
|
||||
} catch (IOException ioe) {
|
||||
fail("Mock sendError call setup failed. Ex: " + ioe);
|
||||
}
|
||||
try {
|
||||
defaultStaticContentLoader.findStaticResource("/static/fake.html", requestMock, responseMock);
|
||||
} catch (IOException ioe) {
|
||||
fail("DefaultStaticContentLoader.findStaticResource() call failed. Ex: " + ioe);
|
||||
}
|
||||
}
|
||||
|
||||
protected void setUp() {
|
||||
requestMock = (HttpServletRequest) createMock(HttpServletRequest.class);
|
||||
responseMock = (HttpServletResponse) createMock(HttpServletResponse.class);
|
||||
hostConfigMock = (HostConfig) createMock(HostConfig.class);
|
||||
expect(hostConfigMock.getInitParameter("packages")).andStubReturn(null);
|
||||
expect(hostConfigMock.getInitParameter("loggerFactory")).andStubReturn(null);
|
||||
defaultStaticContentLoader = new DefaultStaticContentLoader();
|
||||
defaultStaticContentLoader.setHostConfig(hostConfigMock);
|
||||
defaultStaticContentLoader.setEncoding("UTF-8");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ package org.apache.struts2.dispatcher;
|
||||
|
||||
import com.mockobjects.dynamic.C;
|
||||
import com.mockobjects.dynamic.Mock;
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.ObjectFactory;
|
||||
import com.opensymphony.xwork2.XWorkConstants;
|
||||
import com.opensymphony.xwork2.config.Configuration;
|
||||
@@ -36,6 +37,7 @@ import org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper;
|
||||
import org.apache.struts2.util.ObjectFactoryDestroyable;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.mock.web.MockHttpSession;
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
@@ -319,6 +321,260 @@ public class DispatcherTest extends StrutsInternalTestCase {
|
||||
|
||||
req.setContentType("multipart/form-data; boundary=---------------------------207103069210263;charset=UTF-16LE");
|
||||
assertTrue(du.isMultipartRequest(req));
|
||||
|
||||
req.setContentType("multipart/form-data;boundary=---------------------------207103069210263;charset=UTF-16LE");
|
||||
assertTrue(du.isMultipartRequest(req));
|
||||
|
||||
req.setContentType("multipart/form-data;boundary=---------------------------207103069210263; charset=UTF-16LE");
|
||||
assertTrue(du.isMultipartRequest(req));
|
||||
|
||||
req.setContentType("multipart/form-data;boundary=---------------------------207103069210263 ;charset=UTF-16LE");
|
||||
assertTrue(du.isMultipartRequest(req));
|
||||
|
||||
req.setContentType("multipart/form-data;boundary=---------------------------207103069210263 ; charset=UTF-16LE");
|
||||
assertTrue(du.isMultipartRequest(req));
|
||||
|
||||
req.setContentType("multipart/form-data ;boundary=---------------------------207103069210263;charset=UTF-16LE");
|
||||
assertTrue(du.isMultipartRequest(req));
|
||||
|
||||
req.setContentType("multipart/form-data ; boundary=---------------------------207103069210263;charset=UTF-16LE");
|
||||
assertTrue(du.isMultipartRequest(req));
|
||||
|
||||
req.setContentType("Multipart/Form-Data ; boundary=---------------------------207103069210263;charset=UTF-16LE");
|
||||
assertTrue(du.isMultipartRequest(req));
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify proper default (true) handleExceptionState for Dispatcher and that
|
||||
* it properly reflects a manually configured change to false.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public void testHandleException() throws Exception {
|
||||
Dispatcher du = initDispatcher(new HashMap<String, String>());
|
||||
assertTrue("Default Dispatcher handleException state not true ?", du.isHandleException());
|
||||
|
||||
Dispatcher du2 = initDispatcher(new HashMap<String, String>() {{
|
||||
put(StrutsConstants.STRUTS_HANDLE_EXCEPTION, "false");
|
||||
}});
|
||||
assertFalse("Modified Dispatcher handleException state not false ?", du2.isHandleException());
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify proper default (false) devMode for Dispatcher and that
|
||||
* it properly reflects a manually configured change to true.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public void testDevMode() throws Exception {
|
||||
Dispatcher du = initDispatcher(new HashMap<String, String>());
|
||||
assertFalse("Default Dispatcher devMode state not false ?", du.isDevMode());
|
||||
|
||||
Dispatcher du2 = initDispatcher(new HashMap<String, String>() {{
|
||||
put(StrutsConstants.STRUTS_DEVMODE, "true");
|
||||
}});
|
||||
assertTrue("Modified Dispatcher devMode state not true ?", du2.isDevMode());
|
||||
}
|
||||
|
||||
public void testGetLocale_With_DefaultLocale_FromConfiguration() throws Exception {
|
||||
// Given
|
||||
Mock mock = new Mock(HttpServletRequest.class);
|
||||
MockHttpSession mockHttpSession = new MockHttpSession();
|
||||
mock.expectAndReturn("getCharacterEncoding", "utf-8"); // From Dispatcher prepare().
|
||||
mock.expectAndReturn("getHeader", "X-Requested-With", ""); // From Dispatcher prepare().
|
||||
mock.expectAndReturn("getParameterMap", new HashMap<String, Object>()); // From Dispatcher prepare().
|
||||
mock.expectAndReturn("getSession", false, mockHttpSession); // From Dispatcher prepare().
|
||||
mock.expectAndReturn("getSession", true, mockHttpSession); // From createTestContextMap().
|
||||
HttpServletRequest request = (HttpServletRequest) mock.proxy();
|
||||
HttpServletResponse response = new MockHttpServletResponse();
|
||||
|
||||
Dispatcher testDispatcher = initDispatcher(new HashMap<String, String>() {{
|
||||
put(StrutsConstants.STRUTS_I18N_ENCODING, "utf-8");
|
||||
// Not setting a Struts Locale here, so we should receive the default "de_DE" from the test configuration.
|
||||
}});
|
||||
|
||||
// When
|
||||
testDispatcher.prepare(request, response);
|
||||
Map<String, Object> contextMap = createTestContextMap(testDispatcher, request, response);
|
||||
|
||||
// Then
|
||||
assertEquals(Locale.GERMANY, contextMap.get(ActionContext.LOCALE)); // Expect the Dispatcher defaultLocale value "de_DE" from the test configuration.
|
||||
mock.verify();
|
||||
}
|
||||
|
||||
public void testGetLocale_With_DefaultLocale_fr_CA() throws Exception {
|
||||
// Given
|
||||
Mock mock = new Mock(HttpServletRequest.class);
|
||||
MockHttpSession mockHttpSession = new MockHttpSession();
|
||||
mock.expectAndReturn("getCharacterEncoding", "utf-8"); // From Dispatcher prepare().
|
||||
mock.expectAndReturn("getHeader", "X-Requested-With", ""); // From Dispatcher prepare().
|
||||
mock.expectAndReturn("getParameterMap", new HashMap<String, Object>()); // From Dispatcher prepare().
|
||||
mock.expectAndReturn("getSession", false, mockHttpSession); // From Dispatcher prepare().
|
||||
mock.expectAndReturn("getSession", true, mockHttpSession); // From createTestContextMap().
|
||||
HttpServletRequest request = (HttpServletRequest) mock.proxy();
|
||||
HttpServletResponse response = new MockHttpServletResponse();
|
||||
|
||||
Dispatcher testDispatcher = initDispatcher(new HashMap<String, String>() {{
|
||||
put(StrutsConstants.STRUTS_I18N_ENCODING, "utf-8");
|
||||
put(StrutsConstants.STRUTS_LOCALE, Locale.CANADA_FRENCH.toString()); // Set the Dispatcher defaultLocale to fr_CA.
|
||||
}});
|
||||
|
||||
// When
|
||||
testDispatcher.prepare(request, response);
|
||||
Map<String, Object> contextMap = createTestContextMap(testDispatcher, request, response);
|
||||
|
||||
// Then
|
||||
assertEquals(Locale.CANADA_FRENCH, contextMap.get(ActionContext.LOCALE)); // Expect the Dispatcher defaultLocale value.
|
||||
mock.verify();
|
||||
}
|
||||
|
||||
public void testGetLocale_With_BadDefaultLocale_RequestLocale_en_UK() throws Exception {
|
||||
// Given
|
||||
Mock mock = new Mock(HttpServletRequest.class);
|
||||
MockHttpSession mockHttpSession = new MockHttpSession();
|
||||
mock.expectAndReturn("getCharacterEncoding", "utf-8"); // From Dispatcher prepare().
|
||||
mock.expectAndReturn("getHeader", "X-Requested-With", ""); // From Dispatcher prepare().
|
||||
mock.expectAndReturn("getLocale", Locale.UK); // From Dispatcher prepare().
|
||||
mock.expectAndReturn("getParameterMap", new HashMap<String, Object>()); // From Dispatcher prepare().
|
||||
mock.expectAndReturn("getSession", false, mockHttpSession); // From Dispatcher prepare().
|
||||
mock.expectAndReturn("getSession", true, mockHttpSession); // From createTestContextMap().
|
||||
mock.expectAndReturn("getLocale", Locale.UK); // From createTestContextMap().
|
||||
HttpServletRequest request = (HttpServletRequest) mock.proxy();
|
||||
HttpServletResponse response = new MockHttpServletResponse();
|
||||
|
||||
Dispatcher testDispatcher = initDispatcher(new HashMap<String, String>() {{
|
||||
put(StrutsConstants.STRUTS_I18N_ENCODING, "utf-8");
|
||||
put(StrutsConstants.STRUTS_LOCALE, "This_is_not_a_valid_Locale_string"); // Set Dispatcher defaultLocale to an invalid value.
|
||||
}});
|
||||
|
||||
// When
|
||||
testDispatcher.prepare(request, response);
|
||||
Map<String, Object> contextMap = createTestContextMap(testDispatcher, request, response);
|
||||
|
||||
// Then
|
||||
assertEquals(Locale.UK, contextMap.get(ActionContext.LOCALE)); // Expect the request set value from Mock.
|
||||
mock.verify();
|
||||
}
|
||||
|
||||
public void testGetLocale_With_BadDefaultLocale_And_RuntimeException() throws Exception {
|
||||
// Given
|
||||
Mock mock = new Mock(HttpServletRequest.class);
|
||||
MockHttpSession mockHttpSession = new MockHttpSession();
|
||||
mock.expectAndReturn("getCharacterEncoding", "utf-8"); // From Dispatcher prepare().
|
||||
mock.expectAndReturn("getHeader", "X-Requested-With", ""); // From Dispatcher prepare().
|
||||
mock.expectAndReturn("getLocale", Locale.UK); // From Dispatcher prepare().
|
||||
mock.expectAndReturn("getParameterMap", new HashMap<String, Object>()); // From Dispatcher prepare().
|
||||
mock.expectAndReturn("getSession", false, mockHttpSession); // From Dispatcher prepare().
|
||||
mock.expectAndReturn("getSession", true, mockHttpSession); // From createTestContextMap().
|
||||
mock.expectAndThrow("getLocale", new IllegalStateException("Test theoretical state preventing HTTP Request Locale access")); // From createTestContextMap().
|
||||
HttpServletRequest request = (HttpServletRequest) mock.proxy();
|
||||
HttpServletResponse response = new MockHttpServletResponse();
|
||||
|
||||
Dispatcher testDispatcher = initDispatcher(new HashMap<String, String>() {{
|
||||
put(StrutsConstants.STRUTS_I18N_ENCODING, "utf-8");
|
||||
put(StrutsConstants.STRUTS_LOCALE, "This_is_not_a_valid_Locale_string"); // Set the Dispatcher defaultLocale to an invalid value.
|
||||
}});
|
||||
|
||||
// When
|
||||
testDispatcher.prepare(request, response);
|
||||
Map<String, Object> contextMap = createTestContextMap(testDispatcher, request, response);
|
||||
|
||||
// Then
|
||||
assertEquals(Locale.getDefault(), contextMap.get(ActionContext.LOCALE)); // Expect the system default value, when BOTH Dispatcher default Locale AND request access fail.
|
||||
mock.verify();
|
||||
}
|
||||
|
||||
public void testGetLocale_With_NullDefaultLocale() throws Exception {
|
||||
// Given
|
||||
Mock mock = new Mock(HttpServletRequest.class);
|
||||
MockHttpSession mockHttpSession = new MockHttpSession();
|
||||
mock.expectAndReturn("getCharacterEncoding", "utf-8"); // From Dispatcher prepare().
|
||||
mock.expectAndReturn("getHeader", "X-Requested-With", ""); // From Dispatcher prepare().
|
||||
mock.expectAndReturn("getLocale", Locale.CANADA_FRENCH); // From Dispatcher prepare().
|
||||
mock.expectAndReturn("getParameterMap", new HashMap<String, Object>()); // From Dispatcher prepare().
|
||||
mock.expectAndReturn("getSession", false, mockHttpSession); // From Dispatcher prepare().
|
||||
mock.expectAndReturn("getSession", true, mockHttpSession); // From createTestContextMap().
|
||||
mock.expectAndReturn("getLocale", Locale.CANADA_FRENCH); // From createTestContextMap().
|
||||
HttpServletRequest request = (HttpServletRequest) mock.proxy();
|
||||
HttpServletResponse response = new MockHttpServletResponse();
|
||||
|
||||
Dispatcher testDispatcher = initDispatcher(new HashMap<String, String>() {{
|
||||
put(StrutsConstants.STRUTS_I18N_ENCODING, "utf-8");
|
||||
// Attempting to set StrutsConstants.STRUTS_LOCALE to null here via parameters causes an NPE.
|
||||
}});
|
||||
|
||||
testDispatcher.setDefaultLocale(null); // Force a null Struts default locale, otherwise we receive the default "de_DE" from the test configuration.
|
||||
|
||||
// When
|
||||
testDispatcher.prepare(request, response);
|
||||
Map<String, Object> contextMap = createTestContextMap(testDispatcher, request, response);
|
||||
|
||||
// Then
|
||||
assertEquals(Locale.CANADA_FRENCH, contextMap.get(ActionContext.LOCALE)); // Expect the request set value from Mock.
|
||||
mock.verify();
|
||||
}
|
||||
|
||||
public void testGetLocale_With_NullDefaultLocale_And_RuntimeException() throws Exception {
|
||||
// Given
|
||||
Mock mock = new Mock(HttpServletRequest.class);
|
||||
MockHttpSession mockHttpSession = new MockHttpSession();
|
||||
mock.expectAndReturn("getCharacterEncoding", "utf-8"); // From Dispatcher prepare().
|
||||
mock.expectAndReturn("getHeader", "X-Requested-With", ""); // From Dispatcher prepare().
|
||||
mock.expectAndReturn("getLocale", Locale.CANADA_FRENCH); // From Dispatcher prepare().
|
||||
mock.expectAndReturn("getParameterMap", new HashMap<String, Object>()); // From Dispatcher prepare().
|
||||
mock.expectAndReturn("getSession", false, mockHttpSession); // From Dispatcher prepare().
|
||||
mock.expectAndReturn("getSession", true, mockHttpSession); // From createTestContextMap().
|
||||
mock.expectAndThrow("getLocale", new IllegalStateException("Test some theoretical state preventing HTTP Request Locale access")); // From createTestContextMap().
|
||||
HttpServletRequest request = (HttpServletRequest) mock.proxy();
|
||||
HttpServletResponse response = new MockHttpServletResponse();
|
||||
|
||||
Dispatcher testDispatcher = initDispatcher(new HashMap<String, String>() {{
|
||||
put(StrutsConstants.STRUTS_I18N_ENCODING, "utf-8");
|
||||
// Attempting to set StrutsConstants.STRUTS_LOCALE to null via parameters causes an NPE.
|
||||
}});
|
||||
|
||||
testDispatcher.setDefaultLocale(null); // Force a null Struts default locale, otherwise we receive the default "de_DE" from the test configuration.
|
||||
|
||||
// When
|
||||
testDispatcher.prepare(request, response);
|
||||
Map<String, Object> contextMap = createTestContextMap(testDispatcher, request, response);
|
||||
|
||||
// Then
|
||||
assertEquals(Locale.getDefault(), contextMap.get(ActionContext.LOCALE)); // Expect the system default value when Mock request access fails.
|
||||
mock.verify();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a test context Map from a Dispatcher instance.
|
||||
*
|
||||
* The method directly calls getParameterMap() and getSession(true) on the HttpServletRequest.
|
||||
*
|
||||
* The method indirectly calls getLocale(request) on the Dispatcher instance, allowing a test of that code path.
|
||||
* The derived Struts Dispatcher Locale can be retrieved from the Map afterwards.
|
||||
*
|
||||
* @param dispatcher
|
||||
* @param request
|
||||
* @param response
|
||||
* @return
|
||||
*/
|
||||
protected static Map<String, Object> createTestContextMap(Dispatcher dispatcher,
|
||||
HttpServletRequest request, HttpServletResponse response) {
|
||||
if (dispatcher == null) {
|
||||
throw new IllegalArgumentException("Cannot create a test ContextMap from a null Dispatcher");
|
||||
}
|
||||
if (request == null) {
|
||||
throw new IllegalArgumentException("Cannot create a test ContextMap from a null HttpServletRequest");
|
||||
}
|
||||
if (response == null) {
|
||||
throw new IllegalArgumentException("Cannot create a test ContextMap from a null HttpServletResponse");
|
||||
}
|
||||
|
||||
return dispatcher.createContextMap(new RequestMap(request),
|
||||
HttpParameters.create(request.getParameterMap()).build(),
|
||||
new SessionMap(request),
|
||||
new ApplicationMap(request.getSession(true).getServletContext()),
|
||||
request,
|
||||
response);
|
||||
}
|
||||
|
||||
class InternalConfigurationManager extends ConfigurationManager {
|
||||
|
||||
+199
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* 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.interceptor;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.XWorkTestCase;
|
||||
|
||||
import org.apache.struts2.dispatcher.HttpParameters;
|
||||
import org.apache.struts2.ServletActionContext;
|
||||
import org.apache.struts2.dispatcher.Parameter;
|
||||
import org.apache.struts2.dispatcher.mapper.ActionMapping;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* Unit test for {@link ActionMappingParametersInterceptor}.
|
||||
*
|
||||
* Adapted from ParametersInterceptor (@author Jason Carreira)
|
||||
*/
|
||||
public class ActionMappingParametersInterceptorTest extends XWorkTestCase {
|
||||
|
||||
/**
|
||||
* Confirm that ActionMappingParametersInterceptor processing methods avoid
|
||||
* nested wrapping of parameters.
|
||||
*/
|
||||
public void testParametersNoNestedWrapping() {
|
||||
final ActionMappingParametersInterceptor ampi = createActionMappingParametersInterceptor();
|
||||
final ActionContext ac = ActionContext.getContext();
|
||||
final Map<String, Object> parameters = new HashMap<String, Object>() {
|
||||
{
|
||||
put("fooKey", "fooValue");
|
||||
put("barKey", "barValue");
|
||||
}
|
||||
};
|
||||
final Map<String, Object> moreParameters = new HashMap<String, Object>() {
|
||||
{
|
||||
put("fooKey2", "fooValue2");
|
||||
put("barKey2", "barValue3");
|
||||
}
|
||||
};
|
||||
final Map<String, Object> evenMoreParameters = new HashMap<String, Object>() {
|
||||
{
|
||||
put("fooKey3", "fooValue3");
|
||||
put("barKey3", "barValue3");
|
||||
}
|
||||
};
|
||||
// Set up the initial ActionMapping with parameters in the context
|
||||
final HttpParameters wrappedParameters = HttpParameters.create(parameters).build();
|
||||
final ActionMapping actionMapping = new ActionMapping("testAction", "testNameSpace", "testMethod", parameters);
|
||||
ac.put(ServletActionContext.ACTION_MAPPING, actionMapping);
|
||||
|
||||
// Retrieve parameters and confirm both builder result equality and that the contained Object
|
||||
// values of each Parameter element are not of type Parameter (i.e. no double-wrapping).
|
||||
// Note: ampi.retrieveParameters() only ever returns the parameters associated with the ActionContext's
|
||||
// ActionMapping (not changed directly by ampi.addParametersToContext()).
|
||||
final HttpParameters retrievedParameters = ampi.retrieveParameters(ac);
|
||||
assertNotNull("retrievedParameters null ?", retrievedParameters);
|
||||
// Note: Cannot perform equality test on HttpParameters directly as hashCode values differ even
|
||||
// when the contents are equivalent. Instead we compare size and content equality.
|
||||
assertEquals("retrievedParameters size not equal to wrappedParameters size ?",
|
||||
retrievedParameters.size(), wrappedParameters.size());
|
||||
|
||||
for (String parameterName : retrievedParameters.keySet() ) {
|
||||
Parameter retrievedParameter = retrievedParameters.get(parameterName);
|
||||
Parameter wrappedParameter = wrappedParameters.get(parameterName);
|
||||
assertNotNull("retrievedParameter is null ?", retrievedParameter);
|
||||
assertNotNull("wrappedParameter is null ?", wrappedParameter);
|
||||
Object retrievedParameterValue = retrievedParameter.getObject();
|
||||
Object wrappedParameterValue = wrappedParameter.getObject();
|
||||
assertNotNull("retrievedParameterValue is null ?", retrievedParameterValue);
|
||||
assertNotNull("wrappedParameterValue is null ?", wrappedParameterValue);
|
||||
assertFalse("retrievedParameterValue is type Parameter ?", retrievedParameterValue instanceof Parameter);
|
||||
assertEquals("retrievedParameterValue not equal to wrappedParameterValue ?",
|
||||
retrievedParameterValue, wrappedParameterValue);
|
||||
}
|
||||
|
||||
// Set up the initial ActionMapping with (artificially) already-wrapped parameters in the context
|
||||
final Map<String, Object> wrappedParametersMap = new HashMap<>(wrappedParameters.size());
|
||||
for (String parameterName : wrappedParameters.keySet() ) {
|
||||
wrappedParametersMap.put(parameterName, wrappedParameters.get(parameterName));
|
||||
}
|
||||
final ActionMapping actionMappingWrapped = new ActionMapping("testAction", "testNameSpace", "testMethod", wrappedParametersMap);
|
||||
ac.put(ServletActionContext.ACTION_MAPPING, actionMappingWrapped);
|
||||
|
||||
// Retrieve parameters and confirm that the contained Object values of each Parameter
|
||||
// element are not of type Parameter (i.e. no double-wrapping).
|
||||
// Note: ampi.retrieveParameters() only ever returns the parameters associated with the ActionContext's
|
||||
// ActionMapping (not changed directly by ampi.addParametersToContext()).
|
||||
final HttpParameters retrievedWrappedParameters = ampi.retrieveParameters(ac);
|
||||
assertNotNull("retrievedWrappedParameters null ?", retrievedWrappedParameters);
|
||||
// Note: Cannot perform equality test on HttpParameters directly as hashCode values differ even
|
||||
// when the contents are equivalent. Instead we compare size and content equality.
|
||||
assertEquals("retrievedWrappedParameters size not equal to wrappedParametersMap size ?",
|
||||
retrievedWrappedParameters.size(), wrappedParametersMap.size());
|
||||
|
||||
for (String parameterName : retrievedWrappedParameters.keySet() ) {
|
||||
Parameter retrievedParameter = retrievedWrappedParameters.get(parameterName);
|
||||
Object wrappedParameter = wrappedParametersMap.get(parameterName);
|
||||
assertNotNull("retrievedParameter is null ?", retrievedParameter);
|
||||
assertNotNull("wrappedParameter is null ?", wrappedParameter);
|
||||
assertTrue("wrappedParameter is not a Parameter ?", wrappedParameter instanceof Parameter);
|
||||
Object retrievedParameterValue = retrievedParameter.getObject();
|
||||
Object wrappedParameterValue = ((Parameter) wrappedParameter).getObject();
|
||||
assertNotNull("retrievedParameterValue is null ?", retrievedParameterValue);
|
||||
assertNotNull("wrappedParameterValue is null ?", wrappedParameterValue);
|
||||
assertFalse("retrievedParameterValue is type Parameter ?", retrievedParameterValue instanceof Parameter);
|
||||
assertEquals("retrievedParameterValue not equal to wrappedParameterValue ?",
|
||||
retrievedParameterValue, wrappedParameterValue);
|
||||
}
|
||||
|
||||
// Add parameters to the context
|
||||
ampi.addParametersToContext(ac, parameters);
|
||||
|
||||
// Retrieve parameters and confirm the expected size and that the contained Object
|
||||
// values of each Parameter element are not of type Parameter (i.e. no double-wrapping).
|
||||
final HttpParameters retrievedACParameters = ac.getParameters();
|
||||
assertNotNull("retrievedACParameters null ?", retrievedACParameters);
|
||||
assertEquals("retrievedACParameters size not equal to parameters size ?",
|
||||
retrievedACParameters.size(), parameters.size());
|
||||
|
||||
for (String parameterName : retrievedACParameters.keySet() ) {
|
||||
Parameter retrievedACParameter = retrievedACParameters.get(parameterName);
|
||||
assertNotNull("retrievedACParameter is null ?", retrievedACParameter);
|
||||
Object parameterValue = retrievedACParameter.getObject();
|
||||
assertNotNull("parameterValue is null ?", parameterValue);
|
||||
assertFalse("parameterValue is of type Parameter ?", parameterValue instanceof Parameter);
|
||||
}
|
||||
|
||||
// Add additional parameters to the context
|
||||
ampi.addParametersToContext(ac, moreParameters);
|
||||
|
||||
// Retrieve parameters and confirm the expected size and that the contained Object
|
||||
// values of each Parameter element are not of type Parameter (i.e. no double-wrapping).
|
||||
final HttpParameters retrievedACMoreParameters = ac.getParameters();
|
||||
assertNotNull("retrievedACMoreParameters null ?", retrievedACMoreParameters);
|
||||
assertEquals("retrievedACMoreParameters size not equal to combined size (parameters + moreParameters) ?",
|
||||
retrievedACMoreParameters.size(), parameters.size() + moreParameters.size());
|
||||
|
||||
for (String parameterName : retrievedACMoreParameters.keySet() ) {
|
||||
Parameter retrievedACMoreParameter = retrievedACMoreParameters.get(parameterName);
|
||||
assertNotNull("retrievedACMoreParameter is null ?", retrievedACMoreParameter);
|
||||
Object parameterValue = retrievedACMoreParameter.getObject();
|
||||
assertNotNull("parameterValue (more) is null ?", parameterValue);
|
||||
assertFalse("parameterValue (more) is of type Parameter ?", parameterValue instanceof Parameter);
|
||||
}
|
||||
|
||||
// Build some "already wrapped" parameters and attempt to add them to the context
|
||||
final HttpParameters evenMoreWrappedParameters = HttpParameters.create(evenMoreParameters).build();
|
||||
assertNotNull("evenMoreWrappedParameters null ?", evenMoreWrappedParameters);
|
||||
assertEquals("evenMoreWrappedParameters size not equal to evenMoreParameters size ?",
|
||||
evenMoreWrappedParameters.size(), evenMoreParameters.size());
|
||||
ampi.addParametersToContext(ac, evenMoreWrappedParameters);
|
||||
|
||||
// Retrieve parameters and confirm the expected size and that the contained Object
|
||||
// values of each Parameter element are not of type Parameter (i.e. no double-wrapping).
|
||||
final HttpParameters retrievedACEvenMoreParameters = ac.getParameters();
|
||||
assertNotNull("retrievedACEvenMoreParameters null ?", retrievedACEvenMoreParameters);
|
||||
assertEquals("retrievedACEvenMoreParameters size not equal to combined size (parameters + moreParameters + evenMoreParameters) ?",
|
||||
retrievedACEvenMoreParameters.size(), parameters.size() + moreParameters.size() + evenMoreParameters.size());
|
||||
|
||||
for (String parameterName : retrievedACEvenMoreParameters.keySet() ) {
|
||||
Parameter retrievedACEvenMoreParameter = retrievedACEvenMoreParameters.get(parameterName);
|
||||
assertNotNull("retrievedACEvenMoreParameter is null ?", retrievedACEvenMoreParameter);
|
||||
Object parameterValue = retrievedACEvenMoreParameter.getObject();
|
||||
assertNotNull("parameterValue (even more) is null ?", parameterValue);
|
||||
assertFalse("parameterValue (even more) is of type Parameter ?", parameterValue instanceof Parameter);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and configure an ActionMappingParametersInterceptor instance
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private ActionMappingParametersInterceptor createActionMappingParametersInterceptor() {
|
||||
ActionMappingParametersInterceptor ampi = new ActionMappingParametersInterceptor();
|
||||
container.inject(ampi);
|
||||
return ampi;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -40,6 +40,7 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -244,7 +245,7 @@ public class FileUploadInterceptorTest extends StrutsInternalTestCase {
|
||||
public void testNoContentMultipartRequest() throws Exception {
|
||||
MockHttpServletRequest req = new MockHttpServletRequest();
|
||||
|
||||
req.setCharacterEncoding("text/html");
|
||||
req.setCharacterEncoding(StandardCharsets.UTF_8.name());
|
||||
req.setMethod("post");
|
||||
req.addHeader("Content-type", "multipart/form-data");
|
||||
req.setContent(null); // there is no content
|
||||
@@ -265,7 +266,7 @@ public class FileUploadInterceptorTest extends StrutsInternalTestCase {
|
||||
|
||||
public void testSuccessUploadOfATextFileMultipartRequest() throws Exception {
|
||||
MockHttpServletRequest req = new MockHttpServletRequest();
|
||||
req.setCharacterEncoding("text/html");
|
||||
req.setCharacterEncoding(StandardCharsets.UTF_8.name());
|
||||
req.setMethod("post");
|
||||
req.addHeader("Content-type", "multipart/form-data; boundary=---1234");
|
||||
|
||||
@@ -322,7 +323,7 @@ public class FileUploadInterceptorTest extends StrutsInternalTestCase {
|
||||
final String endline = "\r\n";
|
||||
|
||||
MockHttpServletRequest req = new MockHttpServletRequest();
|
||||
req.setCharacterEncoding("text/html");
|
||||
req.setCharacterEncoding(StandardCharsets.UTF_8.name());
|
||||
req.setMethod("POST");
|
||||
req.addHeader("Content-type", "multipart/form-data; boundary=" + bondary);
|
||||
StringBuilder content = new StringBuilder(128);
|
||||
@@ -371,7 +372,7 @@ public class FileUploadInterceptorTest extends StrutsInternalTestCase {
|
||||
|
||||
public void testMultipartRequestLocalizedError() throws Exception {
|
||||
MockHttpServletRequest req = new MockHttpServletRequest();
|
||||
req.setCharacterEncoding("text/html");
|
||||
req.setCharacterEncoding(StandardCharsets.UTF_8.name());
|
||||
req.setMethod("post");
|
||||
req.addHeader("Content-type", "multipart/form-data; boundary=---1234");
|
||||
|
||||
|
||||
@@ -21,9 +21,12 @@ package org.apache.struts2.result;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_SEE_OTHER;
|
||||
import static org.easymock.EasyMock.createControl;
|
||||
import static org.easymock.EasyMock.createNiceMock;
|
||||
import static org.easymock.EasyMock.createMock;
|
||||
import static org.easymock.EasyMock.expect;
|
||||
import static org.easymock.EasyMock.expectLastCall;
|
||||
import static org.easymock.EasyMock.replay;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.ArrayList;
|
||||
@@ -321,6 +324,122 @@ public class ServletRedirectResultTest extends StrutsInternalTestCase implements
|
||||
control.verify();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to exercise the code path and prove sendRedirect() will output
|
||||
* the desired log warning when an IOException is thrown for statusCode SC_FOUND.
|
||||
*/
|
||||
public void testSendRedirectSCFoundIOException() {
|
||||
HttpServletResponse httpServletResponseMock = (HttpServletResponse) createMock(HttpServletResponse.class);
|
||||
boolean ioeCaught = false;
|
||||
view.setLocation("/bar/foo.jsp");
|
||||
view.setStatusCode(HttpServletResponse.SC_FOUND);
|
||||
try {
|
||||
httpServletResponseMock.sendRedirect(view.getLocation());
|
||||
expectLastCall().andStubThrow(new IOException("Fake IO Exception (SC_FOUND)"));
|
||||
replay(httpServletResponseMock);
|
||||
} catch (IOException ioe) {
|
||||
fail("Mock sendRedirect call setup failed. Ex: " + ioe);
|
||||
}
|
||||
try {
|
||||
view.sendRedirect(httpServletResponseMock, view.getLocation());
|
||||
} catch (IOException ioe) {
|
||||
ioeCaught = true; // Verify expected exception was thrown
|
||||
}
|
||||
if (!ioeCaught) {
|
||||
fail("sendRedirect (SC_FOUND) with forced IOException did not propagate from setLocation!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to exercise the code path and prove sendRedirect() will output
|
||||
* the desired log warning when an IOException is thrown for statusCode SC_MOVED_PERMANENTLY.
|
||||
*/
|
||||
public void testSendRedirectSCMovedPermanentlyIOException() {
|
||||
HttpServletResponse httpServletResponseMock = (HttpServletResponse) createMock(HttpServletResponse.class);
|
||||
boolean ioeCaught = false;
|
||||
view.setLocation("/bar/foo.jsp");
|
||||
view.setStatusCode(HttpServletResponse.SC_MOVED_PERMANENTLY); // Any non SC_FOUND will suffice
|
||||
try {
|
||||
httpServletResponseMock.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
|
||||
expectLastCall();
|
||||
httpServletResponseMock.setHeader("Location", view.getLocation());
|
||||
expectLastCall();
|
||||
expect(httpServletResponseMock.getWriter()).andStubThrow(new IOException("Fake IO Exception (SC_MOVED_PERMANENTLY)"));
|
||||
replay(httpServletResponseMock);
|
||||
} catch (IOException ioe) {
|
||||
fail("Mock getWriter call setup failed. Ex: " + ioe);
|
||||
}
|
||||
try {
|
||||
view.sendRedirect(httpServletResponseMock, view.getLocation());
|
||||
} catch (IOException ioe) {
|
||||
ioeCaught = true; // Verify expected exception was thrown
|
||||
}
|
||||
if (!ioeCaught) {
|
||||
fail("sendRedirect (SC_MOVED_PERMANENTLY) with forced IOException did not propagate from setLocation!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to exercise the code path and prove sendRedirect() will output
|
||||
* the desired log warning when an IllegalStateException is thrown for statusCode SC_FOUND.
|
||||
*/
|
||||
public void testSendRedirectSCFoundIllegalStateException() {
|
||||
HttpServletResponse httpServletResponseMock = (HttpServletResponse) createMock(HttpServletResponse.class);
|
||||
boolean iseCaught = false;
|
||||
view.setLocation("/bar/foo.jsp");
|
||||
view.setStatusCode(HttpServletResponse.SC_FOUND);
|
||||
try {
|
||||
httpServletResponseMock.sendRedirect(view.getLocation());
|
||||
expectLastCall().andStubThrow(new IllegalStateException("Fake IllegalState Exception (SC_FOUND)"));
|
||||
expect(httpServletResponseMock.isCommitted()).andStubReturn(Boolean.TRUE);
|
||||
replay(httpServletResponseMock);
|
||||
} catch (IOException ioe) {
|
||||
fail("Mock sendRedirect call setup failed. Ex: " + ioe);
|
||||
}
|
||||
try {
|
||||
view.sendRedirect(httpServletResponseMock, view.getLocation());
|
||||
} catch (IOException ioe) {
|
||||
iseCaught = false;
|
||||
} catch (IllegalStateException ise) {
|
||||
iseCaught = true; // Verify expected exception was thrown
|
||||
}
|
||||
if (!iseCaught) {
|
||||
fail("sendRedirect (SC_FOUND) with forced IllegalStateException did not propagate from setLocation!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to exercise the code path and prove sendRedirect() will output
|
||||
* the desired log warning when an IllegalStateException is thrown for statusCode SC_MOVED_PERMANENTLY.
|
||||
*/
|
||||
public void testSendRedirectSCMovedPermanentlyIllegalStateException() {
|
||||
HttpServletResponse httpServletResponseMock = (HttpServletResponse) createMock(HttpServletResponse.class);
|
||||
boolean iseCaught = false;
|
||||
view.setLocation("/bar/foo.jsp");
|
||||
view.setStatusCode(HttpServletResponse.SC_MOVED_PERMANENTLY); // Any non SC_FOUND will suffice
|
||||
try {
|
||||
httpServletResponseMock.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
|
||||
expectLastCall();
|
||||
httpServletResponseMock.setHeader("Location", view.getLocation());
|
||||
expectLastCall();
|
||||
expect(httpServletResponseMock.getWriter()).andStubThrow(new IllegalStateException("Fake IllegalState Exception (SC_MOVED_PERMANENTLY)"));
|
||||
expect(httpServletResponseMock.isCommitted()).andStubReturn(Boolean.TRUE);
|
||||
replay(httpServletResponseMock);
|
||||
} catch (IOException ioe) {
|
||||
fail("Mock getWriter call setup failed. Ex: " + ioe);
|
||||
}
|
||||
try {
|
||||
view.sendRedirect(httpServletResponseMock, view.getLocation());
|
||||
} catch (IOException ioe) {
|
||||
iseCaught = false;
|
||||
} catch (IllegalStateException ise) {
|
||||
iseCaught = true; // Verify expected exception was thrown
|
||||
}
|
||||
if (!iseCaught) {
|
||||
fail("sendRedirect (SC_MOVED_PERMANENTLY) with forced IllegalStateException did not propagate from setLocation!");
|
||||
}
|
||||
}
|
||||
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
configurationManager.getConfiguration().
|
||||
|
||||
@@ -246,12 +246,19 @@ public class StreamResultTest extends StrutsInternalTestCase {
|
||||
|
||||
public class MyImageAction implements Action {
|
||||
|
||||
public InputStream getStreamForImage() throws Exception {
|
||||
FileInputStream streamForImage;
|
||||
long contentLength;
|
||||
|
||||
public MyImageAction() throws Exception {
|
||||
// just use src/test/log4j2.xml as test file
|
||||
URL url = ClassLoaderUtil.getResource("log4j2.xml", StreamResultTest.class);
|
||||
File file = new File(new URI(url.toString()));
|
||||
FileInputStream fis = new FileInputStream(file);
|
||||
return fis;
|
||||
streamForImage = new FileInputStream(file);
|
||||
contentLength = file.length();
|
||||
}
|
||||
|
||||
public InputStream getStreamForImage() throws Exception {
|
||||
return streamForImage;
|
||||
}
|
||||
|
||||
public String execute() throws Exception {
|
||||
@@ -259,9 +266,7 @@ public class StreamResultTest extends StrutsInternalTestCase {
|
||||
}
|
||||
|
||||
public long getContentLength() throws Exception {
|
||||
URL url = ClassLoaderUtil.getResource("log4j2.xml", StreamResultTest.class);
|
||||
File file = new File(new URI(url.toString()));
|
||||
return file.length();
|
||||
return contentLength;
|
||||
}
|
||||
|
||||
public String getStreamForImageAsString() {
|
||||
|
||||
@@ -31,6 +31,8 @@ import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.apache.struts.mock.MockPageContext;
|
||||
import org.apache.struts2.ServletActionContext;
|
||||
|
||||
|
||||
/**
|
||||
@@ -100,6 +102,35 @@ public class InvocationSessionStoreTest extends StrutsInternalTestCase {
|
||||
assertNull(savedInvocation);//Currently we don't support invocation restore from serialized session
|
||||
}
|
||||
|
||||
public void testStoreAndLoadPreservesPageContext() {
|
||||
ActionContext actionContext = ActionContext.getContext();
|
||||
|
||||
// Create mock PageContext to put with the saved context (simulating a PageContext previously
|
||||
// used and closed after generating JSP output).
|
||||
MockPageContext mockSavedPageContext = new MockPageContext();
|
||||
actionContext.put(ServletActionContext.PAGE_CONTEXT, mockSavedPageContext);
|
||||
assertEquals(mockSavedPageContext, ActionContext.getContext().get(ServletActionContext.PAGE_CONTEXT));
|
||||
|
||||
InvocationSessionStore.storeInvocation(INVOCATION_KEY, TOKEN_VALUE, invocation);
|
||||
|
||||
ActionContext actionContext2 = new ActionContext(new HashMap<String, Object>());
|
||||
actionContext2.setSession(session);
|
||||
ActionContext.setContext(actionContext2);
|
||||
assertEquals(actionContext2, ActionContext.getContext());
|
||||
|
||||
// Create mock PageContext to put with the current context (simulating a PageContext
|
||||
// associated with the current (active) process flow). In real-world processing it
|
||||
// will usually be null, but if non-null it should be preserved/restored upon load of the
|
||||
// saved context.
|
||||
MockPageContext mockPreviousPageContext = new MockPageContext();
|
||||
actionContext2.put(ServletActionContext.PAGE_CONTEXT, mockPreviousPageContext);
|
||||
assertEquals(mockPreviousPageContext, ActionContext.getContext().get(ServletActionContext.PAGE_CONTEXT));
|
||||
|
||||
InvocationSessionStore.loadInvocation(INVOCATION_KEY, TOKEN_VALUE);
|
||||
assertEquals(actionContext, ActionContext.getContext());
|
||||
assertEquals(mockPreviousPageContext, ActionContext.getContext().get(ServletActionContext.PAGE_CONTEXT));
|
||||
}
|
||||
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
stack = ActionContext.getContext().getValueStack();
|
||||
|
||||
@@ -121,6 +121,20 @@ public class SetTagTest extends AbstractUITagTest {
|
||||
assertEquals(beginEndSpaceString, context.get("foo"));
|
||||
}
|
||||
|
||||
public void testEmptyBody() throws JspException {
|
||||
StrutsMockBodyContent mockBodyContent;
|
||||
String variableName = "foo";
|
||||
tag.setName(variableName);
|
||||
tag.setValue(null);
|
||||
mockBodyContent = new StrutsMockBodyContent(new MockJspWriter());
|
||||
String emptyBody = "";
|
||||
mockBodyContent.setString(emptyBody);
|
||||
tag.setBodyContent(mockBodyContent);
|
||||
tag.doStartTag();
|
||||
tag.doEndTag();
|
||||
assertEquals(emptyBody, context.get(variableName));
|
||||
}
|
||||
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
|
||||
@@ -196,6 +196,21 @@ public class RadioTest extends AbstractUITagTest {
|
||||
verify(RadioTag.class.getResource("Radio-7.txt"));
|
||||
}
|
||||
|
||||
public void testNotExistingListValueKey() throws Exception {
|
||||
RadioTag tag = new RadioTag();
|
||||
tag.setName("myname");
|
||||
tag.setLabel("mylabel");
|
||||
tag.setList("#{'a':'aaa', 'b':'bbb', 'c':'ccc'}");
|
||||
tag.setListValueKey("notExistingProperty");
|
||||
|
||||
tag.setPageContext(pageContext);
|
||||
|
||||
tag.doStartTag();
|
||||
tag.doEndTag();
|
||||
|
||||
verify(SelectTag.class.getResource("Radio-8.txt"));
|
||||
}
|
||||
|
||||
private void prepareTagGeneric(RadioTag tag) {
|
||||
TestAction testAction = (TestAction) action;
|
||||
testAction.setFoo("bar");
|
||||
|
||||
@@ -637,6 +637,21 @@ public class SelectTest extends AbstractUITagTest {
|
||||
verify(SelectTag.class.getResource("Select-13.txt"));
|
||||
}
|
||||
|
||||
public void testNotExistingListValueKey() throws Exception {
|
||||
SelectTag tag = new SelectTag();
|
||||
tag.setName("foo");
|
||||
tag.setLabel("mylabel");
|
||||
tag.setList("#{'a':'aaa', 'b':'bbb', 'c':'ccc'}");
|
||||
tag.setListValueKey("notExistingProperty");
|
||||
|
||||
tag.setPageContext(pageContext);
|
||||
|
||||
tag.doStartTag();
|
||||
tag.doEndTag();
|
||||
|
||||
verify(SelectTag.class.getResource("Select-16.txt"));
|
||||
}
|
||||
|
||||
public class IdName {
|
||||
private String name;
|
||||
private Integer id;
|
||||
|
||||
-88
@@ -1,88 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
-->
|
||||
<!DOCTYPE xwork PUBLIC
|
||||
"-//Apache Struts//XWork 2.5//EN"
|
||||
"http://struts.apache.org/dtds/xwork-2.5.dtd"
|
||||
>
|
||||
|
||||
<xwork>
|
||||
|
||||
<constant name="struts.devMode" value="false" />
|
||||
<constant name="devMode" value="false" />
|
||||
<constant name="struts.i18n.reload" value="false" />
|
||||
<constant name="logMissingProperties" value="false" />
|
||||
<constant name="enableOGNLExpressionCache" value="true" />
|
||||
<constant name="enableOGNLEvalExpression" value="false" />
|
||||
<constant name="reloadXmlConfiguration" value="false" />
|
||||
<constant name="struts.ognl.allowStaticMethodAccess" value="false" />
|
||||
<constant name="struts.enable.DynamicMethodInvocation" value="false" />
|
||||
<constant name="struts.dispatcher.errorHandler" value="struts" />
|
||||
|
||||
<bean class="com.opensymphony.xwork2.ObjectFactory" name="default" />
|
||||
<bean class="org.apache.struts2.views.freemarker.FreemarkerThemeTemplateLoader" />
|
||||
<bean class="org.apache.struts2.views.freemarker.FreemarkerManager" name="default" />
|
||||
<bean class="org.apache.struts2.views.velocity.VelocityManager" name="default" optional="true" />
|
||||
<bean class="org.apache.struts2.components.template.TemplateEngineManager" />
|
||||
<bean class="com.opensymphony.xwork2.conversion.impl.XWorkBasicConverter" />
|
||||
<bean class="com.opensymphony.xwork2.ognl.OgnlUtil" />
|
||||
<bean class="org.apache.struts2.views.jsp.ui.OgnlTool" />
|
||||
|
||||
<bean type="com.opensymphony.xwork2.ActionProxyFactory" name="default" class="org.apache.struts2.factory.StrutsActionProxyFactory"/>
|
||||
<bean type="com.opensymphony.xwork2.FileManager" name="system" class="com.opensymphony.xwork2.util.fs.DefaultFileManager" scope="singleton"/>
|
||||
<bean type="com.opensymphony.xwork2.FileManagerFactory" name="default" class="com.opensymphony.xwork2.util.fs.DefaultFileManagerFactory" scope="singleton"/>
|
||||
<bean type="com.opensymphony.xwork2.LocalizedTextProvider" name="global-only" class="com.opensymphony.xwork2.util.GlobalLocalizedTextProvider" scope="singleton" />
|
||||
<bean type="com.opensymphony.xwork2.LocalizedTextProvider" name="default" class="com.opensymphony.xwork2.util.StrutsLocalizedTextProvider" scope="singleton" />
|
||||
<bean type="com.opensymphony.xwork2.TextProvider" name="system" class="com.opensymphony.xwork2.DefaultTextProvider" scope="singleton" />
|
||||
<bean type="com.opensymphony.xwork2.conversion.ConversionAnnotationProcessor" name="default" class="com.opensymphony.xwork2.conversion.impl.DefaultConversionAnnotationProcessor" />
|
||||
<bean type="com.opensymphony.xwork2.conversion.ConversionFileProcessor" name="default" class="com.opensymphony.xwork2.conversion.impl.DefaultConversionFileProcessor" />
|
||||
<bean type="com.opensymphony.xwork2.conversion.ObjectTypeDeterminer" name="default" class="com.opensymphony.xwork2.conversion.impl.DefaultObjectTypeDeterminer"/>
|
||||
<bean type="com.opensymphony.xwork2.conversion.TypeConverterCreator" name="default" class="com.opensymphony.xwork2.conversion.impl.DefaultTypeConverterCreator" />
|
||||
<bean type="com.opensymphony.xwork2.conversion.TypeConverterHolder" name="default" class="com.opensymphony.xwork2.conversion.impl.DefaultTypeConverterHolder" />
|
||||
<bean type="com.opensymphony.xwork2.conversion.impl.XWorkConverter" name="default" class="com.opensymphony.xwork2.conversion.impl.XWorkConverter" />
|
||||
|
||||
<bean type="com.opensymphony.xwork2.factory.ActionFactory" name="default" class="com.opensymphony.xwork2.factory.DefaultActionFactory" />
|
||||
<bean type="com.opensymphony.xwork2.factory.ConverterFactory" name="default" class="com.opensymphony.xwork2.factory.DefaultConverterFactory" />
|
||||
<bean type="com.opensymphony.xwork2.factory.ResultFactory" name="default" class="org.apache.struts2.factory.StrutsResultFactory" />
|
||||
<bean type="com.opensymphony.xwork2.factory.UnknownHandlerFactory" name="default" class="com.opensymphony.xwork2.factory.DefaultUnknownHandlerFactory" />
|
||||
<bean type="com.opensymphony.xwork2.factory.ValidatorFactory" name="default" class="com.opensymphony.xwork2.factory.DefaultValidatorFactory" />
|
||||
<bean type="com.opensymphony.xwork2.factory.InterceptorFactory" name="default" class="com.opensymphony.xwork2.factory.DefaultInterceptorFactory" />
|
||||
<bean type="com.opensymphony.xwork2.util.ValueStackFactory" name="default" class="com.opensymphony.xwork2.ognl.OgnlValueStackFactory" />
|
||||
<bean type="com.opensymphony.xwork2.util.reflection.ReflectionProvider" name="default" class="com.opensymphony.xwork2.ognl.OgnlReflectionProvider" />
|
||||
|
||||
<bean type="ognl.MethodAccessor" name="java.lang.Object" class="com.opensymphony.xwork2.ognl.accessor.XWorkMethodAccessor" />
|
||||
<bean type="ognl.MethodAccessor" name="com.opensymphony.xwork2.util.CompoundRoot" class="com.opensymphony.xwork2.ognl.accessor.CompoundRootAccessor" />
|
||||
<bean type="ognl.PropertyAccessor" name="com.opensymphony.xwork2.util.CompoundRoot" class="com.opensymphony.xwork2.ognl.accessor.CompoundRootAccessor" />
|
||||
<bean type="ognl.PropertyAccessor" name="org.apache.struts2.dispatcher.HttpParameters" class="com.opensymphony.xwork2.ognl.accessor.HttpParametersPropertyAccessor" />
|
||||
<bean type="ognl.PropertyAccessor" name="com.opensymphony.xwork2.ognl.ObjectProxy" class="com.opensymphony.xwork2.ognl.accessor.ObjectProxyPropertyAccessor" />
|
||||
<bean type="ognl.PropertyAccessor" name="org.apache.struts2.dispatcher.Parameter" class="com.opensymphony.xwork2.ognl.accessor.ParameterPropertyAccessor" />
|
||||
<bean type="ognl.PropertyAccessor" name="java.util.ArrayList" class="com.opensymphony.xwork2.ognl.accessor.XWorkListPropertyAccessor" />
|
||||
<bean type="ognl.PropertyAccessor" name="java.util.Collection" class="com.opensymphony.xwork2.ognl.accessor.XWorkCollectionPropertyAccessor" />
|
||||
<bean type="ognl.PropertyAccessor" name="java.util.Enumeration" class="com.opensymphony.xwork2.ognl.accessor.XWorkEnumerationAccessor" />
|
||||
<bean type="ognl.PropertyAccessor" name="java.util.HashMap" class="com.opensymphony.xwork2.ognl.accessor.XWorkMapPropertyAccessor" />
|
||||
<bean type="ognl.PropertyAccessor" name="java.util.HashSet" class="com.opensymphony.xwork2.ognl.accessor.XWorkCollectionPropertyAccessor" />
|
||||
<bean type="ognl.PropertyAccessor" name="java.util.Iterator" class="com.opensymphony.xwork2.ognl.accessor.XWorkIteratorPropertyAccessor" />
|
||||
<bean type="ognl.PropertyAccessor" name="java.util.List" class="com.opensymphony.xwork2.ognl.accessor.XWorkListPropertyAccessor" />
|
||||
<bean type="ognl.PropertyAccessor" name="java.util.Map" class="com.opensymphony.xwork2.ognl.accessor.XWorkMapPropertyAccessor" />
|
||||
<bean type="ognl.PropertyAccessor" name="java.lang.Object" class="com.opensymphony.xwork2.ognl.accessor.ObjectAccessor" />
|
||||
<bean type="ognl.PropertyAccessor" name="java.util.Set" class="com.opensymphony.xwork2.ognl.accessor.XWorkCollectionPropertyAccessor" />
|
||||
|
||||
</xwork>
|
||||
-88
@@ -1,88 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
-->
|
||||
<!DOCTYPE xwork PUBLIC
|
||||
"-//Apache Struts//XWork 2.5//EN"
|
||||
"http://struts.apache.org/dtds/xwork-2.5.dtd"
|
||||
>
|
||||
|
||||
<xwork>
|
||||
|
||||
<constant name="struts.devMode" value="true" />
|
||||
<constant name="devMode" value="true" />
|
||||
<constant name="struts.i18n.reload" value="false" />
|
||||
<constant name="logMissingProperties" value="false" />
|
||||
<constant name="enableOGNLExpressionCache" value="true" />
|
||||
<constant name="enableOGNLEvalExpression" value="false" />
|
||||
<constant name="reloadXmlConfiguration" value="false" />
|
||||
<constant name="struts.ognl.allowStaticMethodAccess" value="true" />
|
||||
<constant name="struts.enable.DynamicMethodInvocation" value="false" />
|
||||
<constant name="struts.dispatcher.errorHandler" value="struts" />
|
||||
|
||||
<bean class="com.opensymphony.xwork2.ObjectFactory" name="default" />
|
||||
<bean class="org.apache.struts2.views.freemarker.FreemarkerThemeTemplateLoader" />
|
||||
<bean class="org.apache.struts2.views.freemarker.FreemarkerManager" name="default" />
|
||||
<bean class="org.apache.struts2.views.velocity.VelocityManager" name="default" optional="true" />
|
||||
<bean class="org.apache.struts2.components.template.TemplateEngineManager" />
|
||||
<bean class="com.opensymphony.xwork2.conversion.impl.XWorkBasicConverter" />
|
||||
<bean class="com.opensymphony.xwork2.ognl.OgnlUtil" />
|
||||
<bean class="org.apache.struts2.views.jsp.ui.OgnlTool" />
|
||||
|
||||
<bean type="com.opensymphony.xwork2.ActionProxyFactory" name="default" class="org.apache.struts2.factory.StrutsActionProxyFactory"/>
|
||||
<bean type="com.opensymphony.xwork2.FileManager" name="system" class="com.opensymphony.xwork2.util.fs.DefaultFileManager" scope="singleton"/>
|
||||
<bean type="com.opensymphony.xwork2.FileManagerFactory" name="default" class="com.opensymphony.xwork2.util.fs.DefaultFileManagerFactory" scope="singleton"/>
|
||||
<bean type="com.opensymphony.xwork2.LocalizedTextProvider" name="global-only" class="com.opensymphony.xwork2.util.GlobalLocalizedTextProvider" scope="singleton" />
|
||||
<bean type="com.opensymphony.xwork2.LocalizedTextProvider" name="default" class="com.opensymphony.xwork2.util.StrutsLocalizedTextProvider" scope="singleton" />
|
||||
<bean type="com.opensymphony.xwork2.TextProvider" name="system" class="com.opensymphony.xwork2.DefaultTextProvider" scope="singleton" />
|
||||
<bean type="com.opensymphony.xwork2.conversion.ConversionAnnotationProcessor" name="default" class="com.opensymphony.xwork2.conversion.impl.DefaultConversionAnnotationProcessor" />
|
||||
<bean type="com.opensymphony.xwork2.conversion.ConversionFileProcessor" name="default" class="com.opensymphony.xwork2.conversion.impl.DefaultConversionFileProcessor" />
|
||||
<bean type="com.opensymphony.xwork2.conversion.ObjectTypeDeterminer" name="default" class="com.opensymphony.xwork2.conversion.impl.DefaultObjectTypeDeterminer"/>
|
||||
<bean type="com.opensymphony.xwork2.conversion.TypeConverterCreator" name="default" class="com.opensymphony.xwork2.conversion.impl.DefaultTypeConverterCreator" />
|
||||
<bean type="com.opensymphony.xwork2.conversion.TypeConverterHolder" name="default" class="com.opensymphony.xwork2.conversion.impl.DefaultTypeConverterHolder" />
|
||||
<bean type="com.opensymphony.xwork2.conversion.impl.XWorkConverter" name="default" class="com.opensymphony.xwork2.conversion.impl.XWorkConverter" />
|
||||
|
||||
<bean type="com.opensymphony.xwork2.factory.ActionFactory" name="default" class="com.opensymphony.xwork2.factory.DefaultActionFactory" />
|
||||
<bean type="com.opensymphony.xwork2.factory.ConverterFactory" name="default" class="com.opensymphony.xwork2.factory.DefaultConverterFactory" />
|
||||
<bean type="com.opensymphony.xwork2.factory.ResultFactory" name="default" class="org.apache.struts2.factory.StrutsResultFactory" />
|
||||
<bean type="com.opensymphony.xwork2.factory.UnknownHandlerFactory" name="default" class="com.opensymphony.xwork2.factory.DefaultUnknownHandlerFactory" />
|
||||
<bean type="com.opensymphony.xwork2.factory.ValidatorFactory" name="default" class="com.opensymphony.xwork2.factory.DefaultValidatorFactory" />
|
||||
<bean type="com.opensymphony.xwork2.factory.InterceptorFactory" name="default" class="com.opensymphony.xwork2.factory.DefaultInterceptorFactory" />
|
||||
<bean type="com.opensymphony.xwork2.util.ValueStackFactory" name="default" class="com.opensymphony.xwork2.ognl.OgnlValueStackFactory" />
|
||||
<bean type="com.opensymphony.xwork2.util.reflection.ReflectionProvider" name="default" class="com.opensymphony.xwork2.ognl.OgnlReflectionProvider" />
|
||||
|
||||
<bean type="ognl.MethodAccessor" name="java.lang.Object" class="com.opensymphony.xwork2.ognl.accessor.XWorkMethodAccessor" />
|
||||
<bean type="ognl.MethodAccessor" name="com.opensymphony.xwork2.util.CompoundRoot" class="com.opensymphony.xwork2.ognl.accessor.CompoundRootAccessor" />
|
||||
<bean type="ognl.PropertyAccessor" name="com.opensymphony.xwork2.util.CompoundRoot" class="com.opensymphony.xwork2.ognl.accessor.CompoundRootAccessor" />
|
||||
<bean type="ognl.PropertyAccessor" name="org.apache.struts2.dispatcher.HttpParameters" class="com.opensymphony.xwork2.ognl.accessor.HttpParametersPropertyAccessor" />
|
||||
<bean type="ognl.PropertyAccessor" name="com.opensymphony.xwork2.ognl.ObjectProxy" class="com.opensymphony.xwork2.ognl.accessor.ObjectProxyPropertyAccessor" />
|
||||
<bean type="ognl.PropertyAccessor" name="org.apache.struts2.dispatcher.Parameter" class="com.opensymphony.xwork2.ognl.accessor.ParameterPropertyAccessor" />
|
||||
<bean type="ognl.PropertyAccessor" name="java.util.ArrayList" class="com.opensymphony.xwork2.ognl.accessor.XWorkListPropertyAccessor" />
|
||||
<bean type="ognl.PropertyAccessor" name="java.util.Collection" class="com.opensymphony.xwork2.ognl.accessor.XWorkCollectionPropertyAccessor" />
|
||||
<bean type="ognl.PropertyAccessor" name="java.util.Enumeration" class="com.opensymphony.xwork2.ognl.accessor.XWorkEnumerationAccessor" />
|
||||
<bean type="ognl.PropertyAccessor" name="java.util.HashMap" class="com.opensymphony.xwork2.ognl.accessor.XWorkMapPropertyAccessor" />
|
||||
<bean type="ognl.PropertyAccessor" name="java.util.HashSet" class="com.opensymphony.xwork2.ognl.accessor.XWorkCollectionPropertyAccessor" />
|
||||
<bean type="ognl.PropertyAccessor" name="java.util.Iterator" class="com.opensymphony.xwork2.ognl.accessor.XWorkIteratorPropertyAccessor" />
|
||||
<bean type="ognl.PropertyAccessor" name="java.util.List" class="com.opensymphony.xwork2.ognl.accessor.XWorkListPropertyAccessor" />
|
||||
<bean type="ognl.PropertyAccessor" name="java.util.Map" class="com.opensymphony.xwork2.ognl.accessor.XWorkMapPropertyAccessor" />
|
||||
<bean type="ognl.PropertyAccessor" name="java.lang.Object" class="com.opensymphony.xwork2.ognl.accessor.ObjectAccessor" />
|
||||
<bean type="ognl.PropertyAccessor" name="java.util.Set" class="com.opensymphony.xwork2.ognl.accessor.XWorkCollectionPropertyAccessor" />
|
||||
|
||||
</xwork>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user