mirror of
https://github.com/apache/struts.git
synced 2026-08-31 19:35:40 +00:00
Compare commits
158 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 631ce98d17 | |||
| 086b637355 | |||
| ae56301979 | |||
| 73da12e723 | |||
| 583da3d5df | |||
| 9e8627ca66 | |||
| c349dfa5ab | |||
| 4d888a5ab6 | |||
| 1ed29d508f | |||
| 3523064939 | |||
| 54dff362f5 | |||
| b12864dd38 | |||
| de90290354 | |||
| ba0563183b | |||
| b348fc552c | |||
| 81371e09c5 | |||
| 431e50e922 | |||
| 156db8e5df | |||
| 6f5ddca471 | |||
| cc0d52f216 | |||
| 23e0181328 | |||
| 030ffa3354 | |||
| 8e67b9144a | |||
| 77017cd677 | |||
| 7d8c3598ef | |||
| 83bb64f4c2 | |||
| ae2840f183 | |||
| e8b48f8bb4 | |||
| 3243448084 | |||
| 5c61c9a575 | |||
| e40dcaa3f4 | |||
| ab6acd2382 | |||
| bf0a01d390 | |||
| 00c47b7ce6 | |||
| c84810ff82 | |||
| 2ea59b31b4 | |||
| fae1e0e799 | |||
| e4ce3ce839 | |||
| 730441b7a7 | |||
| bb22c585b5 | |||
| eccc31ebce | |||
| 1c92a8b2c9 | |||
| b28b78c062 | |||
| 8d172b8e9b | |||
| 88b885339a | |||
| 3f526d630a | |||
| 34025eafec | |||
| 54e8bf1f75 | |||
| 9f2cae2f1e | |||
| 9bcd1bc9f6 | |||
| f052398cd2 | |||
| 237432512d | |||
| ca25476120 | |||
| 731fde544c | |||
| 76eb8f38a3 | |||
| deefeffd11 | |||
| 98d2692e43 | |||
| 46517afb14 | |||
| 5190b53673 | |||
| d36f31b3e1 | |||
| cbae13a6a8 | |||
| 865f1f2784 | |||
| 5ce807aaea | |||
| 17dab897d9 | |||
| 45fa8d0320 | |||
| 6fee2eb1a4 | |||
| 0ac8932aa3 | |||
| 6b497ef8f7 | |||
| 519c767114 | |||
| 8a59ed02c9 | |||
| f48c30eaa7 | |||
| 94c4462658 | |||
| 013077abcb | |||
| fbb91d1bb9 | |||
| a67ac45258 | |||
| 8ca0f2fc46 | |||
| 37842193ab | |||
| 609cb0bb97 | |||
| b155532314 | |||
| 9de0cf4c8b | |||
| 5cf4aacd50 | |||
| d82289a1be | |||
| 6ac2a58da7 | |||
| c714d2674a | |||
| ffa6d5cb5b | |||
| 9860ef829a | |||
| d028886cd1 | |||
| 1b4868df3e | |||
| 5cfe05075b | |||
| 5efc595011 | |||
| 9c7b833668 | |||
| 87979d2821 | |||
| 8a9e01727c | |||
| 5b2b244a66 | |||
| 473403ca17 | |||
| 1bbcd4bf70 | |||
| 469b25f8e1 | |||
| 27260685e1 | |||
| bd5e64e598 | |||
| 9a8f36dee2 | |||
| a6345e79c9 | |||
| 85373951b7 | |||
| c44566bd24 | |||
| 2a6acabe5d | |||
| 377c6cdda0 | |||
| 6438396ef8 | |||
| 69bc2947c1 | |||
| 15c79e1398 | |||
| dd36946573 | |||
| 18bdc141ea | |||
| 2ec3890dc7 | |||
| 10a612c9da | |||
| 1d160f4d5f | |||
| a398793177 | |||
| ff4cdd9670 | |||
| 7c4bb7e7bd | |||
| 5421930b49 | |||
| c6750c110c | |||
| 997966829a | |||
| bd1a121b82 | |||
| 6177cf3387 | |||
| b448d79959 | |||
| 9a0a0a16e9 | |||
| bf46145baf | |||
| 1158b0adda | |||
| 5ed7b36567 | |||
| 2d70ea2f7d | |||
| 9136521d27 | |||
| 5be1590a60 | |||
| f721d5490c | |||
| db318c2116 | |||
| f295852482 | |||
| 8bbfb461c4 | |||
| 38e8f86a75 | |||
| 696e566279 | |||
| 27aa7e1e27 | |||
| f19e4be947 | |||
| 913944b8ee | |||
| ddeb7f0f7c | |||
| 5d30eb1ad2 | |||
| efcea81b67 | |||
| 9862580bbe | |||
| 1dc545e34c | |||
| 90dbf54bf4 | |||
| b4a18d07e4 | |||
| 5d26c6c1e5 | |||
| 22cb8c7e7d | |||
| 0958077762 | |||
| 7848534e2f | |||
| c708876339 | |||
| f65e38bb81 | |||
| 412684bd22 | |||
| 3e2d5da12c | |||
| d15424ee5f | |||
| 30d30e07de | |||
| 755ad3fadb | |||
| f5d455da12 | |||
| 4f817dc7a9 |
+1
-1
@@ -26,7 +26,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.struts</groupId>
|
<groupId>org.apache.struts</groupId>
|
||||||
<artifactId>struts2-apps</artifactId>
|
<artifactId>struts2-apps</artifactId>
|
||||||
<version>2.3.24.1</version>
|
<version>2.3.33</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>struts2-blank</artifactId>
|
<artifactId>struts2-blank</artifactId>
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.struts</groupId>
|
<groupId>org.apache.struts</groupId>
|
||||||
<artifactId>struts2-apps</artifactId>
|
<artifactId>struts2-apps</artifactId>
|
||||||
<version>2.3.24.1</version>
|
<version>2.3.33</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>struts2-jboss-blank</artifactId>
|
<artifactId>struts2-jboss-blank</artifactId>
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.struts</groupId>
|
<groupId>org.apache.struts</groupId>
|
||||||
<artifactId>struts2-apps</artifactId>
|
<artifactId>struts2-apps</artifactId>
|
||||||
<version>2.3.24.1</version>
|
<version>2.3.33</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>struts2-mailreader</artifactId>
|
<artifactId>struts2-mailreader</artifactId>
|
||||||
|
|||||||
+1
-1
@@ -26,7 +26,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.struts</groupId>
|
<groupId>org.apache.struts</groupId>
|
||||||
<artifactId>struts2-parent</artifactId>
|
<artifactId>struts2-parent</artifactId>
|
||||||
<version>2.3.24.1</version>
|
<version>2.3.33</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>struts2-apps</artifactId>
|
<artifactId>struts2-apps</artifactId>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.struts</groupId>
|
<groupId>org.apache.struts</groupId>
|
||||||
<artifactId>struts2-apps</artifactId>
|
<artifactId>struts2-apps</artifactId>
|
||||||
<version>2.3.24.1</version>
|
<version>2.3.33</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>struts2-portlet</artifactId>
|
<artifactId>struts2-portlet</artifactId>
|
||||||
|
|||||||
@@ -5,11 +5,11 @@ Here you'll find examples of what is possible with the Struts Portlet integratio
|
|||||||
<ul>
|
<ul>
|
||||||
<li><a href="<s:url action="formExample"/>">A simple form</a></li>
|
<li><a href="<s:url action="formExample"/>">A simple form</a></li>
|
||||||
<li><a href="<s:url action="formExamplePrg" method="input"/>">Form example with proper PRG</a></li>
|
<li><a href="<s:url action="formExamplePrg" method="input"/>">Form example with proper PRG</a></li>
|
||||||
<li><a href="<s:url action="formExampleModelDriven" method="input"/>">Model driven example</li>
|
<li><a href="<s:url action="formExampleModelDriven" method="input"/>">Model driven example</a>/li>
|
||||||
<li><a href="<s:url action="validationExample"/>">Validation</a></li>
|
<li><a href="<s:url action="validationExample"/>">Validation</a></li>
|
||||||
<li><a href="<s:url action="tokenExample"/>">Token</a></li>
|
<li><a href="<s:url action="tokenExample"/>">Token</a></li>
|
||||||
<li><a href="<s:url action="springExample"/>">Spring integration</a></li>
|
<li><a href="<s:url action="springExample"/>">Spring integration</a></li>
|
||||||
<li><a href="<s:url action="fileUpload" method="input"/>">File upload</li>
|
<li><a href="<s:url action="fileUpload" method="input"/>">File upload</a></li>
|
||||||
<li><a href="<s:url action="freeMarkerExample"/>">FreeMarker</a></li>
|
<li><a href="<s:url action="freeMarkerExample"/>">FreeMarker</a></li>
|
||||||
<li><a href="<s:url action="velocityHelloWorld"/>">Velocity</a></li>
|
<li><a href="<s:url action="velocityHelloWorld"/>">Velocity</a></li>
|
||||||
<li><a href="<s:url action="index" namespace="/tiles"/>">Form Example Validation with Tiles</a></li>
|
<li><a href="<s:url action="index" namespace="/tiles"/>">Form Example Validation with Tiles</a></li>
|
||||||
|
|||||||
@@ -26,12 +26,12 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.struts</groupId>
|
<groupId>org.apache.struts</groupId>
|
||||||
<artifactId>struts2-apps</artifactId>
|
<artifactId>struts2-apps</artifactId>
|
||||||
<version>2.3.24.1</version>
|
<version>2.3.33</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>struts2-rest-showcase</artifactId>
|
<artifactId>struts2-rest-showcase</artifactId>
|
||||||
<packaging>war</packaging>
|
<packaging>war</packaging>
|
||||||
<version>2.3.24.1</version>
|
<version>2.3.33</version>
|
||||||
<name>Struts 2 Rest Showcase Webapp</name>
|
<name>Struts 2 Rest Showcase Webapp</name>
|
||||||
<description>Struts 2 Rest Showcase Example</description>
|
<description>Struts 2 Rest Showcase Example</description>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package org.demo.rest.example;
|
||||||
|
|
||||||
|
import org.apache.struts2.convention.annotation.Action;
|
||||||
|
import org.apache.struts2.convention.annotation.Result;
|
||||||
|
import org.apache.struts2.convention.annotation.Results;
|
||||||
|
|
||||||
|
@Results({
|
||||||
|
@Result(name="success", type="redirectAction", params = {"actionName" , "orders"})
|
||||||
|
})
|
||||||
|
public class IndexController {
|
||||||
|
|
||||||
|
@Action("/")
|
||||||
|
public String index() {
|
||||||
|
return "success";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
|
|
||||||
<% response.sendRedirect("orders"); %>
|
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.struts</groupId>
|
<groupId>org.apache.struts</groupId>
|
||||||
<artifactId>struts2-apps</artifactId>
|
<artifactId>struts2-apps</artifactId>
|
||||||
<version>2.3.24.1</version>
|
<version>2.3.33</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>struts2-showcase</artifactId>
|
<artifactId>struts2-showcase</artifactId>
|
||||||
|
|||||||
+2
-1
@@ -36,7 +36,8 @@ public class SaveGangsterAction extends Action {
|
|||||||
// Some code to save the gangster to the db as necessary
|
// Some code to save the gangster to the db as necessary
|
||||||
GangsterForm gform = (GangsterForm) form;
|
GangsterForm gform = (GangsterForm) form;
|
||||||
ActionMessages messages = new ActionMessages();
|
ActionMessages messages = new ActionMessages();
|
||||||
messages.add("msg", new ActionMessage("Gangster " + gform.getName() + " added successfully"));
|
System.out.println(gform.getName());
|
||||||
|
messages.add("msg", new ActionMessage("struts1.gangsterAdded", gform.getName()));
|
||||||
addMessages(request, messages);
|
addMessages(request, messages);
|
||||||
|
|
||||||
return mapping.findForward("success");
|
return mapping.findForward("success");
|
||||||
|
|||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.struts2.showcase.tiles;
|
||||||
|
|
||||||
|
import org.apache.struts2.convention.annotation.Namespace;
|
||||||
|
import org.apache.struts2.convention.annotation.ParentPackage;
|
||||||
|
import org.apache.struts2.convention.annotation.Result;
|
||||||
|
import org.apache.struts2.tiles.annotation.TilesDefinition;
|
||||||
|
import org.apache.struts2.tiles.annotation.TilesPutAttribute;
|
||||||
|
|
||||||
|
import com.opensymphony.xwork2.ActionSupport;
|
||||||
|
|
||||||
|
@Namespace("/tiles")
|
||||||
|
@ParentPackage("tiles")
|
||||||
|
@Result(name = "success", type="tiles")
|
||||||
|
@TilesDefinition(extend = "showcase.annotations", putAttributes = {
|
||||||
|
@TilesPutAttribute(name = "header", value = "/WEB-INF/tiles/header.jsp"),
|
||||||
|
@TilesPutAttribute(name = "body", value = "/WEB-INF/tiles/body.ftl")
|
||||||
|
})
|
||||||
|
public class TilesAnnotationsAction extends ActionSupport {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 2900509995064928866L;
|
||||||
|
|
||||||
|
}
|
||||||
+152
@@ -0,0 +1,152 @@
|
|||||||
|
package org.apache.struts2.showcase.validation;
|
||||||
|
|
||||||
|
import java.sql.Date;
|
||||||
|
|
||||||
|
import com.opensymphony.xwork2.validator.annotations.DateRangeFieldValidator;
|
||||||
|
import com.opensymphony.xwork2.validator.annotations.EmailValidator;
|
||||||
|
import com.opensymphony.xwork2.validator.annotations.FieldExpressionValidator;
|
||||||
|
import com.opensymphony.xwork2.validator.annotations.IntRangeFieldValidator;
|
||||||
|
import com.opensymphony.xwork2.validator.annotations.RegexFieldValidator;
|
||||||
|
import com.opensymphony.xwork2.validator.annotations.RequiredFieldValidator;
|
||||||
|
import com.opensymphony.xwork2.validator.annotations.RequiredStringValidator;
|
||||||
|
import com.opensymphony.xwork2.validator.annotations.StringLengthFieldValidator;
|
||||||
|
import com.opensymphony.xwork2.validator.annotations.UrlValidator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <!-- START SNIPPET: ajaxFormSubmit -->
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Example Action that shows how forms can be validated and submitted via AJAX
|
||||||
|
* only. Form-submit-and-page-reload functionality of browsers is not used for
|
||||||
|
* this action.
|
||||||
|
* <p>Some things to note:
|
||||||
|
* <ul>
|
||||||
|
* <li>Depends on <code>json-plugin</code>.</li>
|
||||||
|
* <li>Requires <code>jsonValidationInterceptor</code> to be on stack.</li>
|
||||||
|
* <li>Uses result type <code>jsonActionRedirect</code>.</li>
|
||||||
|
* <li>Uses http parameters <code>struts.enableJSONValidation=true</code> and <code>struts.validateOnly=false</code>.</li>
|
||||||
|
* <li>Uses a customized theme to make sure html elements required as error containers are always present and easily selectable in JS.</li>
|
||||||
|
* <li>Uses some custom JS code depending on jQuery to issue AJAX request and to render errors in html.</li>
|
||||||
|
* <li>Shows visual feedback while waiting for AJAX response.</li>
|
||||||
|
* </ul>
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class AjaxFormSubmitAction extends AbstractValidationActionSupport {
|
||||||
|
|
||||||
|
private String requiredValidatorField = null;
|
||||||
|
private String requiredStringValidatorField = null;
|
||||||
|
private Integer integerValidatorField = null;
|
||||||
|
private Date dateValidatorField = null;
|
||||||
|
private String emailValidatorField = null;
|
||||||
|
private String urlValidatorField = null;
|
||||||
|
private String stringLengthValidatorField = null;
|
||||||
|
private String regexValidatorField = null;
|
||||||
|
private String fieldExpressionValidatorField = null;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validate() {
|
||||||
|
if (hasFieldErrors()) {
|
||||||
|
addActionError("Errors present!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getDateValidatorField() {
|
||||||
|
return dateValidatorField;
|
||||||
|
}
|
||||||
|
|
||||||
|
@DateRangeFieldValidator(
|
||||||
|
min="01/01/1990",
|
||||||
|
max="01/01/2000",
|
||||||
|
message="must be a min 01-01-1990 max 01-01-2000 if supplied")
|
||||||
|
public void setDateValidatorField(Date dateValidatorField) {
|
||||||
|
this.dateValidatorField = dateValidatorField;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEmailValidatorField() {
|
||||||
|
return emailValidatorField;
|
||||||
|
}
|
||||||
|
|
||||||
|
@EmailValidator(message="must be a valid email if supplied")
|
||||||
|
public void setEmailValidatorField(String emailValidatorField) {
|
||||||
|
this.emailValidatorField = emailValidatorField;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getIntegerValidatorField() {
|
||||||
|
return integerValidatorField;
|
||||||
|
}
|
||||||
|
|
||||||
|
@IntRangeFieldValidator(min="1", max="10", message="must be integer min 1 max 10 if supplied")
|
||||||
|
public void setIntegerValidatorField(Integer integerValidatorField) {
|
||||||
|
this.integerValidatorField = integerValidatorField;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRegexValidatorField() {
|
||||||
|
return regexValidatorField;
|
||||||
|
}
|
||||||
|
|
||||||
|
@RegexFieldValidator(
|
||||||
|
regex="[^<>]+",
|
||||||
|
message="regexValidatorField must match a regexp (.*\\.txt) if specified")
|
||||||
|
public void setRegexValidatorField(String regexValidatorField) {
|
||||||
|
this.regexValidatorField = regexValidatorField;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRequiredStringValidatorField() {
|
||||||
|
return requiredStringValidatorField;
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiredStringValidator(trim=true, message="required and must be string")
|
||||||
|
public void setRequiredStringValidatorField(String requiredStringValidatorField) {
|
||||||
|
this.requiredStringValidatorField = requiredStringValidatorField;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRequiredValidatorField() {
|
||||||
|
return requiredValidatorField;
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiredFieldValidator(message="required")
|
||||||
|
public void setRequiredValidatorField(String requiredValidatorField) {
|
||||||
|
this.requiredValidatorField = requiredValidatorField;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStringLengthValidatorField() {
|
||||||
|
return stringLengthValidatorField;
|
||||||
|
}
|
||||||
|
|
||||||
|
@StringLengthFieldValidator(
|
||||||
|
minLength="2",
|
||||||
|
maxLength="4",
|
||||||
|
trim=true,
|
||||||
|
message="must be a String of a specific greater than 1 less than 5 if specified")
|
||||||
|
public void setStringLengthValidatorField(String stringLengthValidatorField) {
|
||||||
|
this.stringLengthValidatorField = stringLengthValidatorField;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFieldExpressionValidatorField() {
|
||||||
|
return fieldExpressionValidatorField;
|
||||||
|
}
|
||||||
|
|
||||||
|
@FieldExpressionValidator(
|
||||||
|
expression = "(fieldExpressionValidatorField == requiredValidatorField)",
|
||||||
|
message = "must be the same as the Required Validator Field if specified")
|
||||||
|
public void setFieldExpressionValidatorField(
|
||||||
|
String fieldExpressionValidatorField) {
|
||||||
|
this.fieldExpressionValidatorField = fieldExpressionValidatorField;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUrlValidatorField() {
|
||||||
|
return urlValidatorField;
|
||||||
|
}
|
||||||
|
|
||||||
|
@UrlValidator(message="must be a valid url if supplied")
|
||||||
|
public void setUrlValidatorField(String urlValidatorField) {
|
||||||
|
this.urlValidatorField = urlValidatorField;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <!-- END SNIPPET: ajaxFormSubmit -->
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
package org.apache.struts2.showcase.validation;
|
||||||
|
|
||||||
|
public class AjaxFormSubmitSuccessAction {
|
||||||
|
public String execute() {
|
||||||
|
return "success";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,3 +7,5 @@ item.create=Create {0}
|
|||||||
item.list={0} List
|
item.list={0} List
|
||||||
|
|
||||||
token.transfer.time=The bank transfer was executed at {0,date,HH:mm:ss MM-dd-yyyy}
|
token.transfer.time=The bank transfer was executed at {0,date,HH:mm:ss MM-dd-yyyy}
|
||||||
|
|
||||||
|
struts1.gangsterAdded=Gangster {0} added successfully
|
||||||
@@ -5,3 +5,5 @@ item.create={0} neu anlegen
|
|||||||
item.list={0}-Liste
|
item.list={0}-Liste
|
||||||
|
|
||||||
token.transfer.time=Die \u00dcberweisung wurde am {0,date,HH:mm:ss MM-dd-yyyy} durchgef\u00fchrt
|
token.transfer.time=Die \u00dcberweisung wurde am {0,date,HH:mm:ss MM-dd-yyyy} durchgef\u00fchrt
|
||||||
|
|
||||||
|
struts1.gangsterAdded=Gangster {0} wurde erfolgreich hinzugefugt
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
struts1.gangsterAdded=Gangster {0} anadido con exito
|
||||||
@@ -20,6 +20,11 @@
|
|||||||
<result type="tiles">showcase.freemarkerLayout</result>
|
<result type="tiles">showcase.freemarkerLayout</result>
|
||||||
</action>
|
</action>
|
||||||
|
|
||||||
|
<action name="tiles-annotations" class="org.apache.struts2.showcase.tiles.TilesAnnotationsAction">
|
||||||
|
<!-- note: this is not necessary when using convention-plugin -->
|
||||||
|
<result type="tiles"></result>
|
||||||
|
</action>
|
||||||
|
|
||||||
<action name="sanity">
|
<action name="sanity">
|
||||||
<result type="redirect">/WEB-INF/tiles/layout.jsp</result>
|
<result type="redirect">/WEB-INF/tiles/layout.jsp</result>
|
||||||
<result type="redirect" name="success">/tiles/layout.jsp</result>
|
<result type="redirect" name="success">/tiles/layout.jsp</result>
|
||||||
|
|||||||
@@ -29,6 +29,19 @@
|
|||||||
<result name="input">quiz-ajax.jsp</result>
|
<result name="input">quiz-ajax.jsp</result>
|
||||||
<result>quiz-success.jsp</result>
|
<result>quiz-success.jsp</result>
|
||||||
</action>
|
</action>
|
||||||
|
|
||||||
|
<!-- =========================================== -->
|
||||||
|
<!-- === ajax form submit === -->
|
||||||
|
<!-- =========================================== -->
|
||||||
|
<action name="ajaxFormSubmit" class="org.apache.struts2.showcase.validation.AjaxFormSubmitAction">
|
||||||
|
<interceptor-ref name="jsonValidationWorkflowStack" />
|
||||||
|
<result name="input">/WEB-INF/validation/ajaxFormSubmit.jsp</result>
|
||||||
|
<result type="jsonActionRedirect">ajaxFormSubmitSuccess</result>
|
||||||
|
</action>
|
||||||
|
<action name="ajaxFormSubmitSuccess" class="org.apache.struts2.showcase.validation.AjaxFormSubmitSuccessAction">
|
||||||
|
<result>/WEB-INF/validation/ajaxFormSubmitSuccess.jsp</result>
|
||||||
|
</action>
|
||||||
|
|
||||||
</package>
|
</package>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
<#--
|
||||||
|
/*
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
-->
|
||||||
|
<#--
|
||||||
|
Make sure element is always present. To be filled later via JS.
|
||||||
|
-->
|
||||||
|
<ul<#rt/>
|
||||||
|
<#if parameters.id??>
|
||||||
|
id="${parameters.id?html}"<#rt/>
|
||||||
|
</#if>
|
||||||
|
<#if parameters.cssClass??>
|
||||||
|
class="${parameters.cssClass?html}"<#rt/>
|
||||||
|
<#else>
|
||||||
|
class="errorMessage"<#rt/>
|
||||||
|
</#if>
|
||||||
|
<#if parameters.cssStyle??>
|
||||||
|
style="${parameters.cssStyle?html}"<#rt/>
|
||||||
|
</#if>
|
||||||
|
>
|
||||||
|
<#if (actionErrors?? && actionErrors?size > 0)>
|
||||||
|
<#list actionErrors as error>
|
||||||
|
<#if error??>
|
||||||
|
<li><span><#if parameters.escape>${error!?html}<#else>${error!}</#if></span><#rt/></li><#rt/>
|
||||||
|
</#if>
|
||||||
|
</#list>
|
||||||
|
</#if>
|
||||||
|
</ul>
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
<#--
|
||||||
|
/*
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
-->
|
||||||
|
${parameters.after!}<#t/>
|
||||||
|
</td><#lt/>
|
||||||
|
</tr>
|
||||||
|
<#if (parameters.errorposition!"top") == 'bottom'>
|
||||||
|
<#assign hasFieldErrors = parameters.name?? && fieldErrors?? && fieldErrors[parameters.name]??/>
|
||||||
|
<#if hasFieldErrors>
|
||||||
|
<tr errorFor="${parameters.id}">
|
||||||
|
<td class="tdErrorMessage" colspan="2"><#rt/>
|
||||||
|
<#if hasFieldErrors>
|
||||||
|
<#list fieldErrors[parameters.name] as error>
|
||||||
|
<div class="errorMessage">${error?html}</div><#t/>
|
||||||
|
</#list>
|
||||||
|
</#if>
|
||||||
|
</td><#lt/>
|
||||||
|
</tr>
|
||||||
|
</#if>
|
||||||
|
</#if>
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
<#--
|
||||||
|
/*
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
-->
|
||||||
|
<#--
|
||||||
|
Always include elements to show errors. They may be filled later via AJAX.
|
||||||
|
-->
|
||||||
|
<#assign hasFieldErrors = parameters.name?? && fieldErrors?? && fieldErrors[parameters.name]??/>
|
||||||
|
<#if (parameters.errorposition!"top") == 'top'>
|
||||||
|
<tr errorFor="${parameters.id}">
|
||||||
|
<td class="tdErrorMessage" colspan="2" data-error-for-fieldname="${parameters.name}"><#rt/>
|
||||||
|
<#if hasFieldErrors>
|
||||||
|
<#list fieldErrors[parameters.name] as error>
|
||||||
|
<div class="errorMessage">${error?html}</div><#t/>
|
||||||
|
</#list>
|
||||||
|
</#if>
|
||||||
|
</td><#lt/>
|
||||||
|
</tr>
|
||||||
|
</#if>
|
||||||
|
<#if !parameters.labelposition?? && (parameters.form.labelposition)??>
|
||||||
|
<#assign labelpos = parameters.form.labelposition/>
|
||||||
|
<#elseif parameters.labelposition??>
|
||||||
|
<#assign labelpos = parameters.labelposition/>
|
||||||
|
</#if>
|
||||||
|
<#--
|
||||||
|
if the label position is top,
|
||||||
|
then give the label it's own row in the table
|
||||||
|
-->
|
||||||
|
<tr>
|
||||||
|
<#if (labelpos!"") == 'top'>
|
||||||
|
<td class="tdLabelTop" colspan="2"><#rt/>
|
||||||
|
<#else>
|
||||||
|
<td class="tdLabel"><#rt/>
|
||||||
|
</#if>
|
||||||
|
<#if parameters.label??>
|
||||||
|
<label <#t/>
|
||||||
|
<#if parameters.id??>
|
||||||
|
for="${parameters.id?html}" <#t/>
|
||||||
|
</#if>
|
||||||
|
<#if hasFieldErrors>
|
||||||
|
class="errorLabel"<#t/>
|
||||||
|
<#else>
|
||||||
|
class="label"<#t/>
|
||||||
|
</#if>
|
||||||
|
><#t/>
|
||||||
|
<#if parameters.required!false && parameters.requiredPosition!"right" != 'right'>
|
||||||
|
<span class="required">*</span><#t/>
|
||||||
|
</#if>
|
||||||
|
${parameters.label?html}<#t/>
|
||||||
|
<#if parameters.required!false && parameters.requiredPosition!"right" == 'right'>
|
||||||
|
<span class="required">*</span><#t/>
|
||||||
|
</#if>
|
||||||
|
${parameters.labelseparator!":"?html}<#t/>
|
||||||
|
<#include "/${parameters.templateDir}/${parameters.expandTheme}/tooltip.ftl" />
|
||||||
|
</label><#t/>
|
||||||
|
</#if>
|
||||||
|
</td><#lt/>
|
||||||
|
<#-- add the extra row -->
|
||||||
|
<#if (labelpos!"") == 'top'>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
</#if>
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#
|
||||||
|
# $Id$
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
parent = xhtml
|
||||||
@@ -246,7 +246,6 @@
|
|||||||
</li>
|
</li>
|
||||||
<li><s:a value="/interactive/index.action">Interactive Demo</s:a></li>
|
<li><s:a value="/interactive/index.action">Interactive Demo</s:a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<ul class="nav pull-right">
|
<ul class="nav pull-right">
|
||||||
<li class="dropdown last">
|
<li class="dropdown last">
|
||||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="icon-flag"></i> Help<b
|
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="icon-flag"></i> Help<b
|
||||||
|
|||||||
@@ -45,4 +45,10 @@
|
|||||||
<put-attribute name="body" value="/WEB-INF/tiles/body.ftl"/>
|
<put-attribute name="body" value="/WEB-INF/tiles/body.ftl"/>
|
||||||
</definition>
|
</definition>
|
||||||
|
|
||||||
|
<definition name="showcase.annotations" template="/WEB-INF/tiles/layout-annotations.jsp">
|
||||||
|
<put-attribute name="title" value="Tiles Annotations Showcase"/>
|
||||||
|
<put-attribute name="header" value=".header"/>
|
||||||
|
<put-attribute name="body" value=".body"/>
|
||||||
|
</definition>
|
||||||
|
|
||||||
</tiles-definitions>
|
</tiles-definitions>
|
||||||
|
|||||||
@@ -16,6 +16,9 @@
|
|||||||
<li>
|
<li>
|
||||||
<a href="freemarkerLayout.action">View Example with a FreeMarker Layout</a>
|
<a href="freemarkerLayout.action">View Example with a FreeMarker Layout</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="tiles-annotations.action">View Example with tiles configuration by annotating action</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles" %>
|
||||||
|
<%@ taglib prefix="s" uri="/struts-tags" %>
|
||||||
|
|
||||||
|
<%-- Show usage; Used in Header --%>
|
||||||
|
<tiles:importAttribute name="title" scope="request"/>
|
||||||
|
<html>
|
||||||
|
<head><title>Struts2 Showcase - <tiles:getAsString name="title"/></title></head>
|
||||||
|
<body>
|
||||||
|
<tiles:insertAttribute name="header"/>
|
||||||
|
<tiles:insertAttribute name="body"/>
|
||||||
|
<p>Notice that this is a layout made in JSP</p>
|
||||||
|
<p>It is configured with <strong>annotations!</strong></p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,176 @@
|
|||||||
|
<%@taglib prefix="s" uri="/struts-tags" %>
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Struts2 Showcase - Validation - AJAX Form Submit</title>
|
||||||
|
<s:head theme="xhtml"/>
|
||||||
|
|
||||||
|
<style type="text/css">
|
||||||
|
/* see comment of script element below! */
|
||||||
|
.ajaxVisualFeedback {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
background-image: url('../images/indicator.gif');
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="page-header">
|
||||||
|
<h1>AJAX Form Submit</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
|
||||||
|
<!-- START SNIPPET: ajaxFormSubmit -->
|
||||||
|
|
||||||
|
<h3>Action Errors Will Appear Here</h3>
|
||||||
|
<s:actionerror theme="ajaxErrorContainers"/>
|
||||||
|
|
||||||
|
<hr/>
|
||||||
|
|
||||||
|
<s:form method="POST" theme="xhtml">
|
||||||
|
<s:textfield label="Required Validator Field" name="requiredValidatorField" theme="ajaxErrorContainers"/>
|
||||||
|
<s:textfield label="Required String Validator Field" name="requiredStringValidatorField" theme="ajaxErrorContainers"/>
|
||||||
|
<s:textfield label="Integer Validator Field" name="integerValidatorField" theme="ajaxErrorContainers"/>
|
||||||
|
<s:textfield label="Date Validator Field" name="dateValidatorField" theme="ajaxErrorContainers"/>
|
||||||
|
<s:textfield label="Email Validator Field" name="emailValidatorField" theme="ajaxErrorContainers"/>
|
||||||
|
<s:textfield label="URL Validator Field" name="urlValidatorField" theme="ajaxErrorContainers"/>
|
||||||
|
<s:textfield label="String Length Validator Field" name="stringLengthValidatorField" theme="ajaxErrorContainers"/>
|
||||||
|
<s:textfield label="Regex Validator Field" name="regexValidatorField" theme="ajaxErrorContainers"/>
|
||||||
|
<s:textfield label="Field Expression Validator Field" name="fieldExpressionValidatorField" theme="ajaxErrorContainers"/>
|
||||||
|
<s:submit label="Submit" cssClass="btn btn-primary"/>
|
||||||
|
</s:form>
|
||||||
|
|
||||||
|
<!-- END SNIPPET: ajaxFormSubmit -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
/********************************************************************
|
||||||
|
* JS just used on this page.
|
||||||
|
* Usually this would be placed in a JS file
|
||||||
|
* but as this showcase app is already hard to follow
|
||||||
|
* I place it here so it is easier to find.
|
||||||
|
*
|
||||||
|
* note: this requires jQuery.
|
||||||
|
*******************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates form per AJAX. To be called as onSubmit handler.
|
||||||
|
*
|
||||||
|
* @param event onSubmit event
|
||||||
|
*/
|
||||||
|
function ajaxFormValidation(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
_removeValidationErrors();
|
||||||
|
|
||||||
|
var _form = $(event.target);
|
||||||
|
var _formData = _form.serialize(true);
|
||||||
|
|
||||||
|
// prepare visual feedback
|
||||||
|
// you may want to use other elements here
|
||||||
|
var originalButton = _form.find('.btn-primary');
|
||||||
|
// note: jQuery returns an array-like object
|
||||||
|
if (originalButton && originalButton.length && originalButton.length > 0) {
|
||||||
|
originalButton.hide();
|
||||||
|
var feedbackElement = $('<div class="ajaxVisualFeedback"></div>').insertAfter(originalButton);
|
||||||
|
var restoreFunction = function() {
|
||||||
|
originalButton.show();
|
||||||
|
feedbackElement.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var options = {
|
||||||
|
data: 'struts.enableJSONValidation=true&struts.validateOnly=false&' + _formData,
|
||||||
|
async: true,
|
||||||
|
processData: false,
|
||||||
|
type: 'POST',
|
||||||
|
success: function (response, statusText, xhr) {
|
||||||
|
if (response.location) {
|
||||||
|
// no validation errors
|
||||||
|
// action has been executed and sent a redirect URL wrapped as JSON
|
||||||
|
// cannot use a normal http-redirect (status-code 3xx) as this would be followed by browsers and would not be available here
|
||||||
|
|
||||||
|
// follow JSON-redirect
|
||||||
|
window.location.href = response.location;
|
||||||
|
} else {
|
||||||
|
if (restoreFunction) {
|
||||||
|
restoreFunction();
|
||||||
|
}
|
||||||
|
_handleValidationResult(_form, response);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function(xhr, textStatus, errorThrown) {
|
||||||
|
if (restoreFunction) {
|
||||||
|
restoreFunction();
|
||||||
|
}
|
||||||
|
// show user an error message
|
||||||
|
_handleValidationResult(_form, {errors: ['Network or server error!']})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// send request, after delay to make sure everybody notices the visual feedback :)
|
||||||
|
window.setTimeout(function() {
|
||||||
|
var url = _form[0].action;
|
||||||
|
jQuery.ajax(url, options);
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes validation errors from HTML DOM.
|
||||||
|
*/
|
||||||
|
function _removeValidationErrors() {
|
||||||
|
// action errors
|
||||||
|
// you might want to use a custom ID here
|
||||||
|
$('ul.errorMessage li').remove();
|
||||||
|
|
||||||
|
// field errors
|
||||||
|
$('div.errorMessage').remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Incorporates validation errors in HTML DOM.
|
||||||
|
*
|
||||||
|
* @param form Form containing errors.
|
||||||
|
* @param errors Errors from server.
|
||||||
|
*/
|
||||||
|
function _handleValidationResult(form, errors) {
|
||||||
|
// action errors
|
||||||
|
if (errors.errors) {
|
||||||
|
// you might want to use a custom ID here
|
||||||
|
var errorContainer = $('ul.errorMessage');
|
||||||
|
$.each(errors.errors, function(index, errorMsg) {
|
||||||
|
var li = $('<li><span></span></li>');
|
||||||
|
li.text(errorMsg); // use text() for security reasons
|
||||||
|
errorContainer.append(li);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// field errors
|
||||||
|
if (errors.fieldErrors) {
|
||||||
|
$.each(errors.fieldErrors, function(fieldName, errorMsg) {
|
||||||
|
var td = $('td[data-error-for-fieldname="' + fieldName + '"]');
|
||||||
|
if (td) {
|
||||||
|
var div = $('<div class="errorMessage"></div>');
|
||||||
|
div.text(errorMsg); // use text() for security reasons
|
||||||
|
td.append(div);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// register onSubmit handler
|
||||||
|
$(window).bind('load', function() {
|
||||||
|
$('form').bind('submit', ajaxFormValidation);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<%@taglib prefix="s" uri="/struts-tags" %>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Struts2 Showcase - Validation - Success Field Validators Example</title>
|
||||||
|
<s:head/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="page-header">
|
||||||
|
<h1>Success !</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="container-fluid">
|
||||||
|
Form has been submitted.
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -31,6 +31,7 @@
|
|||||||
<s:url var="clientSideValidationUrl" action="clientSideValidationExample" namespace="/validation"/>
|
<s:url var="clientSideValidationUrl" action="clientSideValidationExample" namespace="/validation"/>
|
||||||
<s:url var="backToShowcase" action="showcase" namespace="/"/>
|
<s:url var="backToShowcase" action="showcase" namespace="/"/>
|
||||||
<s:url var="storeMessageAcrossRequestExample" namespace="/validation" action="storeErrorsAcrossRequestExample"/>
|
<s:url var="storeMessageAcrossRequestExample" namespace="/validation" action="storeErrorsAcrossRequestExample"/>
|
||||||
|
<s:url var="ajaxFormSubmitAction" namespace="/validation" action="ajaxFormSubmit!input"/>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li><s:a href="%{fieldValidatorUrl}">Field Validators</s:a></li>
|
<li><s:a href="%{fieldValidatorUrl}">Field Validators</s:a></li>
|
||||||
@@ -42,6 +43,7 @@
|
|||||||
<li><s:a href="%{quizClient}">Validation (client)</s:a></li>
|
<li><s:a href="%{quizClient}">Validation (client)</s:a></li>
|
||||||
<li><s:a href="%{quizClientCss}">Validation (client using css_xhtml theme)</s:a></li>
|
<li><s:a href="%{quizClientCss}">Validation (client using css_xhtml theme)</s:a></li>
|
||||||
<li><s:a href="%{visitorValidatorUrl}">Visitor Validator</s:a></li>
|
<li><s:a href="%{visitorValidatorUrl}">Visitor Validator</s:a></li>
|
||||||
|
<li><s:a href="%{ajaxFormSubmitAction}">AJAX Form Submit</s:a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+1
-1
@@ -26,7 +26,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.struts</groupId>
|
<groupId>org.apache.struts</groupId>
|
||||||
<artifactId>struts2-parent</artifactId>
|
<artifactId>struts2-parent</artifactId>
|
||||||
<version>2.3.24.1</version>
|
<version>2.3.33</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>struts2-archetypes</artifactId>
|
<artifactId>struts2-archetypes</artifactId>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.struts</groupId>
|
<groupId>org.apache.struts</groupId>
|
||||||
<artifactId>struts2-archetypes</artifactId>
|
<artifactId>struts2-archetypes</artifactId>
|
||||||
<version>2.3.24.1</version>
|
<version>2.3.33</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.struts</groupId>
|
<groupId>org.apache.struts</groupId>
|
||||||
<artifactId>struts2-archetypes</artifactId>
|
<artifactId>struts2-archetypes</artifactId>
|
||||||
<version>2.3.24.1</version>
|
<version>2.3.33</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.struts</groupId>
|
<groupId>org.apache.struts</groupId>
|
||||||
<artifactId>struts2-archetypes</artifactId>
|
<artifactId>struts2-archetypes</artifactId>
|
||||||
<version>2.3.24.1</version>
|
<version>2.3.33</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.struts</groupId>
|
<groupId>org.apache.struts</groupId>
|
||||||
<artifactId>struts2-archetypes</artifactId>
|
<artifactId>struts2-archetypes</artifactId>
|
||||||
<version>2.3.24.1</version>
|
<version>2.3.33</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.struts</groupId>
|
<groupId>org.apache.struts</groupId>
|
||||||
<artifactId>struts2-archetypes</artifactId>
|
<artifactId>struts2-archetypes</artifactId>
|
||||||
<version>2.3.24.1</version>
|
<version>2.3.33</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.struts</groupId>
|
<groupId>org.apache.struts</groupId>
|
||||||
<artifactId>struts2-archetypes</artifactId>
|
<artifactId>struts2-archetypes</artifactId>
|
||||||
<version>2.3.24.1</version>
|
<version>2.3.33</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.struts</groupId>
|
<groupId>org.apache.struts</groupId>
|
||||||
<artifactId>struts2-archetypes</artifactId>
|
<artifactId>struts2-archetypes</artifactId>
|
||||||
<version>2.3.24.1</version>
|
<version>2.3.33</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|||||||
+12
-1
@@ -3,7 +3,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.struts</groupId>
|
<groupId>org.apache.struts</groupId>
|
||||||
<artifactId>struts2-parent</artifactId>
|
<artifactId>struts2-parent</artifactId>
|
||||||
<version>2.3.24.1</version>
|
<version>2.3.33</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>struts2-assembly</artifactId>
|
<artifactId>struts2-assembly</artifactId>
|
||||||
@@ -129,6 +129,7 @@
|
|||||||
<descriptors>
|
<descriptors>
|
||||||
<descriptor>src/main/assembly/all.xml</descriptor>
|
<descriptor>src/main/assembly/all.xml</descriptor>
|
||||||
<descriptor>src/main/assembly/lib.xml</descriptor>
|
<descriptor>src/main/assembly/lib.xml</descriptor>
|
||||||
|
<descriptor>src/main/assembly/min-lib.xml</descriptor>
|
||||||
<descriptor>src/main/assembly/apps.xml</descriptor>
|
<descriptor>src/main/assembly/apps.xml</descriptor>
|
||||||
<descriptor>src/main/assembly/src.xml</descriptor>
|
<descriptor>src/main/assembly/src.xml</descriptor>
|
||||||
<descriptor>src/main/assembly/docs.xml</descriptor>
|
<descriptor>src/main/assembly/docs.xml</descriptor>
|
||||||
@@ -143,6 +144,16 @@
|
|||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.struts.xwork</groupId>
|
||||||
|
<artifactId>xwork-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.struts</groupId>
|
||||||
|
<artifactId>struts2-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.struts</groupId>
|
<groupId>org.apache.struts</groupId>
|
||||||
<artifactId>struts2-cdi-plugin</artifactId>
|
<artifactId>struts2-cdi-plugin</artifactId>
|
||||||
|
|||||||
@@ -21,7 +21,10 @@
|
|||||||
*/
|
*/
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<assembly>
|
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd">
|
||||||
|
|
||||||
<id>all</id>
|
<id>all</id>
|
||||||
<formats>
|
<formats>
|
||||||
<format>zip</format>
|
<format>zip</format>
|
||||||
|
|||||||
@@ -20,7 +20,10 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
-->
|
-->
|
||||||
<assembly>
|
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd">
|
||||||
|
|
||||||
<id>apps</id>
|
<id>apps</id>
|
||||||
<formats>
|
<formats>
|
||||||
<format>zip</format>
|
<format>zip</format>
|
||||||
|
|||||||
@@ -21,7 +21,9 @@
|
|||||||
*/
|
*/
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<assembly>
|
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd">
|
||||||
|
|
||||||
<id>docs</id>
|
<id>docs</id>
|
||||||
<formats>
|
<formats>
|
||||||
|
|||||||
@@ -21,7 +21,10 @@
|
|||||||
*/
|
*/
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<assembly>
|
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd">
|
||||||
|
|
||||||
<id>lib</id>
|
<id>lib</id>
|
||||||
<formats>
|
<formats>
|
||||||
<format>zip</format>
|
<format>zip</format>
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
<!--
|
||||||
|
/*
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
-->
|
||||||
|
|
||||||
|
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd">
|
||||||
|
|
||||||
|
<id>min-lib</id>
|
||||||
|
<formats>
|
||||||
|
<format>zip</format>
|
||||||
|
</formats>
|
||||||
|
<dependencySets>
|
||||||
|
<dependencySet>
|
||||||
|
<useProjectArtifact>true</useProjectArtifact>
|
||||||
|
<useTransitiveDependencies>true</useTransitiveDependencies>
|
||||||
|
<outputDirectory>lib</outputDirectory>
|
||||||
|
<includes>
|
||||||
|
<include>org.apache.struts.xwork:xwork-core</include>
|
||||||
|
<include>org.apache.struts:struts2-core</include>
|
||||||
|
<include>org.freemarker:freemarker</include>
|
||||||
|
<include>org.apache.commons:commons-lang3</include>
|
||||||
|
<include>ognl:ognl</include>
|
||||||
|
<include>javassist:javassist</include>
|
||||||
|
<include>commons-fileupload:commons-fileupload</include>
|
||||||
|
<include>commons-io:commons-io</include>
|
||||||
|
<include>commons-logging:commons-logging</include>
|
||||||
|
</includes>
|
||||||
|
</dependencySet>
|
||||||
|
</dependencySets>
|
||||||
|
</assembly>
|
||||||
@@ -21,7 +21,10 @@
|
|||||||
*/
|
*/
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<assembly>
|
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd">
|
||||||
|
|
||||||
<id>src</id>
|
<id>src</id>
|
||||||
<formats>
|
<formats>
|
||||||
<format>zip</format>
|
<format>zip</format>
|
||||||
|
|||||||
+3
-3
@@ -10,7 +10,7 @@
|
|||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>struts2-bom</artifactId>
|
<artifactId>struts2-bom</artifactId>
|
||||||
<version>2.3.24.1</version>
|
<version>2.3.33</version>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
<name>Struts 2 Bill of Materials</name>
|
<name>Struts 2 Bill of Materials</name>
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
</licenses>
|
</licenses>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<struts-version.version>2.3.24.1</struts-version.version>
|
<struts-version.version>2.3.33</struts-version.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
@@ -197,6 +197,6 @@
|
|||||||
</dependencyManagement>
|
</dependencyManagement>
|
||||||
|
|
||||||
<scm>
|
<scm>
|
||||||
<tag>STRUTS_2_3_24_1</tag>
|
<tag>STRUTS_2_3_33</tag>
|
||||||
</scm>
|
</scm>
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.struts</groupId>
|
<groupId>org.apache.struts</groupId>
|
||||||
<artifactId>struts2-osgi-bundles</artifactId>
|
<artifactId>struts2-osgi-bundles</artifactId>
|
||||||
<version>2.3.24.1</version>
|
<version>2.3.33</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>struts2-osgi-admin-bundle</artifactId>
|
<artifactId>struts2-osgi-admin-bundle</artifactId>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.struts</groupId>
|
<groupId>org.apache.struts</groupId>
|
||||||
<artifactId>struts2-osgi-bundles</artifactId>
|
<artifactId>struts2-osgi-bundles</artifactId>
|
||||||
<version>2.3.24.1</version>
|
<version>2.3.33</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>struts2-osgi-demo-bundle</artifactId>
|
<artifactId>struts2-osgi-demo-bundle</artifactId>
|
||||||
|
|||||||
+1
-1
@@ -26,7 +26,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.struts</groupId>
|
<groupId>org.apache.struts</groupId>
|
||||||
<artifactId>struts2-parent</artifactId>
|
<artifactId>struts2-parent</artifactId>
|
||||||
<version>2.3.24.1</version>
|
<version>2.3.33</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>struts2-osgi-bundles</artifactId>
|
<artifactId>struts2-osgi-bundles</artifactId>
|
||||||
|
|||||||
+1
-1
@@ -26,7 +26,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.struts</groupId>
|
<groupId>org.apache.struts</groupId>
|
||||||
<artifactId>struts2-parent</artifactId>
|
<artifactId>struts2-parent</artifactId>
|
||||||
<version>2.3.24.1</version>
|
<version>2.3.33</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>struts2-core</artifactId>
|
<artifactId>struts2-core</artifactId>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.opensymphony.xwork2.config;
|
||||||
|
|
||||||
|
import javax.servlet.ServletContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dedicated interface to allow inject {@link ServletContext} and perform initialisation based in it
|
||||||
|
*/
|
||||||
|
public interface ServletContextAwareConfigurationProvider extends ConfigurationProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a lazy constructor, called just after class was initiated
|
||||||
|
*
|
||||||
|
* @param servletContext current instance of {@link ServletContext}
|
||||||
|
*/
|
||||||
|
void initWithContext(ServletContext servletContext);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -272,6 +272,13 @@ public final class StrutsConstants {
|
|||||||
|
|
||||||
/** actions names' whitelist **/
|
/** actions names' whitelist **/
|
||||||
public static final String STRUTS_ALLOWED_ACTION_NAMES = "struts.allowed.action.names";
|
public static final String STRUTS_ALLOWED_ACTION_NAMES = "struts.allowed.action.names";
|
||||||
|
/** default action name to use when action didn't match the whitelist **/
|
||||||
|
public static final String STRUTS_DEFAULT_ACTION_NAME = "struts.default.action.name";
|
||||||
|
|
||||||
|
/** methods names' whitelist **/
|
||||||
|
public static final String STRUTS_ALLOWED_METHOD_NAMES = "struts.allowed.method.names";
|
||||||
|
/** default method name to use when method didn't match the whitelist **/
|
||||||
|
public static final String STRUTS_DEFAULT_METHOD_NAME = "struts.default.method.name";
|
||||||
|
|
||||||
/** enables action: prefix **/
|
/** enables action: prefix **/
|
||||||
public static final String STRUTS_MAPPER_ACTION_PREFIX_ENABLED = "struts.mapper.action.prefix.enabled";
|
public static final String STRUTS_MAPPER_ACTION_PREFIX_ENABLED = "struts.mapper.action.prefix.enabled";
|
||||||
@@ -287,6 +294,7 @@ public final class StrutsConstants {
|
|||||||
/** Comma delimited set of excluded classes and package names which cannot be accessed via expressions **/
|
/** Comma delimited set of excluded classes and package names which cannot be accessed via expressions **/
|
||||||
public static final String STRUTS_EXCLUDED_CLASSES = "struts.excludedClasses";
|
public static final String STRUTS_EXCLUDED_CLASSES = "struts.excludedClasses";
|
||||||
public static final String STRUTS_EXCLUDED_PACKAGE_NAME_PATTERNS = "struts.excludedPackageNamePatterns";
|
public static final String STRUTS_EXCLUDED_PACKAGE_NAME_PATTERNS = "struts.excludedPackageNamePatterns";
|
||||||
|
public static final String STRUTS_EXCLUDED_PACKAGE_NAMES = "struts.excludedPackageNames";
|
||||||
|
|
||||||
/** Dedicated services to check if passed string is excluded/accepted **/
|
/** Dedicated services to check if passed string is excluded/accepted **/
|
||||||
public static final String STRUTS_EXCLUDED_PATTERNS_CHECKER = "struts.excludedPatterns.checker";
|
public static final String STRUTS_EXCLUDED_PATTERNS_CHECKER = "struts.excludedPatterns.checker";
|
||||||
|
|||||||
@@ -373,7 +373,11 @@ public class Component {
|
|||||||
*/
|
*/
|
||||||
protected Object findValue(String expr, Class toType) {
|
protected Object findValue(String expr, Class toType) {
|
||||||
if (altSyntax() && toType == String.class) {
|
if (altSyntax() && toType == String.class) {
|
||||||
return TextParseUtil.translateVariables('%', expr, stack);
|
if (ComponentUtils.containsExpression(expr)) {
|
||||||
|
return TextParseUtil.translateVariables('%', expr, stack);
|
||||||
|
} else {
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
expr = stripExpressionIfAltSyntax(expr);
|
expr = stripExpressionIfAltSyntax(expr);
|
||||||
|
|
||||||
|
|||||||
@@ -22,14 +22,12 @@
|
|||||||
package org.apache.struts2.components;
|
package org.apache.struts2.components;
|
||||||
|
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
import org.apache.struts2.views.annotations.StrutsTag;
|
import org.apache.struts2.views.annotations.StrutsTag;
|
||||||
import org.apache.struts2.views.annotations.StrutsTagAttribute;
|
import org.apache.struts2.views.annotations.StrutsTagAttribute;
|
||||||
import org.apache.struts2.StrutsException;
|
import org.apache.struts2.StrutsException;
|
||||||
|
|
||||||
import com.opensymphony.xwork2.ActionContext;
|
|
||||||
import com.opensymphony.xwork2.LocaleProvider;
|
import com.opensymphony.xwork2.LocaleProvider;
|
||||||
import com.opensymphony.xwork2.TextProviderFactory;
|
import com.opensymphony.xwork2.TextProviderFactory;
|
||||||
import com.opensymphony.xwork2.TextProvider;
|
import com.opensymphony.xwork2.TextProvider;
|
||||||
@@ -95,6 +93,8 @@ public class I18n extends Component {
|
|||||||
protected String name;
|
protected String name;
|
||||||
protected Container container;
|
protected Container container;
|
||||||
private TextProvider textProvider;
|
private TextProvider textProvider;
|
||||||
|
private TextProvider defaultTextProvider;
|
||||||
|
private LocaleProvider localeProvider;
|
||||||
|
|
||||||
public I18n(ValueStack stack) {
|
public I18n(ValueStack stack) {
|
||||||
super(stack);
|
super(stack);
|
||||||
@@ -105,26 +105,31 @@ public class I18n extends Component {
|
|||||||
this.container = container;
|
this.container = container;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public void setTextProvider(TextProvider textProvider) {
|
||||||
|
this.defaultTextProvider = textProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public void setLocaleProvider(LocaleProvider localeProvider) {
|
||||||
|
this.localeProvider = localeProvider;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean start(Writer writer) {
|
public boolean start(Writer writer) {
|
||||||
boolean result = super.start(writer);
|
boolean result = super.start(writer);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String name = this.findString(this.name, "name", "Resource bundle name is required. Example: foo or foo_en");
|
String name = this.findString(this.name, "name", "Resource bundle name is required. Example: foo or foo_en");
|
||||||
ResourceBundle bundle = (ResourceBundle) findValue("getTexts('" + name + "')");
|
ResourceBundle bundle = defaultTextProvider.getTexts(name);
|
||||||
|
|
||||||
if (bundle == null) {
|
if (bundle == null) {
|
||||||
bundle = LocalizedTextUtil.findResourceBundle(name, (Locale) getStack().getContext().get(ActionContext.LOCALE));
|
bundle = LocalizedTextUtil.findResourceBundle(name, localeProvider.getLocale());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bundle != null) {
|
if (bundle != null) {
|
||||||
final Locale locale = (Locale) getStack().getContext().get(ActionContext.LOCALE);
|
|
||||||
TextProviderFactory tpf = new TextProviderFactory();
|
TextProviderFactory tpf = new TextProviderFactory();
|
||||||
container.inject(tpf);
|
container.inject(tpf);
|
||||||
textProvider = tpf.createInstance(bundle, new LocaleProvider() {
|
textProvider = tpf.createInstance(bundle, localeProvider);
|
||||||
public Locale getLocale() {
|
|
||||||
return locale;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
getStack().push(textProvider);
|
getStack().push(textProvider);
|
||||||
pushed = true;
|
pushed = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -422,6 +422,7 @@ public class DefaultBeanSelectionProvider extends AbstractBeanSelectionProvider
|
|||||||
|
|
||||||
convertIfExist(props, StrutsConstants.STRUTS_EXCLUDED_CLASSES, XWorkConstants.OGNL_EXCLUDED_CLASSES);
|
convertIfExist(props, StrutsConstants.STRUTS_EXCLUDED_CLASSES, XWorkConstants.OGNL_EXCLUDED_CLASSES);
|
||||||
convertIfExist(props, StrutsConstants.STRUTS_EXCLUDED_PACKAGE_NAME_PATTERNS, XWorkConstants.OGNL_EXCLUDED_PACKAGE_NAME_PATTERNS);
|
convertIfExist(props, StrutsConstants.STRUTS_EXCLUDED_PACKAGE_NAME_PATTERNS, XWorkConstants.OGNL_EXCLUDED_PACKAGE_NAME_PATTERNS);
|
||||||
|
convertIfExist(props, StrutsConstants.STRUTS_EXCLUDED_PACKAGE_NAMES, XWorkConstants.OGNL_EXCLUDED_PACKAGE_NAMES);
|
||||||
|
|
||||||
convertIfExist(props, StrutsConstants.STRUTS_ADDITIONAL_EXCLUDED_PATTERNS, XWorkConstants.ADDITIONAL_EXCLUDED_PATTERNS);
|
convertIfExist(props, StrutsConstants.STRUTS_ADDITIONAL_EXCLUDED_PATTERNS, XWorkConstants.ADDITIONAL_EXCLUDED_PATTERNS);
|
||||||
convertIfExist(props, StrutsConstants.STRUTS_ADDITIONAL_ACCEPTED_PATTERNS, XWorkConstants.ADDITIONAL_ACCEPTED_PATTERNS);
|
convertIfExist(props, StrutsConstants.STRUTS_ADDITIONAL_ACCEPTED_PATTERNS, XWorkConstants.ADDITIONAL_ACCEPTED_PATTERNS);
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ package org.apache.struts2.dispatcher;
|
|||||||
|
|
||||||
import com.opensymphony.xwork2.inject.Inject;
|
import com.opensymphony.xwork2.inject.Inject;
|
||||||
import com.opensymphony.xwork2.util.ClassLoaderUtil;
|
import com.opensymphony.xwork2.util.ClassLoaderUtil;
|
||||||
|
import org.apache.commons.lang3.BooleanUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import com.opensymphony.xwork2.util.logging.Logger;
|
import com.opensymphony.xwork2.util.logging.Logger;
|
||||||
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||||
import org.apache.struts2.StrutsConstants;
|
import org.apache.struts2.StrutsConstants;
|
||||||
@@ -39,6 +41,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -93,6 +96,7 @@ public class DefaultStaticContentLoader implements StaticContentLoader {
|
|||||||
*/
|
*/
|
||||||
protected String encoding;
|
protected String encoding;
|
||||||
|
|
||||||
|
protected boolean devMode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Modify state of StrutsConstants.STRUTS_SERVE_STATIC_CONTENT setting.
|
* Modify state of StrutsConstants.STRUTS_SERVE_STATIC_CONTENT setting.
|
||||||
@@ -101,29 +105,34 @@ public class DefaultStaticContentLoader implements StaticContentLoader {
|
|||||||
* New setting
|
* New setting
|
||||||
*/
|
*/
|
||||||
@Inject(StrutsConstants.STRUTS_SERVE_STATIC_CONTENT)
|
@Inject(StrutsConstants.STRUTS_SERVE_STATIC_CONTENT)
|
||||||
public void setServeStaticContent(String val) {
|
public void setServeStaticContent(String serveStaticContent) {
|
||||||
serveStatic = "true".equals(val);
|
this.serveStatic = BooleanUtils.toBoolean(serveStaticContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Modify state of StrutsConstants.STRUTS_SERVE_STATIC_BROWSER_CACHE
|
* Modify state of StrutsConstants.STRUTS_SERVE_STATIC_BROWSER_CACHE
|
||||||
* setting.
|
* setting.
|
||||||
*
|
*
|
||||||
* @param val
|
* @param serveStaticBrowserCache
|
||||||
* New setting
|
* New setting
|
||||||
*/
|
*/
|
||||||
@Inject(StrutsConstants.STRUTS_SERVE_STATIC_BROWSER_CACHE)
|
@Inject(StrutsConstants.STRUTS_SERVE_STATIC_BROWSER_CACHE)
|
||||||
public void setServeStaticBrowserCache(String val) {
|
public void setServeStaticBrowserCache(String serveStaticBrowserCache) {
|
||||||
serveStaticBrowserCache = "true".equals(val);
|
this.serveStaticBrowserCache = BooleanUtils.toBoolean(serveStaticBrowserCache);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Modify state of StrutsConstants.STRUTS_I18N_ENCODING setting.
|
* Modify state of StrutsConstants.STRUTS_I18N_ENCODING setting.
|
||||||
* @param val New setting
|
* @param encoding New setting
|
||||||
*/
|
*/
|
||||||
@Inject(StrutsConstants.STRUTS_I18N_ENCODING)
|
@Inject(StrutsConstants.STRUTS_I18N_ENCODING)
|
||||||
public void setEncoding(String val) {
|
public void setEncoding(String encoding) {
|
||||||
encoding = val;
|
this.encoding = encoding;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(StrutsConstants.STRUTS_DEVMODE)
|
||||||
|
public void setDevMode(String devMode) {
|
||||||
|
this.devMode = Boolean.parseBoolean(devMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -141,7 +150,16 @@ public class DefaultStaticContentLoader implements StaticContentLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected String getAdditionalPackages() {
|
protected String getAdditionalPackages() {
|
||||||
return "org.apache.struts2.static template org.apache.struts2.interceptor.debugging static";
|
List<String> packages = new LinkedList<String>();
|
||||||
|
packages.add("org.apache.struts2.static");
|
||||||
|
packages.add("template");
|
||||||
|
packages.add("static");
|
||||||
|
|
||||||
|
if (devMode) {
|
||||||
|
packages.add("org.apache.struts2.interceptor.debugging");
|
||||||
|
}
|
||||||
|
|
||||||
|
return StringUtils.join(packages.iterator(), ' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -273,7 +291,7 @@ public class DefaultStaticContentLoader implements StaticContentLoader {
|
|||||||
* @param name resource name
|
* @param name resource name
|
||||||
* @param packagePrefix The package prefix to use to locate the resource
|
* @param packagePrefix The package prefix to use to locate the resource
|
||||||
* @return full path
|
* @return full path
|
||||||
* @throws UnsupportedEncodingException
|
* @throws UnsupportedEncodingException If there is a encoding problem
|
||||||
*/
|
*/
|
||||||
protected String buildPath(String name, String packagePrefix) throws UnsupportedEncodingException {
|
protected String buildPath(String name, String packagePrefix) throws UnsupportedEncodingException {
|
||||||
String resourcePath;
|
String resourcePath;
|
||||||
|
|||||||
@@ -211,7 +211,11 @@ public class Dispatcher {
|
|||||||
*/
|
*/
|
||||||
@Inject(StrutsConstants.STRUTS_DEVMODE)
|
@Inject(StrutsConstants.STRUTS_DEVMODE)
|
||||||
public void setDevMode(String mode) {
|
public void setDevMode(String mode) {
|
||||||
devMode = "true".equals(mode);
|
devMode = Boolean.parseBoolean(mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDevMode() {
|
||||||
|
return devMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -265,6 +269,10 @@ public class Dispatcher {
|
|||||||
this.handleException = Boolean.parseBoolean(handleException);
|
this.handleException = Boolean.parseBoolean(handleException);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isHandleException() {
|
||||||
|
return handleException;
|
||||||
|
}
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public void setDispatcherErrorHandler(DispatcherErrorHandler errorHandler) {
|
public void setDispatcherErrorHandler(DispatcherErrorHandler errorHandler) {
|
||||||
this.errorHandler = errorHandler;
|
this.errorHandler = errorHandler;
|
||||||
@@ -394,6 +402,9 @@ public class Dispatcher {
|
|||||||
try {
|
try {
|
||||||
Class cls = ClassLoaderUtil.loadClass(cname, this.getClass());
|
Class cls = ClassLoaderUtil.loadClass(cname, this.getClass());
|
||||||
ConfigurationProvider prov = (ConfigurationProvider)cls.newInstance();
|
ConfigurationProvider prov = (ConfigurationProvider)cls.newInstance();
|
||||||
|
if (prov instanceof ServletContextAwareConfigurationProvider) {
|
||||||
|
((ServletContextAwareConfigurationProvider)prov).initWithContext(servletContext);
|
||||||
|
}
|
||||||
configurationManager.addContainerProvider(prov);
|
configurationManager.addContainerProvider(prov);
|
||||||
} catch (InstantiationException e) {
|
} catch (InstantiationException e) {
|
||||||
throw new ConfigurationException("Unable to instantiate provider: "+cname, e);
|
throw new ConfigurationException("Unable to instantiate provider: "+cname, e);
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import org.apache.commons.lang3.StringUtils;
|
|||||||
import org.apache.struts2.RequestUtils;
|
import org.apache.struts2.RequestUtils;
|
||||||
import org.apache.struts2.ServletActionContext;
|
import org.apache.struts2.ServletActionContext;
|
||||||
import org.apache.struts2.StrutsConstants;
|
import org.apache.struts2.StrutsConstants;
|
||||||
|
import org.apache.struts2.StrutsException;
|
||||||
import org.apache.struts2.util.PrefixTrie;
|
import org.apache.struts2.util.PrefixTrie;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
@@ -120,6 +121,11 @@ public class DefaultActionMapper implements ActionMapper {
|
|||||||
protected boolean alwaysSelectFullNamespace = false;
|
protected boolean alwaysSelectFullNamespace = false;
|
||||||
protected PrefixTrie prefixTrie = null;
|
protected PrefixTrie prefixTrie = null;
|
||||||
protected Pattern allowedActionNames = Pattern.compile("[a-zA-Z0-9._!/\\-]*");
|
protected Pattern allowedActionNames = Pattern.compile("[a-zA-Z0-9._!/\\-]*");
|
||||||
|
protected String defaultActionName = "index";
|
||||||
|
|
||||||
|
protected Pattern allowedMethodNames = Pattern.compile("[a-zA-Z_]*[0-9]*");
|
||||||
|
protected String defaultMethodName = "execute";
|
||||||
|
|
||||||
private boolean allowActionPrefix = false;
|
private boolean allowActionPrefix = false;
|
||||||
private boolean allowActionCrossNamespaceAccess = false;
|
private boolean allowActionCrossNamespaceAccess = false;
|
||||||
|
|
||||||
@@ -136,7 +142,7 @@ public class DefaultActionMapper implements ActionMapper {
|
|||||||
put(METHOD_PREFIX, new ParameterAction() {
|
put(METHOD_PREFIX, new ParameterAction() {
|
||||||
public void execute(String key, ActionMapping mapping) {
|
public void execute(String key, ActionMapping mapping) {
|
||||||
if (allowDynamicMethodCalls) {
|
if (allowDynamicMethodCalls) {
|
||||||
mapping.setMethod(key.substring(METHOD_PREFIX.length()));
|
mapping.setMethod(cleanupMethodName(key.substring(METHOD_PREFIX.length())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -148,7 +154,7 @@ public class DefaultActionMapper implements ActionMapper {
|
|||||||
if (allowDynamicMethodCalls) {
|
if (allowDynamicMethodCalls) {
|
||||||
int bang = name.indexOf('!');
|
int bang = name.indexOf('!');
|
||||||
if (bang != -1) {
|
if (bang != -1) {
|
||||||
String method = name.substring(bang + 1);
|
String method = cleanupMethodName(name.substring(bang + 1));
|
||||||
mapping.setMethod(method);
|
mapping.setMethod(method);
|
||||||
name = name.substring(0, bang);
|
name = name.substring(0, bang);
|
||||||
}
|
}
|
||||||
@@ -204,6 +210,21 @@ public class DefaultActionMapper implements ActionMapper {
|
|||||||
this.allowedActionNames = Pattern.compile(allowedActionNames);
|
this.allowedActionNames = Pattern.compile(allowedActionNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Inject(value = StrutsConstants.STRUTS_DEFAULT_ACTION_NAME, required = false)
|
||||||
|
public void setDefaultActionName(String defaultActionName) {
|
||||||
|
this.defaultActionName = defaultActionName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(value = StrutsConstants.STRUTS_ALLOWED_METHOD_NAMES, required = false)
|
||||||
|
public void setAllowedMethodNames(String allowedMethodNames) {
|
||||||
|
this.allowedMethodNames = Pattern.compile(allowedMethodNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(value = StrutsConstants.STRUTS_DEFAULT_METHOD_NAME, required = false)
|
||||||
|
public void setDefaultMethodName(String defaultMethodName) {
|
||||||
|
this.defaultMethodName = defaultMethodName;
|
||||||
|
}
|
||||||
|
|
||||||
@Inject(value = StrutsConstants.STRUTS_MAPPER_ACTION_PREFIX_ENABLED)
|
@Inject(value = StrutsConstants.STRUTS_MAPPER_ACTION_PREFIX_ENABLED)
|
||||||
public void setAllowActionPrefix(String allowActionPrefix) {
|
public void setAllowActionPrefix(String allowActionPrefix) {
|
||||||
this.allowActionPrefix = "true".equalsIgnoreCase(allowActionPrefix);
|
this.allowActionPrefix = "true".equalsIgnoreCase(allowActionPrefix);
|
||||||
@@ -375,7 +396,7 @@ public class DefaultActionMapper implements ActionMapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cleans up action name from suspicious characters
|
* Checks action name against allowed pattern if not matched returns default action name
|
||||||
*
|
*
|
||||||
* @param rawActionName action name extracted from URI
|
* @param rawActionName action name extracted from URI
|
||||||
* @return safe action name
|
* @return safe action name
|
||||||
@@ -384,18 +405,23 @@ public class DefaultActionMapper implements ActionMapper {
|
|||||||
if (allowedActionNames.matcher(rawActionName).matches()) {
|
if (allowedActionNames.matcher(rawActionName).matches()) {
|
||||||
return rawActionName;
|
return rawActionName;
|
||||||
} else {
|
} else {
|
||||||
if (LOG.isWarnEnabled()) {
|
LOG.warn("{} did not match allowed action names {} - default action {} will be used!", rawActionName, allowedActionNames, defaultActionName);
|
||||||
LOG.warn("Action [#0] does not match allowed action names pattern [#1], cleaning it up!",
|
return defaultActionName;
|
||||||
rawActionName, allowedActionNames);
|
}
|
||||||
}
|
}
|
||||||
String cleanActionName = rawActionName;
|
|
||||||
for (String chunk : allowedActionNames.split(rawActionName)) {
|
/**
|
||||||
cleanActionName = cleanActionName.replace(chunk, "");
|
* Checks method name (when DMI is enabled) against allowed pattern if not matched returns default action name
|
||||||
}
|
*
|
||||||
if (LOG.isDebugEnabled()) {
|
* @param rawMethodName method name extracted from URI
|
||||||
LOG.debug("Cleaned action name [#0]", cleanActionName);
|
* @return safe method name
|
||||||
}
|
*/
|
||||||
return cleanActionName;
|
protected String cleanupMethodName(final String rawMethodName) {
|
||||||
|
if (allowedMethodNames.matcher(rawMethodName).matches()) {
|
||||||
|
return rawMethodName;
|
||||||
|
} else {
|
||||||
|
LOG.warn("{} did not match allowed method names {} - default method {} will be used!", rawMethodName, allowedMethodNames, defaultMethodName);
|
||||||
|
return defaultMethodName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,9 +26,9 @@ import com.opensymphony.xwork2.inject.Inject;
|
|||||||
import com.opensymphony.xwork2.util.logging.Logger;
|
import com.opensymphony.xwork2.util.logging.Logger;
|
||||||
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||||
import org.apache.struts2.StrutsConstants;
|
import org.apache.struts2.StrutsConstants;
|
||||||
|
import org.apache.struts2.util.URLDecoderUtil;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import java.net.URLDecoder;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
@@ -133,10 +133,10 @@ public class Restful2ActionMapper extends DefaultActionMapper {
|
|||||||
|
|
||||||
while (st.hasMoreTokens()) {
|
while (st.hasMoreTokens()) {
|
||||||
if (isNameTok) {
|
if (isNameTok) {
|
||||||
paramName = URLDecoder.decode(st.nextToken(), "UTF-8");
|
paramName = URLDecoderUtil.decode(st.nextToken(), "UTF-8");
|
||||||
isNameTok = false;
|
isNameTok = false;
|
||||||
} else {
|
} else {
|
||||||
paramValue = URLDecoder.decode(st.nextToken(), "UTF-8");
|
paramValue = URLDecoderUtil.decode(st.nextToken(), "UTF-8");
|
||||||
|
|
||||||
if ((paramName != null) && (paramName.length() > 0)) {
|
if ((paramName != null) && (paramName.length() > 0)) {
|
||||||
parameters.put(paramName, paramValue);
|
parameters.put(paramName, paramValue);
|
||||||
|
|||||||
@@ -25,9 +25,9 @@ import com.opensymphony.xwork2.config.ConfigurationManager;
|
|||||||
import com.opensymphony.xwork2.util.logging.Logger;
|
import com.opensymphony.xwork2.util.logging.Logger;
|
||||||
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||||
import org.apache.struts2.RequestUtils;
|
import org.apache.struts2.RequestUtils;
|
||||||
|
import org.apache.struts2.util.URLDecoderUtil;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import java.net.URLDecoder;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
@@ -67,10 +67,10 @@ public class RestfulActionMapper implements ActionMapper {
|
|||||||
|
|
||||||
while (st.hasMoreTokens()) {
|
while (st.hasMoreTokens()) {
|
||||||
if (isNameTok) {
|
if (isNameTok) {
|
||||||
paramName = URLDecoder.decode(st.nextToken(), "UTF-8");
|
paramName = URLDecoderUtil.decode(st.nextToken(), "UTF-8");
|
||||||
isNameTok = false;
|
isNameTok = false;
|
||||||
} else {
|
} else {
|
||||||
paramValue = URLDecoder.decode(st.nextToken(), "UTF-8");
|
paramValue = URLDecoderUtil.decode(st.nextToken(), "UTF-8");
|
||||||
|
|
||||||
if ((paramName != null) && (paramName.length() > 0)) {
|
if ((paramName != null) && (paramName.length() > 0)) {
|
||||||
parameters.put(paramName, paramValue);
|
parameters.put(paramName, paramValue);
|
||||||
|
|||||||
+5
-1
@@ -120,7 +120,11 @@ public class JakartaMultiPartRequest implements MultiPartRequest {
|
|||||||
if (LOG.isDebugEnabled()) {
|
if (LOG.isDebugEnabled()) {
|
||||||
LOG.debug("Preparing error message for key: [#0]", errorKey);
|
LOG.debug("Preparing error message for key: [#0]", errorKey);
|
||||||
}
|
}
|
||||||
return LocalizedTextUtil.findText(this.getClass(), errorKey, defaultLocale, e.getMessage(), args);
|
if (LocalizedTextUtil.findText(this.getClass(), errorKey, defaultLocale, null, new Object[0]) == null) {
|
||||||
|
return LocalizedTextUtil.findText(this.getClass(), "struts.messages.error.uploading", defaultLocale, null, new Object[] { e.getMessage() });
|
||||||
|
} else {
|
||||||
|
return LocalizedTextUtil.findText(this.getClass(), errorKey, defaultLocale, null, args);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void processUpload(HttpServletRequest request, String saveDir) throws FileUploadException, UnsupportedEncodingException {
|
protected void processUpload(HttpServletRequest request, String saveDir) throws FileUploadException, UnsupportedEncodingException {
|
||||||
|
|||||||
+16
-1
@@ -28,6 +28,7 @@ import java.util.HashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Multi-part form data request adapter for Jakarta Commons FileUpload package that
|
* Multi-part form data request adapter for Jakarta Commons FileUpload package that
|
||||||
@@ -392,6 +393,12 @@ public class JakartaStreamMultiPartRequest implements MultiPartRequest {
|
|||||||
* @param location
|
* @param location
|
||||||
*/
|
*/
|
||||||
private void processFileItemStreamAsFileField(FileItemStream itemStream, String location) {
|
private void processFileItemStreamAsFileField(FileItemStream itemStream, String location) {
|
||||||
|
// Skip file uploads that don't have a file name - meaning that no file was selected.
|
||||||
|
if (itemStream.getName() == null || itemStream.getName().trim().length() < 1) {
|
||||||
|
LOG.debug("No file has been uploaded for the field: {}", itemStream.getFieldName());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
File file = null;
|
File file = null;
|
||||||
try {
|
try {
|
||||||
// Create the temporary upload file.
|
// Create the temporary upload file.
|
||||||
@@ -433,6 +440,10 @@ public class JakartaStreamMultiPartRequest implements MultiPartRequest {
|
|||||||
suffix = name.substring(name.lastIndexOf('.'));
|
suffix = name.substring(name.lastIndexOf('.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (prefix.length() < 3) {
|
||||||
|
prefix = UUID.randomUUID().toString();
|
||||||
|
}
|
||||||
|
|
||||||
File file = File.createTempFile(prefix + "_", suffix, new File(location));
|
File file = File.createTempFile(prefix + "_", suffix, new File(location));
|
||||||
LOG.debug("Creating temporary file '#0' (originally '#1').", file.getName(), fileName);
|
LOG.debug("Creating temporary file '#0' (originally '#1').", file.getName(), fileName);
|
||||||
return file;
|
return file;
|
||||||
@@ -528,7 +539,11 @@ public class JakartaStreamMultiPartRequest implements MultiPartRequest {
|
|||||||
String errorKey = "struts.message.upload.error." + e.getClass().getSimpleName();
|
String errorKey = "struts.message.upload.error." + e.getClass().getSimpleName();
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("Preparing error message for key: [#0]", errorKey);
|
LOG.debug("Preparing error message for key: [#0]", errorKey);
|
||||||
return LocalizedTextUtil.findText(this.getClass(), errorKey, defaultLocale, e.getMessage(), args);
|
if (LocalizedTextUtil.findText(this.getClass(), errorKey, defaultLocale, null, new Object[0]) == null) {
|
||||||
|
return LocalizedTextUtil.findText(this.getClass(), "struts.messages.error.uploading", defaultLocale, null, new Object[] { e.getMessage() });
|
||||||
|
} else {
|
||||||
|
return LocalizedTextUtil.findText(this.getClass(), errorKey, defaultLocale, null, args);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+5
-1
@@ -108,7 +108,11 @@ public class MultiPartRequestWrapper extends StrutsRequestWrapper {
|
|||||||
if (LOG.isDebugEnabled()) {
|
if (LOG.isDebugEnabled()) {
|
||||||
LOG.debug("Preparing error message for key: [#0]", errorKey);
|
LOG.debug("Preparing error message for key: [#0]", errorKey);
|
||||||
}
|
}
|
||||||
return LocalizedTextUtil.findText(this.getClass(), errorKey, defaultLocale, e.getMessage(), args);
|
if (LocalizedTextUtil.findText(this.getClass(), errorKey, getLocale(), null, new Object[0]) == null) {
|
||||||
|
return LocalizedTextUtil.findText(this.getClass(), "struts.messages.error.uploading", defaultLocale, null, new Object[] { e.getMessage() });
|
||||||
|
} else {
|
||||||
|
return LocalizedTextUtil.findText(this.getClass(), errorKey, defaultLocale, null, args);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import com.opensymphony.xwork2.util.ValueStackFactory;
|
|||||||
import com.opensymphony.xwork2.util.logging.Logger;
|
import com.opensymphony.xwork2.util.logging.Logger;
|
||||||
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||||
import org.apache.struts2.RequestUtils;
|
import org.apache.struts2.RequestUtils;
|
||||||
|
import org.apache.struts2.ServletActionContext;
|
||||||
import org.apache.struts2.StrutsException;
|
import org.apache.struts2.StrutsException;
|
||||||
import org.apache.struts2.dispatcher.Dispatcher;
|
import org.apache.struts2.dispatcher.Dispatcher;
|
||||||
import org.apache.struts2.dispatcher.mapper.ActionMapper;
|
import org.apache.struts2.dispatcher.mapper.ActionMapper;
|
||||||
@@ -135,6 +136,7 @@ public class PrepareOperations {
|
|||||||
// Wrap request first, just in case it is multipart/form-data
|
// Wrap request first, just in case it is multipart/form-data
|
||||||
// parameters might not be accessible through before encoding (ww-1278)
|
// parameters might not be accessible through before encoding (ww-1278)
|
||||||
request = dispatcher.wrapRequest(request);
|
request = dispatcher.wrapRequest(request);
|
||||||
|
ServletActionContext.setRequest(request);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new ServletException("Could not wrap servlet request with MultipartRequestWrapper!", e);
|
throw new ServletException("Could not wrap servlet request with MultipartRequestWrapper!", e);
|
||||||
}
|
}
|
||||||
@@ -166,7 +168,9 @@ public class PrepareOperations {
|
|||||||
request.setAttribute(STRUTS_ACTION_MAPPING_KEY, mapping);
|
request.setAttribute(STRUTS_ACTION_MAPPING_KEY, mapping);
|
||||||
}
|
}
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
dispatcher.sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);
|
if (dispatcher.isHandleException() || dispatcher.isDevMode()) {
|
||||||
|
dispatcher.sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ package org.apache.struts2.interceptor;
|
|||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
import com.opensymphony.xwork2.ActionContext;
|
||||||
import com.opensymphony.xwork2.ActionInvocation;
|
import com.opensymphony.xwork2.ActionInvocation;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -78,6 +79,7 @@ public class BackgroundProcess implements Serializable {
|
|||||||
* @throws Exception any exception thrown will be thrown, in turn, by the ExecuteAndWaitInterceptor
|
* @throws Exception any exception thrown will be thrown, in turn, by the ExecuteAndWaitInterceptor
|
||||||
*/
|
*/
|
||||||
protected void beforeInvocation() throws Exception {
|
protected void beforeInvocation() throws Exception {
|
||||||
|
ActionContext.setContext(invocation.getInvocationContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -88,6 +90,7 @@ public class BackgroundProcess implements Serializable {
|
|||||||
* @throws Exception any exception thrown will be thrown, in turn, by the ExecuteAndWaitInterceptor
|
* @throws Exception any exception thrown will be thrown, in turn, by the ExecuteAndWaitInterceptor
|
||||||
*/
|
*/
|
||||||
protected void afterInvocation() throws Exception {
|
protected void afterInvocation() throws Exception {
|
||||||
|
ActionContext.setContext(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -237,7 +237,7 @@ public class CookieInterceptor extends AbstractInterceptor {
|
|||||||
String name = cookie.getName();
|
String name = cookie.getName();
|
||||||
String value = cookie.getValue();
|
String value = cookie.getValue();
|
||||||
|
|
||||||
if (isAcceptableName(name) && isAcceptableValue(value)) {
|
if (isAcceptableName(name)) {
|
||||||
if (cookiesNameSet.contains("*")) {
|
if (cookiesNameSet.contains("*")) {
|
||||||
if (LOG.isDebugEnabled()) {
|
if (LOG.isDebugEnabled()) {
|
||||||
LOG.debug("contains cookie name [*] in configured cookies name set, cookie with name [" + name + "] with value [" + value + "] will be injected");
|
LOG.debug("contains cookie name [*] in configured cookies name set, cookie with name [" + name + "] with value [" + value + "] will be injected");
|
||||||
@@ -258,16 +258,6 @@ public class CookieInterceptor extends AbstractInterceptor {
|
|||||||
return invocation.invoke();
|
return invocation.invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if value of Cookie doesn't contain vulnerable code
|
|
||||||
*
|
|
||||||
* @param value of Cookie
|
|
||||||
* @return true|false
|
|
||||||
*/
|
|
||||||
protected boolean isAcceptableValue(String value) {
|
|
||||||
return !isExcluded(value) && isAccepted(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if name of Cookie doesn't contain vulnerable code
|
* Checks if name of Cookie doesn't contain vulnerable code
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -192,12 +192,6 @@ public class ExecuteAndWaitInterceptor extends MethodFilterInterceptor {
|
|||||||
this.container = container;
|
this.container = container;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
|
||||||
* @see com.opensymphony.xwork2.interceptor.Interceptor#init()
|
|
||||||
*/
|
|
||||||
public void init() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new background process
|
* Creates a new background process
|
||||||
*
|
*
|
||||||
@@ -303,13 +297,6 @@ public class ExecuteAndWaitInterceptor extends MethodFilterInterceptor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* (non-Javadoc)
|
|
||||||
* @see com.opensymphony.xwork2.interceptor.Interceptor#destroy()
|
|
||||||
*/
|
|
||||||
public void destroy() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs the initial delay.
|
* Performs the initial delay.
|
||||||
* <p/>
|
* <p/>
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import java.util.Collection;
|
|||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.struts2.ServletActionContext;
|
||||||
import org.apache.struts2.dispatcher.ServletRedirectResult;
|
import org.apache.struts2.dispatcher.ServletRedirectResult;
|
||||||
|
|
||||||
import com.opensymphony.xwork2.ActionContext;
|
import com.opensymphony.xwork2.ActionContext;
|
||||||
@@ -170,6 +171,7 @@ public class MessageStoreInterceptor extends AbstractInterceptor {
|
|||||||
public void setAllowRequestParameterSwitch(boolean allowRequestParameterSwitch) {
|
public void setAllowRequestParameterSwitch(boolean allowRequestParameterSwitch) {
|
||||||
this.allowRequestParameterSwitch = allowRequestParameterSwitch;
|
this.allowRequestParameterSwitch = allowRequestParameterSwitch;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean getAllowRequestParameterSwitch() {
|
public boolean getAllowRequestParameterSwitch() {
|
||||||
return this.allowRequestParameterSwitch;
|
return this.allowRequestParameterSwitch;
|
||||||
}
|
}
|
||||||
@@ -177,6 +179,7 @@ public class MessageStoreInterceptor extends AbstractInterceptor {
|
|||||||
public void setRequestParameterSwitch(String requestParameterSwitch) {
|
public void setRequestParameterSwitch(String requestParameterSwitch) {
|
||||||
this.requestParameterSwitch = requestParameterSwitch;
|
this.requestParameterSwitch = requestParameterSwitch;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getRequestParameterSwitch() {
|
public String getRequestParameterSwitch() {
|
||||||
return this.requestParameterSwitch;
|
return this.requestParameterSwitch;
|
||||||
}
|
}
|
||||||
@@ -184,18 +187,23 @@ public class MessageStoreInterceptor extends AbstractInterceptor {
|
|||||||
public void setOperationMode(String operationMode) {
|
public void setOperationMode(String operationMode) {
|
||||||
this.operationMode = operationMode;
|
this.operationMode = operationMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getOperationModel() {
|
public String getOperationModel() {
|
||||||
return this.operationMode;
|
return this.operationMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String intercept(ActionInvocation invocation) throws Exception {
|
public String intercept(ActionInvocation invocation) throws Exception {
|
||||||
|
LOG.trace("entering MessageStoreInterceptor ...");
|
||||||
if (LOG.isDebugEnabled()) {
|
if (LOG.isDebugEnabled()) {
|
||||||
LOG.debug("entering MessageStoreInterceptor ...");
|
LOG.debug("entering MessageStoreInterceptor ...");
|
||||||
}
|
}
|
||||||
|
|
||||||
before(invocation);
|
before(invocation);
|
||||||
|
|
||||||
|
LOG.trace("Registering listener to store messages before result will be executed");
|
||||||
|
invocation.addPreResultListener(new MessageStorePreResultListener(this));
|
||||||
|
|
||||||
String result = invocation.invoke();
|
String result = invocation.invoke();
|
||||||
after(invocation, result);
|
|
||||||
|
|
||||||
if (LOG.isDebugEnabled()) {
|
if (LOG.isDebugEnabled()) {
|
||||||
LOG.debug("exit executing MessageStoreInterceptor");
|
LOG.debug("exit executing MessageStoreInterceptor");
|
||||||
@@ -261,50 +269,6 @@ public class MessageStoreInterceptor extends AbstractInterceptor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle the storing of field errors / action messages / field errors, which is
|
|
||||||
* done after action invocation, and the <code>operationMode</code> is in 'STORE'.
|
|
||||||
*
|
|
||||||
* @param invocation
|
|
||||||
* @param result
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
protected void after(ActionInvocation invocation, String result) throws Exception {
|
|
||||||
|
|
||||||
String reqOperationMode = getRequestOperationMode(invocation);
|
|
||||||
boolean isRedirect = invocation.getResult() instanceof ServletRedirectResult;
|
|
||||||
if (STORE_MODE.equalsIgnoreCase(reqOperationMode) ||
|
|
||||||
STORE_MODE.equalsIgnoreCase(operationMode) ||
|
|
||||||
(AUTOMATIC_MODE.equalsIgnoreCase(operationMode) && isRedirect)) {
|
|
||||||
|
|
||||||
Object action = invocation.getAction();
|
|
||||||
if (action instanceof ValidationAware) {
|
|
||||||
// store error / messages into session
|
|
||||||
Map session = (Map) invocation.getInvocationContext().get(ActionContext.SESSION);
|
|
||||||
|
|
||||||
if (session == null) {
|
|
||||||
if (LOG.isDebugEnabled()) {
|
|
||||||
LOG.debug("Could not store action ["+action+"] error/messages into session, because session hasn't been opened yet.");
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (LOG.isDebugEnabled()) {
|
|
||||||
LOG.debug("store action ["+action+"] error/messages into session ");
|
|
||||||
}
|
|
||||||
|
|
||||||
ValidationAware validationAwareAction = (ValidationAware) action;
|
|
||||||
session.put(actionErrorsSessionKey, validationAwareAction.getActionErrors());
|
|
||||||
session.put(actionMessagesSessionKey, validationAwareAction.getActionMessages());
|
|
||||||
session.put(fieldErrorsSessionKey, validationAwareAction.getFieldErrors());
|
|
||||||
}
|
|
||||||
else if(LOG.isDebugEnabled()) {
|
|
||||||
LOG.debug("Action ["+action+"] is not ValidationAware, no message / error that are storeable");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the operationMode through request paramter, if <code>allowRequestParameterSwitch</code>
|
* Get the operationMode through request paramter, if <code>allowRequestParameterSwitch</code>
|
||||||
* is 'true', else it simply returns 'NONE', meaning its neither in the 'STORE_MODE' nor
|
* is 'true', else it simply returns 'NONE', meaning its neither in the 'STORE_MODE' nor
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* 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.ActionInvocation;
|
||||||
|
import com.opensymphony.xwork2.ValidationAware;
|
||||||
|
import com.opensymphony.xwork2.config.entities.ResultConfig;
|
||||||
|
import com.opensymphony.xwork2.interceptor.PreResultListener;
|
||||||
|
import com.opensymphony.xwork2.util.logging.Logger;
|
||||||
|
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||||
|
import org.apache.struts2.ServletActionContext;
|
||||||
|
import org.apache.struts2.dispatcher.ServletActionRedirectResult;
|
||||||
|
import org.apache.struts2.dispatcher.ServletRedirectResult;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This listener is used by {@link MessageStoreInterceptor} to store messages in HttpSession
|
||||||
|
* just before result will be executed. It must be done that way as after result will be executed
|
||||||
|
* HttpSession cannot be modified (response was already sent to browser).
|
||||||
|
*/
|
||||||
|
class MessageStorePreResultListener implements PreResultListener {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(MessageStorePreResultListener.class);
|
||||||
|
|
||||||
|
private MessageStoreInterceptor interceptor;
|
||||||
|
|
||||||
|
public MessageStorePreResultListener(MessageStoreInterceptor interceptor) {
|
||||||
|
this.interceptor = interceptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void beforeResult(ActionInvocation invocation, String resultCode) {
|
||||||
|
|
||||||
|
boolean isCommitted = ServletActionContext.getResponse().isCommitted();
|
||||||
|
if (isCommitted) {
|
||||||
|
LOG.trace("Response was already committed, cannot store messages!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isInvalidated = ServletActionContext.getRequest().getSession(false) == null;
|
||||||
|
if (isInvalidated) {
|
||||||
|
LOG.trace("Session was invalidated or never created, cannot store messages!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> session = invocation.getInvocationContext().getSession();
|
||||||
|
if (session == null) {
|
||||||
|
LOG.trace("Could not store action [#0] error/messages into session, because session hasn't been opened yet.", invocation.getAction());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String reqOperationMode = interceptor.getRequestOperationMode(invocation);
|
||||||
|
|
||||||
|
boolean isRedirect = false;
|
||||||
|
try {
|
||||||
|
ResultConfig resultConfig = invocation.getProxy().getConfig().getResults().get(resultCode);
|
||||||
|
if (resultConfig != null) {
|
||||||
|
isRedirect = ServletRedirectResult.class.isAssignableFrom(Class.forName(resultConfig.getClassName()));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.warn("Cannot read result!", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MessageStoreInterceptor.STORE_MODE.equalsIgnoreCase(reqOperationMode) ||
|
||||||
|
MessageStoreInterceptor.STORE_MODE.equalsIgnoreCase(interceptor.getOperationModel()) ||
|
||||||
|
(MessageStoreInterceptor.AUTOMATIC_MODE.equalsIgnoreCase(interceptor.getOperationModel()) && isRedirect)) {
|
||||||
|
|
||||||
|
Object action = invocation.getAction();
|
||||||
|
if (action instanceof ValidationAware) {
|
||||||
|
LOG.debug("Storing action [#0] error/messages into session ", action);
|
||||||
|
|
||||||
|
ValidationAware validationAwareAction = (ValidationAware) action;
|
||||||
|
session.put(MessageStoreInterceptor.actionErrorsSessionKey, validationAwareAction.getActionErrors());
|
||||||
|
session.put(MessageStoreInterceptor.actionMessagesSessionKey, validationAwareAction.getActionMessages());
|
||||||
|
session.put(MessageStoreInterceptor.fieldErrorsSessionKey, validationAwareAction.getFieldErrors());
|
||||||
|
|
||||||
|
} else {
|
||||||
|
LOG.debug("Action [#0] is not ValidationAware, no message / error that are storeable", action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -40,12 +40,15 @@ public class ComponentUtils {
|
|||||||
/**
|
/**
|
||||||
* Check if object is expression base on altSyntax
|
* Check if object is expression base on altSyntax
|
||||||
*
|
*
|
||||||
* @param value to treat as an expression
|
* @param expr to treat as an expression
|
||||||
* @return true if it is an expression
|
* @return true if it is an expression
|
||||||
*/
|
*/
|
||||||
public static boolean isExpression(Object value) {
|
public static boolean isExpression(String expr) {
|
||||||
String expr = value.toString();
|
return expr != null && expr.startsWith("%{") && expr.endsWith("}");
|
||||||
return expr.startsWith("%{") && expr.endsWith("}");
|
}
|
||||||
|
|
||||||
|
public static boolean containsExpression(String expr) {
|
||||||
|
return expr != null && expr.contains("%{") && expr.contains("}");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package org.apache.struts2.util;
|
||||||
|
|
||||||
|
import org.apache.struts2.util.tomcat.buf.UDecoder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* URLDecoderUtil serves as a facade for a correct URL decoding implementation.
|
||||||
|
* As of Struts 2.3.25 it uses Tomcat URLDecoder functionality rather than the one found in java.io.
|
||||||
|
*/
|
||||||
|
public class URLDecoderUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes a <code>x-www-form-urlencoded</code> string.
|
||||||
|
* @param sequence the String to decode
|
||||||
|
* @param charset The name of a supported character encoding.
|
||||||
|
* @return the newly decoded <code>String</code>
|
||||||
|
* @exception IllegalArgumentException If the encoding is not valid
|
||||||
|
*/
|
||||||
|
public static String decode(String sequence, String charset) {
|
||||||
|
return UDecoder.URLDecode(sequence, charset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes a <code>x-www-form-urlencoded</code> string.
|
||||||
|
* @param sequence the String to decode
|
||||||
|
* @param charset The name of a supported character encoding.
|
||||||
|
* @param isQueryString whether input is a query string. If <code>true</code> other decoding rules apply.
|
||||||
|
* @return the newly decoded <code>String</code>
|
||||||
|
* @exception IllegalArgumentException If the encoding is not valid
|
||||||
|
*/
|
||||||
|
public static String decode(String sequence, String charset, boolean isQueryString) {
|
||||||
|
return UDecoder.URLDecode(sequence, charset, isQueryString);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,255 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.struts2.util.tomcat.buf;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class implements some basic ASCII character handling functions.
|
||||||
|
*
|
||||||
|
* @author dac@eng.sun.com
|
||||||
|
* @author James Todd [gonzo@eng.sun.com]
|
||||||
|
*/
|
||||||
|
public final class Ascii {
|
||||||
|
/*
|
||||||
|
* Character translation tables.
|
||||||
|
*/
|
||||||
|
|
||||||
|
private static final byte[] toUpper = new byte[256];
|
||||||
|
private static final byte[] toLower = new byte[256];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Character type tables.
|
||||||
|
*/
|
||||||
|
|
||||||
|
private static final boolean[] isAlpha = new boolean[256];
|
||||||
|
private static final boolean[] isUpper = new boolean[256];
|
||||||
|
private static final boolean[] isLower = new boolean[256];
|
||||||
|
private static final boolean[] isWhite = new boolean[256];
|
||||||
|
private static final boolean[] isDigit = new boolean[256];
|
||||||
|
|
||||||
|
private static final long OVERFLOW_LIMIT = Long.MAX_VALUE / 10;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize character translation and type tables.
|
||||||
|
*/
|
||||||
|
static {
|
||||||
|
for (int i = 0; i < 256; i++) {
|
||||||
|
toUpper[i] = (byte)i;
|
||||||
|
toLower[i] = (byte)i;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int lc = 'a'; lc <= 'z'; lc++) {
|
||||||
|
int uc = lc + 'A' - 'a';
|
||||||
|
|
||||||
|
toUpper[lc] = (byte)uc;
|
||||||
|
toLower[uc] = (byte)lc;
|
||||||
|
isAlpha[lc] = true;
|
||||||
|
isAlpha[uc] = true;
|
||||||
|
isLower[lc] = true;
|
||||||
|
isUpper[uc] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
isWhite[ ' '] = true;
|
||||||
|
isWhite['\t'] = true;
|
||||||
|
isWhite['\r'] = true;
|
||||||
|
isWhite['\n'] = true;
|
||||||
|
isWhite['\f'] = true;
|
||||||
|
isWhite['\b'] = true;
|
||||||
|
|
||||||
|
for (int d = '0'; d <= '9'; d++) {
|
||||||
|
isDigit[d] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the upper case equivalent of the specified ASCII character.
|
||||||
|
* @deprecated Unused. Will be removed in Tomcat 8.0.x onwards.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static int toUpper(int c) {
|
||||||
|
return toUpper[c & 0xff] & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the lower case equivalent of the specified ASCII character.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public static int toLower(int c) {
|
||||||
|
return toLower[c & 0xff] & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the specified ASCII character is upper or lower case.
|
||||||
|
* @deprecated Unused. Will be removed in Tomcat 8.0.x onwards.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static boolean isAlpha(int c) {
|
||||||
|
return isAlpha[c & 0xff];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the specified ASCII character is upper case.
|
||||||
|
* @deprecated Unused. Will be removed in Tomcat 8.0.x onwards.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static boolean isUpper(int c) {
|
||||||
|
return isUpper[c & 0xff];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the specified ASCII character is lower case.
|
||||||
|
* @deprecated Unused. Will be removed in Tomcat 8.0.x onwards.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static boolean isLower(int c) {
|
||||||
|
return isLower[c & 0xff];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the specified ASCII character is white space.
|
||||||
|
* @deprecated Unused. Will be removed in Tomcat 8.0.x onwards.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static boolean isWhite(int c) {
|
||||||
|
return isWhite[c & 0xff];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the specified ASCII character is a digit.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public static boolean isDigit(int c) {
|
||||||
|
return isDigit[c & 0xff];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses an unsigned integer from the specified subarray of bytes.
|
||||||
|
* @param b the bytes to parse
|
||||||
|
* @param off the start offset of the bytes
|
||||||
|
* @param len the length of the bytes
|
||||||
|
* @exception NumberFormatException if the integer format was invalid
|
||||||
|
* @deprecated Unused. Will be removed in Tomcat 8.0.x onwards.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static int parseInt(byte[] b, int off, int len)
|
||||||
|
throws NumberFormatException
|
||||||
|
{
|
||||||
|
int c;
|
||||||
|
|
||||||
|
if (b == null || len <= 0 || !isDigit(c = b[off++])) {
|
||||||
|
throw new NumberFormatException();
|
||||||
|
}
|
||||||
|
|
||||||
|
int n = c - '0';
|
||||||
|
|
||||||
|
while (--len > 0) {
|
||||||
|
if (!isDigit(c = b[off++])) {
|
||||||
|
throw new NumberFormatException();
|
||||||
|
}
|
||||||
|
n = n * 10 + c - '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Unused. Will be removed in Tomcat 8.0.x onwards.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static int parseInt(char[] b, int off, int len)
|
||||||
|
throws NumberFormatException
|
||||||
|
{
|
||||||
|
int c;
|
||||||
|
|
||||||
|
if (b == null || len <= 0 || !isDigit(c = b[off++])) {
|
||||||
|
throw new NumberFormatException();
|
||||||
|
}
|
||||||
|
|
||||||
|
int n = c - '0';
|
||||||
|
|
||||||
|
while (--len > 0) {
|
||||||
|
if (!isDigit(c = b[off++])) {
|
||||||
|
throw new NumberFormatException();
|
||||||
|
}
|
||||||
|
n = n * 10 + c - '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses an unsigned long from the specified subarray of bytes.
|
||||||
|
* @param b the bytes to parse
|
||||||
|
* @param off the start offset of the bytes
|
||||||
|
* @param len the length of the bytes
|
||||||
|
* @exception NumberFormatException if the long format was invalid
|
||||||
|
*/
|
||||||
|
public static long parseLong(byte[] b, int off, int len)
|
||||||
|
throws NumberFormatException
|
||||||
|
{
|
||||||
|
int c;
|
||||||
|
|
||||||
|
if (b == null || len <= 0 || !isDigit(c = b[off++])) {
|
||||||
|
throw new NumberFormatException();
|
||||||
|
}
|
||||||
|
|
||||||
|
long n = c - '0';
|
||||||
|
while (--len > 0) {
|
||||||
|
if (isDigit(c = b[off++]) &&
|
||||||
|
(n < OVERFLOW_LIMIT || (n == OVERFLOW_LIMIT && (c - '0') < 8))) {
|
||||||
|
n = n * 10 + c - '0';
|
||||||
|
} else {
|
||||||
|
throw new NumberFormatException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Unused. Will be removed in Tomcat 8.0.x onwards.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static long parseLong(char[] b, int off, int len)
|
||||||
|
throws NumberFormatException
|
||||||
|
{
|
||||||
|
int c;
|
||||||
|
|
||||||
|
if (b == null || len <= 0 || !isDigit(c = b[off++])) {
|
||||||
|
throw new NumberFormatException();
|
||||||
|
}
|
||||||
|
|
||||||
|
long n = c - '0';
|
||||||
|
long m;
|
||||||
|
|
||||||
|
while (--len > 0) {
|
||||||
|
if (!isDigit(c = b[off++])) {
|
||||||
|
throw new NumberFormatException();
|
||||||
|
}
|
||||||
|
m = n * 10 + c - '0';
|
||||||
|
|
||||||
|
if (m < n) {
|
||||||
|
// Overflow
|
||||||
|
throw new NumberFormatException();
|
||||||
|
} else {
|
||||||
|
n = m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,201 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.struts2.util.tomcat.buf;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.CharBuffer;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.nio.charset.CharsetDecoder;
|
||||||
|
import java.nio.charset.CoderResult;
|
||||||
|
import java.nio.charset.CodingErrorAction;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NIO based character decoder.
|
||||||
|
*/
|
||||||
|
public class B2CConverter {
|
||||||
|
|
||||||
|
private static final Map<String, Charset> encodingToCharsetCache =
|
||||||
|
new HashMap<String, Charset>();
|
||||||
|
|
||||||
|
public static final Charset ISO_8859_1;
|
||||||
|
public static final Charset UTF_8;
|
||||||
|
|
||||||
|
// Protected so unit tests can use it
|
||||||
|
protected static final int LEFTOVER_SIZE = 9;
|
||||||
|
|
||||||
|
static {
|
||||||
|
for (Charset charset: Charset.availableCharsets().values()) {
|
||||||
|
encodingToCharsetCache.put(
|
||||||
|
charset.name().toLowerCase(Locale.ENGLISH), charset);
|
||||||
|
for (String alias : charset.aliases()) {
|
||||||
|
encodingToCharsetCache.put(
|
||||||
|
alias.toLowerCase(Locale.ENGLISH), charset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Charset iso88591 = null;
|
||||||
|
Charset utf8 = null;
|
||||||
|
try {
|
||||||
|
iso88591 = getCharset("ISO-8859-1");
|
||||||
|
utf8 = getCharset("UTF-8");
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
// Impossible. All JVMs must support these.
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
ISO_8859_1 = iso88591;
|
||||||
|
UTF_8 = utf8;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Charset getCharset(String enc)
|
||||||
|
throws UnsupportedEncodingException {
|
||||||
|
|
||||||
|
// Encoding names should all be ASCII
|
||||||
|
String lowerCaseEnc = enc.toLowerCase(Locale.ENGLISH);
|
||||||
|
|
||||||
|
return getCharsetLower(lowerCaseEnc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only to be used when it is known that the encoding name is in lower case.
|
||||||
|
*/
|
||||||
|
public static Charset getCharsetLower(String lowerCaseEnc)
|
||||||
|
throws UnsupportedEncodingException {
|
||||||
|
|
||||||
|
Charset charset = encodingToCharsetCache.get(lowerCaseEnc);
|
||||||
|
|
||||||
|
if (charset == null) {
|
||||||
|
// Pre-population of the cache means this must be invalid
|
||||||
|
throw new UnsupportedEncodingException("The character encoding " + lowerCaseEnc + " is not supported");
|
||||||
|
}
|
||||||
|
return charset;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final CharsetDecoder decoder;
|
||||||
|
private ByteBuffer bb = null;
|
||||||
|
private CharBuffer cb = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Leftover buffer used for incomplete characters.
|
||||||
|
*/
|
||||||
|
private final ByteBuffer leftovers;
|
||||||
|
|
||||||
|
public B2CConverter(String encoding) throws IOException {
|
||||||
|
this(encoding, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public B2CConverter(String encoding, boolean replaceOnError)
|
||||||
|
throws IOException {
|
||||||
|
byte[] left = new byte[LEFTOVER_SIZE];
|
||||||
|
leftovers = ByteBuffer.wrap(left);
|
||||||
|
CodingErrorAction action;
|
||||||
|
if (replaceOnError) {
|
||||||
|
action = CodingErrorAction.REPLACE;
|
||||||
|
} else {
|
||||||
|
action = CodingErrorAction.REPORT;
|
||||||
|
}
|
||||||
|
Charset charset = getCharset(encoding);
|
||||||
|
// Special case. Use the Apache Harmony based UTF-8 decoder because it
|
||||||
|
// - a) rejects invalid sequences that the JVM decoder does not
|
||||||
|
// - b) fails faster for some invalid sequences
|
||||||
|
if (charset.equals(UTF_8)) {
|
||||||
|
decoder = new Utf8Decoder();
|
||||||
|
} else {
|
||||||
|
decoder = charset.newDecoder();
|
||||||
|
}
|
||||||
|
decoder.onMalformedInput(action);
|
||||||
|
decoder.onUnmappableCharacter(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the decoder state.
|
||||||
|
*/
|
||||||
|
public void recycle() {
|
||||||
|
decoder.reset();
|
||||||
|
leftovers.position(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert the given bytes to characters.
|
||||||
|
*
|
||||||
|
* @param bc byte input
|
||||||
|
* @param cc char output
|
||||||
|
* @param endOfInput Is this all of the available data
|
||||||
|
*/
|
||||||
|
public void convert(ByteChunk bc, CharChunk cc, boolean endOfInput)
|
||||||
|
throws IOException {
|
||||||
|
if ((bb == null) || (bb.array() != bc.getBuffer())) {
|
||||||
|
// Create a new byte buffer if anything changed
|
||||||
|
bb = ByteBuffer.wrap(bc.getBuffer(), bc.getStart(), bc.getLength());
|
||||||
|
} else {
|
||||||
|
// Initialize the byte buffer
|
||||||
|
bb.limit(bc.getEnd());
|
||||||
|
bb.position(bc.getStart());
|
||||||
|
}
|
||||||
|
if ((cb == null) || (cb.array() != cc.getBuffer())) {
|
||||||
|
// Create a new char buffer if anything changed
|
||||||
|
cb = CharBuffer.wrap(cc.getBuffer(), cc.getEnd(),
|
||||||
|
cc.getBuffer().length - cc.getEnd());
|
||||||
|
} else {
|
||||||
|
// Initialize the char buffer
|
||||||
|
cb.limit(cc.getBuffer().length);
|
||||||
|
cb.position(cc.getEnd());
|
||||||
|
}
|
||||||
|
CoderResult result = null;
|
||||||
|
// Parse leftover if any are present
|
||||||
|
if (leftovers.position() > 0) {
|
||||||
|
int pos = cb.position();
|
||||||
|
// Loop until one char is decoded or there is a decoder error
|
||||||
|
do {
|
||||||
|
leftovers.put(bc.substractB());
|
||||||
|
leftovers.flip();
|
||||||
|
result = decoder.decode(leftovers, cb, endOfInput);
|
||||||
|
leftovers.position(leftovers.limit());
|
||||||
|
leftovers.limit(leftovers.array().length);
|
||||||
|
} while (result.isUnderflow() && (cb.position() == pos));
|
||||||
|
if (result.isError() || result.isMalformed()) {
|
||||||
|
result.throwException();
|
||||||
|
}
|
||||||
|
bb.position(bc.getStart());
|
||||||
|
leftovers.position(0);
|
||||||
|
}
|
||||||
|
// Do the decoding and get the results into the byte chunk and the char
|
||||||
|
// chunk
|
||||||
|
result = decoder.decode(bb, cb, endOfInput);
|
||||||
|
if (result.isError() || result.isMalformed()) {
|
||||||
|
result.throwException();
|
||||||
|
} else if (result.isOverflow()) {
|
||||||
|
// Propagate current positions to the byte chunk and char chunk, if
|
||||||
|
// this continues the char buffer will get resized
|
||||||
|
bc.setOffset(bb.position());
|
||||||
|
cc.setEnd(cb.position());
|
||||||
|
} else if (result.isUnderflow()) {
|
||||||
|
// Propagate current positions to the byte chunk and char chunk
|
||||||
|
bc.setOffset(bb.position());
|
||||||
|
cc.setEnd(cb.position());
|
||||||
|
// Put leftovers in the leftovers byte buffer
|
||||||
|
if (bc.getLength() > 0) {
|
||||||
|
leftovers.limit(leftovers.array().length);
|
||||||
|
leftovers.position(bc.getLength());
|
||||||
|
bc.substract(leftovers.array(), 0, bc.getLength());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,935 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.struts2.util.tomcat.buf;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.CharBuffer;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In a server it is very important to be able to operate on
|
||||||
|
* the original byte[] without converting everything to chars.
|
||||||
|
* Some protocols are ASCII only, and some allow different
|
||||||
|
* non-UNICODE encodings. The encoding is not known beforehand,
|
||||||
|
* and can even change during the execution of the protocol.
|
||||||
|
* ( for example a multipart message may have parts with different
|
||||||
|
* encoding )
|
||||||
|
*
|
||||||
|
* For HTTP it is not very clear how the encoding of RequestURI
|
||||||
|
* and mime values can be determined, but it is a great advantage
|
||||||
|
* to be able to parse the request without converting to string.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TODO: This class could either extend ByteBuffer, or better a ByteBuffer
|
||||||
|
// inside this way it could provide the search/etc on ByteBuffer, as a helper.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is used to represent a chunk of bytes, and
|
||||||
|
* utilities to manipulate byte[].
|
||||||
|
*
|
||||||
|
* The buffer can be modified and used for both input and output.
|
||||||
|
*
|
||||||
|
* There are 2 modes: The chunk can be associated with a sink - ByteInputChannel
|
||||||
|
* or ByteOutputChannel, which will be used when the buffer is empty (on input)
|
||||||
|
* or filled (on output).
|
||||||
|
* For output, it can also grow. This operating mode is selected by calling
|
||||||
|
* setLimit() or allocate(initial, limit) with limit != -1.
|
||||||
|
*
|
||||||
|
* Various search and append method are defined - similar with String and
|
||||||
|
* StringBuffer, but operating on bytes.
|
||||||
|
*
|
||||||
|
* This is important because it allows processing the http headers directly on
|
||||||
|
* the received bytes, without converting to chars and Strings until the strings
|
||||||
|
* are needed. In addition, the charset is determined later, from headers or
|
||||||
|
* user code.
|
||||||
|
*
|
||||||
|
* @author dac@sun.com
|
||||||
|
* @author James Todd [gonzo@sun.com]
|
||||||
|
* @author Costin Manolache
|
||||||
|
* @author Remy Maucherat
|
||||||
|
*/
|
||||||
|
public final class ByteChunk implements Cloneable, Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/** Input interface, used when the buffer is empty
|
||||||
|
*
|
||||||
|
* Same as java.nio.channel.ReadableByteChannel
|
||||||
|
*/
|
||||||
|
public static interface ByteInputChannel {
|
||||||
|
/**
|
||||||
|
* Read new bytes ( usually the internal conversion buffer ).
|
||||||
|
* The implementation is allowed to ignore the parameters,
|
||||||
|
* and mutate the chunk if it wishes to implement its own buffering.
|
||||||
|
*/
|
||||||
|
public int realReadBytes(byte cbuf[], int off, int len)
|
||||||
|
throws IOException;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Same as java.nio.channel.WrittableByteChannel.
|
||||||
|
*/
|
||||||
|
public static interface ByteOutputChannel {
|
||||||
|
/**
|
||||||
|
* Send the bytes ( usually the internal conversion buffer ).
|
||||||
|
* Expect 8k output if the buffer is full.
|
||||||
|
*/
|
||||||
|
public void realWriteBytes(byte cbuf[], int off, int len)
|
||||||
|
throws IOException;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------
|
||||||
|
|
||||||
|
/** Default encoding used to convert to strings. It should be UTF8,
|
||||||
|
as most standards seem to converge, but the servlet API requires
|
||||||
|
8859_1, and this object is used mostly for servlets.
|
||||||
|
*/
|
||||||
|
public static final Charset DEFAULT_CHARSET = B2CConverter.ISO_8859_1;
|
||||||
|
|
||||||
|
// byte[]
|
||||||
|
private byte[] buff;
|
||||||
|
|
||||||
|
private int start=0;
|
||||||
|
private int end;
|
||||||
|
|
||||||
|
private Charset charset;
|
||||||
|
|
||||||
|
private boolean isSet=false; // XXX
|
||||||
|
|
||||||
|
// How much can it grow, when data is added
|
||||||
|
private int limit=-1;
|
||||||
|
|
||||||
|
private ByteInputChannel in = null;
|
||||||
|
private ByteOutputChannel out = null;
|
||||||
|
|
||||||
|
private boolean optimizedWrite=true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new, uninitialized ByteChunk object.
|
||||||
|
*/
|
||||||
|
public ByteChunk() {
|
||||||
|
// NO-OP
|
||||||
|
}
|
||||||
|
|
||||||
|
public ByteChunk( int initial ) {
|
||||||
|
allocate( initial, -1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Unused. Will be removed in Tomcat 8.0.x onwards.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public ByteChunk getClone() {
|
||||||
|
try {
|
||||||
|
return (ByteChunk)this.clone();
|
||||||
|
} catch( Exception ex) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isNull() {
|
||||||
|
return ! isSet; // buff==null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the message buff to an uninitialized state.
|
||||||
|
*/
|
||||||
|
public void recycle() {
|
||||||
|
// buff = null;
|
||||||
|
charset=null;
|
||||||
|
start=0;
|
||||||
|
end=0;
|
||||||
|
isSet=false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
buff=null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------- Setup --------------------
|
||||||
|
|
||||||
|
public void allocate( int initial, int limit ) {
|
||||||
|
if( buff==null || buff.length < initial ) {
|
||||||
|
buff=new byte[initial];
|
||||||
|
}
|
||||||
|
this.limit=limit;
|
||||||
|
start=0;
|
||||||
|
end=0;
|
||||||
|
isSet=true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the message bytes to the specified subarray of bytes.
|
||||||
|
*
|
||||||
|
* @param b the ascii bytes
|
||||||
|
* @param off the start offset of the bytes
|
||||||
|
* @param len the length of the bytes
|
||||||
|
*/
|
||||||
|
public void setBytes(byte[] b, int off, int len) {
|
||||||
|
buff = b;
|
||||||
|
start = off;
|
||||||
|
end = start+ len;
|
||||||
|
isSet=true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Unused. Will be removed in Tomcat 8.0.x onwards.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public void setOptimizedWrite(boolean optimizedWrite) {
|
||||||
|
this.optimizedWrite = optimizedWrite;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCharset(Charset charset) {
|
||||||
|
this.charset = charset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Charset getCharset() {
|
||||||
|
if (charset == null) {
|
||||||
|
charset = DEFAULT_CHARSET;
|
||||||
|
}
|
||||||
|
return charset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the message bytes.
|
||||||
|
*/
|
||||||
|
public byte[] getBytes() {
|
||||||
|
return getBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the message bytes.
|
||||||
|
*/
|
||||||
|
public byte[] getBuffer() {
|
||||||
|
return buff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the start offset of the bytes.
|
||||||
|
* For output this is the end of the buffer.
|
||||||
|
*/
|
||||||
|
public int getStart() {
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOffset() {
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOffset(int off) {
|
||||||
|
if (end < off ) {
|
||||||
|
end=off;
|
||||||
|
}
|
||||||
|
start=off;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the length of the bytes.
|
||||||
|
* XXX need to clean this up
|
||||||
|
*/
|
||||||
|
public int getLength() {
|
||||||
|
return end-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Maximum amount of data in this buffer.
|
||||||
|
*
|
||||||
|
* If -1 or not set, the buffer will grow indefinitely.
|
||||||
|
* Can be smaller than the current buffer size ( which will not shrink ).
|
||||||
|
* When the limit is reached, the buffer will be flushed ( if out is set )
|
||||||
|
* or throw exception.
|
||||||
|
*/
|
||||||
|
public void setLimit(int limit) {
|
||||||
|
this.limit=limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLimit() {
|
||||||
|
return limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When the buffer is empty, read the data from the input channel.
|
||||||
|
*/
|
||||||
|
public void setByteInputChannel(ByteInputChannel in) {
|
||||||
|
this.in = in;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** When the buffer is full, write the data to the output channel.
|
||||||
|
* Also used when large amount of data is appended.
|
||||||
|
*
|
||||||
|
* If not set, the buffer will grow to the limit.
|
||||||
|
*/
|
||||||
|
public void setByteOutputChannel(ByteOutputChannel out) {
|
||||||
|
this.out=out;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getEnd() {
|
||||||
|
return end;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnd( int i ) {
|
||||||
|
end=i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------- Adding data to the buffer --------------------
|
||||||
|
/** Append a char, by casting it to byte. This IS NOT intended for unicode.
|
||||||
|
*
|
||||||
|
* @param c
|
||||||
|
* @throws IOException
|
||||||
|
* @deprecated Unused. Will be removed in Tomcat 8.0.x onwards.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public void append( char c )
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
append( (byte)c);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void append( byte b )
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
makeSpace( 1 );
|
||||||
|
|
||||||
|
// couldn't make space
|
||||||
|
if( limit >0 && end >= limit ) {
|
||||||
|
flushBuffer();
|
||||||
|
}
|
||||||
|
buff[end++]=b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void append( ByteChunk src )
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
append( src.getBytes(), src.getStart(), src.getLength());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Add data to the buffer
|
||||||
|
*/
|
||||||
|
public void append( byte src[], int off, int len )
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
// will grow, up to limit
|
||||||
|
makeSpace( len );
|
||||||
|
|
||||||
|
// if we don't have limit: makeSpace can grow as it wants
|
||||||
|
if( limit < 0 ) {
|
||||||
|
// assert: makeSpace made enough space
|
||||||
|
System.arraycopy( src, off, buff, end, len );
|
||||||
|
end+=len;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optimize on a common case.
|
||||||
|
// If the buffer is empty and the source is going to fill up all the
|
||||||
|
// space in buffer, may as well write it directly to the output,
|
||||||
|
// and avoid an extra copy
|
||||||
|
if ( optimizedWrite && len == limit && end == start && out != null ) {
|
||||||
|
out.realWriteBytes( src, off, len );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// if we have limit and we're below
|
||||||
|
if( len <= limit - end ) {
|
||||||
|
// makeSpace will grow the buffer to the limit,
|
||||||
|
// so we have space
|
||||||
|
System.arraycopy( src, off, buff, end, len );
|
||||||
|
end+=len;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// need more space than we can afford, need to flush
|
||||||
|
// buffer
|
||||||
|
|
||||||
|
// the buffer is already at ( or bigger than ) limit
|
||||||
|
|
||||||
|
// We chunk the data into slices fitting in the buffer limit, although
|
||||||
|
// if the data is written directly if it doesn't fit
|
||||||
|
|
||||||
|
int avail=limit-end;
|
||||||
|
System.arraycopy(src, off, buff, end, avail);
|
||||||
|
end += avail;
|
||||||
|
|
||||||
|
flushBuffer();
|
||||||
|
|
||||||
|
int remain = len - avail;
|
||||||
|
|
||||||
|
while (remain > (limit - end)) {
|
||||||
|
out.realWriteBytes( src, (off + len) - remain, limit - end );
|
||||||
|
remain = remain - (limit - end);
|
||||||
|
}
|
||||||
|
|
||||||
|
System.arraycopy(src, (off + len) - remain, buff, end, remain);
|
||||||
|
end += remain;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -------------------- Removing data from the buffer --------------------
|
||||||
|
|
||||||
|
public int substract()
|
||||||
|
throws IOException {
|
||||||
|
|
||||||
|
if ((end - start) == 0) {
|
||||||
|
if (in == null) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int n = in.realReadBytes( buff, 0, buff.length );
|
||||||
|
if (n < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (buff[start++] & 0xFF);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Unused. Will be removed in Tomcat 8.0.x onwards.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public int substract(ByteChunk src)
|
||||||
|
throws IOException {
|
||||||
|
|
||||||
|
if ((end - start) == 0) {
|
||||||
|
if (in == null) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int n = in.realReadBytes( buff, 0, buff.length );
|
||||||
|
if (n < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int len = getLength();
|
||||||
|
src.append(buff, start, len);
|
||||||
|
start = end;
|
||||||
|
return len;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public byte substractB()
|
||||||
|
throws IOException {
|
||||||
|
|
||||||
|
if ((end - start) == 0) {
|
||||||
|
if (in == null)
|
||||||
|
return -1;
|
||||||
|
int n = in.realReadBytes( buff, 0, buff.length );
|
||||||
|
if (n < 0)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (buff[start++]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public int substract( byte src[], int off, int len )
|
||||||
|
throws IOException {
|
||||||
|
|
||||||
|
if ((end - start) == 0) {
|
||||||
|
if (in == null) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int n = in.realReadBytes( buff, 0, buff.length );
|
||||||
|
if (n < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int n = len;
|
||||||
|
if (len > getLength()) {
|
||||||
|
n = getLength();
|
||||||
|
}
|
||||||
|
System.arraycopy(buff, start, src, off, n);
|
||||||
|
start += n;
|
||||||
|
return n;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send the buffer to the sink. Called by append() when the limit is
|
||||||
|
* reached. You can also call it explicitly to force the data to be written.
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public void flushBuffer()
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
//assert out!=null
|
||||||
|
if( out==null ) {
|
||||||
|
throw new IOException( "Buffer overflow, no sink " + limit + " " +
|
||||||
|
buff.length );
|
||||||
|
}
|
||||||
|
out.realWriteBytes( buff, start, end-start );
|
||||||
|
end=start;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make space for len chars. If len is small, allocate a reserve space too.
|
||||||
|
* Never grow bigger than limit.
|
||||||
|
*/
|
||||||
|
public void makeSpace(int count) {
|
||||||
|
byte[] tmp = null;
|
||||||
|
|
||||||
|
int newSize;
|
||||||
|
int desiredSize=end + count;
|
||||||
|
|
||||||
|
// Can't grow above the limit
|
||||||
|
if( limit > 0 &&
|
||||||
|
desiredSize > limit) {
|
||||||
|
desiredSize=limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( buff==null ) {
|
||||||
|
if( desiredSize < 256 )
|
||||||
|
{
|
||||||
|
desiredSize=256; // take a minimum
|
||||||
|
}
|
||||||
|
buff=new byte[desiredSize];
|
||||||
|
}
|
||||||
|
|
||||||
|
// limit < buf.length ( the buffer is already big )
|
||||||
|
// or we already have space XXX
|
||||||
|
if( desiredSize <= buff.length ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// grow in larger chunks
|
||||||
|
if( desiredSize < 2 * buff.length ) {
|
||||||
|
newSize= buff.length * 2;
|
||||||
|
if( limit >0 &&
|
||||||
|
newSize > limit ) {
|
||||||
|
newSize=limit;
|
||||||
|
}
|
||||||
|
tmp=new byte[newSize];
|
||||||
|
} else {
|
||||||
|
newSize= buff.length * 2 + count ;
|
||||||
|
if( limit > 0 &&
|
||||||
|
newSize > limit ) {
|
||||||
|
newSize=limit;
|
||||||
|
}
|
||||||
|
tmp=new byte[newSize];
|
||||||
|
}
|
||||||
|
|
||||||
|
System.arraycopy(buff, start, tmp, 0, end-start);
|
||||||
|
buff = tmp;
|
||||||
|
tmp = null;
|
||||||
|
end=end-start;
|
||||||
|
start=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------- Conversion and getters --------------------
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (null == buff) {
|
||||||
|
return null;
|
||||||
|
} else if (end-start == 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return StringCache.toString(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toStringInternal() {
|
||||||
|
if (charset == null) {
|
||||||
|
charset = DEFAULT_CHARSET;
|
||||||
|
}
|
||||||
|
// new String(byte[], int, int, Charset) takes a defensive copy of the
|
||||||
|
// entire byte array. This is expensive if only a small subset of the
|
||||||
|
// bytes will be used. The code below is from Apache Harmony.
|
||||||
|
CharBuffer cb;
|
||||||
|
cb = charset.decode(ByteBuffer.wrap(buff, start, end-start));
|
||||||
|
return new String(cb.array(), cb.arrayOffset(), cb.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Unused. Will be removed in Tomcat 8.0.x onwards.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public int getInt()
|
||||||
|
{
|
||||||
|
return Ascii.parseInt(buff, start,end-start);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getLong() {
|
||||||
|
return Ascii.parseLong(buff, start,end-start);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -------------------- equals --------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compares the message bytes to the specified String object.
|
||||||
|
* @param s the String to compare
|
||||||
|
* @return true if the comparison succeeded, false otherwise
|
||||||
|
*/
|
||||||
|
public boolean equals(String s) {
|
||||||
|
// XXX ENCODING - this only works if encoding is UTF8-compat
|
||||||
|
// ( ok for tomcat, where we compare ascii - header names, etc )!!!
|
||||||
|
|
||||||
|
byte[] b = buff;
|
||||||
|
int blen = end-start;
|
||||||
|
if (b == null || blen != s.length()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int boff = start;
|
||||||
|
for (int i = 0; i < blen; i++) {
|
||||||
|
if (b[boff++] != s.charAt(i)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compares the message bytes to the specified String object.
|
||||||
|
* @param s the String to compare
|
||||||
|
* @return true if the comparison succeeded, false otherwise
|
||||||
|
*/
|
||||||
|
public boolean equalsIgnoreCase(String s) {
|
||||||
|
byte[] b = buff;
|
||||||
|
int blen = end-start;
|
||||||
|
if (b == null || blen != s.length()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int boff = start;
|
||||||
|
for (int i = 0; i < blen; i++) {
|
||||||
|
if (Ascii.toLower(b[boff++]) != Ascii.toLower(s.charAt(i))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean equals( ByteChunk bb ) {
|
||||||
|
return equals( bb.getBytes(), bb.getStart(), bb.getLength());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean equals( byte b2[], int off2, int len2) {
|
||||||
|
byte b1[]=buff;
|
||||||
|
if( b1==null && b2==null ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int len=end-start;
|
||||||
|
if ( len2 != len || b1==null || b2==null ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int off1 = start;
|
||||||
|
|
||||||
|
while ( len-- > 0) {
|
||||||
|
if (b1[off1++] != b2[off2++]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean equals( CharChunk cc ) {
|
||||||
|
return equals( cc.getChars(), cc.getStart(), cc.getLength());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean equals( char c2[], int off2, int len2) {
|
||||||
|
// XXX works only for enc compatible with ASCII/UTF !!!
|
||||||
|
byte b1[]=buff;
|
||||||
|
if( c2==null && b1==null ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (b1== null || c2==null || end-start != len2 ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int off1 = start;
|
||||||
|
int len=end-start;
|
||||||
|
|
||||||
|
while ( len-- > 0) {
|
||||||
|
if ( (char)b1[off1++] != c2[off2++]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the message bytes starts with the specified string.
|
||||||
|
* @param s the string
|
||||||
|
* @deprecated Unused. Will be removed in Tomcat 8.0.x onwards.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public boolean startsWith(String s) {
|
||||||
|
// Works only if enc==UTF
|
||||||
|
byte[] b = buff;
|
||||||
|
int blen = s.length();
|
||||||
|
if (b == null || blen > end-start) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int boff = start;
|
||||||
|
for (int i = 0; i < blen; i++) {
|
||||||
|
if (b[boff++] != s.charAt(i)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the message bytes start with the specified byte array.
|
||||||
|
* @deprecated Unused. Will be removed in Tomcat 8.0.x onwards.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public boolean startsWith(byte[] b2) {
|
||||||
|
byte[] b1 = buff;
|
||||||
|
if (b1 == null && b2 == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int len = end - start;
|
||||||
|
if (b1 == null || b2 == null || b2.length > len) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int i = start, j = 0; i < end && j < b2.length;) {
|
||||||
|
if (b1[i++] != b2[j++]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the message bytes starts with the specified string.
|
||||||
|
* @param s the string
|
||||||
|
* @param pos The position
|
||||||
|
*/
|
||||||
|
public boolean startsWithIgnoreCase(String s, int pos) {
|
||||||
|
byte[] b = buff;
|
||||||
|
int len = s.length();
|
||||||
|
if (b == null || len+pos > end-start) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int off = start+pos;
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
if (Ascii.toLower( b[off++] ) != Ascii.toLower( s.charAt(i))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int indexOf( String src, int srcOff, int srcLen, int myOff ) {
|
||||||
|
char first=src.charAt( srcOff );
|
||||||
|
|
||||||
|
// Look for first char
|
||||||
|
int srcEnd = srcOff + srcLen;
|
||||||
|
|
||||||
|
mainLoop:
|
||||||
|
for( int i=myOff+start; i <= (end - srcLen); i++ ) {
|
||||||
|
if( buff[i] != first ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// found first char, now look for a match
|
||||||
|
int myPos=i+1;
|
||||||
|
for( int srcPos=srcOff + 1; srcPos< srcEnd;) {
|
||||||
|
if( buff[myPos++] != src.charAt( srcPos++ )) {
|
||||||
|
continue mainLoop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i-start; // found it
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------- Hash code --------------------
|
||||||
|
|
||||||
|
// normal hash.
|
||||||
|
public int hash() {
|
||||||
|
return hashBytes( buff, start, end-start);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Unused. Will be removed in Tomcat 8.0.x onwards.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public int hashIgnoreCase() {
|
||||||
|
return hashBytesIC( buff, start, end-start );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int hashBytes( byte buff[], int start, int bytesLen ) {
|
||||||
|
int max=start+bytesLen;
|
||||||
|
byte bb[]=buff;
|
||||||
|
int code=0;
|
||||||
|
for (int i = start; i < max ; i++) {
|
||||||
|
code = code * 37 + bb[i];
|
||||||
|
}
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int hashBytesIC( byte bytes[], int start,
|
||||||
|
int bytesLen )
|
||||||
|
{
|
||||||
|
int max=start+bytesLen;
|
||||||
|
byte bb[]=bytes;
|
||||||
|
int code=0;
|
||||||
|
for (int i = start; i < max ; i++) {
|
||||||
|
code = code * 37 + Ascii.toLower(bb[i]);
|
||||||
|
}
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the first instance of the given character in this ByteChunk
|
||||||
|
* starting at the specified byte. If the character is not found, -1 is
|
||||||
|
* returned.
|
||||||
|
* <br/>
|
||||||
|
* NOTE: This only works for characters in the range 0-127.
|
||||||
|
*
|
||||||
|
* @param c The character
|
||||||
|
* @param starting The start position
|
||||||
|
* @return The position of the first instance of the character or
|
||||||
|
* -1 if the character is not found.
|
||||||
|
*/
|
||||||
|
public int indexOf(char c, int starting) {
|
||||||
|
int ret = indexOf(buff, start + starting, end, c);
|
||||||
|
return (ret >= start) ? ret - start : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the first instance of the given character in the given byte array
|
||||||
|
* between the specified start and end.
|
||||||
|
* <br/>
|
||||||
|
* NOTE: This only works for characters in the range 0-127.
|
||||||
|
*
|
||||||
|
* @param bytes The byte array to search
|
||||||
|
* @param start The point to start searching from in the byte array
|
||||||
|
* @param end The point to stop searching in the byte array
|
||||||
|
* @param c The character to search for
|
||||||
|
* @return The position of the first instance of the character or -1
|
||||||
|
* if the character is not found.
|
||||||
|
*/
|
||||||
|
public static int indexOf(byte bytes[], int start, int end, char c) {
|
||||||
|
int offset = start;
|
||||||
|
|
||||||
|
while (offset < end) {
|
||||||
|
byte b=bytes[offset];
|
||||||
|
if (b == c) {
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
offset++;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the first instance of the given byte in the byte array between
|
||||||
|
* the specified start and end.
|
||||||
|
*
|
||||||
|
* @param bytes The byte array to search
|
||||||
|
* @param start The point to start searching from in the byte array
|
||||||
|
* @param end The point to stop searching in the byte array
|
||||||
|
* @param b The byte to search for
|
||||||
|
* @return The position of the first instance of the byte or -1 if the
|
||||||
|
* byte is not found.
|
||||||
|
*/
|
||||||
|
public static int findByte(byte bytes[], int start, int end, byte b) {
|
||||||
|
int offset = start;
|
||||||
|
while (offset < end) {
|
||||||
|
if (bytes[offset] == b) {
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
offset++;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the first instance of any of the given bytes in the byte array
|
||||||
|
* between the specified start and end.
|
||||||
|
*
|
||||||
|
* @param bytes The byte array to search
|
||||||
|
* @param start The point to start searching from in the byte array
|
||||||
|
* @param end The point to stop searching in the byte array
|
||||||
|
* @param b The array of bytes to search for
|
||||||
|
* @return The position of the first instance of the byte or -1 if the
|
||||||
|
* byte is not found.
|
||||||
|
*/
|
||||||
|
public static int findBytes(byte bytes[], int start, int end, byte b[]) {
|
||||||
|
int blen = b.length;
|
||||||
|
int offset = start;
|
||||||
|
while (offset < end) {
|
||||||
|
for (int i = 0; i < blen; i++) {
|
||||||
|
if (bytes[offset] == b[i]) {
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
offset++;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the first instance of any byte that is not one of the given bytes
|
||||||
|
* in the byte array between the specified start and end.
|
||||||
|
*
|
||||||
|
* @param bytes The byte array to search
|
||||||
|
* @param start The point to start searching from in the byte array
|
||||||
|
* @param end The point to stop searching in the byte array
|
||||||
|
* @param b The list of bytes to search for
|
||||||
|
* @return The position of the first instance a byte that is not
|
||||||
|
* in the list of bytes to search for or -1 if no such byte
|
||||||
|
* is found.
|
||||||
|
* @deprecated Unused. Will be removed in Tomcat 8.0.x onwards.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static int findNotBytes(byte bytes[], int start, int end, byte b[]) {
|
||||||
|
int blen = b.length;
|
||||||
|
int offset = start;
|
||||||
|
boolean found;
|
||||||
|
|
||||||
|
while (offset < end) {
|
||||||
|
found = true;
|
||||||
|
for (int i = 0; i < blen; i++) {
|
||||||
|
if (bytes[offset] == b[i]) {
|
||||||
|
found=false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (found) {
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
offset++;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert specified String to a byte array. This ONLY WORKS for ascii, UTF
|
||||||
|
* chars will be truncated.
|
||||||
|
*
|
||||||
|
* @param value to convert to byte array
|
||||||
|
* @return the byte array value
|
||||||
|
*/
|
||||||
|
public static final byte[] convertToBytes(String value) {
|
||||||
|
byte[] result = new byte[value.length()];
|
||||||
|
for (int i = 0; i < value.length(); i++) {
|
||||||
|
result[i] = (byte) value.charAt(i);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,700 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.struts2.util.tomcat.buf;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utilities to manipulate char chunks. While String is
|
||||||
|
* the easiest way to manipulate chars ( search, substrings, etc),
|
||||||
|
* it is known to not be the most efficient solution - Strings are
|
||||||
|
* designed as immutable and secure objects.
|
||||||
|
*
|
||||||
|
* @author dac@sun.com
|
||||||
|
* @author James Todd [gonzo@sun.com]
|
||||||
|
* @author Costin Manolache
|
||||||
|
* @author Remy Maucherat
|
||||||
|
*/
|
||||||
|
public final class CharChunk implements Cloneable, Serializable, CharSequence {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
// Input interface, used when the buffer is emptied.
|
||||||
|
public static interface CharInputChannel {
|
||||||
|
/**
|
||||||
|
* Read new bytes ( usually the internal conversion buffer ).
|
||||||
|
* The implementation is allowed to ignore the parameters,
|
||||||
|
* and mutate the chunk if it wishes to implement its own buffering.
|
||||||
|
*/
|
||||||
|
public int realReadChars(char cbuf[], int off, int len)
|
||||||
|
throws IOException;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* When we need more space we'll either
|
||||||
|
* grow the buffer ( up to the limit ) or send it to a channel.
|
||||||
|
*/
|
||||||
|
public static interface CharOutputChannel {
|
||||||
|
/** Send the bytes ( usually the internal conversion buffer ).
|
||||||
|
* Expect 8k output if the buffer is full.
|
||||||
|
*/
|
||||||
|
public void realWriteChars(char cbuf[], int off, int len)
|
||||||
|
throws IOException;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------
|
||||||
|
|
||||||
|
private int hashCode = 0;
|
||||||
|
// did we compute the hashcode ?
|
||||||
|
private boolean hasHashCode = false;
|
||||||
|
|
||||||
|
// char[]
|
||||||
|
private char buff[];
|
||||||
|
|
||||||
|
private int start;
|
||||||
|
private int end;
|
||||||
|
|
||||||
|
private boolean isSet=false; // XXX
|
||||||
|
|
||||||
|
// -1: grow indefinitely
|
||||||
|
// maximum amount to be cached
|
||||||
|
private int limit=-1;
|
||||||
|
|
||||||
|
private CharInputChannel in = null;
|
||||||
|
private CharOutputChannel out = null;
|
||||||
|
|
||||||
|
private boolean optimizedWrite=true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new, uninitialized CharChunk object.
|
||||||
|
*/
|
||||||
|
public CharChunk() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public CharChunk(int size) {
|
||||||
|
allocate( size, -1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------
|
||||||
|
|
||||||
|
public boolean isNull() {
|
||||||
|
if( end > 0 ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return !isSet; //XXX
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the message bytes to an uninitialized state.
|
||||||
|
*/
|
||||||
|
public void recycle() {
|
||||||
|
// buff=null;
|
||||||
|
isSet=false; // XXX
|
||||||
|
hasHashCode = false;
|
||||||
|
start=0;
|
||||||
|
end=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------- Setup --------------------
|
||||||
|
|
||||||
|
public void allocate( int initial, int limit ) {
|
||||||
|
if( buff==null || buff.length < initial ) {
|
||||||
|
buff=new char[initial];
|
||||||
|
}
|
||||||
|
this.limit=limit;
|
||||||
|
start=0;
|
||||||
|
end=0;
|
||||||
|
isSet=true;
|
||||||
|
hasHashCode = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setOptimizedWrite(boolean optimizedWrite) {
|
||||||
|
this.optimizedWrite = optimizedWrite;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setChars( char[] c, int off, int len ) {
|
||||||
|
buff=c;
|
||||||
|
start=off;
|
||||||
|
end=start + len;
|
||||||
|
isSet=true;
|
||||||
|
hasHashCode = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Maximum amount of data in this buffer.
|
||||||
|
*
|
||||||
|
* If -1 or not set, the buffer will grow indefinitely.
|
||||||
|
* Can be smaller than the current buffer size ( which will not shrink ).
|
||||||
|
* When the limit is reached, the buffer will be flushed ( if out is set )
|
||||||
|
* or throw exception.
|
||||||
|
*/
|
||||||
|
public void setLimit(int limit) {
|
||||||
|
this.limit=limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLimit() {
|
||||||
|
return limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When the buffer is empty, read the data from the input channel.
|
||||||
|
*/
|
||||||
|
public void setCharInputChannel(CharInputChannel in) {
|
||||||
|
this.in = in;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** When the buffer is full, write the data to the output channel.
|
||||||
|
* Also used when large amount of data is appended.
|
||||||
|
*
|
||||||
|
* If not set, the buffer will grow to the limit.
|
||||||
|
*/
|
||||||
|
public void setCharOutputChannel(CharOutputChannel out) {
|
||||||
|
this.out=out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// compat
|
||||||
|
public char[] getChars()
|
||||||
|
{
|
||||||
|
return getBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
public char[] getBuffer()
|
||||||
|
{
|
||||||
|
return buff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the start offset of the bytes.
|
||||||
|
* For output this is the end of the buffer.
|
||||||
|
*/
|
||||||
|
public int getStart() {
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOffset() {
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the start offset of the bytes.
|
||||||
|
*/
|
||||||
|
public void setOffset(int off) {
|
||||||
|
start=off;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the length of the bytes.
|
||||||
|
*/
|
||||||
|
public int getLength() {
|
||||||
|
return end-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public int getEnd() {
|
||||||
|
return end;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnd( int i ) {
|
||||||
|
end=i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------- Adding data --------------------
|
||||||
|
|
||||||
|
public void append( char b )
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
makeSpace( 1 );
|
||||||
|
|
||||||
|
// couldn't make space
|
||||||
|
if( limit >0 && end >= limit ) {
|
||||||
|
flushBuffer();
|
||||||
|
}
|
||||||
|
buff[end++]=b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void append( CharChunk src )
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
append( src.getBuffer(), src.getOffset(), src.getLength());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Add data to the buffer
|
||||||
|
*/
|
||||||
|
public void append( char src[], int off, int len )
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
// will grow, up to limit
|
||||||
|
makeSpace( len );
|
||||||
|
|
||||||
|
// if we don't have limit: makeSpace can grow as it wants
|
||||||
|
if( limit < 0 ) {
|
||||||
|
// assert: makeSpace made enough space
|
||||||
|
System.arraycopy( src, off, buff, end, len );
|
||||||
|
end+=len;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optimize on a common case.
|
||||||
|
// If the source is going to fill up all the space in buffer, may
|
||||||
|
// as well write it directly to the output, and avoid an extra copy
|
||||||
|
if ( optimizedWrite && len == limit && end == start && out != null ) {
|
||||||
|
out.realWriteChars( src, off, len );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we have limit and we're below
|
||||||
|
if( len <= limit - end ) {
|
||||||
|
// makeSpace will grow the buffer to the limit,
|
||||||
|
// so we have space
|
||||||
|
System.arraycopy( src, off, buff, end, len );
|
||||||
|
|
||||||
|
end+=len;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// need more space than we can afford, need to flush
|
||||||
|
// buffer
|
||||||
|
|
||||||
|
// the buffer is already at ( or bigger than ) limit
|
||||||
|
|
||||||
|
// Optimization:
|
||||||
|
// If len-avail < length ( i.e. after we fill the buffer with
|
||||||
|
// what we can, the remaining will fit in the buffer ) we'll just
|
||||||
|
// copy the first part, flush, then copy the second part - 1 write
|
||||||
|
// and still have some space for more. We'll still have 2 writes, but
|
||||||
|
// we write more on the first.
|
||||||
|
|
||||||
|
if( len + end < 2 * limit ) {
|
||||||
|
/* If the request length exceeds the size of the output buffer,
|
||||||
|
flush the output buffer and then write the data directly.
|
||||||
|
We can't avoid 2 writes, but we can write more on the second
|
||||||
|
*/
|
||||||
|
int avail=limit-end;
|
||||||
|
System.arraycopy(src, off, buff, end, avail);
|
||||||
|
end += avail;
|
||||||
|
|
||||||
|
flushBuffer();
|
||||||
|
|
||||||
|
System.arraycopy(src, off+avail, buff, end, len - avail);
|
||||||
|
end+= len - avail;
|
||||||
|
|
||||||
|
} else { // len > buf.length + avail
|
||||||
|
// long write - flush the buffer and write the rest
|
||||||
|
// directly from source
|
||||||
|
flushBuffer();
|
||||||
|
|
||||||
|
out.realWriteChars( src, off, len );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Append a string to the buffer
|
||||||
|
*/
|
||||||
|
public void append(String s) throws IOException {
|
||||||
|
append(s, 0, s.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Append a string to the buffer
|
||||||
|
*/
|
||||||
|
public void append(String s, int off, int len) throws IOException {
|
||||||
|
if (s==null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// will grow, up to limit
|
||||||
|
makeSpace( len );
|
||||||
|
|
||||||
|
// if we don't have limit: makeSpace can grow as it wants
|
||||||
|
if( limit < 0 ) {
|
||||||
|
// assert: makeSpace made enough space
|
||||||
|
s.getChars(off, off+len, buff, end );
|
||||||
|
end+=len;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sOff = off;
|
||||||
|
int sEnd = off + len;
|
||||||
|
while (sOff < sEnd) {
|
||||||
|
int d = min(limit - end, sEnd - sOff);
|
||||||
|
s.getChars( sOff, sOff+d, buff, end);
|
||||||
|
sOff += d;
|
||||||
|
end += d;
|
||||||
|
if (end >= limit) {
|
||||||
|
flushBuffer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------- Removing data from the buffer --------------------
|
||||||
|
|
||||||
|
public int substract()
|
||||||
|
throws IOException {
|
||||||
|
|
||||||
|
if ((end - start) == 0) {
|
||||||
|
if (in == null) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int n = in.realReadChars(buff, end, buff.length - end);
|
||||||
|
if (n < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (buff[start++]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public int substract( char src[], int off, int len )
|
||||||
|
throws IOException {
|
||||||
|
|
||||||
|
if ((end - start) == 0) {
|
||||||
|
if (in == null) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int n = in.realReadChars( buff, end, buff.length - end);
|
||||||
|
if (n < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int n = len;
|
||||||
|
if (len > getLength()) {
|
||||||
|
n = getLength();
|
||||||
|
}
|
||||||
|
System.arraycopy(buff, start, src, off, n);
|
||||||
|
start += n;
|
||||||
|
return n;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void flushBuffer()
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
//assert out!=null
|
||||||
|
if( out==null ) {
|
||||||
|
throw new IOException( "Buffer overflow, no sink " + limit + " " +
|
||||||
|
buff.length );
|
||||||
|
}
|
||||||
|
out.realWriteChars( buff, start, end - start );
|
||||||
|
end=start;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Make space for len chars. If len is small, allocate
|
||||||
|
* a reserve space too. Never grow bigger than limit.
|
||||||
|
*/
|
||||||
|
public void makeSpace(int count)
|
||||||
|
{
|
||||||
|
char[] tmp = null;
|
||||||
|
|
||||||
|
int newSize;
|
||||||
|
int desiredSize=end + count;
|
||||||
|
|
||||||
|
// Can't grow above the limit
|
||||||
|
if( limit > 0 &&
|
||||||
|
desiredSize > limit) {
|
||||||
|
desiredSize=limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( buff==null ) {
|
||||||
|
if( desiredSize < 256 )
|
||||||
|
{
|
||||||
|
desiredSize=256; // take a minimum
|
||||||
|
}
|
||||||
|
buff=new char[desiredSize];
|
||||||
|
}
|
||||||
|
|
||||||
|
// limit < buf.length ( the buffer is already big )
|
||||||
|
// or we already have space XXX
|
||||||
|
if( desiredSize <= buff.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// grow in larger chunks
|
||||||
|
if( desiredSize < 2 * buff.length ) {
|
||||||
|
newSize= buff.length * 2;
|
||||||
|
if( limit >0 &&
|
||||||
|
newSize > limit ) {
|
||||||
|
newSize=limit;
|
||||||
|
}
|
||||||
|
tmp=new char[newSize];
|
||||||
|
} else {
|
||||||
|
newSize= buff.length * 2 + count ;
|
||||||
|
if( limit > 0 &&
|
||||||
|
newSize > limit ) {
|
||||||
|
newSize=limit;
|
||||||
|
}
|
||||||
|
tmp=new char[newSize];
|
||||||
|
}
|
||||||
|
|
||||||
|
System.arraycopy(buff, 0, tmp, 0, end);
|
||||||
|
buff = tmp;
|
||||||
|
tmp = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------- Conversion and getters --------------------
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (null == buff) {
|
||||||
|
return null;
|
||||||
|
} else if (end-start == 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return StringCache.toString(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toStringInternal() {
|
||||||
|
return new String(buff, start, end-start);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------- equals --------------------
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj instanceof CharChunk) {
|
||||||
|
return equals((CharChunk) obj);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compares the message bytes to the specified String object.
|
||||||
|
* @param s the String to compare
|
||||||
|
* @return true if the comparison succeeded, false otherwise
|
||||||
|
*/
|
||||||
|
public boolean equals(String s) {
|
||||||
|
char[] c = buff;
|
||||||
|
int len = end-start;
|
||||||
|
if (c == null || len != s.length()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int off = start;
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
if (c[off++] != s.charAt(i)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compares the message bytes to the specified String object.
|
||||||
|
* @param s the String to compare
|
||||||
|
* @return true if the comparison succeeded, false otherwise
|
||||||
|
*/
|
||||||
|
public boolean equalsIgnoreCase(String s) {
|
||||||
|
char[] c = buff;
|
||||||
|
int len = end-start;
|
||||||
|
if (c == null || len != s.length()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int off = start;
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
if (Ascii.toLower( c[off++] ) != Ascii.toLower( s.charAt(i))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean equals(CharChunk cc) {
|
||||||
|
return equals( cc.getChars(), cc.getOffset(), cc.getLength());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean equals(char b2[], int off2, int len2) {
|
||||||
|
char b1[]=buff;
|
||||||
|
if( b1==null && b2==null ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (b1== null || b2==null || end-start != len2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int off1 = start;
|
||||||
|
int len=end-start;
|
||||||
|
while ( len-- > 0) {
|
||||||
|
if (b1[off1++] != b2[off2++]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the message bytes starts with the specified string.
|
||||||
|
* @param s the string
|
||||||
|
*/
|
||||||
|
public boolean startsWith(String s) {
|
||||||
|
char[] c = buff;
|
||||||
|
int len = s.length();
|
||||||
|
if (c == null || len > end-start) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int off = start;
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
if (c[off++] != s.charAt(i)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the message bytes starts with the specified string.
|
||||||
|
* @param s the string
|
||||||
|
*/
|
||||||
|
public boolean startsWithIgnoreCase(String s, int pos) {
|
||||||
|
char[] c = buff;
|
||||||
|
int len = s.length();
|
||||||
|
if (c == null || len+pos > end-start) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int off = start+pos;
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
if (Ascii.toLower( c[off++] ) != Ascii.toLower( s.charAt(i))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the message bytes end with the specified string.
|
||||||
|
* @param s the string
|
||||||
|
*/
|
||||||
|
public boolean endsWith(String s) {
|
||||||
|
char[] c = buff;
|
||||||
|
int len = s.length();
|
||||||
|
if (c == null || len > end-start) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int off = end - len;
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
if (c[off++] != s.charAt(i)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------- Hash code --------------------
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
if (hasHashCode) {
|
||||||
|
return hashCode;
|
||||||
|
}
|
||||||
|
int code = 0;
|
||||||
|
|
||||||
|
code = hash();
|
||||||
|
hashCode = code;
|
||||||
|
hasHashCode = true;
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
// normal hash.
|
||||||
|
public int hash() {
|
||||||
|
int code=0;
|
||||||
|
for (int i = start; i < start + end-start; i++) {
|
||||||
|
code = code * 37 + buff[i];
|
||||||
|
}
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int indexOf(char c) {
|
||||||
|
return indexOf( c, start);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the message bytes starts with the specified string.
|
||||||
|
* @param c the character
|
||||||
|
*/
|
||||||
|
public int indexOf(char c, int starting) {
|
||||||
|
int ret = indexOf( buff, start+starting, end, c );
|
||||||
|
return (ret >= start) ? ret - start : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int indexOf( char chars[], int off, int cend, char qq )
|
||||||
|
{
|
||||||
|
while( off < cend ) {
|
||||||
|
char b=chars[off];
|
||||||
|
if( b==qq ) {
|
||||||
|
return off;
|
||||||
|
}
|
||||||
|
off++;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public int indexOf(String src, int srcOff, int srcLen, int myOff ) {
|
||||||
|
char first=src.charAt( srcOff );
|
||||||
|
|
||||||
|
// Look for first char
|
||||||
|
int srcEnd = srcOff + srcLen;
|
||||||
|
|
||||||
|
for( int i=myOff+start; i <= (end - srcLen); i++ ) {
|
||||||
|
if( buff[i] != first ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// found first char, now look for a match
|
||||||
|
int myPos=i+1;
|
||||||
|
for( int srcPos=srcOff + 1; srcPos< srcEnd;) {
|
||||||
|
if( buff[myPos++] != src.charAt( srcPos++ )) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if( srcPos==srcEnd )
|
||||||
|
{
|
||||||
|
return i-start; // found it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------- utils
|
||||||
|
private int min(int a, int b) {
|
||||||
|
if (a < b) {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Char sequence impl
|
||||||
|
|
||||||
|
public char charAt(int index) {
|
||||||
|
return buff[index + start];
|
||||||
|
}
|
||||||
|
|
||||||
|
public CharSequence subSequence(int start, int end) {
|
||||||
|
try {
|
||||||
|
CharChunk result = (CharChunk) this.clone();
|
||||||
|
result.setOffset(this.start + start);
|
||||||
|
result.setEnd(this.start + end);
|
||||||
|
return result;
|
||||||
|
} catch (CloneNotSupportedException e) {
|
||||||
|
// Cannot happen
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int length() {
|
||||||
|
return end - start;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.struts2.util.tomcat.buf;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tables useful when converting byte arrays to and from strings of hexadecimal
|
||||||
|
* digits.
|
||||||
|
* Code from Ajp11, from Apache's JServ.
|
||||||
|
*
|
||||||
|
* @author Craig R. McClanahan
|
||||||
|
*/
|
||||||
|
public final class HexUtils {
|
||||||
|
|
||||||
|
// -------------------------------------------------------------- Constants
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Table for HEX to DEC byte translation.
|
||||||
|
*/
|
||||||
|
private static final int[] DEC = {
|
||||||
|
00, 01, 02, 03, 04, 05, 06, 07, 8, 9, -1, -1, -1, -1, -1, -1,
|
||||||
|
-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||||
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||||
|
-1, 10, 11, 12, 13, 14, 15,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Table for DEC to HEX byte translation.
|
||||||
|
*/
|
||||||
|
private static final byte[] HEX =
|
||||||
|
{ (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5',
|
||||||
|
(byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) 'a', (byte) 'b',
|
||||||
|
(byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f' };
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Table for byte to hex string translation.
|
||||||
|
*/
|
||||||
|
private static final char[] hex = "0123456789abcdef".toCharArray();
|
||||||
|
|
||||||
|
|
||||||
|
// --------------------------------------------------------- Static Methods
|
||||||
|
|
||||||
|
public static int getDec(int index) {
|
||||||
|
// Fast for correct values, slower for incorrect ones
|
||||||
|
try {
|
||||||
|
return DEC[index - '0'];
|
||||||
|
} catch (ArrayIndexOutOfBoundsException ex) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static byte getHex(int index) {
|
||||||
|
return HEX[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String toHexString(byte[] bytes) {
|
||||||
|
if (null == bytes) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder(bytes.length << 1);
|
||||||
|
|
||||||
|
for(int i = 0; i < bytes.length; ++i) {
|
||||||
|
sb.append(hex[(bytes[i] & 0xf0) >> 4])
|
||||||
|
.append(hex[(bytes[i] & 0x0f)])
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static byte[] fromHexString(String input) {
|
||||||
|
if (input == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((input.length() & 1) == 1) {
|
||||||
|
// Odd number of characters
|
||||||
|
throw new IllegalArgumentException("The input must consist of an even number of hex digits");
|
||||||
|
}
|
||||||
|
|
||||||
|
char[] inputChars = input.toCharArray();
|
||||||
|
byte[] result = new byte[input.length() >> 1];
|
||||||
|
for (int i = 0; i < result.length; i++) {
|
||||||
|
int upperNibble = getDec(inputChars[2*i]);
|
||||||
|
int lowerNibble = getDec(inputChars[2*i + 1]);
|
||||||
|
if (upperNibble < 0 || lowerNibble < 0) {
|
||||||
|
// Non hex character
|
||||||
|
throw new IllegalArgumentException("The input must consist only of hex digits");
|
||||||
|
}
|
||||||
|
result[i] = (byte) ((upperNibble << 4) + lowerNibble);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,546 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.struts2.util.tomcat.buf;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is used to represent a subarray of bytes in an HTTP message.
|
||||||
|
* It represents all request/response elements. The byte/char conversions are
|
||||||
|
* delayed and cached. Everything is recyclable.
|
||||||
|
*
|
||||||
|
* The object can represent a byte[], a char[], or a (sub) String. All
|
||||||
|
* operations can be made in case sensitive mode or not.
|
||||||
|
*
|
||||||
|
* @author dac@eng.sun.com
|
||||||
|
* @author James Todd [gonzo@eng.sun.com]
|
||||||
|
* @author Costin Manolache
|
||||||
|
*/
|
||||||
|
public final class MessageBytes implements Cloneable, Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
// primary type ( whatever is set as original value )
|
||||||
|
private int type = T_NULL;
|
||||||
|
|
||||||
|
public static final int T_NULL = 0;
|
||||||
|
/** getType() is T_STR if the the object used to create the MessageBytes
|
||||||
|
was a String */
|
||||||
|
public static final int T_STR = 1;
|
||||||
|
/** getType() is T_STR if the the object used to create the MessageBytes
|
||||||
|
was a byte[] */
|
||||||
|
public static final int T_BYTES = 2;
|
||||||
|
/** getType() is T_STR if the the object used to create the MessageBytes
|
||||||
|
was a char[] */
|
||||||
|
public static final int T_CHARS = 3;
|
||||||
|
|
||||||
|
private int hashCode=0;
|
||||||
|
// did we compute the hashcode ?
|
||||||
|
private boolean hasHashCode=false;
|
||||||
|
|
||||||
|
// Internal objects to represent array + offset, and specific methods
|
||||||
|
private final ByteChunk byteC=new ByteChunk();
|
||||||
|
private final CharChunk charC=new CharChunk();
|
||||||
|
|
||||||
|
// String
|
||||||
|
private String strValue;
|
||||||
|
// true if a String value was computed. Probably not needed,
|
||||||
|
// strValue!=null is the same
|
||||||
|
private boolean hasStrValue=false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new, uninitialized MessageBytes object.
|
||||||
|
* Use static newInstance() in order to allow
|
||||||
|
* future hooks.
|
||||||
|
*/
|
||||||
|
private MessageBytes() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Construct a new MessageBytes instance
|
||||||
|
*/
|
||||||
|
public static MessageBytes newInstance() {
|
||||||
|
return factory.newInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isNull() {
|
||||||
|
// should we check also hasStrValue ???
|
||||||
|
return byteC.isNull() && charC.isNull() && ! hasStrValue;
|
||||||
|
// bytes==null && strValue==null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the message bytes to an uninitialized (NULL) state.
|
||||||
|
*/
|
||||||
|
public void recycle() {
|
||||||
|
type=T_NULL;
|
||||||
|
byteC.recycle();
|
||||||
|
charC.recycle();
|
||||||
|
|
||||||
|
strValue=null;
|
||||||
|
|
||||||
|
hasStrValue=false;
|
||||||
|
hasHashCode=false;
|
||||||
|
hasLongValue=false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the content to the specified subarray of bytes.
|
||||||
|
*
|
||||||
|
* @param b the bytes
|
||||||
|
* @param off the start offset of the bytes
|
||||||
|
* @param len the length of the bytes
|
||||||
|
*/
|
||||||
|
public void setBytes(byte[] b, int off, int len) {
|
||||||
|
byteC.setBytes( b, off, len );
|
||||||
|
type=T_BYTES;
|
||||||
|
hasStrValue=false;
|
||||||
|
hasHashCode=false;
|
||||||
|
hasLongValue=false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the content to be a char[]
|
||||||
|
*
|
||||||
|
* @param c the bytes
|
||||||
|
* @param off the start offset of the bytes
|
||||||
|
* @param len the length of the bytes
|
||||||
|
*/
|
||||||
|
public void setChars( char[] c, int off, int len ) {
|
||||||
|
charC.setChars( c, off, len );
|
||||||
|
type=T_CHARS;
|
||||||
|
hasStrValue=false;
|
||||||
|
hasHashCode=false;
|
||||||
|
hasLongValue=false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the content to be a string
|
||||||
|
*/
|
||||||
|
public void setString( String s ) {
|
||||||
|
strValue=s;
|
||||||
|
hasHashCode=false;
|
||||||
|
hasLongValue=false;
|
||||||
|
if (s == null) {
|
||||||
|
hasStrValue=false;
|
||||||
|
type=T_NULL;
|
||||||
|
} else {
|
||||||
|
hasStrValue=true;
|
||||||
|
type=T_STR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------- Conversion and getters --------------------
|
||||||
|
|
||||||
|
/** Compute the string value
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if( hasStrValue ) {
|
||||||
|
return strValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case T_CHARS:
|
||||||
|
strValue=charC.toString();
|
||||||
|
hasStrValue=true;
|
||||||
|
return strValue;
|
||||||
|
case T_BYTES:
|
||||||
|
strValue=byteC.toString();
|
||||||
|
hasStrValue=true;
|
||||||
|
return strValue;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------
|
||||||
|
/** Return the type of the original content. Can be
|
||||||
|
* T_STR, T_BYTES, T_CHARS or T_NULL
|
||||||
|
*/
|
||||||
|
public int getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the byte chunk, representing the byte[] and offset/length.
|
||||||
|
* Valid only if T_BYTES or after a conversion was made.
|
||||||
|
*/
|
||||||
|
public ByteChunk getByteChunk() {
|
||||||
|
return byteC;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the char chunk, representing the char[] and offset/length.
|
||||||
|
* Valid only if T_CHARS or after a conversion was made.
|
||||||
|
*/
|
||||||
|
public CharChunk getCharChunk() {
|
||||||
|
return charC;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the string value.
|
||||||
|
* Valid only if T_STR or after a conversion was made.
|
||||||
|
*/
|
||||||
|
public String getString() {
|
||||||
|
return strValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Charset used for string<->byte conversions.
|
||||||
|
*/
|
||||||
|
public Charset getCharset() {
|
||||||
|
return byteC.getCharset();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the Charset used for string<->byte conversions.
|
||||||
|
*/
|
||||||
|
public void setCharset(Charset charset) {
|
||||||
|
byteC.setCharset(charset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Do a char->byte conversion.
|
||||||
|
*/
|
||||||
|
public void toBytes() {
|
||||||
|
if (!byteC.isNull()) {
|
||||||
|
type=T_BYTES;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
toString();
|
||||||
|
type=T_BYTES;
|
||||||
|
Charset charset = byteC.getCharset();
|
||||||
|
ByteBuffer result = charset.encode(strValue);
|
||||||
|
byteC.setBytes(result.array(), result.arrayOffset(), result.limit());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Convert to char[] and fill the CharChunk.
|
||||||
|
* XXX Not optimized - it converts to String first.
|
||||||
|
*/
|
||||||
|
public void toChars() {
|
||||||
|
if( ! charC.isNull() ) {
|
||||||
|
type=T_CHARS;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// inefficient
|
||||||
|
toString();
|
||||||
|
type=T_CHARS;
|
||||||
|
char cc[]=strValue.toCharArray();
|
||||||
|
charC.setChars(cc, 0, cc.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the length of the original buffer.
|
||||||
|
* Note that the length in bytes may be different from the length
|
||||||
|
* in chars.
|
||||||
|
*/
|
||||||
|
public int getLength() {
|
||||||
|
if(type==T_BYTES) {
|
||||||
|
return byteC.getLength();
|
||||||
|
}
|
||||||
|
if(type==T_CHARS) {
|
||||||
|
return charC.getLength();
|
||||||
|
}
|
||||||
|
if(type==T_STR) {
|
||||||
|
return strValue.length();
|
||||||
|
}
|
||||||
|
toString();
|
||||||
|
if( strValue==null ) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return strValue.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------- equals --------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compares the message bytes to the specified String object.
|
||||||
|
* @param s the String to compare
|
||||||
|
* @return true if the comparison succeeded, false otherwise
|
||||||
|
*/
|
||||||
|
public boolean equals(String s) {
|
||||||
|
switch (type) {
|
||||||
|
case T_STR:
|
||||||
|
if (strValue == null) {
|
||||||
|
return s == null;
|
||||||
|
}
|
||||||
|
return strValue.equals( s );
|
||||||
|
case T_CHARS:
|
||||||
|
return charC.equals( s );
|
||||||
|
case T_BYTES:
|
||||||
|
return byteC.equals( s );
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compares the message bytes to the specified String object.
|
||||||
|
* @param s the String to compare
|
||||||
|
* @return true if the comparison succeeded, false otherwise
|
||||||
|
*/
|
||||||
|
public boolean equalsIgnoreCase(String s) {
|
||||||
|
switch (type) {
|
||||||
|
case T_STR:
|
||||||
|
if (strValue == null) {
|
||||||
|
return s == null;
|
||||||
|
}
|
||||||
|
return strValue.equalsIgnoreCase( s );
|
||||||
|
case T_CHARS:
|
||||||
|
return charC.equalsIgnoreCase( s );
|
||||||
|
case T_BYTES:
|
||||||
|
return byteC.equalsIgnoreCase( s );
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj instanceof MessageBytes) {
|
||||||
|
return equals((MessageBytes) obj);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean equals(MessageBytes mb) {
|
||||||
|
switch (type) {
|
||||||
|
case T_STR:
|
||||||
|
return mb.equals( strValue );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( mb.type != T_CHARS &&
|
||||||
|
mb.type!= T_BYTES ) {
|
||||||
|
// it's a string or int/date string value
|
||||||
|
return equals( mb.toString() );
|
||||||
|
}
|
||||||
|
|
||||||
|
// mb is either CHARS or BYTES.
|
||||||
|
// this is either CHARS or BYTES
|
||||||
|
// Deal with the 4 cases ( in fact 3, one is symmetric)
|
||||||
|
|
||||||
|
if( mb.type == T_CHARS && type==T_CHARS ) {
|
||||||
|
return charC.equals( mb.charC );
|
||||||
|
}
|
||||||
|
if( mb.type==T_BYTES && type== T_BYTES ) {
|
||||||
|
return byteC.equals( mb.byteC );
|
||||||
|
}
|
||||||
|
if( mb.type== T_CHARS && type== T_BYTES ) {
|
||||||
|
return byteC.equals( mb.charC );
|
||||||
|
}
|
||||||
|
if( mb.type== T_BYTES && type== T_CHARS ) {
|
||||||
|
return mb.byteC.equals( charC );
|
||||||
|
}
|
||||||
|
// can't happen
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the message bytes starts with the specified string.
|
||||||
|
* @param s the string
|
||||||
|
* @param pos The start position
|
||||||
|
*/
|
||||||
|
public boolean startsWithIgnoreCase(String s, int pos) {
|
||||||
|
switch (type) {
|
||||||
|
case T_STR:
|
||||||
|
if( strValue==null ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if( strValue.length() < pos + s.length() ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for( int i=0; i<s.length(); i++ ) {
|
||||||
|
if( Ascii.toLower( s.charAt( i ) ) !=
|
||||||
|
Ascii.toLower( strValue.charAt( pos + i ))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
case T_CHARS:
|
||||||
|
return charC.startsWithIgnoreCase( s, pos );
|
||||||
|
case T_BYTES:
|
||||||
|
return byteC.startsWithIgnoreCase( s, pos );
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -------------------- Hash code --------------------
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
if( hasHashCode ) {
|
||||||
|
return hashCode;
|
||||||
|
}
|
||||||
|
int code = 0;
|
||||||
|
|
||||||
|
code=hash();
|
||||||
|
hashCode=code;
|
||||||
|
hasHashCode=true;
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
// normal hash.
|
||||||
|
private int hash() {
|
||||||
|
int code=0;
|
||||||
|
switch (type) {
|
||||||
|
case T_STR:
|
||||||
|
// We need to use the same hash function
|
||||||
|
for (int i = 0; i < strValue.length(); i++) {
|
||||||
|
code = code * 37 + strValue.charAt( i );
|
||||||
|
}
|
||||||
|
return code;
|
||||||
|
case T_CHARS:
|
||||||
|
return charC.hash();
|
||||||
|
case T_BYTES:
|
||||||
|
return byteC.hash();
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inefficient initial implementation. Will be replaced on the next
|
||||||
|
// round of tune-up
|
||||||
|
public int indexOf(String s, int starting) {
|
||||||
|
toString();
|
||||||
|
return strValue.indexOf( s, starting );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inefficient initial implementation. Will be replaced on the next
|
||||||
|
// round of tune-up
|
||||||
|
public int indexOf(String s) {
|
||||||
|
return indexOf( s, 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
public int indexOfIgnoreCase(String s, int starting) {
|
||||||
|
toString();
|
||||||
|
String upper=strValue.toUpperCase(Locale.ENGLISH);
|
||||||
|
String sU=s.toUpperCase(Locale.ENGLISH);
|
||||||
|
return upper.indexOf( sU, starting );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Copy the src into this MessageBytes, allocating more space if
|
||||||
|
* needed
|
||||||
|
*/
|
||||||
|
public void duplicate( MessageBytes src ) throws IOException
|
||||||
|
{
|
||||||
|
switch( src.getType() ) {
|
||||||
|
case MessageBytes.T_BYTES:
|
||||||
|
type=T_BYTES;
|
||||||
|
ByteChunk bc=src.getByteChunk();
|
||||||
|
byteC.allocate( 2 * bc.getLength(), -1 );
|
||||||
|
byteC.append( bc );
|
||||||
|
break;
|
||||||
|
case MessageBytes.T_CHARS:
|
||||||
|
type=T_CHARS;
|
||||||
|
CharChunk cc=src.getCharChunk();
|
||||||
|
charC.allocate( 2 * cc.getLength(), -1 );
|
||||||
|
charC.append( cc );
|
||||||
|
break;
|
||||||
|
case MessageBytes.T_STR:
|
||||||
|
type=T_STR;
|
||||||
|
String sc=src.getString();
|
||||||
|
this.setString( sc );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------- Deprecated code --------------------
|
||||||
|
// efficient long
|
||||||
|
// XXX used only for headers - shouldn't be stored here.
|
||||||
|
private long longValue;
|
||||||
|
private boolean hasLongValue=false;
|
||||||
|
|
||||||
|
/** Set the buffer to the representation of an long
|
||||||
|
*/
|
||||||
|
public void setLong(long l) {
|
||||||
|
byteC.allocate(32, 64);
|
||||||
|
long current = l;
|
||||||
|
byte[] buf = byteC.getBuffer();
|
||||||
|
int start = 0;
|
||||||
|
int end = 0;
|
||||||
|
if (l == 0) {
|
||||||
|
buf[end++] = (byte) '0';
|
||||||
|
}
|
||||||
|
if (l < 0) {
|
||||||
|
current = -l;
|
||||||
|
buf[end++] = (byte) '-';
|
||||||
|
}
|
||||||
|
while (current > 0) {
|
||||||
|
int digit = (int) (current % 10);
|
||||||
|
current = current / 10;
|
||||||
|
buf[end++] = HexUtils.getHex(digit);
|
||||||
|
}
|
||||||
|
byteC.setOffset(0);
|
||||||
|
byteC.setEnd(end);
|
||||||
|
// Inverting buffer
|
||||||
|
end--;
|
||||||
|
if (l < 0) {
|
||||||
|
start++;
|
||||||
|
}
|
||||||
|
while (end > start) {
|
||||||
|
byte temp = buf[start];
|
||||||
|
buf[start] = buf[end];
|
||||||
|
buf[end] = temp;
|
||||||
|
start++;
|
||||||
|
end--;
|
||||||
|
}
|
||||||
|
longValue=l;
|
||||||
|
hasStrValue=false;
|
||||||
|
hasHashCode=false;
|
||||||
|
hasLongValue=true;
|
||||||
|
type=T_BYTES;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used for headers conversion
|
||||||
|
/** Convert the buffer to an long, cache the value
|
||||||
|
*/
|
||||||
|
public long getLong() {
|
||||||
|
if( hasLongValue ) {
|
||||||
|
return longValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case T_BYTES:
|
||||||
|
longValue=byteC.getLong();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
longValue= Long.parseLong(toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
hasLongValue=true;
|
||||||
|
return longValue;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------- Future may be different --------------------
|
||||||
|
|
||||||
|
private static final MessageBytesFactory factory=new MessageBytesFactory();
|
||||||
|
|
||||||
|
private static class MessageBytesFactory {
|
||||||
|
protected MessageBytesFactory() {
|
||||||
|
}
|
||||||
|
public MessageBytes newInstance() {
|
||||||
|
return new MessageBytes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,695 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.struts2.util.tomcat.buf;
|
||||||
|
|
||||||
|
import com.opensymphony.xwork2.util.logging.Logger;
|
||||||
|
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||||
|
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class implements a String cache for ByteChunk and CharChunk.
|
||||||
|
*
|
||||||
|
* @author Remy Maucherat
|
||||||
|
*/
|
||||||
|
public class StringCache {
|
||||||
|
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(StringCache.class);
|
||||||
|
|
||||||
|
|
||||||
|
// ------------------------------------------------------- Static Variables
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enabled ?
|
||||||
|
*/
|
||||||
|
protected static boolean byteEnabled = ("true".equals(System.getProperty(
|
||||||
|
"tomcat.util.buf.StringCache.byte.enabled", "false")));
|
||||||
|
|
||||||
|
|
||||||
|
protected static boolean charEnabled = ("true".equals(System.getProperty(
|
||||||
|
"tomcat.util.buf.StringCache.char.enabled", "false")));
|
||||||
|
|
||||||
|
|
||||||
|
protected static int trainThreshold = Integer.parseInt(System.getProperty(
|
||||||
|
"tomcat.util.buf.StringCache.trainThreshold", "20000"));
|
||||||
|
|
||||||
|
|
||||||
|
protected static int cacheSize = Integer.parseInt(System.getProperty(
|
||||||
|
"tomcat.util.buf.StringCache.cacheSize", "200"));
|
||||||
|
|
||||||
|
|
||||||
|
protected static final int maxStringSize =
|
||||||
|
Integer.parseInt(System.getProperty(
|
||||||
|
"tomcat.util.buf.StringCache.maxStringSize", "128"));
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Statistics hash map for byte chunk.
|
||||||
|
*/
|
||||||
|
protected static final HashMap<ByteEntry,int[]> bcStats =
|
||||||
|
new HashMap<ByteEntry, int[]>(cacheSize);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* toString count for byte chunk.
|
||||||
|
*/
|
||||||
|
protected static int bcCount = 0;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache for byte chunk.
|
||||||
|
*/
|
||||||
|
protected static ByteEntry[] bcCache = null;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Statistics hash map for char chunk.
|
||||||
|
*/
|
||||||
|
protected static final HashMap<CharEntry,int[]> ccStats =
|
||||||
|
new HashMap<CharEntry, int[]>(cacheSize);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* toString count for char chunk.
|
||||||
|
*/
|
||||||
|
protected static int ccCount = 0;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache for char chunk.
|
||||||
|
*/
|
||||||
|
protected static CharEntry[] ccCache = null;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Access count.
|
||||||
|
*/
|
||||||
|
protected static int accessCount = 0;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hit count.
|
||||||
|
*/
|
||||||
|
protected static int hitCount = 0;
|
||||||
|
|
||||||
|
|
||||||
|
// ------------------------------------------------------------ Properties
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Returns the cacheSize.
|
||||||
|
*/
|
||||||
|
public int getCacheSize() {
|
||||||
|
return cacheSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param cacheSize The cacheSize to set.
|
||||||
|
*/
|
||||||
|
public void setCacheSize(int cacheSize) {
|
||||||
|
StringCache.cacheSize = cacheSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Returns the enabled.
|
||||||
|
*/
|
||||||
|
public boolean getByteEnabled() {
|
||||||
|
return byteEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param byteEnabled The enabled to set.
|
||||||
|
*/
|
||||||
|
public void setByteEnabled(boolean byteEnabled) {
|
||||||
|
StringCache.byteEnabled = byteEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Returns the enabled.
|
||||||
|
*/
|
||||||
|
public boolean getCharEnabled() {
|
||||||
|
return charEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param charEnabled The enabled to set.
|
||||||
|
*/
|
||||||
|
public void setCharEnabled(boolean charEnabled) {
|
||||||
|
StringCache.charEnabled = charEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Returns the trainThreshold.
|
||||||
|
*/
|
||||||
|
public int getTrainThreshold() {
|
||||||
|
return trainThreshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param trainThreshold The trainThreshold to set.
|
||||||
|
*/
|
||||||
|
public void setTrainThreshold(int trainThreshold) {
|
||||||
|
StringCache.trainThreshold = trainThreshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Returns the accessCount.
|
||||||
|
*/
|
||||||
|
public int getAccessCount() {
|
||||||
|
return accessCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Returns the hitCount.
|
||||||
|
*/
|
||||||
|
public int getHitCount() {
|
||||||
|
return hitCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -------------------------------------------------- Public Static Methods
|
||||||
|
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
hitCount = 0;
|
||||||
|
accessCount = 0;
|
||||||
|
synchronized (bcStats) {
|
||||||
|
bcCache = null;
|
||||||
|
bcCount = 0;
|
||||||
|
}
|
||||||
|
synchronized (ccStats) {
|
||||||
|
ccCache = null;
|
||||||
|
ccCount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String toString(ByteChunk bc) {
|
||||||
|
|
||||||
|
// If the cache is null, then either caching is disabled, or we're
|
||||||
|
// still training
|
||||||
|
if (bcCache == null) {
|
||||||
|
String value = bc.toStringInternal();
|
||||||
|
if (byteEnabled && (value.length() < maxStringSize)) {
|
||||||
|
// If training, everything is synced
|
||||||
|
synchronized (bcStats) {
|
||||||
|
// If the cache has been generated on a previous invocation
|
||||||
|
// while waiting for the lock, just return the toString
|
||||||
|
// value we just calculated
|
||||||
|
if (bcCache != null) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
// Two cases: either we just exceeded the train count, in
|
||||||
|
// which case the cache must be created, or we just update
|
||||||
|
// the count for the string
|
||||||
|
if (bcCount > trainThreshold) {
|
||||||
|
long t1 = System.currentTimeMillis();
|
||||||
|
// Sort the entries according to occurrence
|
||||||
|
TreeMap<Integer,ArrayList<ByteEntry>> tempMap =
|
||||||
|
new TreeMap<Integer, ArrayList<ByteEntry>>();
|
||||||
|
for (Entry<ByteEntry,int[]> item : bcStats.entrySet()) {
|
||||||
|
ByteEntry entry = item.getKey();
|
||||||
|
int[] countA = item.getValue();
|
||||||
|
Integer count = Integer.valueOf(countA[0]);
|
||||||
|
// Add to the list for that count
|
||||||
|
ArrayList<ByteEntry> list = tempMap.get(count);
|
||||||
|
if (list == null) {
|
||||||
|
// Create list
|
||||||
|
list = new ArrayList<ByteEntry>();
|
||||||
|
tempMap.put(count, list);
|
||||||
|
}
|
||||||
|
list.add(entry);
|
||||||
|
}
|
||||||
|
// Allocate array of the right size
|
||||||
|
int size = bcStats.size();
|
||||||
|
if (size > cacheSize) {
|
||||||
|
size = cacheSize;
|
||||||
|
}
|
||||||
|
ByteEntry[] tempbcCache = new ByteEntry[size];
|
||||||
|
// Fill it up using an alphabetical order
|
||||||
|
// and a dumb insert sort
|
||||||
|
ByteChunk tempChunk = new ByteChunk();
|
||||||
|
int n = 0;
|
||||||
|
while (n < size) {
|
||||||
|
Object key = tempMap.lastKey();
|
||||||
|
ArrayList<ByteEntry> list = tempMap.get(key);
|
||||||
|
for (int i = 0; i < list.size() && n < size; i++) {
|
||||||
|
ByteEntry entry = list.get(i);
|
||||||
|
tempChunk.setBytes(entry.name, 0,
|
||||||
|
entry.name.length);
|
||||||
|
int insertPos = findClosest(tempChunk,
|
||||||
|
tempbcCache, n);
|
||||||
|
if (insertPos == n) {
|
||||||
|
tempbcCache[n + 1] = entry;
|
||||||
|
} else {
|
||||||
|
System.arraycopy(tempbcCache, insertPos + 1,
|
||||||
|
tempbcCache, insertPos + 2,
|
||||||
|
n - insertPos - 1);
|
||||||
|
tempbcCache[insertPos + 1] = entry;
|
||||||
|
}
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
tempMap.remove(key);
|
||||||
|
}
|
||||||
|
bcCount = 0;
|
||||||
|
bcStats.clear();
|
||||||
|
bcCache = tempbcCache;
|
||||||
|
if (log.isDebugEnabled()) {
|
||||||
|
long t2 = System.currentTimeMillis();
|
||||||
|
log.debug("ByteCache generation time: " +
|
||||||
|
(t2 - t1) + "ms");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bcCount++;
|
||||||
|
// Allocate new ByteEntry for the lookup
|
||||||
|
ByteEntry entry = new ByteEntry();
|
||||||
|
entry.value = value;
|
||||||
|
int[] count = bcStats.get(entry);
|
||||||
|
if (count == null) {
|
||||||
|
int end = bc.getEnd();
|
||||||
|
int start = bc.getStart();
|
||||||
|
// Create byte array and copy bytes
|
||||||
|
entry.name = new byte[bc.getLength()];
|
||||||
|
System.arraycopy(bc.getBuffer(), start, entry.name,
|
||||||
|
0, end - start);
|
||||||
|
// Set encoding
|
||||||
|
entry.charset = bc.getCharset();
|
||||||
|
// Initialize occurrence count to one
|
||||||
|
count = new int[1];
|
||||||
|
count[0] = 1;
|
||||||
|
// Set in the stats hash map
|
||||||
|
bcStats.put(entry, count);
|
||||||
|
} else {
|
||||||
|
count[0] = count[0] + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
} else {
|
||||||
|
accessCount++;
|
||||||
|
// Find the corresponding String
|
||||||
|
String result = find(bc);
|
||||||
|
if (result == null) {
|
||||||
|
return bc.toStringInternal();
|
||||||
|
}
|
||||||
|
// Note: We don't care about safety for the stats
|
||||||
|
hitCount++;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String toString(CharChunk cc) {
|
||||||
|
|
||||||
|
// If the cache is null, then either caching is disabled, or we're
|
||||||
|
// still training
|
||||||
|
if (ccCache == null) {
|
||||||
|
String value = cc.toStringInternal();
|
||||||
|
if (charEnabled && (value.length() < maxStringSize)) {
|
||||||
|
// If training, everything is synced
|
||||||
|
synchronized (ccStats) {
|
||||||
|
// If the cache has been generated on a previous invocation
|
||||||
|
// while waiting for the lock, just return the toString
|
||||||
|
// value we just calculated
|
||||||
|
if (ccCache != null) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
// Two cases: either we just exceeded the train count, in
|
||||||
|
// which case the cache must be created, or we just update
|
||||||
|
// the count for the string
|
||||||
|
if (ccCount > trainThreshold) {
|
||||||
|
long t1 = System.currentTimeMillis();
|
||||||
|
// Sort the entries according to occurrence
|
||||||
|
TreeMap<Integer,ArrayList<CharEntry>> tempMap =
|
||||||
|
new TreeMap<Integer, ArrayList<CharEntry>>();
|
||||||
|
for (Entry<CharEntry,int[]> item : ccStats.entrySet()) {
|
||||||
|
CharEntry entry = item.getKey();
|
||||||
|
int[] countA = item.getValue();
|
||||||
|
Integer count = Integer.valueOf(countA[0]);
|
||||||
|
// Add to the list for that count
|
||||||
|
ArrayList<CharEntry> list = tempMap.get(count);
|
||||||
|
if (list == null) {
|
||||||
|
// Create list
|
||||||
|
list = new ArrayList<CharEntry>();
|
||||||
|
tempMap.put(count, list);
|
||||||
|
}
|
||||||
|
list.add(entry);
|
||||||
|
}
|
||||||
|
// Allocate array of the right size
|
||||||
|
int size = ccStats.size();
|
||||||
|
if (size > cacheSize) {
|
||||||
|
size = cacheSize;
|
||||||
|
}
|
||||||
|
CharEntry[] tempccCache = new CharEntry[size];
|
||||||
|
// Fill it up using an alphabetical order
|
||||||
|
// and a dumb insert sort
|
||||||
|
CharChunk tempChunk = new CharChunk();
|
||||||
|
int n = 0;
|
||||||
|
while (n < size) {
|
||||||
|
Object key = tempMap.lastKey();
|
||||||
|
ArrayList<CharEntry> list = tempMap.get(key);
|
||||||
|
for (int i = 0; i < list.size() && n < size; i++) {
|
||||||
|
CharEntry entry = list.get(i);
|
||||||
|
tempChunk.setChars(entry.name, 0,
|
||||||
|
entry.name.length);
|
||||||
|
int insertPos = findClosest(tempChunk,
|
||||||
|
tempccCache, n);
|
||||||
|
if (insertPos == n) {
|
||||||
|
tempccCache[n + 1] = entry;
|
||||||
|
} else {
|
||||||
|
System.arraycopy(tempccCache, insertPos + 1,
|
||||||
|
tempccCache, insertPos + 2,
|
||||||
|
n - insertPos - 1);
|
||||||
|
tempccCache[insertPos + 1] = entry;
|
||||||
|
}
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
tempMap.remove(key);
|
||||||
|
}
|
||||||
|
ccCount = 0;
|
||||||
|
ccStats.clear();
|
||||||
|
ccCache = tempccCache;
|
||||||
|
if (log.isDebugEnabled()) {
|
||||||
|
long t2 = System.currentTimeMillis();
|
||||||
|
log.debug("CharCache generation time: " +
|
||||||
|
(t2 - t1) + "ms");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ccCount++;
|
||||||
|
// Allocate new CharEntry for the lookup
|
||||||
|
CharEntry entry = new CharEntry();
|
||||||
|
entry.value = value;
|
||||||
|
int[] count = ccStats.get(entry);
|
||||||
|
if (count == null) {
|
||||||
|
int end = cc.getEnd();
|
||||||
|
int start = cc.getStart();
|
||||||
|
// Create char array and copy chars
|
||||||
|
entry.name = new char[cc.getLength()];
|
||||||
|
System.arraycopy(cc.getBuffer(), start, entry.name,
|
||||||
|
0, end - start);
|
||||||
|
// Initialize occurrence count to one
|
||||||
|
count = new int[1];
|
||||||
|
count[0] = 1;
|
||||||
|
// Set in the stats hash map
|
||||||
|
ccStats.put(entry, count);
|
||||||
|
} else {
|
||||||
|
count[0] = count[0] + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
} else {
|
||||||
|
accessCount++;
|
||||||
|
// Find the corresponding String
|
||||||
|
String result = find(cc);
|
||||||
|
if (result == null) {
|
||||||
|
return cc.toStringInternal();
|
||||||
|
}
|
||||||
|
// Note: We don't care about safety for the stats
|
||||||
|
hitCount++;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------- Protected Methods
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare given byte chunk with byte array.
|
||||||
|
* Return -1, 0 or +1 if inferior, equal, or superior to the String.
|
||||||
|
*/
|
||||||
|
protected static final int compare(ByteChunk name, byte[] compareTo) {
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
byte[] b = name.getBuffer();
|
||||||
|
int start = name.getStart();
|
||||||
|
int end = name.getEnd();
|
||||||
|
int len = compareTo.length;
|
||||||
|
|
||||||
|
if ((end - start) < len) {
|
||||||
|
len = end - start;
|
||||||
|
}
|
||||||
|
for (int i = 0; (i < len) && (result == 0); i++) {
|
||||||
|
if (b[i + start] > compareTo[i]) {
|
||||||
|
result = 1;
|
||||||
|
} else if (b[i + start] < compareTo[i]) {
|
||||||
|
result = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (result == 0) {
|
||||||
|
if (compareTo.length > (end - start)) {
|
||||||
|
result = -1;
|
||||||
|
} else if (compareTo.length < (end - start)) {
|
||||||
|
result = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find an entry given its name in the cache and return the associated
|
||||||
|
* String.
|
||||||
|
*/
|
||||||
|
protected static final String find(ByteChunk name) {
|
||||||
|
int pos = findClosest(name, bcCache, bcCache.length);
|
||||||
|
if ((pos < 0) || (compare(name, bcCache[pos].name) != 0)
|
||||||
|
|| !(name.getCharset().equals(bcCache[pos].charset))) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return bcCache[pos].value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find an entry given its name in a sorted array of map elements.
|
||||||
|
* This will return the index for the closest inferior or equal item in the
|
||||||
|
* given array.
|
||||||
|
*/
|
||||||
|
protected static final int findClosest(ByteChunk name, ByteEntry[] array,
|
||||||
|
int len) {
|
||||||
|
|
||||||
|
int a = 0;
|
||||||
|
int b = len - 1;
|
||||||
|
|
||||||
|
// Special cases: -1 and 0
|
||||||
|
if (b == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compare(name, array[0].name) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (b == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
while (true) {
|
||||||
|
i = (b + a) >>> 1;
|
||||||
|
int result = compare(name, array[i].name);
|
||||||
|
if (result == 1) {
|
||||||
|
a = i;
|
||||||
|
} else if (result == 0) {
|
||||||
|
return i;
|
||||||
|
} else {
|
||||||
|
b = i;
|
||||||
|
}
|
||||||
|
if ((b - a) == 1) {
|
||||||
|
int result2 = compare(name, array[b].name);
|
||||||
|
if (result2 < 0) {
|
||||||
|
return a;
|
||||||
|
} else {
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare given char chunk with char array.
|
||||||
|
* Return -1, 0 or +1 if inferior, equal, or superior to the String.
|
||||||
|
*/
|
||||||
|
protected static final int compare(CharChunk name, char[] compareTo) {
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
char[] c = name.getBuffer();
|
||||||
|
int start = name.getStart();
|
||||||
|
int end = name.getEnd();
|
||||||
|
int len = compareTo.length;
|
||||||
|
|
||||||
|
if ((end - start) < len) {
|
||||||
|
len = end - start;
|
||||||
|
}
|
||||||
|
for (int i = 0; (i < len) && (result == 0); i++) {
|
||||||
|
if (c[i + start] > compareTo[i]) {
|
||||||
|
result = 1;
|
||||||
|
} else if (c[i + start] < compareTo[i]) {
|
||||||
|
result = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (result == 0) {
|
||||||
|
if (compareTo.length > (end - start)) {
|
||||||
|
result = -1;
|
||||||
|
} else if (compareTo.length < (end - start)) {
|
||||||
|
result = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find an entry given its name in the cache and return the associated
|
||||||
|
* String.
|
||||||
|
*/
|
||||||
|
protected static final String find(CharChunk name) {
|
||||||
|
int pos = findClosest(name, ccCache, ccCache.length);
|
||||||
|
if ((pos < 0) || (compare(name, ccCache[pos].name) != 0)) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return ccCache[pos].value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find an entry given its name in a sorted array of map elements.
|
||||||
|
* This will return the index for the closest inferior or equal item in the
|
||||||
|
* given array.
|
||||||
|
*/
|
||||||
|
protected static final int findClosest(CharChunk name, CharEntry[] array,
|
||||||
|
int len) {
|
||||||
|
|
||||||
|
int a = 0;
|
||||||
|
int b = len - 1;
|
||||||
|
|
||||||
|
// Special cases: -1 and 0
|
||||||
|
if (b == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compare(name, array[0].name) < 0 ) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (b == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
while (true) {
|
||||||
|
i = (b + a) >>> 1;
|
||||||
|
int result = compare(name, array[i].name);
|
||||||
|
if (result == 1) {
|
||||||
|
a = i;
|
||||||
|
} else if (result == 0) {
|
||||||
|
return i;
|
||||||
|
} else {
|
||||||
|
b = i;
|
||||||
|
}
|
||||||
|
if ((b - a) == 1) {
|
||||||
|
int result2 = compare(name, array[b].name);
|
||||||
|
if (result2 < 0) {
|
||||||
|
return a;
|
||||||
|
} else {
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -------------------------------------------------- ByteEntry Inner Class
|
||||||
|
|
||||||
|
|
||||||
|
private static class ByteEntry {
|
||||||
|
|
||||||
|
private byte[] name = null;
|
||||||
|
private Charset charset = null;
|
||||||
|
private String value = null;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return value.hashCode();
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj instanceof ByteEntry) {
|
||||||
|
return value.equals(((ByteEntry) obj).value);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -------------------------------------------------- CharEntry Inner Class
|
||||||
|
|
||||||
|
|
||||||
|
private static class CharEntry {
|
||||||
|
|
||||||
|
private char[] name = null;
|
||||||
|
private String value = null;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return value.hashCode();
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj instanceof CharEntry) {
|
||||||
|
return value.equals(((CharEntry) obj).value);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,421 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.struts2.util.tomcat.buf;
|
||||||
|
|
||||||
|
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||||
|
|
||||||
|
import com.opensymphony.xwork2.util.logging.Logger;
|
||||||
|
import java.io.CharConversionException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All URL decoding happens here. This way we can reuse, review, optimize
|
||||||
|
* without adding complexity to the buffers.
|
||||||
|
*
|
||||||
|
* The conversion will modify the original buffer.
|
||||||
|
*
|
||||||
|
* @author Costin Manolache
|
||||||
|
*/
|
||||||
|
public final class UDecoder {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(UDecoder.class);
|
||||||
|
|
||||||
|
public static final boolean ALLOW_ENCODED_SLASH =
|
||||||
|
Boolean.parseBoolean(System.getProperty("org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH", "false"));
|
||||||
|
|
||||||
|
private static class DecodeException extends CharConversionException {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
public DecodeException(String s) {
|
||||||
|
super(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized Throwable fillInStackTrace() {
|
||||||
|
// This class does not provide a stack trace
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Unexpected end of data. */
|
||||||
|
private static final IOException EXCEPTION_EOF = new DecodeException("EOF");
|
||||||
|
|
||||||
|
/** %xx with not-hex digit */
|
||||||
|
private static final IOException EXCEPTION_NOT_HEX_DIGIT = new DecodeException(
|
||||||
|
"isHexDigit");
|
||||||
|
|
||||||
|
/** %-encoded slash is forbidden in resource path */
|
||||||
|
private static final IOException EXCEPTION_SLASH = new DecodeException(
|
||||||
|
"noSlash");
|
||||||
|
|
||||||
|
public UDecoder()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/** URLDecode, will modify the source.
|
||||||
|
*/
|
||||||
|
public void convert( ByteChunk mb, boolean query )
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
int start=mb.getOffset();
|
||||||
|
byte buff[]=mb.getBytes();
|
||||||
|
int end=mb.getEnd();
|
||||||
|
|
||||||
|
int idx= ByteChunk.findByte( buff, start, end, (byte) '%' );
|
||||||
|
int idx2=-1;
|
||||||
|
if( query ) {
|
||||||
|
idx2= ByteChunk.findByte( buff, start, (idx >= 0 ? idx : end), (byte) '+' );
|
||||||
|
}
|
||||||
|
if( idx<0 && idx2<0 ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// idx will be the smallest positive index ( first % or + )
|
||||||
|
if( (idx2 >= 0 && idx2 < idx) || idx < 0 ) {
|
||||||
|
idx=idx2;
|
||||||
|
}
|
||||||
|
|
||||||
|
final boolean noSlash = !(ALLOW_ENCODED_SLASH || query);
|
||||||
|
|
||||||
|
for( int j=idx; j<end; j++, idx++ ) {
|
||||||
|
if( buff[ j ] == '+' && query) {
|
||||||
|
buff[idx]= (byte)' ' ;
|
||||||
|
} else if( buff[ j ] != '%' ) {
|
||||||
|
buff[idx]= buff[j];
|
||||||
|
} else {
|
||||||
|
// read next 2 digits
|
||||||
|
if( j+2 >= end ) {
|
||||||
|
throw EXCEPTION_EOF;
|
||||||
|
}
|
||||||
|
byte b1= buff[j+1];
|
||||||
|
byte b2=buff[j+2];
|
||||||
|
if( !isHexDigit( b1 ) || ! isHexDigit(b2 )) {
|
||||||
|
throw EXCEPTION_NOT_HEX_DIGIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
j+=2;
|
||||||
|
int res=x2c( b1, b2 );
|
||||||
|
if (noSlash && (res == '/')) {
|
||||||
|
throw EXCEPTION_SLASH;
|
||||||
|
}
|
||||||
|
buff[idx]=(byte)res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mb.setEnd( idx );
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------- Additional methods --------------------
|
||||||
|
// XXX What do we do about charset ????
|
||||||
|
|
||||||
|
/** In-buffer processing - the buffer will be modified
|
||||||
|
*/
|
||||||
|
public void convert( CharChunk mb, boolean query )
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
// log( "Converting a char chunk ");
|
||||||
|
int start=mb.getOffset();
|
||||||
|
char buff[]=mb.getBuffer();
|
||||||
|
int cend=mb.getEnd();
|
||||||
|
|
||||||
|
int idx= CharChunk.indexOf( buff, start, cend, '%' );
|
||||||
|
int idx2=-1;
|
||||||
|
if( query ) {
|
||||||
|
idx2= CharChunk.indexOf( buff, start, (idx >= 0 ? idx : cend), '+' );
|
||||||
|
}
|
||||||
|
if( idx<0 && idx2<0 ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// idx will be the smallest positive index ( first % or + )
|
||||||
|
if( (idx2 >= 0 && idx2 < idx) || idx < 0 ) {
|
||||||
|
idx=idx2;
|
||||||
|
}
|
||||||
|
|
||||||
|
final boolean noSlash = !(ALLOW_ENCODED_SLASH || query);
|
||||||
|
|
||||||
|
for( int j=idx; j<cend; j++, idx++ ) {
|
||||||
|
if( buff[ j ] == '+' && query ) {
|
||||||
|
buff[idx]=( ' ' );
|
||||||
|
} else if( buff[ j ] != '%' ) {
|
||||||
|
buff[idx]=buff[j];
|
||||||
|
} else {
|
||||||
|
// read next 2 digits
|
||||||
|
if( j+2 >= cend ) {
|
||||||
|
// invalid
|
||||||
|
throw EXCEPTION_EOF;
|
||||||
|
}
|
||||||
|
char b1= buff[j+1];
|
||||||
|
char b2=buff[j+2];
|
||||||
|
if( !isHexDigit( b1 ) || ! isHexDigit(b2 )) {
|
||||||
|
throw EXCEPTION_NOT_HEX_DIGIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
j+=2;
|
||||||
|
int res=x2c( b1, b2 );
|
||||||
|
if (noSlash && (res == '/')) {
|
||||||
|
throw EXCEPTION_SLASH;
|
||||||
|
}
|
||||||
|
buff[idx]=(char)res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mb.setEnd( idx );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** URLDecode, will modify the source
|
||||||
|
*/
|
||||||
|
public void convert(MessageBytes mb, boolean query)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
|
||||||
|
switch (mb.getType()) {
|
||||||
|
case MessageBytes.T_STR:
|
||||||
|
String strValue=mb.toString();
|
||||||
|
if( strValue==null ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
mb.setString( convert( strValue, query ));
|
||||||
|
} catch (RuntimeException ex) {
|
||||||
|
throw new DecodeException(ex.getMessage());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MessageBytes.T_CHARS:
|
||||||
|
CharChunk charC=mb.getCharChunk();
|
||||||
|
convert( charC, query );
|
||||||
|
break;
|
||||||
|
case MessageBytes.T_BYTES:
|
||||||
|
ByteChunk bytesC=mb.getByteChunk();
|
||||||
|
convert( bytesC, query );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX Old code, needs to be replaced !!!!
|
||||||
|
//
|
||||||
|
public final String convert(String str, boolean query)
|
||||||
|
{
|
||||||
|
if (str == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( (!query || str.indexOf( '+' ) < 0) && str.indexOf( '%' ) < 0 ) {
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
final boolean noSlash = !(ALLOW_ENCODED_SLASH || query);
|
||||||
|
|
||||||
|
StringBuilder dec = new StringBuilder(); // decoded string output
|
||||||
|
int strPos = 0;
|
||||||
|
int strLen = str.length();
|
||||||
|
|
||||||
|
dec.ensureCapacity(str.length());
|
||||||
|
while (strPos < strLen) {
|
||||||
|
int laPos; // lookahead position
|
||||||
|
|
||||||
|
// look ahead to next URLencoded metacharacter, if any
|
||||||
|
for (laPos = strPos; laPos < strLen; laPos++) {
|
||||||
|
char laChar = str.charAt(laPos);
|
||||||
|
if ((laChar == '+' && query) || (laChar == '%')) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there were non-metacharacters, copy them all as a block
|
||||||
|
if (laPos > strPos) {
|
||||||
|
dec.append(str.substring(strPos,laPos));
|
||||||
|
strPos = laPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
// shortcut out of here if we're at the end of the string
|
||||||
|
if (strPos >= strLen) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// process next metacharacter
|
||||||
|
char metaChar = str.charAt(strPos);
|
||||||
|
if (metaChar == '+') {
|
||||||
|
dec.append(' ');
|
||||||
|
strPos++;
|
||||||
|
continue;
|
||||||
|
} else if (metaChar == '%') {
|
||||||
|
// We throw the original exception - the super will deal with
|
||||||
|
// it
|
||||||
|
// try {
|
||||||
|
char res = (char) Integer.parseInt(
|
||||||
|
str.substring(strPos + 1, strPos + 3), 16);
|
||||||
|
if (noSlash && (res == '/')) {
|
||||||
|
throw new IllegalArgumentException("noSlash");
|
||||||
|
}
|
||||||
|
dec.append(res);
|
||||||
|
strPos += 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dec.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode and return the specified URL-encoded String.
|
||||||
|
* When the byte array is converted to a string, the system default
|
||||||
|
* character encoding is used... This may be different than some other
|
||||||
|
* servers. It is assumed the string is not a query string.
|
||||||
|
*
|
||||||
|
* @param str The url-encoded string
|
||||||
|
*
|
||||||
|
* @exception IllegalArgumentException if a '%' character is not followed
|
||||||
|
* by a valid 2-digit hexadecimal number
|
||||||
|
*/
|
||||||
|
public static String URLDecode(String str) {
|
||||||
|
return URLDecode(str, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode and return the specified URL-encoded String. It is assumed the
|
||||||
|
* string is not a query string.
|
||||||
|
*
|
||||||
|
* @param str The url-encoded string
|
||||||
|
* @param enc The encoding to use; if null, the default encoding is used. If
|
||||||
|
* an unsupported encoding is specified null will be returned
|
||||||
|
* @exception IllegalArgumentException if a '%' character is not followed
|
||||||
|
* by a valid 2-digit hexadecimal number
|
||||||
|
*/
|
||||||
|
public static String URLDecode(String str, String enc) {
|
||||||
|
return URLDecode(str, enc, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode and return the specified URL-encoded String.
|
||||||
|
*
|
||||||
|
* @param str The url-encoded string
|
||||||
|
* @param enc The encoding to use; if null, the default encoding is used. If
|
||||||
|
* an unsupported encoding is specified null will be returned
|
||||||
|
* @param isQuery Is this a query string being processed
|
||||||
|
* @exception IllegalArgumentException if a '%' character is not followed
|
||||||
|
* by a valid 2-digit hexadecimal number
|
||||||
|
*/
|
||||||
|
public static String URLDecode(String str, String enc, boolean isQuery) {
|
||||||
|
if (str == null)
|
||||||
|
return (null);
|
||||||
|
|
||||||
|
// use the specified encoding to extract bytes out of the
|
||||||
|
// given string so that the encoding is not lost. If an
|
||||||
|
// encoding is not specified, use ISO-8859-1
|
||||||
|
byte[] bytes = null;
|
||||||
|
try {
|
||||||
|
if (enc == null) {
|
||||||
|
bytes = str.getBytes("ISO-8859-1");
|
||||||
|
} else {
|
||||||
|
bytes = str.getBytes(B2CConverter.getCharset(enc));
|
||||||
|
}
|
||||||
|
} catch (UnsupportedEncodingException uee) {
|
||||||
|
if (log.isDebugEnabled()) {
|
||||||
|
log.debug("Unable to URL decode the specified input since the encoding "+ enc + " is not supported.", uee);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return URLDecode(bytes, enc, isQuery);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode and return the specified URL-encoded byte array.
|
||||||
|
*
|
||||||
|
* @param bytes The url-encoded byte array
|
||||||
|
* @param enc The encoding to use; if null, the default encoding is used. If
|
||||||
|
* an unsupported encoding is specified null will be returned
|
||||||
|
* @param isQuery Is this a query string being processed
|
||||||
|
* @exception IllegalArgumentException if a '%' character is not followed
|
||||||
|
* by a valid 2-digit hexadecimal number
|
||||||
|
*/
|
||||||
|
public static String URLDecode(byte[] bytes, String enc, boolean isQuery) {
|
||||||
|
|
||||||
|
if (bytes == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
int len = bytes.length;
|
||||||
|
int ix = 0;
|
||||||
|
int ox = 0;
|
||||||
|
while (ix < len) {
|
||||||
|
byte b = bytes[ix++]; // Get byte to test
|
||||||
|
if (b == '+' && isQuery) {
|
||||||
|
b = (byte)' ';
|
||||||
|
} else if (b == '%') {
|
||||||
|
if (ix + 2 > len) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"The % character must be followed by two hexademical digits");
|
||||||
|
}
|
||||||
|
b = (byte) ((convertHexDigit(bytes[ix++]) << 4)
|
||||||
|
+ convertHexDigit(bytes[ix++]));
|
||||||
|
}
|
||||||
|
bytes[ox++] = b;
|
||||||
|
}
|
||||||
|
if (enc != null) {
|
||||||
|
try {
|
||||||
|
return new String(bytes, 0, ox, B2CConverter.getCharset(enc));
|
||||||
|
} catch (UnsupportedEncodingException uee) {
|
||||||
|
if (log.isDebugEnabled()) {
|
||||||
|
log.debug("Unable to URL decode the specified input since the encoding " + enc + " is not supported.", uee);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new String(bytes, 0, ox);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static byte convertHexDigit( byte b ) {
|
||||||
|
if ((b >= '0') && (b <= '9')) return (byte)(b - '0');
|
||||||
|
if ((b >= 'a') && (b <= 'f')) return (byte)(b - 'a' + 10);
|
||||||
|
if ((b >= 'A') && (b <= 'F')) return (byte)(b - 'A' + 10);
|
||||||
|
throw new IllegalArgumentException(((char) b) + " is not a hexadecimal digit");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static boolean isHexDigit( int c ) {
|
||||||
|
return ( ( c>='0' && c<='9' ) ||
|
||||||
|
( c>='a' && c<='f' ) ||
|
||||||
|
( c>='A' && c<='F' ));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static int x2c( byte b1, byte b2 ) {
|
||||||
|
int digit= (b1>='A') ? ( (b1 & 0xDF)-'A') + 10 :
|
||||||
|
(b1 -'0');
|
||||||
|
digit*=16;
|
||||||
|
digit +=(b2>='A') ? ( (b2 & 0xDF)-'A') + 10 :
|
||||||
|
(b2 -'0');
|
||||||
|
return digit;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static int x2c( char b1, char b2 ) {
|
||||||
|
int digit= (b1>='A') ? ( (b1 & 0xDF)-'A') + 10 :
|
||||||
|
(b1 -'0');
|
||||||
|
digit*=16;
|
||||||
|
digit +=(b2>='A') ? ( (b2 & 0xDF)-'A') + 10 :
|
||||||
|
(b2 -'0');
|
||||||
|
return digit;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,293 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.struts2.util.tomcat.buf;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.CharBuffer;
|
||||||
|
import java.nio.charset.CharsetDecoder;
|
||||||
|
import java.nio.charset.CoderResult;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes bytes to UTF-8. Extracted from Apache Harmony and modified to reject
|
||||||
|
* code points from U+D800 to U+DFFF as per RFC3629. The standard Java decoder
|
||||||
|
* does not reject these. It has also been modified to reject code points
|
||||||
|
* greater than U+10FFFF which the standard Java decoder rejects but the harmony
|
||||||
|
* one does not.
|
||||||
|
*/
|
||||||
|
public class Utf8Decoder extends CharsetDecoder {
|
||||||
|
|
||||||
|
// The next table contains information about UTF-8 charset and
|
||||||
|
// correspondence of 1st byte to the length of sequence
|
||||||
|
// For information please visit http://www.ietf.org/rfc/rfc3629.txt
|
||||||
|
//
|
||||||
|
// Please note, o means 0, actually.
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
// 0 1 2 3 Value
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
// oxxxxxxx 00000000 00000000 0xxxxxxx
|
||||||
|
// 11oyyyyy 1oxxxxxx 00000000 00000yyy yyxxxxxx
|
||||||
|
// 111ozzzz 1oyyyyyy 1oxxxxxx 00000000 zzzzyyyy yyxxxxxx
|
||||||
|
// 1111ouuu 1ouuzzzz 1oyyyyyy 1oxxxxxx 000uuuuu zzzzyyyy yyxxxxxx
|
||||||
|
private static final int remainingBytes[] = {
|
||||||
|
// 1owwwwww
|
||||||
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||||
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||||
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||||
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||||
|
// 11oyyyyy
|
||||||
|
-1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||||
|
// 111ozzzz
|
||||||
|
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||||
|
// 1111ouuu
|
||||||
|
3, 3, 3, 3, 3, -1, -1, -1,
|
||||||
|
// > 11110111
|
||||||
|
-1, -1, -1, -1, -1, -1, -1, -1};
|
||||||
|
private static final int remainingNumbers[] = {0, // 0 1 2 3
|
||||||
|
4224, // (01o00000b << 6)+(1o000000b)
|
||||||
|
401536, // (011o0000b << 12)+(1o000000b << 6)+(1o000000b)
|
||||||
|
29892736 // (0111o000b << 18)+(1o000000b << 12)+(1o000000b <<
|
||||||
|
// 6)+(1o000000b)
|
||||||
|
};
|
||||||
|
private static final int lowerEncodingLimit[] = {-1, 0x80, 0x800, 0x10000};
|
||||||
|
|
||||||
|
|
||||||
|
public Utf8Decoder() {
|
||||||
|
super(B2CConverter.UTF_8, 1.0f, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) {
|
||||||
|
if (in.hasArray() && out.hasArray()) {
|
||||||
|
return decodeHasArray(in, out);
|
||||||
|
}
|
||||||
|
return decodeNotHasArray(in, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private CoderResult decodeNotHasArray(ByteBuffer in, CharBuffer out) {
|
||||||
|
int outRemaining = out.remaining();
|
||||||
|
int pos = in.position();
|
||||||
|
int limit = in.limit();
|
||||||
|
try {
|
||||||
|
while (pos < limit) {
|
||||||
|
if (outRemaining == 0) {
|
||||||
|
return CoderResult.OVERFLOW;
|
||||||
|
}
|
||||||
|
int jchar = in.get();
|
||||||
|
if (jchar < 0) {
|
||||||
|
jchar = jchar & 0x7F;
|
||||||
|
int tail = remainingBytes[jchar];
|
||||||
|
if (tail == -1) {
|
||||||
|
return CoderResult.malformedForLength(1);
|
||||||
|
}
|
||||||
|
if (limit - pos < 1 + tail) {
|
||||||
|
// No early test for invalid sequences here as peeking
|
||||||
|
// at the next byte is harder
|
||||||
|
return CoderResult.UNDERFLOW;
|
||||||
|
}
|
||||||
|
int nextByte;
|
||||||
|
for (int i = 0; i < tail; i++) {
|
||||||
|
nextByte = in.get() & 0xFF;
|
||||||
|
if ((nextByte & 0xC0) != 0x80) {
|
||||||
|
return CoderResult.malformedForLength(1 + i);
|
||||||
|
}
|
||||||
|
jchar = (jchar << 6) + nextByte;
|
||||||
|
}
|
||||||
|
jchar -= remainingNumbers[tail];
|
||||||
|
if (jchar < lowerEncodingLimit[tail]) {
|
||||||
|
// Should have been encoded in a fewer octets
|
||||||
|
return CoderResult.malformedForLength(1);
|
||||||
|
}
|
||||||
|
pos += tail;
|
||||||
|
}
|
||||||
|
// Apache Tomcat added test
|
||||||
|
if (jchar >= 0xD800 && jchar <= 0xDFFF) {
|
||||||
|
return CoderResult.unmappableForLength(3);
|
||||||
|
}
|
||||||
|
// Apache Tomcat added test
|
||||||
|
if (jchar > 0x10FFFF) {
|
||||||
|
return CoderResult.unmappableForLength(4);
|
||||||
|
}
|
||||||
|
if (jchar <= 0xffff) {
|
||||||
|
out.put((char) jchar);
|
||||||
|
outRemaining--;
|
||||||
|
} else {
|
||||||
|
if (outRemaining < 2) {
|
||||||
|
return CoderResult.OVERFLOW;
|
||||||
|
}
|
||||||
|
out.put((char) ((jchar >> 0xA) + 0xD7C0));
|
||||||
|
out.put((char) ((jchar & 0x3FF) + 0xDC00));
|
||||||
|
outRemaining -= 2;
|
||||||
|
}
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
return CoderResult.UNDERFLOW;
|
||||||
|
} finally {
|
||||||
|
in.position(pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private CoderResult decodeHasArray(ByteBuffer in, CharBuffer out) {
|
||||||
|
int outRemaining = out.remaining();
|
||||||
|
int pos = in.position();
|
||||||
|
int limit = in.limit();
|
||||||
|
final byte[] bArr = in.array();
|
||||||
|
final char[] cArr = out.array();
|
||||||
|
final int inIndexLimit = limit + in.arrayOffset();
|
||||||
|
int inIndex = pos + in.arrayOffset();
|
||||||
|
int outIndex = out.position() + out.arrayOffset();
|
||||||
|
// if someone would change the limit in process,
|
||||||
|
// he would face consequences
|
||||||
|
for (; inIndex < inIndexLimit && outRemaining > 0; inIndex++) {
|
||||||
|
int jchar = bArr[inIndex];
|
||||||
|
if (jchar < 0) {
|
||||||
|
jchar = jchar & 0x7F;
|
||||||
|
// If first byte is invalid, tail will be set to -1
|
||||||
|
int tail = remainingBytes[jchar];
|
||||||
|
if (tail == -1) {
|
||||||
|
in.position(inIndex - in.arrayOffset());
|
||||||
|
out.position(outIndex - out.arrayOffset());
|
||||||
|
return CoderResult.malformedForLength(1);
|
||||||
|
}
|
||||||
|
// Additional checks to detect invalid sequences ASAP
|
||||||
|
// Checks derived from Unicode 6.2, Chapter 3, Table 3-7
|
||||||
|
// Check 2nd byte
|
||||||
|
int tailAvailable = inIndexLimit - inIndex - 1;
|
||||||
|
if (tailAvailable > 0) {
|
||||||
|
// First byte C2..DF, second byte 80..BF
|
||||||
|
if (jchar > 0x41 && jchar < 0x60 &&
|
||||||
|
(bArr[inIndex + 1] & 0xC0) != 0x80) {
|
||||||
|
in.position(inIndex - in.arrayOffset());
|
||||||
|
out.position(outIndex - out.arrayOffset());
|
||||||
|
return CoderResult.malformedForLength(1);
|
||||||
|
}
|
||||||
|
// First byte E0, second byte A0..BF
|
||||||
|
if (jchar == 0x60 && (bArr[inIndex + 1] & 0xE0) != 0xA0) {
|
||||||
|
in.position(inIndex - in.arrayOffset());
|
||||||
|
out.position(outIndex - out.arrayOffset());
|
||||||
|
return CoderResult.malformedForLength(1);
|
||||||
|
}
|
||||||
|
// First byte E1..EC, second byte 80..BF
|
||||||
|
if (jchar > 0x60 && jchar < 0x6D &&
|
||||||
|
(bArr[inIndex + 1] & 0xC0) != 0x80) {
|
||||||
|
in.position(inIndex - in.arrayOffset());
|
||||||
|
out.position(outIndex - out.arrayOffset());
|
||||||
|
return CoderResult.malformedForLength(1);
|
||||||
|
}
|
||||||
|
// First byte ED, second byte 80..9F
|
||||||
|
if (jchar == 0x6D && (bArr[inIndex + 1] & 0xE0) != 0x80) {
|
||||||
|
in.position(inIndex - in.arrayOffset());
|
||||||
|
out.position(outIndex - out.arrayOffset());
|
||||||
|
return CoderResult.malformedForLength(1);
|
||||||
|
}
|
||||||
|
// First byte EE..EF, second byte 80..BF
|
||||||
|
if (jchar > 0x6D && jchar < 0x70 &&
|
||||||
|
(bArr[inIndex + 1] & 0xC0) != 0x80) {
|
||||||
|
in.position(inIndex - in.arrayOffset());
|
||||||
|
out.position(outIndex - out.arrayOffset());
|
||||||
|
return CoderResult.malformedForLength(1);
|
||||||
|
}
|
||||||
|
// First byte F0, second byte 90..BF
|
||||||
|
if (jchar == 0x70 &&
|
||||||
|
((bArr[inIndex + 1] & 0xFF) < 0x90 ||
|
||||||
|
(bArr[inIndex + 1] & 0xFF) > 0xBF)) {
|
||||||
|
in.position(inIndex - in.arrayOffset());
|
||||||
|
out.position(outIndex - out.arrayOffset());
|
||||||
|
return CoderResult.malformedForLength(1);
|
||||||
|
}
|
||||||
|
// First byte F1..F3, second byte 80..BF
|
||||||
|
if (jchar > 0x70 && jchar < 0x74 &&
|
||||||
|
(bArr[inIndex + 1] & 0xC0) != 0x80) {
|
||||||
|
in.position(inIndex - in.arrayOffset());
|
||||||
|
out.position(outIndex - out.arrayOffset());
|
||||||
|
return CoderResult.malformedForLength(1);
|
||||||
|
}
|
||||||
|
// First byte F4, second byte 80..8F
|
||||||
|
if (jchar == 0x74 &&
|
||||||
|
(bArr[inIndex + 1] & 0xF0) != 0x80) {
|
||||||
|
in.position(inIndex - in.arrayOffset());
|
||||||
|
out.position(outIndex - out.arrayOffset());
|
||||||
|
return CoderResult.malformedForLength(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check third byte if present and expected
|
||||||
|
if (tailAvailable > 1 && tail > 1) {
|
||||||
|
if ((bArr[inIndex + 2] & 0xC0) != 0x80) {
|
||||||
|
in.position(inIndex - in.arrayOffset());
|
||||||
|
out.position(outIndex - out.arrayOffset());
|
||||||
|
return CoderResult.malformedForLength(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check fourth byte if present and expected
|
||||||
|
if (tailAvailable > 2 && tail > 2) {
|
||||||
|
if ((bArr[inIndex + 3] & 0xC0) != 0x80) {
|
||||||
|
in.position(inIndex - in.arrayOffset());
|
||||||
|
out.position(outIndex - out.arrayOffset());
|
||||||
|
return CoderResult.malformedForLength(3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tailAvailable < tail) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < tail; i++) {
|
||||||
|
int nextByte = bArr[inIndex + i + 1] & 0xFF;
|
||||||
|
if ((nextByte & 0xC0) != 0x80) {
|
||||||
|
in.position(inIndex - in.arrayOffset());
|
||||||
|
out.position(outIndex - out.arrayOffset());
|
||||||
|
return CoderResult.malformedForLength(1 + i);
|
||||||
|
}
|
||||||
|
jchar = (jchar << 6) + nextByte;
|
||||||
|
}
|
||||||
|
jchar -= remainingNumbers[tail];
|
||||||
|
if (jchar < lowerEncodingLimit[tail]) {
|
||||||
|
// Should have been encoded in fewer octets
|
||||||
|
in.position(inIndex - in.arrayOffset());
|
||||||
|
out.position(outIndex - out.arrayOffset());
|
||||||
|
return CoderResult.malformedForLength(1);
|
||||||
|
}
|
||||||
|
inIndex += tail;
|
||||||
|
}
|
||||||
|
// Apache Tomcat added test
|
||||||
|
if (jchar >= 0xD800 && jchar <= 0xDFFF) {
|
||||||
|
return CoderResult.unmappableForLength(3);
|
||||||
|
}
|
||||||
|
// Apache Tomcat added test
|
||||||
|
if (jchar > 0x10FFFF) {
|
||||||
|
return CoderResult.unmappableForLength(4);
|
||||||
|
}
|
||||||
|
if (jchar <= 0xffff) {
|
||||||
|
cArr[outIndex++] = (char) jchar;
|
||||||
|
outRemaining--;
|
||||||
|
} else {
|
||||||
|
if (outRemaining < 2) {
|
||||||
|
return CoderResult.OVERFLOW;
|
||||||
|
}
|
||||||
|
cArr[outIndex++] = (char) ((jchar >> 0xA) + 0xD7C0);
|
||||||
|
cArr[outIndex++] = (char) ((jchar & 0x3FF) + 0xDC00);
|
||||||
|
outRemaining -= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
in.position(inIndex - in.arrayOffset());
|
||||||
|
out.position(outIndex - out.arrayOffset());
|
||||||
|
return (outRemaining == 0 && inIndex < inIndexLimit) ?
|
||||||
|
CoderResult.OVERFLOW :
|
||||||
|
CoderResult.UNDERFLOW;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -365,6 +365,7 @@ public class FreemarkerManager {
|
|||||||
servletContext.setAttribute(ATTR_APPLICATION_MODEL, servletContextModel);
|
servletContext.setAttribute(ATTR_APPLICATION_MODEL, servletContextModel);
|
||||||
}
|
}
|
||||||
TaglibFactory taglibs = new TaglibFactory(servletContext);
|
TaglibFactory taglibs = new TaglibFactory(servletContext);
|
||||||
|
taglibs.setObjectWrapper(wrapper);
|
||||||
servletContext.setAttribute(ATTR_JSP_TAGLIBS_MODEL, taglibs);
|
servletContext.setAttribute(ATTR_JSP_TAGLIBS_MODEL, taglibs);
|
||||||
}
|
}
|
||||||
model.put(KEY_APPLICATION, servletContextModel);
|
model.put(KEY_APPLICATION, servletContextModel);
|
||||||
|
|||||||
@@ -306,7 +306,7 @@ public abstract class AbstractUITag extends ComponentTagSupport implements Dynam
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setDynamicAttribute(String uri, String localName, Object value) throws JspException {
|
public void setDynamicAttribute(String uri, String localName, Object value) throws JspException {
|
||||||
if (ComponentUtils.altSyntax(getStack()) && ComponentUtils.isExpression(value)) {
|
if (ComponentUtils.altSyntax(getStack()) && ComponentUtils.isExpression(value.toString())) {
|
||||||
dynamicAttributes.put(localName, String.valueOf(ObjectUtils.defaultIfNull(findValue(value.toString()), value)));
|
dynamicAttributes.put(localName, String.valueOf(ObjectUtils.defaultIfNull(findValue(value.toString()), value)));
|
||||||
} else {
|
} else {
|
||||||
dynamicAttributes.put(localName, value);
|
dynamicAttributes.put(localName, value);
|
||||||
|
|||||||
@@ -24,14 +24,26 @@ package org.apache.struts2.views.util;
|
|||||||
import com.opensymphony.xwork2.inject.Inject;
|
import com.opensymphony.xwork2.inject.Inject;
|
||||||
import com.opensymphony.xwork2.util.logging.Logger;
|
import com.opensymphony.xwork2.util.logging.Logger;
|
||||||
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringEscapeUtils;
|
import org.apache.commons.lang3.StringEscapeUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.struts2.StrutsConstants;
|
import org.apache.struts2.StrutsConstants;
|
||||||
|
import org.apache.struts2.util.URLDecoderUtil;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.net.URLDecoder;
|
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@@ -85,12 +97,17 @@ public class DefaultUrlHelper implements UrlHelper {
|
|||||||
return buildUrl(action, request, response, params, scheme, includeContext, encodeResult, forceAddSchemeHostAndPort, true);
|
return buildUrl(action, request, response, params, scheme, includeContext, encodeResult, forceAddSchemeHostAndPort, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String buildUrl(String action, HttpServletRequest request, HttpServletResponse response, Map<String, Object> params, String scheme,
|
public String buildUrl(String action, HttpServletRequest request, HttpServletResponse response, Map<String, Object> params, String urlScheme,
|
||||||
boolean includeContext, boolean encodeResult, boolean forceAddSchemeHostAndPort, boolean escapeAmp) {
|
boolean includeContext, boolean encodeResult, boolean forceAddSchemeHostAndPort, boolean escapeAmp) {
|
||||||
|
|
||||||
StringBuilder link = new StringBuilder();
|
StringBuilder link = new StringBuilder();
|
||||||
boolean changedScheme = false;
|
boolean changedScheme = false;
|
||||||
|
|
||||||
|
String scheme = null;
|
||||||
|
if (isValidScheme(urlScheme)) {
|
||||||
|
scheme = urlScheme;
|
||||||
|
}
|
||||||
|
|
||||||
// only append scheme if it is different to the current scheme *OR*
|
// only append scheme if it is different to the current scheme *OR*
|
||||||
// if we explicity want it to be appended by having forceAddSchemeHostAndPort = true
|
// if we explicity want it to be appended by having forceAddSchemeHostAndPort = true
|
||||||
if (forceAddSchemeHostAndPort) {
|
if (forceAddSchemeHostAndPort) {
|
||||||
@@ -174,9 +191,9 @@ public class DefaultUrlHelper implements UrlHelper {
|
|||||||
|
|
||||||
//if the action was not explicitly set grab the params from the request
|
//if the action was not explicitly set grab the params from the request
|
||||||
if (escapeAmp) {
|
if (escapeAmp) {
|
||||||
buildParametersString(params, link, AMP);
|
buildParametersString(params, link, AMP, true);
|
||||||
} else {
|
} else {
|
||||||
buildParametersString(params, link, "&");
|
buildParametersString(params, link, "&", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
String result = link.toString();
|
String result = link.toString();
|
||||||
@@ -197,6 +214,10 @@ public class DefaultUrlHelper implements UrlHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void buildParametersString(Map<String, Object> params, StringBuilder link, String paramSeparator) {
|
public void buildParametersString(Map<String, Object> params, StringBuilder link, String paramSeparator) {
|
||||||
|
buildParametersString(params, link, paramSeparator, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void buildParametersString(Map<String, Object> params, StringBuilder link, String paramSeparator, boolean encode) {
|
||||||
if ((params != null) && (params.size() > 0)) {
|
if ((params != null) && (params.size() > 0)) {
|
||||||
if (!link.toString().contains("?")) {
|
if (!link.toString().contains("?")) {
|
||||||
link.append("?");
|
link.append("?");
|
||||||
@@ -214,7 +235,7 @@ public class DefaultUrlHelper implements UrlHelper {
|
|||||||
if (value instanceof Iterable) {
|
if (value instanceof Iterable) {
|
||||||
for (Iterator iterator = ((Iterable) value).iterator(); iterator.hasNext();) {
|
for (Iterator iterator = ((Iterable) value).iterator(); iterator.hasNext();) {
|
||||||
Object paramValue = iterator.next();
|
Object paramValue = iterator.next();
|
||||||
link.append(buildParameterSubstring(name, paramValue != null ? paramValue.toString() : StringUtils.EMPTY));
|
link.append(buildParameterSubstring(name, paramValue != null ? paramValue.toString() : StringUtils.EMPTY, encode));
|
||||||
|
|
||||||
if (iterator.hasNext()) {
|
if (iterator.hasNext()) {
|
||||||
link.append(paramSeparator);
|
link.append(paramSeparator);
|
||||||
@@ -224,14 +245,14 @@ public class DefaultUrlHelper implements UrlHelper {
|
|||||||
Object[] array = (Object[]) value;
|
Object[] array = (Object[]) value;
|
||||||
for (int i = 0; i < array.length; i++) {
|
for (int i = 0; i < array.length; i++) {
|
||||||
Object paramValue = array[i];
|
Object paramValue = array[i];
|
||||||
link.append(buildParameterSubstring(name, paramValue != null ? paramValue.toString() : StringUtils.EMPTY));
|
link.append(buildParameterSubstring(name, paramValue != null ? paramValue.toString() : StringUtils.EMPTY, encode));
|
||||||
|
|
||||||
if (i < array.length - 1) {
|
if (i < array.length - 1) {
|
||||||
link.append(paramSeparator);
|
link.append(paramSeparator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
link.append(buildParameterSubstring(name, value != null ? value.toString() : StringUtils.EMPTY));
|
link.append(buildParameterSubstring(name, value != null ? value.toString() : StringUtils.EMPTY, encode));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iter.hasNext()) {
|
if (iter.hasNext()) {
|
||||||
@@ -241,11 +262,15 @@ public class DefaultUrlHelper implements UrlHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String buildParameterSubstring(String name, String value) {
|
protected boolean isValidScheme(String scheme) {
|
||||||
|
return HTTP_PROTOCOL.equals(scheme) || HTTPS_PROTOCOL.equals(scheme);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildParameterSubstring(String name, String value, boolean encode) {
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
builder.append(encode(name));
|
builder.append(encode ? encode(name) : name);
|
||||||
builder.append('=');
|
builder.append('=');
|
||||||
builder.append(encode(value));
|
builder.append(encode ? encode(value) : value);
|
||||||
return builder.toString();
|
return builder.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,15 +292,15 @@ public class DefaultUrlHelper implements UrlHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decodes the URL using {@link java.net.URLDecoder#decode(String, String)} with the encoding specified in the configuration.
|
* Decodes the URL using {@link URLDecoderUtil#decode(String, String)} with the encoding specified in the configuration.
|
||||||
*
|
*
|
||||||
* @param input the input to decode
|
* @param input the input to decode
|
||||||
* @return the encoded string
|
* @return the encoded string
|
||||||
*/
|
*/
|
||||||
public String decode( String input ) {
|
public String decode( String input ) {
|
||||||
try {
|
try {
|
||||||
return URLDecoder.decode(input, encoding);
|
return URLDecoderUtil.decode(input, encoding, false);
|
||||||
} catch (UnsupportedEncodingException e) {
|
} catch (Exception e) {
|
||||||
if (LOG.isWarnEnabled()) {
|
if (LOG.isWarnEnabled()) {
|
||||||
LOG.warn("Could not decode URL parameter '#0', returning value un-decoded", input);
|
LOG.warn("Could not decode URL parameter '#0', returning value un-decoded", input);
|
||||||
}
|
}
|
||||||
@@ -283,6 +308,22 @@ public class DefaultUrlHelper implements UrlHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes the URL using {@link URLDecoderUtil#decode(String, String, boolean)} with the encoding specified in the configuration.
|
||||||
|
*
|
||||||
|
* @param input the input to decode
|
||||||
|
* @param isQueryString whether input is a query string. If <code>true</code> other decoding rules apply.
|
||||||
|
* @return the encoded string
|
||||||
|
*/
|
||||||
|
public String decode( String input, boolean isQueryString ) {
|
||||||
|
try {
|
||||||
|
return URLDecoderUtil.decode(input, encoding, isQueryString);
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.warn("Could not decode URL parameter '{}', returning value un-decoded", input);
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Map<String, Object> parseQueryString(String queryString, boolean forceValueArray) {
|
public Map<String, Object> parseQueryString(String queryString, boolean forceValueArray) {
|
||||||
Map<String, Object> queryParams = new LinkedHashMap<String, Object>();
|
Map<String, Object> queryParams = new LinkedHashMap<String, Object>();
|
||||||
if (queryString != null) {
|
if (queryString != null) {
|
||||||
@@ -299,8 +340,8 @@ public class DefaultUrlHelper implements UrlHelper {
|
|||||||
paramValue = tmpParams[1];
|
paramValue = tmpParams[1];
|
||||||
}
|
}
|
||||||
if (paramName != null) {
|
if (paramName != null) {
|
||||||
paramName = decode(paramName);
|
paramName = decode(paramName, true);
|
||||||
String translatedParamValue = decode(paramValue);
|
String translatedParamValue = decode(paramValue, true);
|
||||||
|
|
||||||
if (queryParams.containsKey(paramName) || forceValueArray) {
|
if (queryParams.containsKey(paramName) || forceValueArray) {
|
||||||
// WW-1619 append new param value to existing value(s)
|
// WW-1619 append new param value to existing value(s)
|
||||||
|
|||||||
@@ -448,12 +448,7 @@ public class XSLTResult implements Result {
|
|||||||
ServletActionContext.getServletContext());
|
ServletActionContext.getServletContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Templates getTemplates(String path) throws TransformerException, IOException {
|
protected Templates getTemplates(final String path) throws TransformerException, IOException {
|
||||||
String pathFromRequest = ServletActionContext.getRequest().getParameter("xslt.location");
|
|
||||||
|
|
||||||
if (pathFromRequest != null)
|
|
||||||
path = pathFromRequest;
|
|
||||||
|
|
||||||
if (path == null)
|
if (path == null)
|
||||||
throw new TransformerException("Stylesheet path is null");
|
throw new TransformerException("Stylesheet path is null");
|
||||||
|
|
||||||
|
|||||||
@@ -46,13 +46,21 @@
|
|||||||
java.lang.Class,
|
java.lang.Class,
|
||||||
java.lang.ClassLoader,
|
java.lang.ClassLoader,
|
||||||
java.lang.Shutdown,
|
java.lang.Shutdown,
|
||||||
|
java.lang.ProcessBuilder,
|
||||||
ognl.OgnlContext,
|
ognl.OgnlContext,
|
||||||
ognl.MemberAccess,
|
|
||||||
ognl.ClassResolver,
|
ognl.ClassResolver,
|
||||||
ognl.TypeConverter,
|
ognl.TypeConverter,
|
||||||
|
ognl.MemberAccess,
|
||||||
|
ognl.DefaultMemberAccess,
|
||||||
|
com.opensymphony.xwork2.ognl.SecurityMemberAccess,
|
||||||
com.opensymphony.xwork2.ActionContext" />
|
com.opensymphony.xwork2.ActionContext" />
|
||||||
|
|
||||||
<!-- this must be valid regex, each '.' in package name must be escaped! -->
|
<!-- this must be valid regex, each '.' in package name must be escaped! -->
|
||||||
<constant name="struts.excludedPackageNamePatterns" value="^java\.lang\..*,^ognl.*,^(?!javax\.servlet\..+)(javax\..+)" />
|
<!-- it's more flexible but slower than simple string comparison -->
|
||||||
|
<!-- constant name="struts.excludedPackageNamePatterns" value="^java\.lang\..*,^ognl.*,^(?!javax\.servlet\..+)(javax\..+)" / -->
|
||||||
|
|
||||||
|
<!-- this is simpler version of the above used with string comparison -->
|
||||||
|
<constant name="struts.excludedPackageNames" value="java.lang.,ognl,javax" />
|
||||||
|
|
||||||
<bean class="com.opensymphony.xwork2.ObjectFactory" name="struts"/>
|
<bean class="com.opensymphony.xwork2.ObjectFactory" name="struts"/>
|
||||||
<bean type="com.opensymphony.xwork2.factory.ResultFactory" name="struts" class="org.apache.struts2.factory.StrutsResultFactory" />
|
<bean type="com.opensymphony.xwork2.factory.ResultFactory" name="struts" class="org.apache.struts2.factory.StrutsResultFactory" />
|
||||||
|
|||||||
@@ -26,8 +26,10 @@
|
|||||||
<#assign itemCount = itemCount + 1/>
|
<#assign itemCount = itemCount + 1/>
|
||||||
<#if parameters.listKey??>
|
<#if parameters.listKey??>
|
||||||
<#assign itemKey = stack.findValue(parameters.listKey)/>
|
<#assign itemKey = stack.findValue(parameters.listKey)/>
|
||||||
<#else>
|
<#assign itemKeyStr = stack.findString(parameters.listKey)/>
|
||||||
<#assign itemKey = stack.findValue('top')/>
|
<#else>
|
||||||
|
<#assign itemKey = stack.findValue('top')/>
|
||||||
|
<#assign itemKeyStr = stack.findString('top')>
|
||||||
</#if>
|
</#if>
|
||||||
<#if parameters.listLabelKey??>
|
<#if parameters.listLabelKey??>
|
||||||
<#-- checks the valueStack for the 'valueKey.' The valueKey is then looked-up in the locale
|
<#-- checks the valueStack for the 'valueKey.' The valueKey is then looked-up in the locale
|
||||||
@@ -59,7 +61,6 @@
|
|||||||
<#assign itemTitle = ''/>
|
<#assign itemTitle = ''/>
|
||||||
</#if>
|
</#if>
|
||||||
</#if>
|
</#if>
|
||||||
<#assign itemKeyStr=itemKey.toString() />
|
|
||||||
<input type="checkbox" name="${parameters.name?html}" value="${itemKeyStr?html}"<#rt/>
|
<input type="checkbox" name="${parameters.name?html}" value="${itemKeyStr?html}"<#rt/>
|
||||||
<#if parameters.id?has_content>
|
<#if parameters.id?has_content>
|
||||||
id="${parameters.id?html}-${itemCount}"<#rt/>
|
id="${parameters.id?html}-${itemCount}"<#rt/>
|
||||||
|
|||||||
@@ -76,7 +76,7 @@
|
|||||||
<#assign itemCount = startCount/>
|
<#assign itemCount = startCount/>
|
||||||
var ${parameters.id}Group = new Array(${parameters.listSize} + ${startCount});
|
var ${parameters.id}Group = new Array(${parameters.listSize} + ${startCount});
|
||||||
for (i = 0; i < (${parameters.listSize} + ${startCount}); i++)
|
for (i = 0; i < (${parameters.listSize} + ${startCount}); i++)
|
||||||
${parameters.id}Group[i] = new Array();
|
${parameters.id}Group[i] = [];
|
||||||
|
|
||||||
<@s.iterator value="parameters.list">
|
<@s.iterator value="parameters.list">
|
||||||
<#if parameters.listKey??>
|
<#if parameters.listKey??>
|
||||||
@@ -91,7 +91,7 @@
|
|||||||
</#if>
|
</#if>
|
||||||
<#assign doubleItemCount = 0/>
|
<#assign doubleItemCount = 0/>
|
||||||
<#if parameters.doubleHeaderKey?? && parameters.doubleHeaderValue??>
|
<#if parameters.doubleHeaderKey?? && parameters.doubleHeaderValue??>
|
||||||
${parameters.id}Group[${itemCount}][${doubleItemCount}] = new Option("${doubleHeaderValue?js_string}", "${doubleHeaderKey?js_string}");
|
${parameters.id}Group[${itemCount}][${doubleItemCount}] = new Option("${parameters.doubleHeaderValue?js_string}", "${parameters.doubleHeaderKey?js_string}");
|
||||||
<#assign doubleItemCount = doubleItemCount + 1/>
|
<#assign doubleItemCount = doubleItemCount + 1/>
|
||||||
</#if>
|
</#if>
|
||||||
<#if parameters.doubleEmptyOption??>
|
<#if parameters.doubleEmptyOption??>
|
||||||
@@ -172,10 +172,7 @@
|
|||||||
${parameters.id}Temp.options[i] = new Option(${parameters.id}Group[x][i].text, ${parameters.id}Group[x][i].value);
|
${parameters.id}Temp.options[i] = new Option(${parameters.id}Group[x][i].text, ${parameters.id}Group[x][i].value);
|
||||||
<#if parameters.doubleNameValue??>
|
<#if parameters.doubleNameValue??>
|
||||||
<#if parameters.doubleMultiple??>
|
<#if parameters.doubleMultiple??>
|
||||||
for (j = 0; j < ${parameters.doubleNameValue}.
|
for (j = 0; j < ${parameters.doubleNameValue}.length; j++)
|
||||||
length;
|
|
||||||
j++
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
if (${parameters.id}Temp.options[i].value == ${parameters.doubleNameValue?js_string}[j]) {
|
if (${parameters.id}Temp.options[i].value == ${parameters.doubleNameValue?js_string}[j]) {
|
||||||
${parameters.id}Temp.options[i].selected = true;
|
${parameters.id}Temp.options[i].selected = true;
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<#--
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
-->
|
||||||
|
|
||||||
|
<#--
|
||||||
|
Code that will add javascript needed for tooltips
|
||||||
|
--><#t/>
|
||||||
|
<#if (parameters.hasTooltip?default(false))><#t/>
|
||||||
|
<#lt/><!-- javascript that is needed for tooltips -->
|
||||||
|
<#lt/><script type="text/javascript" src='<@s.url value="/struts/domTT.js" includeParams="none" encode="false" />'></script>
|
||||||
|
<#lt/><link rel="stylesheet" type="text/css" href="<@s.url value="/struts/domTT.css" includeParams="none" encode="false" />"/>
|
||||||
|
|
||||||
|
</#if><#t/>
|
||||||
@@ -101,13 +101,4 @@
|
|||||||
</script>
|
</script>
|
||||||
</#if>
|
</#if>
|
||||||
|
|
||||||
|
<#include "/${parameters.templateDir}/${parameters.expandTheme}/form-close-tooltips.ftl" />
|
||||||
<#--
|
|
||||||
Code that will add javascript needed for tooltips
|
|
||||||
--><#t/>
|
|
||||||
<#if (parameters.hasTooltip?default(false))><#t/>
|
|
||||||
<#lt/><!-- javascript that is needed for tooltips -->
|
|
||||||
<#lt/><script type="text/javascript" src='<@s.url value="/struts/domTT.js" includeParams="none" encode="false" />'></script>
|
|
||||||
<#lt/><link rel="stylesheet" type="text/css" href="<@s.url value="/struts/domTT.css" includeParams="none" encode="false" />"/>
|
|
||||||
|
|
||||||
</#if><#t/>
|
|
||||||
|
|||||||
@@ -23,10 +23,11 @@
|
|||||||
<@s.iterator value="parameters.list">
|
<@s.iterator value="parameters.list">
|
||||||
<#if parameters.listKey??>
|
<#if parameters.listKey??>
|
||||||
<#assign itemKey = stack.findValue(parameters.listKey)/>
|
<#assign itemKey = stack.findValue(parameters.listKey)/>
|
||||||
|
<#assign itemKeyStr = stack.findString(parameters.listKey)/>
|
||||||
<#else>
|
<#else>
|
||||||
<#assign itemKey = stack.findValue('top')/>
|
<#assign itemKey = stack.findValue('top')/>
|
||||||
|
<#assign itemKeyStr = stack.findString('top')>
|
||||||
</#if>
|
</#if>
|
||||||
<#assign itemKeyStr = itemKey.toString() />
|
|
||||||
<#if parameters.listValueKey??>
|
<#if parameters.listValueKey??>
|
||||||
<#-- checks the valueStack for the 'valueKey.' The valueKey is then looked-up in the locale
|
<#-- checks the valueStack for the 'valueKey.' The valueKey is then looked-up in the locale
|
||||||
file for it's localized value. This is then used as a label -->
|
file for it's localized value. This is then used as a label -->
|
||||||
@@ -64,7 +65,7 @@
|
|||||||
name="${parameters.name?html}"<#rt/>
|
name="${parameters.name?html}"<#rt/>
|
||||||
</#if>
|
</#if>
|
||||||
id="${parameters.id?html}${itemKeyStr?html}"<#rt/>
|
id="${parameters.id?html}${itemKeyStr?html}"<#rt/>
|
||||||
<#if tag.contains(parameters.nameValue!'', itemKeyStr)>
|
<#if tag.contains(parameters.nameValue!'', itemKey)>
|
||||||
checked="checked"<#rt/>
|
checked="checked"<#rt/>
|
||||||
</#if>
|
</#if>
|
||||||
<#if itemKey??>
|
<#if itemKey??>
|
||||||
|
|||||||
@@ -61,6 +61,9 @@
|
|||||||
</#if>
|
</#if>
|
||||||
<#else>
|
<#else>
|
||||||
<input type="submit"<#rt/>
|
<input type="submit"<#rt/>
|
||||||
|
<#if parameters.nameValue?has_content>
|
||||||
|
value="<@s.property value="parameters.nameValue"/>"<#rt/>
|
||||||
|
</#if>
|
||||||
</#if>
|
</#if>
|
||||||
<#if parameters.id?has_content>
|
<#if parameters.id?has_content>
|
||||||
id="${parameters.id?html}"<#rt/>
|
id="${parameters.id?html}"<#rt/>
|
||||||
@@ -68,9 +71,6 @@
|
|||||||
<#if parameters.name?has_content>
|
<#if parameters.name?has_content>
|
||||||
name="${parameters.name?html}"<#rt/>
|
name="${parameters.name?html}"<#rt/>
|
||||||
</#if>
|
</#if>
|
||||||
<#if parameters.nameValue?has_content>
|
|
||||||
value="<@s.property value="parameters.nameValue"/>"<#rt/>
|
|
||||||
</#if>
|
|
||||||
<#if parameters.disabled!false>
|
<#if parameters.disabled!false>
|
||||||
disabled="disabled"<#rt/>
|
disabled="disabled"<#rt/>
|
||||||
</#if>
|
</#if>
|
||||||
|
|||||||
@@ -164,21 +164,4 @@ public class UIBeanTest extends StrutsInternalTestCase {
|
|||||||
assertEquals("12", txtFld.getTheme());
|
assertEquals("12", txtFld.getTheme());
|
||||||
}
|
}
|
||||||
|
|
||||||
// I couldn't figure out how to make this test work. Bailing for now.
|
|
||||||
// public void testEscapeLabel() throws Exception {
|
|
||||||
// ValueStack stack = ActionContext.getContext().getValueStack();
|
|
||||||
// MockHttpServletRequest req = new MockHttpServletRequest();
|
|
||||||
// MockHttpServletResponse res = new MockHttpServletResponse();
|
|
||||||
// stack.push(this);
|
|
||||||
//
|
|
||||||
// TextField txtFld = new TextField(stack, req, res);
|
|
||||||
// txtFld.setKey("test['foo']");
|
|
||||||
// txtFld.evaluateParams();
|
|
||||||
// assertEquals("test_label", txtFld.getParameters().get("label"));
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public String getText(String key) {
|
|
||||||
// assertEquals("test[\\'foo\\']", key);
|
|
||||||
// return "test_label";
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-6
@@ -30,6 +30,7 @@ import com.opensymphony.xwork2.config.ConfigurationManager;
|
|||||||
import com.opensymphony.xwork2.config.entities.PackageConfig;
|
import com.opensymphony.xwork2.config.entities.PackageConfig;
|
||||||
import com.opensymphony.xwork2.config.impl.DefaultConfiguration;
|
import com.opensymphony.xwork2.config.impl.DefaultConfiguration;
|
||||||
import org.apache.struts2.ServletActionContext;
|
import org.apache.struts2.ServletActionContext;
|
||||||
|
import org.apache.struts2.StrutsException;
|
||||||
import org.apache.struts2.StrutsInternalTestCase;
|
import org.apache.struts2.StrutsInternalTestCase;
|
||||||
import org.apache.struts2.dispatcher.StrutsResultSupport;
|
import org.apache.struts2.dispatcher.StrutsResultSupport;
|
||||||
import org.apache.struts2.views.jsp.StrutsMockHttpServletRequest;
|
import org.apache.struts2.views.jsp.StrutsMockHttpServletRequest;
|
||||||
@@ -162,8 +163,8 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase {
|
|||||||
|
|
||||||
public void testGetMappingWithNamespaceSlash() throws Exception {
|
public void testGetMappingWithNamespaceSlash() throws Exception {
|
||||||
|
|
||||||
req.setupGetRequestURI("/my.hh/abc.action");
|
req.setupGetRequestURI("/my-hh/abc.action");
|
||||||
req.setupGetServletPath("/my.hh/abc.action");
|
req.setupGetServletPath("/my-hh/abc.action");
|
||||||
req.setupGetAttribute(null);
|
req.setupGetAttribute(null);
|
||||||
req.addExpectedGetAttributeName("javax.servlet.include.servlet_path");
|
req.addExpectedGetAttributeName("javax.servlet.include.servlet_path");
|
||||||
|
|
||||||
@@ -180,7 +181,7 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase {
|
|||||||
mapping = mapper.getMapping(req, configManager);
|
mapping = mapper.getMapping(req, configManager);
|
||||||
|
|
||||||
assertEquals("", mapping.getNamespace());
|
assertEquals("", mapping.getNamespace());
|
||||||
assertEquals("my.hh/abc", mapping.getName());
|
assertEquals("my-hh/abc", mapping.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testGetMappingWithUnknownNamespace() throws Exception {
|
public void testGetMappingWithUnknownNamespace() throws Exception {
|
||||||
@@ -845,13 +846,13 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase {
|
|||||||
assertEquals(actionName, mapper.cleanupActionName(actionName));
|
assertEquals(actionName, mapper.cleanupActionName(actionName));
|
||||||
|
|
||||||
actionName = "${action}";
|
actionName = "${action}";
|
||||||
assertEquals("action", mapper.cleanupActionName(actionName));
|
assertEquals(mapper.defaultActionName, mapper.cleanupActionName(actionName));
|
||||||
|
|
||||||
actionName = "${${%{action}}}";
|
actionName = "${${%{action}}}";
|
||||||
assertEquals("action", mapper.cleanupActionName(actionName));
|
assertEquals(mapper.defaultActionName, mapper.cleanupActionName(actionName));
|
||||||
|
|
||||||
actionName = "${#foo='action',#foo}";
|
actionName = "${#foo='action',#foo}";
|
||||||
assertEquals("fooactionfoo", mapper.cleanupActionName(actionName));
|
assertEquals(mapper.defaultActionName, mapper.cleanupActionName(actionName));
|
||||||
|
|
||||||
actionName = "test-action";
|
actionName = "test-action";
|
||||||
assertEquals("test-action", mapper.cleanupActionName(actionName));
|
assertEquals("test-action", mapper.cleanupActionName(actionName));
|
||||||
@@ -863,4 +864,19 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase {
|
|||||||
assertEquals("test!bar.action", mapper.cleanupActionName(actionName));
|
assertEquals("test!bar.action", mapper.cleanupActionName(actionName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testAllowedMethodNames() throws Exception {
|
||||||
|
DefaultActionMapper mapper = new DefaultActionMapper();
|
||||||
|
|
||||||
|
assertEquals("", mapper.cleanupMethodName(""));
|
||||||
|
assertEquals("test", mapper.cleanupMethodName("test"));
|
||||||
|
assertEquals("test_method", mapper.cleanupMethodName("test_method"));
|
||||||
|
assertEquals("_test", mapper.cleanupMethodName("_test"));
|
||||||
|
assertEquals("test1", mapper.cleanupMethodName("test1"));
|
||||||
|
|
||||||
|
assertEquals(mapper.defaultMethodName, mapper.cleanupMethodName("2test"));
|
||||||
|
assertEquals(mapper.defaultMethodName, mapper.cleanupMethodName("%{exp}"));
|
||||||
|
assertEquals(mapper.defaultMethodName, mapper.cleanupMethodName("${%{foo}}"));
|
||||||
|
assertEquals(mapper.defaultMethodName, mapper.cleanupMethodName("${#foo='method',#foo}"));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -361,7 +361,6 @@ public class CookieInterceptorTest extends StrutsInternalTestCase {
|
|||||||
ServletActionContext.setRequest(request);
|
ServletActionContext.setRequest(request);
|
||||||
|
|
||||||
final Map<String, Boolean> excludedName = new HashMap<String, Boolean>();
|
final Map<String, Boolean> excludedName = new HashMap<String, Boolean>();
|
||||||
final Map<String, Boolean> excludedValue = new HashMap<String, Boolean>();
|
|
||||||
|
|
||||||
CookieInterceptor interceptor = new CookieInterceptor() {
|
CookieInterceptor interceptor = new CookieInterceptor() {
|
||||||
@Override
|
@Override
|
||||||
@@ -370,13 +369,6 @@ public class CookieInterceptorTest extends StrutsInternalTestCase {
|
|||||||
excludedName.put(name, accepted);
|
excludedName.put(name, accepted);
|
||||||
return accepted;
|
return accepted;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean isAcceptableValue(String value) {
|
|
||||||
boolean accepted = super.isAcceptableValue(value);
|
|
||||||
excludedValue.put(value, accepted);
|
|
||||||
return accepted;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
DefaultExcludedPatternsChecker excludedPatternsChecker = new DefaultExcludedPatternsChecker();
|
DefaultExcludedPatternsChecker excludedPatternsChecker = new DefaultExcludedPatternsChecker();
|
||||||
excludedPatternsChecker.setAdditionalExcludePatterns(".*(^|\\.|\\[|'|\")class(\\.|\\[|'|\").*");
|
excludedPatternsChecker.setAdditionalExcludePatterns(".*(^|\\.|\\[|'|\")class(\\.|\\[|'|\").*");
|
||||||
@@ -395,13 +387,6 @@ public class CookieInterceptorTest extends StrutsInternalTestCase {
|
|||||||
assertFalse(excludedName.get(pollution4));
|
assertFalse(excludedName.get(pollution4));
|
||||||
assertFalse(excludedName.get(pollution5));
|
assertFalse(excludedName.get(pollution5));
|
||||||
assertFalse(excludedName.get(pollution6));
|
assertFalse(excludedName.get(pollution6));
|
||||||
|
|
||||||
assertFalse(excludedValue.get(pollution1));
|
|
||||||
assertFalse(excludedValue.get(pollution2));
|
|
||||||
assertFalse(excludedValue.get(pollution3));
|
|
||||||
assertFalse(excludedValue.get(pollution4));
|
|
||||||
assertFalse(excludedValue.get(pollution5));
|
|
||||||
assertFalse(excludedValue.get(pollution6));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testCookiesWithStrutsInternalsAccess() throws Exception {
|
public void testCookiesWithStrutsInternalsAccess() throws Exception {
|
||||||
@@ -424,7 +409,6 @@ public class CookieInterceptorTest extends StrutsInternalTestCase {
|
|||||||
ServletActionContext.setRequest(request);
|
ServletActionContext.setRequest(request);
|
||||||
|
|
||||||
final Map<String, Boolean> excludedName = new HashMap<String, Boolean>();
|
final Map<String, Boolean> excludedName = new HashMap<String, Boolean>();
|
||||||
final Map<String, Boolean> excludedValue = new HashMap<String, Boolean>();
|
|
||||||
|
|
||||||
CookieInterceptor interceptor = new CookieInterceptor() {
|
CookieInterceptor interceptor = new CookieInterceptor() {
|
||||||
@Override
|
@Override
|
||||||
@@ -433,13 +417,6 @@ public class CookieInterceptorTest extends StrutsInternalTestCase {
|
|||||||
excludedName.put(name, accepted);
|
excludedName.put(name, accepted);
|
||||||
return accepted;
|
return accepted;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean isAcceptableValue(String value) {
|
|
||||||
boolean accepted = super.isAcceptableValue(value);
|
|
||||||
excludedValue.put(value, accepted);
|
|
||||||
return accepted;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
interceptor.setExcludedPatternsChecker(new DefaultExcludedPatternsChecker());
|
interceptor.setExcludedPatternsChecker(new DefaultExcludedPatternsChecker());
|
||||||
interceptor.setAcceptedPatternsChecker(new DefaultAcceptedPatternsChecker());
|
interceptor.setAcceptedPatternsChecker(new DefaultAcceptedPatternsChecker());
|
||||||
@@ -453,10 +430,6 @@ public class CookieInterceptorTest extends StrutsInternalTestCase {
|
|||||||
assertFalse(excludedName.get(sessionCookieName));
|
assertFalse(excludedName.get(sessionCookieName));
|
||||||
assertFalse(excludedName.get(appCookieName));
|
assertFalse(excludedName.get(appCookieName));
|
||||||
assertFalse(excludedName.get(reqCookieName));
|
assertFalse(excludedName.get(reqCookieName));
|
||||||
|
|
||||||
assertFalse(excludedValue.get(sessionCookieValue));
|
|
||||||
assertFalse(excludedValue.get(appCookieValue));
|
|
||||||
assertFalse(excludedValue.get(reqCookieValue));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class MockActionWithCookieAware extends ActionSupport implements CookiesAware {
|
public static class MockActionWithCookieAware extends ActionSupport implements CookiesAware {
|
||||||
|
|||||||
@@ -173,12 +173,12 @@ public class I18nInterceptorTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCountryOnlyLocale() throws Exception {
|
public void testCountryOnlyLocale() throws Exception {
|
||||||
params.put(I18nInterceptor.DEFAULT_PARAMETER, "DK");
|
params.put(I18nInterceptor.DEFAULT_PARAMETER, "NL");
|
||||||
interceptor.intercept(mai);
|
interceptor.intercept(mai);
|
||||||
|
|
||||||
assertNull(params.get(I18nInterceptor.DEFAULT_PARAMETER)); // should have been removed
|
assertNull(params.get(I18nInterceptor.DEFAULT_PARAMETER)); // should have been removed
|
||||||
|
|
||||||
Locale denmark = new Locale("DK");
|
Locale denmark = new Locale("NL");
|
||||||
assertNotNull(session.get(I18nInterceptor.DEFAULT_SESSION_ATTRIBUTE)); // should be stored here
|
assertNotNull(session.get(I18nInterceptor.DEFAULT_SESSION_ATTRIBUTE)); // should be stored here
|
||||||
assertEquals(denmark, session.get(I18nInterceptor.DEFAULT_SESSION_ATTRIBUTE)); // should create a locale object
|
assertEquals(denmark, session.get(I18nInterceptor.DEFAULT_SESSION_ATTRIBUTE)); // should create a locale object
|
||||||
}
|
}
|
||||||
@@ -197,31 +197,31 @@ public class I18nInterceptorTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWithVariant() throws Exception {
|
public void testWithVariant() throws Exception {
|
||||||
params.put(I18nInterceptor.DEFAULT_PARAMETER, "fr_CA_xx");
|
params.put(I18nInterceptor.DEFAULT_PARAMETER, "ja_JP_JP");
|
||||||
interceptor.intercept(mai);
|
interceptor.intercept(mai);
|
||||||
|
|
||||||
assertNull(params.get(I18nInterceptor.DEFAULT_PARAMETER)); // should have been removed
|
assertNull(params.get(I18nInterceptor.DEFAULT_PARAMETER)); // should have been removed
|
||||||
|
|
||||||
Locale variant = new Locale("fr", "CA", "xx");
|
Locale variant = new Locale("ja", "JP", "JP");
|
||||||
Locale locale = (Locale) session.get(I18nInterceptor.DEFAULT_SESSION_ATTRIBUTE);
|
Locale locale = (Locale) session.get(I18nInterceptor.DEFAULT_SESSION_ATTRIBUTE);
|
||||||
assertNotNull(locale); // should be stored here
|
assertNotNull(locale); // should be stored here
|
||||||
assertEquals(variant, locale);
|
assertEquals(variant, locale);
|
||||||
assertEquals("xx", locale.getVariant());
|
assertEquals("JP", locale.getVariant());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWithVariantRequestOnly() throws Exception {
|
public void testWithVariantRequestOnly() throws Exception {
|
||||||
params.put(I18nInterceptor.DEFAULT_REQUESTONLY_PARAMETER, "fr_CA_xx");
|
params.put(I18nInterceptor.DEFAULT_REQUESTONLY_PARAMETER, "ja_JP_JP");
|
||||||
interceptor.intercept(mai);
|
interceptor.intercept(mai);
|
||||||
|
|
||||||
assertNull(params.get(I18nInterceptor.DEFAULT_PARAMETER)); // should have been removed
|
assertNull(params.get(I18nInterceptor.DEFAULT_PARAMETER)); // should have been removed
|
||||||
assertNull(session.get(I18nInterceptor.DEFAULT_SESSION_ATTRIBUTE));
|
assertNull(session.get(I18nInterceptor.DEFAULT_SESSION_ATTRIBUTE));
|
||||||
|
|
||||||
Locale variant = new Locale("fr", "CA", "xx");
|
Locale variant = new Locale("ja", "JP", "JP");
|
||||||
Locale locale = mai.getInvocationContext().getLocale();
|
Locale locale = mai.getInvocationContext().getLocale();
|
||||||
assertNotNull(locale); // should be stored here
|
assertNotNull(locale); // should be stored here
|
||||||
assertEquals(variant, locale);
|
assertEquals(variant, locale);
|
||||||
assertEquals("xx", locale.getVariant());
|
assertEquals("JP", locale.getVariant());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
+48
-146
@@ -27,8 +27,9 @@ import java.util.LinkedHashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.opensymphony.xwork2.interceptor.PreResultListener;
|
||||||
|
import org.apache.struts2.ServletActionContext;
|
||||||
import org.apache.struts2.StrutsInternalTestCase;
|
import org.apache.struts2.StrutsInternalTestCase;
|
||||||
import org.apache.struts2.dispatcher.ServletActionRedirectResult;
|
|
||||||
import org.easymock.EasyMock;
|
import org.easymock.EasyMock;
|
||||||
|
|
||||||
import com.opensymphony.xwork2.Action;
|
import com.opensymphony.xwork2.Action;
|
||||||
@@ -36,6 +37,10 @@ import com.opensymphony.xwork2.ActionContext;
|
|||||||
import com.opensymphony.xwork2.ActionInvocation;
|
import com.opensymphony.xwork2.ActionInvocation;
|
||||||
import com.opensymphony.xwork2.ActionSupport;
|
import com.opensymphony.xwork2.ActionSupport;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.servlet.http.HttpSession;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test case for MessageStoreInterceptor.
|
* Test case for MessageStoreInterceptor.
|
||||||
@@ -44,73 +49,16 @@ import com.opensymphony.xwork2.ActionSupport;
|
|||||||
*/
|
*/
|
||||||
public class MessageStoreInterceptorTest extends StrutsInternalTestCase {
|
public class MessageStoreInterceptorTest extends StrutsInternalTestCase {
|
||||||
|
|
||||||
public void testStoreMessage() throws Exception {
|
@Override
|
||||||
MessageStoreInterceptor interceptor = new MessageStoreInterceptor();
|
public void setUp() throws Exception {
|
||||||
interceptor.setAllowRequestParameterSwitch(true);
|
super.setUp();
|
||||||
interceptor.setOperationMode(MessageStoreInterceptor.STORE_MODE);
|
|
||||||
|
|
||||||
|
HttpServletResponse response = EasyMock.createNiceControl().createMock(HttpServletResponse.class);
|
||||||
|
response.isCommitted();
|
||||||
|
EasyMock.expectLastCall().andReturn(Boolean.FALSE);
|
||||||
|
EasyMock.replay(response);
|
||||||
|
|
||||||
Map paramMap = new LinkedHashMap();
|
ServletActionContext.setResponse(response);
|
||||||
Map sessionMap = new LinkedHashMap();
|
|
||||||
|
|
||||||
ActionSupport action = new ActionSupport();
|
|
||||||
action.addActionError("some action error 1");
|
|
||||||
action.addActionError("some action error 2");
|
|
||||||
action.addActionMessage("some action message 1");
|
|
||||||
action.addActionMessage("some action message 2");
|
|
||||||
action.addFieldError("field1", "some field error 1");
|
|
||||||
action.addFieldError("field2", "some field error 2");
|
|
||||||
|
|
||||||
ActionContext actionContext = new ActionContext(new HashMap());
|
|
||||||
actionContext.put(ActionContext.PARAMETERS, paramMap);
|
|
||||||
actionContext.put(ActionContext.SESSION, sessionMap);
|
|
||||||
|
|
||||||
// Mock (ActionInvocation)
|
|
||||||
ActionInvocation mockActionInvocation = EasyMock.createControl().createMock(ActionInvocation.class);
|
|
||||||
mockActionInvocation.getInvocationContext();
|
|
||||||
EasyMock.expectLastCall().andReturn(actionContext);
|
|
||||||
EasyMock.expectLastCall().anyTimes();
|
|
||||||
|
|
||||||
mockActionInvocation.invoke();
|
|
||||||
EasyMock.expectLastCall().andReturn(Action.SUCCESS);
|
|
||||||
|
|
||||||
mockActionInvocation.getAction();
|
|
||||||
EasyMock.expectLastCall().andReturn(action);
|
|
||||||
|
|
||||||
mockActionInvocation.getResult();
|
|
||||||
EasyMock.expectLastCall().andReturn(new ServletActionRedirectResult());
|
|
||||||
|
|
||||||
EasyMock.replay(mockActionInvocation);
|
|
||||||
|
|
||||||
interceptor.init();
|
|
||||||
interceptor.intercept(mockActionInvocation);
|
|
||||||
interceptor.destroy();
|
|
||||||
|
|
||||||
assertEquals(sessionMap.size(), 3);
|
|
||||||
assertTrue(sessionMap.containsKey(MessageStoreInterceptor.actionErrorsSessionKey));
|
|
||||||
assertTrue(sessionMap.containsKey(MessageStoreInterceptor.actionMessagesSessionKey));
|
|
||||||
assertTrue(sessionMap.containsKey(MessageStoreInterceptor.fieldErrorsSessionKey));
|
|
||||||
|
|
||||||
List actionErrors = (List) sessionMap.get(MessageStoreInterceptor.actionErrorsSessionKey);
|
|
||||||
List actionMessages = (List) sessionMap.get(MessageStoreInterceptor.actionMessagesSessionKey);
|
|
||||||
Map fieldErrors = (Map) sessionMap.get(MessageStoreInterceptor.fieldErrorsSessionKey);
|
|
||||||
|
|
||||||
assertEquals(actionErrors.size(), 2);
|
|
||||||
assertEquals(actionMessages.size(), 2);
|
|
||||||
assertEquals(fieldErrors.size(), 2);
|
|
||||||
|
|
||||||
assertTrue(actionErrors.contains("some action error 1"));
|
|
||||||
assertTrue(actionErrors.contains("some action error 2"));
|
|
||||||
assertTrue(actionMessages.contains("some action message 1"));
|
|
||||||
assertTrue(actionMessages.contains("some action message 2"));
|
|
||||||
assertTrue(fieldErrors.containsKey("field1"));
|
|
||||||
assertTrue(fieldErrors.containsKey("field2"));
|
|
||||||
assertEquals(((List)fieldErrors.get("field1")).size(), 1);
|
|
||||||
assertEquals(((List)fieldErrors.get("field2")).size(), 1);
|
|
||||||
assertEquals(((List)fieldErrors.get("field1")).get(0), "some field error 1");
|
|
||||||
assertEquals(((List)fieldErrors.get("field2")).get(0), "some field error 2");
|
|
||||||
|
|
||||||
EasyMock.verify(mockActionInvocation);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testIgnoreMessageWithoutSession() throws Exception {
|
public void testIgnoreMessageWithoutSession() throws Exception {
|
||||||
@@ -128,6 +76,15 @@ public class MessageStoreInterceptorTest extends StrutsInternalTestCase {
|
|||||||
ActionContext actionContext = new ActionContext(new HashMap());
|
ActionContext actionContext = new ActionContext(new HashMap());
|
||||||
actionContext.put(ActionContext.PARAMETERS, paramMap);
|
actionContext.put(ActionContext.PARAMETERS, paramMap);
|
||||||
|
|
||||||
|
HttpSession mockedSession = EasyMock.createControl().createMock(HttpSession.class);
|
||||||
|
HttpServletRequest mockedRequest = EasyMock.createControl().createMock(HttpServletRequest.class);
|
||||||
|
mockedRequest.getSession(false);
|
||||||
|
EasyMock.expectLastCall().andReturn(mockedSession);
|
||||||
|
EasyMock.expectLastCall().once();
|
||||||
|
ServletActionContext.setRequest(mockedRequest);
|
||||||
|
|
||||||
|
EasyMock.replay(mockedRequest);
|
||||||
|
|
||||||
// Mock (ActionInvocation)
|
// Mock (ActionInvocation)
|
||||||
ActionInvocation mockActionInvocation = EasyMock.createControl().createMock(ActionInvocation.class);
|
ActionInvocation mockActionInvocation = EasyMock.createControl().createMock(ActionInvocation.class);
|
||||||
mockActionInvocation.getInvocationContext();
|
mockActionInvocation.getInvocationContext();
|
||||||
@@ -137,11 +94,8 @@ public class MessageStoreInterceptorTest extends StrutsInternalTestCase {
|
|||||||
mockActionInvocation.invoke();
|
mockActionInvocation.invoke();
|
||||||
EasyMock.expectLastCall().andReturn(Action.SUCCESS);
|
EasyMock.expectLastCall().andReturn(Action.SUCCESS);
|
||||||
|
|
||||||
mockActionInvocation.getAction();
|
mockActionInvocation.addPreResultListener(EasyMock.<PreResultListener>anyObject());
|
||||||
EasyMock.expectLastCall().andReturn(action);
|
EasyMock.expectLastCall();
|
||||||
|
|
||||||
mockActionInvocation.getResult();
|
|
||||||
EasyMock.expectLastCall().andReturn(new ServletActionRedirectResult());
|
|
||||||
|
|
||||||
EasyMock.replay(mockActionInvocation);
|
EasyMock.replay(mockActionInvocation);
|
||||||
|
|
||||||
@@ -186,6 +140,14 @@ public class MessageStoreInterceptorTest extends StrutsInternalTestCase {
|
|||||||
sessionMap.put(MessageStoreInterceptor.actionMessagesSessionKey, actionMessages);
|
sessionMap.put(MessageStoreInterceptor.actionMessagesSessionKey, actionMessages);
|
||||||
sessionMap.put(MessageStoreInterceptor.fieldErrorsSessionKey, fieldErrors);
|
sessionMap.put(MessageStoreInterceptor.fieldErrorsSessionKey, fieldErrors);
|
||||||
|
|
||||||
|
HttpSession mockedSession = EasyMock.createControl().createMock(HttpSession.class);
|
||||||
|
HttpServletRequest mockedRequest = EasyMock.createControl().createMock(HttpServletRequest.class);
|
||||||
|
mockedRequest.getSession(false);
|
||||||
|
EasyMock.expectLastCall().andReturn(mockedSession);
|
||||||
|
EasyMock.expectLastCall().once();
|
||||||
|
ServletActionContext.setRequest(mockedRequest);
|
||||||
|
|
||||||
|
EasyMock.replay(mockedRequest);
|
||||||
|
|
||||||
ActionContext actionContext = new ActionContext(new HashMap());
|
ActionContext actionContext = new ActionContext(new HashMap());
|
||||||
actionContext.put(ActionContext.PARAMETERS, paramsMap);
|
actionContext.put(ActionContext.PARAMETERS, paramsMap);
|
||||||
@@ -199,8 +161,8 @@ public class MessageStoreInterceptorTest extends StrutsInternalTestCase {
|
|||||||
EasyMock.expectLastCall().andReturn(action);
|
EasyMock.expectLastCall().andReturn(action);
|
||||||
EasyMock.expectLastCall().anyTimes();
|
EasyMock.expectLastCall().anyTimes();
|
||||||
|
|
||||||
mockActionInvocation.getResult();
|
mockActionInvocation.addPreResultListener(EasyMock.<PreResultListener>anyObject());
|
||||||
EasyMock.expectLastCall().andReturn(new ServletActionRedirectResult());
|
EasyMock.expectLastCall();
|
||||||
|
|
||||||
EasyMock.replay(mockActionInvocation);
|
EasyMock.replay(mockActionInvocation);
|
||||||
|
|
||||||
@@ -244,12 +206,24 @@ public class MessageStoreInterceptorTest extends StrutsInternalTestCase {
|
|||||||
actionContext.put(ActionContext.PARAMETERS, paramMap);
|
actionContext.put(ActionContext.PARAMETERS, paramMap);
|
||||||
actionContext.put(ActionContext.SESSION, sessionMap);
|
actionContext.put(ActionContext.SESSION, sessionMap);
|
||||||
|
|
||||||
|
HttpSession mockedSession = EasyMock.createControl().createMock(HttpSession.class);
|
||||||
|
HttpServletRequest mockedRequest = EasyMock.createControl().createMock(HttpServletRequest.class);
|
||||||
|
mockedRequest.getSession(false);
|
||||||
|
EasyMock.expectLastCall().andReturn(mockedSession);
|
||||||
|
EasyMock.expectLastCall().once();
|
||||||
|
ServletActionContext.setRequest(mockedRequest);
|
||||||
|
|
||||||
|
EasyMock.replay(mockedRequest);
|
||||||
|
|
||||||
// Mock (ActionInvocation)
|
// Mock (ActionInvocation)
|
||||||
ActionInvocation mockActionInvocation = EasyMock.createControl().createMock(ActionInvocation.class);
|
ActionInvocation mockActionInvocation = EasyMock.createControl().createMock(ActionInvocation.class);
|
||||||
mockActionInvocation.getInvocationContext();
|
mockActionInvocation.getInvocationContext();
|
||||||
EasyMock.expectLastCall().andReturn(actionContext);
|
EasyMock.expectLastCall().andReturn(actionContext);
|
||||||
EasyMock.expectLastCall().anyTimes();
|
EasyMock.expectLastCall().anyTimes();
|
||||||
|
|
||||||
|
mockActionInvocation.addPreResultListener(EasyMock.<PreResultListener>anyObject());
|
||||||
|
EasyMock.expectLastCall();
|
||||||
|
|
||||||
mockActionInvocation.invoke();
|
mockActionInvocation.invoke();
|
||||||
EasyMock.expectLastCall().andReturn(Action.SUCCESS);
|
EasyMock.expectLastCall().andReturn(Action.SUCCESS);
|
||||||
|
|
||||||
@@ -257,85 +231,12 @@ public class MessageStoreInterceptorTest extends StrutsInternalTestCase {
|
|||||||
EasyMock.expectLastCall().andReturn(action);
|
EasyMock.expectLastCall().andReturn(action);
|
||||||
EasyMock.expectLastCall().anyTimes();
|
EasyMock.expectLastCall().anyTimes();
|
||||||
|
|
||||||
mockActionInvocation.getResult();
|
|
||||||
EasyMock.expectLastCall().andReturn(new ServletActionRedirectResult());
|
|
||||||
|
|
||||||
EasyMock.replay(mockActionInvocation);
|
EasyMock.replay(mockActionInvocation);
|
||||||
|
|
||||||
interceptor.init();
|
interceptor.init();
|
||||||
interceptor.intercept(mockActionInvocation);
|
interceptor.intercept(mockActionInvocation);
|
||||||
interceptor.destroy();
|
interceptor.destroy();
|
||||||
|
|
||||||
assertEquals(3, sessionMap.size());
|
|
||||||
assertTrue(sessionMap.containsKey(MessageStoreInterceptor.actionErrorsSessionKey));
|
|
||||||
assertTrue(sessionMap.containsKey(MessageStoreInterceptor.actionMessagesSessionKey));
|
|
||||||
assertTrue(sessionMap.containsKey(MessageStoreInterceptor.fieldErrorsSessionKey));
|
|
||||||
|
|
||||||
List actionErrors = (List) sessionMap.get(MessageStoreInterceptor.actionErrorsSessionKey);
|
|
||||||
List actionMessages = (List) sessionMap.get(MessageStoreInterceptor.actionMessagesSessionKey);
|
|
||||||
Map fieldErrors = (Map) sessionMap.get(MessageStoreInterceptor.fieldErrorsSessionKey);
|
|
||||||
|
|
||||||
assertEquals(2, actionErrors.size());
|
|
||||||
assertEquals(2, actionMessages.size());
|
|
||||||
assertEquals(2, fieldErrors.size());
|
|
||||||
|
|
||||||
assertTrue(actionErrors.contains("some action error 1"));
|
|
||||||
assertTrue(actionErrors.contains("some action error 2"));
|
|
||||||
assertTrue(actionMessages.contains("some action message 1"));
|
|
||||||
assertTrue(actionMessages.contains("some action message 2"));
|
|
||||||
assertTrue(fieldErrors.containsKey("field1"));
|
|
||||||
assertTrue(fieldErrors.containsKey("field2"));
|
|
||||||
assertEquals(((List)fieldErrors.get("field1")).size(), 1);
|
|
||||||
assertEquals(((List)fieldErrors.get("field2")).size(), 1);
|
|
||||||
assertEquals(((List)fieldErrors.get("field1")).get(0), "some field error 1");
|
|
||||||
assertEquals(((List)fieldErrors.get("field2")).get(0), "some field error 2");
|
|
||||||
|
|
||||||
EasyMock.verify(mockActionInvocation);
|
|
||||||
|
|
||||||
action = new ActionSupport();
|
|
||||||
|
|
||||||
mockActionInvocation = EasyMock.createControl().createMock(ActionInvocation.class);
|
|
||||||
mockActionInvocation.invoke();
|
|
||||||
EasyMock.expectLastCall().andReturn(Action.SUCCESS);
|
|
||||||
|
|
||||||
sessionMap.put(MessageStoreInterceptor.actionErrorsSessionKey, actionErrors);
|
|
||||||
sessionMap.put(MessageStoreInterceptor.actionMessagesSessionKey, actionMessages);
|
|
||||||
sessionMap.put(MessageStoreInterceptor.fieldErrorsSessionKey, fieldErrors);
|
|
||||||
|
|
||||||
|
|
||||||
actionContext = new ActionContext(new HashMap());
|
|
||||||
actionContext.put(ActionContext.PARAMETERS, paramMap);
|
|
||||||
actionContext.put(ActionContext.SESSION, sessionMap);
|
|
||||||
|
|
||||||
mockActionInvocation.getInvocationContext();
|
|
||||||
EasyMock.expectLastCall().andReturn(actionContext);
|
|
||||||
EasyMock.expectLastCall().anyTimes();
|
|
||||||
|
|
||||||
mockActionInvocation.getAction();
|
|
||||||
EasyMock.expectLastCall().andReturn(action);
|
|
||||||
EasyMock.expectLastCall().anyTimes();
|
|
||||||
|
|
||||||
mockActionInvocation.getResult();
|
|
||||||
EasyMock.expectLastCall().andReturn(new ServletActionRedirectResult());
|
|
||||||
|
|
||||||
EasyMock.replay(mockActionInvocation);
|
|
||||||
|
|
||||||
interceptor.init();
|
|
||||||
interceptor.intercept(mockActionInvocation);
|
|
||||||
interceptor.destroy();
|
|
||||||
|
|
||||||
assertEquals(action.getActionErrors().size(), 2);
|
|
||||||
assertEquals(action.getActionMessages().size(), 2);
|
|
||||||
assertEquals(action.getFieldErrors().size(), 2);
|
|
||||||
assertTrue(action.getActionErrors().contains("some action error 1"));
|
|
||||||
assertTrue(action.getActionErrors().contains("some action error 2"));
|
|
||||||
assertTrue(action.getActionMessages().contains("some action message 1"));
|
|
||||||
assertTrue(action.getActionMessages().contains("some action message 2"));
|
|
||||||
assertEquals(((List)action.getFieldErrors().get("field1")).size(), 1);
|
|
||||||
assertEquals(((List)action.getFieldErrors().get("field2")).size(), 1);
|
|
||||||
assertEquals(((List)action.getFieldErrors().get("field1")).get(0), "some field error 1");
|
|
||||||
assertEquals(((List)action.getFieldErrors().get("field2")).get(0), "some field error 2");
|
|
||||||
|
|
||||||
EasyMock.verify(mockActionInvocation);
|
EasyMock.verify(mockActionInvocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -407,4 +308,5 @@ public class MessageStoreInterceptorTest extends StrutsInternalTestCase {
|
|||||||
EasyMock.verify(mockActionInvocation);
|
EasyMock.verify(mockActionInvocation);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+266
@@ -0,0 +1,266 @@
|
|||||||
|
package org.apache.struts2.interceptor;
|
||||||
|
|
||||||
|
import com.opensymphony.xwork2.Action;
|
||||||
|
import com.opensymphony.xwork2.ActionContext;
|
||||||
|
import com.opensymphony.xwork2.ActionInvocation;
|
||||||
|
import com.opensymphony.xwork2.ActionSupport;
|
||||||
|
import com.opensymphony.xwork2.config.entities.ActionConfig;
|
||||||
|
import com.opensymphony.xwork2.config.entities.ResultConfig;
|
||||||
|
import com.opensymphony.xwork2.mock.MockActionProxy;
|
||||||
|
import org.apache.struts2.ServletActionContext;
|
||||||
|
import org.apache.struts2.StrutsInternalTestCase;
|
||||||
|
import org.apache.struts2.dispatcher.ServletActionRedirectResult;
|
||||||
|
import org.apache.struts2.dispatcher.ServletRedirectResult;
|
||||||
|
import org.easymock.EasyMock;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.servlet.http.HttpSession;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class MessageStorePreResultListenerTest extends StrutsInternalTestCase {
|
||||||
|
|
||||||
|
public void testSessionWasInvalidated() throws Exception {
|
||||||
|
// given
|
||||||
|
ActionContext actionContext = new ActionContext(new HashMap());
|
||||||
|
actionContext.put(ActionContext.PARAMETERS, new LinkedHashMap());
|
||||||
|
|
||||||
|
ActionInvocation mockActionInvocation = EasyMock.createControl().createMock(ActionInvocation.class);
|
||||||
|
|
||||||
|
mockActionInvocation.getInvocationContext();
|
||||||
|
EasyMock.expectLastCall().andReturn(actionContext);
|
||||||
|
EasyMock.expectLastCall().anyTimes();
|
||||||
|
|
||||||
|
EasyMock.replay(mockActionInvocation);
|
||||||
|
|
||||||
|
HttpServletRequest mockedRequest = EasyMock.createControl().createMock(HttpServletRequest.class);
|
||||||
|
mockedRequest.getSession(false);
|
||||||
|
EasyMock.expectLastCall().andReturn(null);
|
||||||
|
EasyMock.expectLastCall().once();
|
||||||
|
ServletActionContext.setRequest(mockedRequest);
|
||||||
|
|
||||||
|
EasyMock.replay(mockedRequest);
|
||||||
|
|
||||||
|
HttpServletResponse mockedResponse = EasyMock.createControl().createMock(HttpServletResponse.class);
|
||||||
|
mockedResponse.isCommitted();
|
||||||
|
EasyMock.expectLastCall().andReturn(false);
|
||||||
|
EasyMock.expectLastCall().once();
|
||||||
|
ServletActionContext.setResponse(mockedResponse);
|
||||||
|
|
||||||
|
EasyMock.replay(mockedResponse);
|
||||||
|
|
||||||
|
// when
|
||||||
|
MessageStoreInterceptor msi = new MessageStoreInterceptor();
|
||||||
|
MessageStorePreResultListener listener = new MessageStorePreResultListener(msi);
|
||||||
|
listener.beforeResult(mockActionInvocation, Action.SUCCESS);
|
||||||
|
|
||||||
|
// then
|
||||||
|
EasyMock.verify(mockActionInvocation);
|
||||||
|
EasyMock.verify(mockedRequest);
|
||||||
|
EasyMock.verify(mockedResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testResponseWasComitted() throws Exception {
|
||||||
|
// given
|
||||||
|
ActionContext actionContext = new ActionContext(new HashMap());
|
||||||
|
actionContext.put(ActionContext.PARAMETERS, new LinkedHashMap());
|
||||||
|
|
||||||
|
ActionInvocation mockActionInvocation = EasyMock.createControl().createMock(ActionInvocation.class);
|
||||||
|
|
||||||
|
mockActionInvocation.getInvocationContext();
|
||||||
|
EasyMock.expectLastCall().andReturn(actionContext);
|
||||||
|
EasyMock.expectLastCall().anyTimes();
|
||||||
|
|
||||||
|
EasyMock.replay(mockActionInvocation);
|
||||||
|
|
||||||
|
HttpServletResponse mockedResponse = EasyMock.createControl().createMock(HttpServletResponse.class);
|
||||||
|
mockedResponse.isCommitted();
|
||||||
|
EasyMock.expectLastCall().andReturn(true);
|
||||||
|
EasyMock.expectLastCall().once();
|
||||||
|
ServletActionContext.setResponse(mockedResponse);
|
||||||
|
|
||||||
|
EasyMock.replay(mockedResponse);
|
||||||
|
|
||||||
|
// when
|
||||||
|
MessageStoreInterceptor msi = new MessageStoreInterceptor();
|
||||||
|
MessageStorePreResultListener listener = new MessageStorePreResultListener(msi);
|
||||||
|
listener.beforeResult(mockActionInvocation, Action.SUCCESS);
|
||||||
|
|
||||||
|
// then
|
||||||
|
EasyMock.verify(mockActionInvocation);
|
||||||
|
EasyMock.verify(mockedResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAutomatic() throws Exception {
|
||||||
|
MessageStoreInterceptor interceptor = new MessageStoreInterceptor();
|
||||||
|
interceptor.setOperationMode(MessageStoreInterceptor.AUTOMATIC_MODE);
|
||||||
|
|
||||||
|
MessageStorePreResultListener listener = new MessageStorePreResultListener(interceptor);
|
||||||
|
|
||||||
|
Map paramMap = new LinkedHashMap();
|
||||||
|
Map sessionMap = new LinkedHashMap();
|
||||||
|
|
||||||
|
ActionSupport action = new ActionSupport();
|
||||||
|
action.addActionError("some action error 1");
|
||||||
|
action.addActionError("some action error 2");
|
||||||
|
action.addActionMessage("some action message 1");
|
||||||
|
action.addActionMessage("some action message 2");
|
||||||
|
action.addFieldError("field1", "some field error 1");
|
||||||
|
action.addFieldError("field2", "some field error 2");
|
||||||
|
|
||||||
|
ActionContext actionContext = new ActionContext(new HashMap());
|
||||||
|
actionContext.put(ActionContext.PARAMETERS, paramMap);
|
||||||
|
actionContext.put(ActionContext.SESSION, sessionMap);
|
||||||
|
|
||||||
|
HttpSession mockedSession = EasyMock.createControl().createMock(HttpSession.class);
|
||||||
|
HttpServletRequest mockedRequest = EasyMock.createControl().createMock(HttpServletRequest.class);
|
||||||
|
mockedRequest.getSession(false);
|
||||||
|
EasyMock.expectLastCall().andReturn(mockedSession);
|
||||||
|
EasyMock.expectLastCall().once();
|
||||||
|
ServletActionContext.setRequest(mockedRequest);
|
||||||
|
|
||||||
|
EasyMock.replay(mockedRequest);
|
||||||
|
|
||||||
|
HttpServletResponse mockedResponse = EasyMock.createControl().createMock(HttpServletResponse.class);
|
||||||
|
mockedResponse.isCommitted();
|
||||||
|
EasyMock.expectLastCall().andReturn(false);
|
||||||
|
EasyMock.expectLastCall().once();
|
||||||
|
ServletActionContext.setResponse(mockedResponse);
|
||||||
|
|
||||||
|
EasyMock.replay(mockedResponse);
|
||||||
|
|
||||||
|
// Mock (ActionInvocation)
|
||||||
|
ActionInvocation mockActionInvocation = EasyMock.createControl().createMock(ActionInvocation.class);
|
||||||
|
mockActionInvocation.getInvocationContext();
|
||||||
|
EasyMock.expectLastCall().andReturn(actionContext);
|
||||||
|
EasyMock.expectLastCall().anyTimes();
|
||||||
|
|
||||||
|
mockActionInvocation.getAction();
|
||||||
|
EasyMock.expectLastCall().andReturn(action);
|
||||||
|
EasyMock.expectLastCall().anyTimes();
|
||||||
|
|
||||||
|
mockActionInvocation.getProxy();
|
||||||
|
MockActionProxy actionProxy = new MockActionProxy();
|
||||||
|
ResultConfig resultConfig = new ResultConfig.Builder(Action.SUCCESS, ServletRedirectResult.class.getName()).build();
|
||||||
|
ActionConfig actionConfig = new ActionConfig.Builder("", "test", action.getClass().getName()).addResultConfig(resultConfig).build();
|
||||||
|
actionProxy.setConfig(actionConfig);
|
||||||
|
EasyMock.expectLastCall().andReturn(actionProxy);
|
||||||
|
EasyMock.expectLastCall().anyTimes();
|
||||||
|
|
||||||
|
EasyMock.replay(mockActionInvocation);
|
||||||
|
|
||||||
|
interceptor.init();
|
||||||
|
listener.beforeResult(mockActionInvocation, Action.SUCCESS);
|
||||||
|
|
||||||
|
List actionErrors = (List) sessionMap.get(MessageStoreInterceptor.actionErrorsSessionKey);
|
||||||
|
List actionMessages = (List) sessionMap.get(MessageStoreInterceptor.actionMessagesSessionKey);
|
||||||
|
Map fieldErrors = (Map) sessionMap.get(MessageStoreInterceptor.fieldErrorsSessionKey);
|
||||||
|
|
||||||
|
assertEquals(actionErrors.size(), 2);
|
||||||
|
assertEquals(actionMessages.size(), 2);
|
||||||
|
assertEquals(fieldErrors.size(), 2);
|
||||||
|
assertTrue(actionErrors.contains("some action error 1"));
|
||||||
|
assertTrue(actionErrors.contains("some action error 2"));
|
||||||
|
assertTrue(actionMessages.contains("some action message 1"));
|
||||||
|
assertTrue(actionMessages.contains("some action message 2"));
|
||||||
|
assertEquals(((List) fieldErrors.get("field1")).size(), 1);
|
||||||
|
assertEquals(((List) fieldErrors.get("field2")).size(), 1);
|
||||||
|
assertEquals(((List) fieldErrors.get("field1")).get(0), "some field error 1");
|
||||||
|
assertEquals(((List) fieldErrors.get("field2")).get(0), "some field error 2");
|
||||||
|
|
||||||
|
EasyMock.verify(mockActionInvocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testStoreMessage() throws Exception {
|
||||||
|
MessageStoreInterceptor interceptor = new MessageStoreInterceptor();
|
||||||
|
interceptor.setAllowRequestParameterSwitch(true);
|
||||||
|
interceptor.setOperationMode(MessageStoreInterceptor.STORE_MODE);
|
||||||
|
|
||||||
|
MessageStorePreResultListener listener = new MessageStorePreResultListener(interceptor);
|
||||||
|
|
||||||
|
Map paramMap = new LinkedHashMap();
|
||||||
|
Map sessionMap = new LinkedHashMap();
|
||||||
|
|
||||||
|
ActionSupport action = new ActionSupport();
|
||||||
|
action.addActionError("some action error 1");
|
||||||
|
action.addActionError("some action error 2");
|
||||||
|
action.addActionMessage("some action message 1");
|
||||||
|
action.addActionMessage("some action message 2");
|
||||||
|
action.addFieldError("field1", "some field error 1");
|
||||||
|
action.addFieldError("field2", "some field error 2");
|
||||||
|
|
||||||
|
ActionContext actionContext = new ActionContext(new HashMap());
|
||||||
|
actionContext.put(ActionContext.PARAMETERS, paramMap);
|
||||||
|
actionContext.put(ActionContext.SESSION, sessionMap);
|
||||||
|
|
||||||
|
HttpSession mockedSession = EasyMock.createControl().createMock(HttpSession.class);
|
||||||
|
HttpServletRequest mockedRequest = EasyMock.createControl().createMock(HttpServletRequest.class);
|
||||||
|
mockedRequest.getSession(false);
|
||||||
|
EasyMock.expectLastCall().andReturn(mockedSession);
|
||||||
|
EasyMock.expectLastCall().once();
|
||||||
|
ServletActionContext.setRequest(mockedRequest);
|
||||||
|
|
||||||
|
EasyMock.replay(mockedRequest);
|
||||||
|
|
||||||
|
HttpServletResponse mockedResponse = EasyMock.createControl().createMock(HttpServletResponse.class);
|
||||||
|
mockedResponse.isCommitted();
|
||||||
|
EasyMock.expectLastCall().andReturn(false);
|
||||||
|
EasyMock.expectLastCall().once();
|
||||||
|
ServletActionContext.setResponse(mockedResponse);
|
||||||
|
|
||||||
|
EasyMock.replay(mockedResponse);
|
||||||
|
|
||||||
|
// Mock (ActionInvocation)
|
||||||
|
ActionInvocation mockActionInvocation = EasyMock.createControl().createMock(ActionInvocation.class);
|
||||||
|
mockActionInvocation.getInvocationContext();
|
||||||
|
EasyMock.expectLastCall().andReturn(actionContext);
|
||||||
|
EasyMock.expectLastCall().anyTimes();
|
||||||
|
|
||||||
|
mockActionInvocation.getAction();
|
||||||
|
EasyMock.expectLastCall().andReturn(action);
|
||||||
|
|
||||||
|
mockActionInvocation.getProxy();
|
||||||
|
MockActionProxy actionProxy = new MockActionProxy();
|
||||||
|
ResultConfig resultConfig = new ResultConfig.Builder(Action.SUCCESS, ServletRedirectResult.class.getName()).build();
|
||||||
|
ActionConfig actionConfig = new ActionConfig.Builder("", "test", action.getClass().getName()).addResultConfig(resultConfig).build();
|
||||||
|
actionProxy.setConfig(actionConfig);
|
||||||
|
EasyMock.expectLastCall().andReturn(actionProxy);
|
||||||
|
EasyMock.expectLastCall().anyTimes();
|
||||||
|
|
||||||
|
EasyMock.replay(mockActionInvocation);
|
||||||
|
|
||||||
|
interceptor.init();
|
||||||
|
listener.beforeResult(mockActionInvocation, Action.SUCCESS);
|
||||||
|
|
||||||
|
assertEquals(sessionMap.size(), 3);
|
||||||
|
assertTrue(sessionMap.containsKey(MessageStoreInterceptor.actionErrorsSessionKey));
|
||||||
|
assertTrue(sessionMap.containsKey(MessageStoreInterceptor.actionMessagesSessionKey));
|
||||||
|
assertTrue(sessionMap.containsKey(MessageStoreInterceptor.fieldErrorsSessionKey));
|
||||||
|
|
||||||
|
List actionErrors = (List) sessionMap.get(MessageStoreInterceptor.actionErrorsSessionKey);
|
||||||
|
List actionMessages = (List) sessionMap.get(MessageStoreInterceptor.actionMessagesSessionKey);
|
||||||
|
Map fieldErrors = (Map) sessionMap.get(MessageStoreInterceptor.fieldErrorsSessionKey);
|
||||||
|
|
||||||
|
assertEquals(actionErrors.size(), 2);
|
||||||
|
assertEquals(actionMessages.size(), 2);
|
||||||
|
assertEquals(fieldErrors.size(), 2);
|
||||||
|
|
||||||
|
assertTrue(actionErrors.contains("some action error 1"));
|
||||||
|
assertTrue(actionErrors.contains("some action error 2"));
|
||||||
|
assertTrue(actionMessages.contains("some action message 1"));
|
||||||
|
assertTrue(actionMessages.contains("some action message 2"));
|
||||||
|
assertTrue(fieldErrors.containsKey("field1"));
|
||||||
|
assertTrue(fieldErrors.containsKey("field2"));
|
||||||
|
assertEquals(((List)fieldErrors.get("field1")).size(), 1);
|
||||||
|
assertEquals(((List)fieldErrors.get("field2")).size(), 1);
|
||||||
|
assertEquals(((List)fieldErrors.get("field1")).get(0), "some field error 1");
|
||||||
|
assertEquals(((List)fieldErrors.get("field2")).get(0), "some field error 2");
|
||||||
|
|
||||||
|
EasyMock.verify(mockActionInvocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -71,6 +71,17 @@ public class ComponentUtilsTest extends StrutsInternalTestCase {
|
|||||||
assertTrue(actual);
|
assertTrue(actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testIsExpressionIsFalseWhenCombined() throws Exception {
|
||||||
|
// given
|
||||||
|
String anExpression = "bar%{foo}";
|
||||||
|
|
||||||
|
// when
|
||||||
|
boolean actual = ComponentUtils.isExpression(anExpression);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertFalse(actual);
|
||||||
|
}
|
||||||
|
|
||||||
public void testIsExpressionIsFalse() throws Exception {
|
public void testIsExpressionIsFalse() throws Exception {
|
||||||
// given
|
// given
|
||||||
String anExpression = "foo";
|
String anExpression = "foo";
|
||||||
@@ -81,6 +92,47 @@ public class ComponentUtilsTest extends StrutsInternalTestCase {
|
|||||||
// then
|
// then
|
||||||
assertFalse(actual);
|
assertFalse(actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testIsExpressionIsFalseWhenNull() throws Exception {
|
||||||
|
assertFalse(ComponentUtils.isExpression(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testContainsExpressionIsTrue() throws Exception {
|
||||||
|
// given
|
||||||
|
String anExpression = "%{foo}";
|
||||||
|
|
||||||
|
// when
|
||||||
|
boolean actual = ComponentUtils.containsExpression(anExpression);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertTrue(actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testIsContainsIsTrueWhenCombined() throws Exception {
|
||||||
|
// given
|
||||||
|
String anExpression = "bar%{foo}";
|
||||||
|
|
||||||
|
// when
|
||||||
|
boolean actual = ComponentUtils.containsExpression(anExpression);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertTrue(actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testContainsExpressionIsFalse() throws Exception {
|
||||||
|
// given
|
||||||
|
String anExpression = "foo";
|
||||||
|
|
||||||
|
// when
|
||||||
|
boolean actual = ComponentUtils.containsExpression(anExpression);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertFalse(actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testContainsExpressionIsFalseWhenNull() throws Exception {
|
||||||
|
assertFalse(ComponentUtils.containsExpression(null));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MockConfigurationProvider implements ConfigurationProvider {
|
class MockConfigurationProvider implements ConfigurationProvider {
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
package org.apache.struts2.util;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
public class URLDecoderUtilTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testURLDecodeStringInvalid() {
|
||||||
|
// %n rather than %nn should throw an IAE according to the Javadoc
|
||||||
|
Exception exception = null;
|
||||||
|
try {
|
||||||
|
URLDecoderUtil.decode("%5xxxxx", "ISO-8859-1");
|
||||||
|
} catch (Exception e) {
|
||||||
|
exception = e;
|
||||||
|
}
|
||||||
|
assertTrue(exception instanceof IllegalArgumentException);
|
||||||
|
|
||||||
|
// Edge case trying to trigger ArrayIndexOutOfBoundsException
|
||||||
|
exception = null;
|
||||||
|
try {
|
||||||
|
URLDecoderUtil.decode("%5", "ISO-8859-1");
|
||||||
|
} catch (Exception e) {
|
||||||
|
exception = e;
|
||||||
|
}
|
||||||
|
assertTrue(exception instanceof IllegalArgumentException);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testURLDecodeStringValidIso88591Start() {
|
||||||
|
|
||||||
|
String result = URLDecoderUtil.decode("%41xxxx", "ISO-8859-1");
|
||||||
|
assertEquals("Axxxx", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testURLDecodeStringValidIso88591Middle() {
|
||||||
|
|
||||||
|
String result = URLDecoderUtil.decode("xx%41xx", "ISO-8859-1");
|
||||||
|
assertEquals("xxAxx", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testURLDecodeStringValidIso88591End() {
|
||||||
|
|
||||||
|
String result = URLDecoderUtil.decode("xxxx%41", "ISO-8859-1");
|
||||||
|
assertEquals("xxxxA", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testURLDecodeStringValidUtf8Start() {
|
||||||
|
String result = URLDecoderUtil.decode("%c3%aaxxxx", "UTF-8");
|
||||||
|
assertEquals("\u00eaxxxx", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testURLDecodeStringValidUtf8Middle() {
|
||||||
|
|
||||||
|
String result = URLDecoderUtil.decode("xx%c3%aaxx", "UTF-8");
|
||||||
|
assertEquals("xx\u00eaxx", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testURLDecodeStringValidUtf8End() {
|
||||||
|
|
||||||
|
String result = URLDecoderUtil.decode("xxxx%c3%aa", "UTF-8");
|
||||||
|
assertEquals("xxxx\u00ea", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testURLDecodePlusCharAsSpace() {
|
||||||
|
|
||||||
|
String result = URLDecoderUtil.decode("a+b", "UTF-8", true);
|
||||||
|
assertEquals("a b", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -657,8 +657,7 @@ public class URLTagTest extends AbstractUITagTest {
|
|||||||
tag.doEndTag();
|
tag.doEndTag();
|
||||||
|
|
||||||
Object allowMethodAccess = stack.findValue("\u0023_memberAccess['allowStaticMethodAccess']");
|
Object allowMethodAccess = stack.findValue("\u0023_memberAccess['allowStaticMethodAccess']");
|
||||||
assertNotNull(allowMethodAccess);
|
assertNull(allowMethodAccess);
|
||||||
assertEquals(Boolean.FALSE, allowMethodAccess);
|
|
||||||
|
|
||||||
assertNull(session.get("foo"));
|
assertNull(session.get("foo"));
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user