mirror of
https://github.com/apache/struts.git
synced 2026-08-08 16:16:58 +00:00
Compare commits
231 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a249ed5f31 | |||
| 5d999d6ac1 | |||
| 8f53b6f59e | |||
| 0d6442bab5 | |||
| 33e1eeb386 | |||
| fa20b71fc2 | |||
| 4c386c663c | |||
| 31f0252e60 | |||
| 05f3b7a9b9 | |||
| 93dba5bce2 | |||
| 216c52095e | |||
| 6478815291 | |||
| 7987c38d5a | |||
| 56f31bec42 | |||
| 2a8a6869d0 | |||
| 843693fdd4 | |||
| 0100a6bebf | |||
| 46153e78da | |||
| 8e9f9fb89f | |||
| 2fb431d97e | |||
| 3a9f86a605 | |||
| cb0f42abcc | |||
| 8b862f7cb2 | |||
| 51afa63183 | |||
| 45aae9cd80 | |||
| 229afea64e | |||
| 20eced9500 | |||
| 266d78d32c | |||
| f874f9cde5 | |||
| 5a6c336947 | |||
| 7fa41f1885 | |||
| 9a8b4f4ebc | |||
| 5a0f2e1aaf | |||
| 4604333649 | |||
| fd168c423f | |||
| 7360b5e195 | |||
| b842a4c779 | |||
| 0314ddcb5c | |||
| c2f2de06ce | |||
| 38a2ebb72a | |||
| 3034b3a970 | |||
| 6dad53c5e3 | |||
| bbcd9dc251 | |||
| 1df89792f9 | |||
| bbd4a9e8c5 | |||
| bb19a62098 | |||
| 2e23d7a07e | |||
| 8bf77a1d19 | |||
| 3ffc9b8450 | |||
| 45d1a49131 | |||
| b46a6e7956 | |||
| f06caf7ca7 | |||
| f5125bcd1b | |||
| 787150d041 | |||
| d739be34bc | |||
| 8f75e1a19d | |||
| 775d1c8c65 | |||
| 6ef9f7a3aa | |||
| faf72b5a09 | |||
| 9eec07b310 | |||
| d8284846db | |||
| b465791dc2 | |||
| defaf43a4c | |||
| 11cccb68a9 | |||
| 9512410412 | |||
| a58452571c | |||
| ec24124925 | |||
| 3e76564d3c | |||
| 47552bdb06 | |||
| eb4fcb4afc | |||
| 0813eaa0c8 | |||
| 743d27f841 | |||
| fc497360be | |||
| 01d710bdf3 | |||
| 293d2fd177 | |||
| 5fd605eb97 | |||
| 9d4aac0589 | |||
| 13f49a0978 | |||
| 30ec6f8310 | |||
| 034e098184 | |||
| b1f9068f2b | |||
| 1ad6d2c317 | |||
| 569e1f6c82 | |||
| 6f272e4873 | |||
| 2fe050578a | |||
| 16e8f10f66 | |||
| df5583f117 | |||
| 01a56ca117 | |||
| e66fd538b5 | |||
| 158eda9783 | |||
| caca3d057d | |||
| 1162ddf823 | |||
| c56d95d28c | |||
| b4f8d03910 | |||
| ed20ccd3f3 | |||
| 3cee495a12 | |||
| f0a9226ffb | |||
| 67069a9724 | |||
| 2d5f0aaa66 | |||
| 7627b83555 | |||
| 350c77aba8 | |||
| 3d3bee0218 | |||
| 98467c2753 | |||
| 748da3f8ce | |||
| d053df4924 | |||
| 4e9fa84239 | |||
| 82f61666f8 | |||
| e1bb1a70c0 | |||
| 915e162e8e | |||
| e9d7cd09db | |||
| 0378161365 | |||
| 88a39333ce | |||
| e8a598c7bc | |||
| 132829cc24 | |||
| c039685cf9 | |||
| ee6c927db6 | |||
| 944b1a2303 | |||
| 16d1d60ed8 | |||
| 207ac17744 | |||
| 3b73544a8f | |||
| 6dcff10a68 | |||
| c83e07ec46 | |||
| d7069fc393 | |||
| 54974223fa | |||
| 9376bb0ca1 | |||
| 4f46f15184 | |||
| 9d60f5bbc6 | |||
| 4eb22020a4 | |||
| 82983713ae | |||
| 5452f56648 | |||
| c815f5c54d | |||
| 47382411d7 | |||
| f912dab8f3 | |||
| 4b022bfbe5 | |||
| 3b738ad3ef | |||
| 09be1b9b77 | |||
| 636821aa2d | |||
| b42b9f16c4 | |||
| 6ee73dce79 | |||
| fefb91fe11 | |||
| 3a84a626f9 | |||
| 008c28ac67 | |||
| c3416e8ea0 | |||
| 6e89179eb2 | |||
| 31ddb8b602 | |||
| f784a16cb7 | |||
| ca0f62925a | |||
| f1a963b323 | |||
| d1fbf6a897 | |||
| 8852e3d118 | |||
| 6d60064bea | |||
| ce2261f6f5 | |||
| 664895a338 | |||
| 2b3082ee40 | |||
| 55ea82cd4f | |||
| f56c59c982 | |||
| 451eb88307 | |||
| 57106ccda2 | |||
| c063704d27 | |||
| 3dc8480988 | |||
| d7cf060905 | |||
| 3be5aa0fdb | |||
| 89d95b64cd | |||
| 728055dc46 | |||
| db8ebe710d | |||
| da5da67d80 | |||
| 6c19875c78 | |||
| 1ab616b10b | |||
| 442a52fe48 | |||
| 0b16543d6f | |||
| 741f3fa107 | |||
| 524343c3cf | |||
| 2733f158d8 | |||
| 4ef6418505 | |||
| 239327ea36 | |||
| fa1a5f9e69 | |||
| ae09c6a087 | |||
| b3b8b909e3 | |||
| 6b8272ce47 | |||
| f487d7e7d6 | |||
| 26fa06b117 | |||
| 78db281cd6 | |||
| 38409e0e08 | |||
| c8630ac56d | |||
| 9fcf2dfca7 | |||
| 805cdd7754 | |||
| a399932ae9 | |||
| f22873e127 | |||
| 3dafd2a356 | |||
| 534dfc6bd8 | |||
| 4b96958be9 | |||
| 301844bbaa | |||
| c84b7967e9 | |||
| 51c0bcbb8b | |||
| 7140e0192b | |||
| 0437efc6f9 | |||
| 533b236fdb | |||
| ad367777cb | |||
| 637ad1c370 | |||
| 1a8111e0ea | |||
| 26bc9cd64b | |||
| 58667e6497 | |||
| 47a415ac5c | |||
| 465a15664f | |||
| f0f4e9ece7 | |||
| 45580718ab | |||
| 87f113ebb2 | |||
| 0023d9664b | |||
| ee27b6604a | |||
| 7f80ef1bb5 | |||
| 253799c6c5 | |||
| 20c54df03a | |||
| ff8d731594 | |||
| 880c4c2d33 | |||
| d12bb7cddb | |||
| f48c9620fc | |||
| fc6ffba9cf | |||
| 08e181a4fe | |||
| a7f4e255b0 | |||
| 2b12f06b01 | |||
| b9c05a7181 | |||
| 14d56fbb93 | |||
| 97419283ea | |||
| 55fed53764 | |||
| 98979423a1 | |||
| 3144b6c995 | |||
| 3ce21403ee | |||
| e38a71365a | |||
| f78d90ee22 | |||
| 7e4d1b45ea | |||
| 4727265e59 |
+1
-1
@@ -26,7 +26,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-parent</artifactId>
|
||||
<version>2.5.8</version>
|
||||
<version>2.5.11</version>
|
||||
</parent>
|
||||
<artifactId>struts2-apps</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
@@ -26,12 +26,12 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-apps</artifactId>
|
||||
<version>2.5.8</version>
|
||||
<version>2.5.11</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-rest-showcase</artifactId>
|
||||
<packaging>war</packaging>
|
||||
<version>2.5.8</version>
|
||||
<version>2.5.11</version>
|
||||
<name>Struts 2 Rest Showcase Webapp</name>
|
||||
<description>Struts 2 Rest Showcase Example</description>
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-apps</artifactId>
|
||||
<version>2.5.8</version>
|
||||
<version>2.5.11</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-showcase</artifactId>
|
||||
|
||||
+2
-8
@@ -24,23 +24,17 @@ package org.apache.struts2.showcase.chat;
|
||||
import com.opensymphony.xwork2.Action;
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.ActionInvocation;
|
||||
import com.opensymphony.xwork2.interceptor.Interceptor;
|
||||
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.struts2.dispatcher.SessionMap;
|
||||
|
||||
public class ChatAuthenticationInterceptor implements Interceptor {
|
||||
public class ChatAuthenticationInterceptor extends AbstractInterceptor {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Logger LOG = LogManager.getLogger(ChatAuthenticationInterceptor.class);
|
||||
public static final String USER_SESSION_KEY = "chatUserSessionKey";
|
||||
|
||||
public void destroy() {
|
||||
}
|
||||
|
||||
public void init() {
|
||||
}
|
||||
|
||||
public String intercept(ActionInvocation invocation) throws Exception {
|
||||
|
||||
LOG.debug("Authenticating chat user");
|
||||
|
||||
@@ -23,7 +23,7 @@ package org.apache.struts2.showcase.chat;
|
||||
import com.opensymphony.xwork2.Action;
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.ActionInvocation;
|
||||
import com.opensymphony.xwork2.interceptor.Interceptor;
|
||||
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
|
||||
@@ -32,7 +32,7 @@ import javax.servlet.http.HttpSession;
|
||||
/**
|
||||
* Authenticate showcase chat example, make sure everyone have a username.
|
||||
*/
|
||||
public class ChatInterceptor implements Interceptor {
|
||||
public class ChatInterceptor extends AbstractInterceptor {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(ChatInterceptor.class);
|
||||
|
||||
@@ -40,12 +40,6 @@ public class ChatInterceptor implements Interceptor {
|
||||
|
||||
public static final String CHAT_USER_SESSION_KEY = "ChatUserSessionKey";
|
||||
|
||||
public void destroy() {
|
||||
}
|
||||
|
||||
public void init() {
|
||||
}
|
||||
|
||||
public String intercept(ActionInvocation invocation) throws Exception {
|
||||
HttpSession session = (HttpSession) ActionContext.getContext().get(ActionContext.SESSION);
|
||||
User chatUser = (User) session.getAttribute(CHAT_USER_SESSION_KEY);
|
||||
|
||||
@@ -112,8 +112,13 @@ function ajaxFormValidation(event) {
|
||||
if (restoreFunction) {
|
||||
restoreFunction();
|
||||
}
|
||||
// show user an error message
|
||||
_handleValidationResult(_form, {errors: ['Network or server error!']})
|
||||
// struts sends status code 400 when validation errors are present
|
||||
if (xhr.status === 400) {
|
||||
_handleValidationResult(_form, JSON.parse(xhr.responseText))
|
||||
} else {
|
||||
// a real error occurred -> show user an error message
|
||||
_handleValidationResult(_form, {errors: ['Network or server error!']})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-parent</artifactId>
|
||||
<version>2.5.8</version>
|
||||
<version>2.5.11</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-assembly</artifactId>
|
||||
|
||||
+3
-3
@@ -10,7 +10,7 @@
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-bom</artifactId>
|
||||
<version>2.5.8</version>
|
||||
<version>2.5.11</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<name>Struts 2 Bill of Materials</name>
|
||||
@@ -25,7 +25,7 @@
|
||||
</licenses>
|
||||
|
||||
<properties>
|
||||
<struts-version.version>2.5.8</struts-version.version>
|
||||
<struts-version.version>2.5.11</struts-version.version>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
@@ -172,6 +172,6 @@
|
||||
</dependencyManagement>
|
||||
|
||||
<scm>
|
||||
<tag>STRUTS_2_5_8</tag>
|
||||
<tag>STRUTS_2_5_11</tag>
|
||||
</scm>
|
||||
</project>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-osgi-bundles</artifactId>
|
||||
<version>2.5.8</version>
|
||||
<version>2.5.11</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-osgi-admin-bundle</artifactId>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-osgi-bundles</artifactId>
|
||||
<version>2.5.8</version>
|
||||
<version>2.5.11</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-osgi-demo-bundle</artifactId>
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-parent</artifactId>
|
||||
<version>2.5.8</version>
|
||||
<version>2.5.11</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-osgi-bundles</artifactId>
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-parent</artifactId>
|
||||
<version>2.5.8</version>
|
||||
<version>2.5.11</version>
|
||||
</parent>
|
||||
<artifactId>struts2-core</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
@@ -19,24 +19,20 @@ import com.opensymphony.xwork2.inject.Container;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.interceptor.ValidationAware;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
|
||||
|
||||
/**
|
||||
* Provides a default implementation for the most common actions.
|
||||
* See the documentation for all the interfaces this class implements for more detailed information.
|
||||
*/
|
||||
public class ActionSupport implements Action, Validateable, ValidationAware, TextProvider, LocaleProvider, Serializable {
|
||||
|
||||
protected static Logger LOG = LogManager.getLogger(ActionSupport.class);
|
||||
|
||||
private final ValidationAwareSupport validationAware = new ValidationAwareSupport();
|
||||
|
||||
private transient TextProvider textProvider;
|
||||
private transient LocaleProvider localeProvider;
|
||||
|
||||
protected Container container;
|
||||
|
||||
@@ -66,17 +62,17 @@ public class ActionSupport implements Action, Validateable, ValidationAware, Tex
|
||||
|
||||
@Override
|
||||
public Locale getLocale() {
|
||||
return container.getInstance(LocaleProvider.class).getLocale();
|
||||
return getLocaleProvider().getLocale();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidLocaleString(String localeStr) {
|
||||
return container.getInstance(LocaleProvider.class).isValidLocaleString(localeStr);
|
||||
return getLocaleProvider().isValidLocaleString(localeStr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidLocale(Locale locale) {
|
||||
return container.getInstance(LocaleProvider.class).isValidLocale(locale);
|
||||
return getLocaleProvider().isValidLocale(locale);
|
||||
}
|
||||
|
||||
public boolean hasKey(String key) {
|
||||
@@ -277,17 +273,22 @@ public class ActionSupport implements Action, Validateable, ValidationAware, Tex
|
||||
*
|
||||
* @return reference to field with TextProvider
|
||||
*/
|
||||
private TextProvider getTextProvider() {
|
||||
protected TextProvider getTextProvider() {
|
||||
if (textProvider == null) {
|
||||
TextProviderFactory tpf = new TextProviderFactory();
|
||||
if (container != null) {
|
||||
container.inject(tpf);
|
||||
}
|
||||
textProvider = tpf.createInstance(getClass(), this);
|
||||
TextProviderFactory tpf = container.getInstance(TextProviderFactory.class);
|
||||
textProvider = tpf.createInstance(getClass());
|
||||
}
|
||||
return textProvider;
|
||||
}
|
||||
|
||||
protected LocaleProvider getLocaleProvider() {
|
||||
if (localeProvider == null) {
|
||||
LocaleProviderFactory localeProviderFactory = container.getInstance(LocaleProviderFactory.class);
|
||||
localeProvider = localeProviderFactory.createLocaleProvider();
|
||||
}
|
||||
return localeProvider;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setContainer(Container container) {
|
||||
this.container = container;
|
||||
|
||||
@@ -21,7 +21,9 @@ import com.opensymphony.xwork2.config.entities.InterceptorMapping;
|
||||
import com.opensymphony.xwork2.config.entities.ResultConfig;
|
||||
import com.opensymphony.xwork2.inject.Container;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.interceptor.Interceptor;
|
||||
import com.opensymphony.xwork2.interceptor.PreResultListener;
|
||||
import com.opensymphony.xwork2.interceptor.WithLazyParams;
|
||||
import com.opensymphony.xwork2.ognl.OgnlUtil;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
import com.opensymphony.xwork2.util.ValueStackFactory;
|
||||
@@ -67,6 +69,7 @@ public class DefaultActionInvocation implements ActionInvocation {
|
||||
protected Container container;
|
||||
protected UnknownHandlerManager unknownHandlerManager;
|
||||
protected OgnlUtil ognlUtil;
|
||||
protected WithLazyParams.LazyParamInjector lazyParamInjector;
|
||||
|
||||
public DefaultActionInvocation(final Map<String, Object> extraContext, final boolean pushAction) {
|
||||
this.extraContext = extraContext;
|
||||
@@ -233,11 +236,15 @@ public class DefaultActionInvocation implements ActionInvocation {
|
||||
}
|
||||
|
||||
if (interceptors.hasNext()) {
|
||||
final InterceptorMapping interceptor = interceptors.next();
|
||||
String interceptorMsg = "interceptor: " + interceptor.getName();
|
||||
final InterceptorMapping interceptorMapping = interceptors.next();
|
||||
String interceptorMsg = "interceptorMapping: " + interceptorMapping.getName();
|
||||
UtilTimerStack.push(interceptorMsg);
|
||||
try {
|
||||
resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
|
||||
Interceptor interceptor = interceptorMapping.getInterceptor();
|
||||
if (interceptor instanceof WithLazyParams) {
|
||||
interceptor = lazyParamInjector.injectParams(interceptor, interceptorMapping.getParams(), invocationContext);
|
||||
}
|
||||
resultCode = interceptor.intercept(DefaultActionInvocation.this);
|
||||
} finally {
|
||||
UtilTimerStack.pop(interceptorMsg);
|
||||
}
|
||||
@@ -400,6 +407,13 @@ public class DefaultActionInvocation implements ActionInvocation {
|
||||
invocationContext.setName(proxy.getActionName());
|
||||
|
||||
createInterceptors(proxy);
|
||||
|
||||
prepareLazyParamInjector(invocationContext.getValueStack());
|
||||
}
|
||||
|
||||
protected void prepareLazyParamInjector(ValueStack valueStack) {
|
||||
lazyParamInjector = new WithLazyParams.LazyParamInjector(valueStack);
|
||||
container.inject(lazyParamInjector);
|
||||
}
|
||||
|
||||
protected void createInterceptors(ActionProxy proxy) {
|
||||
|
||||
@@ -24,7 +24,6 @@ import com.opensymphony.xwork2.config.Configuration;
|
||||
import com.opensymphony.xwork2.config.ConfigurationException;
|
||||
import com.opensymphony.xwork2.config.entities.ActionConfig;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.util.LocalizedTextUtil;
|
||||
import com.opensymphony.xwork2.util.profiling.UtilTimerStack;
|
||||
import org.apache.commons.lang3.StringEscapeUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@@ -53,6 +52,8 @@ public class DefaultActionProxy implements ActionProxy, Serializable {
|
||||
protected ActionConfig config;
|
||||
protected ActionInvocation invocation;
|
||||
protected UnknownHandlerManager unknownHandlerManager;
|
||||
protected LocalizedTextProvider localizedTextProvider;
|
||||
|
||||
protected String actionName;
|
||||
protected String namespace;
|
||||
protected String method;
|
||||
@@ -113,6 +114,11 @@ public class DefaultActionProxy implements ActionProxy, Serializable {
|
||||
this.actionEventListener = listener;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setLocalizedTextProvider(LocalizedTextProvider localizedTextProvider) {
|
||||
this.localizedTextProvider = localizedTextProvider;
|
||||
}
|
||||
|
||||
public Object getAction() {
|
||||
return invocation.getAction();
|
||||
}
|
||||
@@ -205,7 +211,7 @@ public class DefaultActionProxy implements ActionProxy, Serializable {
|
||||
}
|
||||
|
||||
protected String prepareNotAllowedErrorMessage() {
|
||||
return LocalizedTextUtil.findDefaultText(
|
||||
return localizedTextProvider.findDefaultText(
|
||||
"struts.exception.method-not-allowed",
|
||||
Locale.getDefault(),
|
||||
new String[]{method, actionName}
|
||||
@@ -214,12 +220,12 @@ public class DefaultActionProxy implements ActionProxy, Serializable {
|
||||
|
||||
protected String getErrorMessage() {
|
||||
if ((namespace != null) && (namespace.trim().length() > 0)) {
|
||||
return LocalizedTextUtil.findDefaultText(
|
||||
return localizedTextProvider.findDefaultText(
|
||||
"xwork.exception.missing-package-action",
|
||||
Locale.getDefault(),
|
||||
new String[]{namespace, actionName});
|
||||
} else {
|
||||
return LocalizedTextUtil.findDefaultText(
|
||||
return localizedTextProvider.findDefaultText(
|
||||
"xwork.exception.missing-action",
|
||||
Locale.getDefault(),
|
||||
new String[]{actionName});
|
||||
|
||||
@@ -1,8 +1,24 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import com.opensymphony.xwork2.util.LocalizedTextUtil;
|
||||
import org.apache.commons.lang3.LocaleUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.message.ParameterizedMessage;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
@@ -14,6 +30,7 @@ public class DefaultLocaleProvider implements LocaleProvider {
|
||||
|
||||
private final static Logger LOG = LogManager.getLogger(DefaultLocaleProvider.class);
|
||||
|
||||
@Override
|
||||
public Locale getLocale() {
|
||||
ActionContext ctx = ActionContext.getContext();
|
||||
if (ctx != null) {
|
||||
@@ -26,7 +43,13 @@ public class DefaultLocaleProvider implements LocaleProvider {
|
||||
|
||||
@Override
|
||||
public boolean isValidLocaleString(String localeStr) {
|
||||
return isValidLocale(LocalizedTextUtil.localeFromString(localeStr, getLocale()));
|
||||
Locale locale = null;
|
||||
try {
|
||||
locale = LocaleUtils.toLocale(localeStr);
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOG.warn(new ParameterizedMessage("Cannot convert [{}] to proper locale", localeStr, e));
|
||||
}
|
||||
return isValidLocale(locale);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.opensymphony.xwork2;
|
||||
|
||||
public class DefaultLocaleProviderFactory implements LocaleProviderFactory {
|
||||
|
||||
@Override
|
||||
public LocaleProvider createLocaleProvider() {
|
||||
return new DefaultLocaleProvider();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package com.opensymphony.xwork2;
|
||||
|
||||
import com.opensymphony.xwork2.util.LocalizedTextUtil;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
|
||||
import java.io.Serializable;
|
||||
@@ -26,26 +26,28 @@ import java.util.List;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
/**
|
||||
* DefaultTextProvider gets texts from only the default resource bundles associated with the
|
||||
* LocalizedTextUtil.
|
||||
*
|
||||
* @author Jason Carreira jcarreira@gmail.com
|
||||
* @author Rainer Hermanns
|
||||
* @see LocalizedTextUtil#addDefaultResourceBundle(String)
|
||||
* DefaultTextProvider gets texts from only the default resource bundles associated with the default bundles.
|
||||
*/
|
||||
public class DefaultTextProvider implements TextProvider, Serializable, Unchainable {
|
||||
|
||||
private static final Object[] EMPTY_ARGS = new Object[0];
|
||||
|
||||
protected LocalizedTextProvider localizedTextProvider;
|
||||
|
||||
public DefaultTextProvider() {
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setLocalizedTextProvider(LocalizedTextProvider localizedTextProvider) {
|
||||
this.localizedTextProvider = localizedTextProvider;
|
||||
}
|
||||
|
||||
public boolean hasKey(String key) {
|
||||
return getText(key) != null;
|
||||
}
|
||||
|
||||
public String getText(String key) {
|
||||
return LocalizedTextUtil.findDefaultText(key, ActionContext.getContext().getLocale());
|
||||
return localizedTextProvider.findDefaultText(key, ActionContext.getContext().getLocale());
|
||||
}
|
||||
|
||||
public String getText(String key, String defaultValue) {
|
||||
@@ -64,7 +66,7 @@ public class DefaultTextProvider implements TextProvider, Serializable, Unchaina
|
||||
params = EMPTY_ARGS;
|
||||
}
|
||||
|
||||
return LocalizedTextUtil.findDefaultText(key, ActionContext.getContext().getLocale(), params);
|
||||
return localizedTextProvider.findDefaultText(key, ActionContext.getContext().getLocale(), params);
|
||||
}
|
||||
|
||||
public String getText(String key, String[] args) {
|
||||
@@ -75,7 +77,7 @@ public class DefaultTextProvider implements TextProvider, Serializable, Unchaina
|
||||
params = EMPTY_ARGS;
|
||||
}
|
||||
|
||||
return LocalizedTextUtil.findDefaultText(key, ActionContext.getContext().getLocale(), params);
|
||||
return localizedTextProvider.findDefaultText(key, ActionContext.getContext().getLocale(), params);
|
||||
}
|
||||
|
||||
public String getText(String key, String defaultValue, List<?> args) {
|
||||
@@ -136,7 +138,7 @@ public class DefaultTextProvider implements TextProvider, Serializable, Unchaina
|
||||
}
|
||||
|
||||
public ResourceBundle getTexts(String bundleName) {
|
||||
return LocalizedTextUtil.findResourceBundle(bundleName, ActionContext.getContext().getLocale());
|
||||
return localizedTextProvider.findResourceBundle(bundleName, ActionContext.getContext().getLocale());
|
||||
}
|
||||
|
||||
public ResourceBundle getTexts() {
|
||||
|
||||
@@ -13,7 +13,7 @@ public interface FileManager {
|
||||
/**
|
||||
* Enables configs reloading when config file changed
|
||||
*
|
||||
* @param reloadingConfigs {@link XWorkConstants#RELOAD_XML_CONFIGURATION}
|
||||
* @param reloadingConfigs {@link org.apache.struts2.StrutsConstants#STRUTS_CONFIGURATION_XML_RELOAD}
|
||||
*/
|
||||
void setReloadingConfigs(boolean reloadingConfigs);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
* Allows delegate creation of {@link LocaleProvider} to another implementation provided
|
||||
* by a user. It also allows avoid problems with too many dependencies as {@link LocaleProvider}
|
||||
* is implemented by the {@link ActionSupport} which can be defined as a bean in Spring.
|
||||
*/
|
||||
public interface LocaleProviderFactory {
|
||||
|
||||
/**
|
||||
* Create a new instance of {@link LocaleProvider}.
|
||||
*
|
||||
* @return the localeProvider.
|
||||
*/
|
||||
LocaleProvider createLocaleProvider();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package com.opensymphony.xwork2;
|
||||
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Locale;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
public interface LocalizedTextProvider extends Serializable {
|
||||
|
||||
String findDefaultText(String aTextName, Locale locale);
|
||||
|
||||
String findDefaultText(String aTextName, Locale locale, Object[] params);
|
||||
|
||||
ResourceBundle findResourceBundle(String aBundleName, Locale locale);
|
||||
|
||||
String findText(Class aClass, String aTextName, Locale locale);
|
||||
|
||||
String findText(Class aClass, String aTextName, Locale locale, String defaultMessage, Object[] args);
|
||||
|
||||
String findText(Class aClass, String aTextName, Locale locale, String defaultMessage, Object[] args, ValueStack valueStack);
|
||||
|
||||
String findText(ResourceBundle bundle, String aTextName, Locale locale);
|
||||
|
||||
String findText(ResourceBundle bundle, String aTextName, Locale locale, String defaultMessage, Object[] args);
|
||||
|
||||
String findText(ResourceBundle bundle, String aTextName, Locale locale, String defaultMessage, Object[] args, ValueStack valueStack);
|
||||
|
||||
void addDefaultResourceBundle(String resourceBundleName);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2002-2007,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;
|
||||
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
/**
|
||||
* This factory enables users to provide and correctly initialize a custom TextProvider.
|
||||
*/
|
||||
public class StrutsTextProviderFactory implements TextProviderFactory {
|
||||
|
||||
protected LocaleProviderFactory localeProviderFactory;
|
||||
protected LocalizedTextProvider localizedTextProvider;
|
||||
|
||||
@Inject
|
||||
public void setLocaleProviderFactory(LocaleProviderFactory localeProviderFactory) {
|
||||
this.localeProviderFactory = localeProviderFactory;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setLocalizedTextProvider(LocalizedTextProvider localizedTextProvider) {
|
||||
this.localizedTextProvider = localizedTextProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextProvider createInstance(Class clazz) {
|
||||
TextProvider instance = getTextProvider(clazz);
|
||||
if (instance instanceof ResourceBundleTextProvider) {
|
||||
((ResourceBundleTextProvider) instance).setClazz(clazz);
|
||||
((ResourceBundleTextProvider) instance).setLocaleProvider(localeProviderFactory.createLocaleProvider());
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextProvider createInstance(ResourceBundle bundle) {
|
||||
TextProvider instance = getTextProvider(bundle);
|
||||
if (instance instanceof ResourceBundleTextProvider) {
|
||||
((ResourceBundleTextProvider) instance).setBundle(bundle);
|
||||
((ResourceBundleTextProvider) instance).setLocaleProvider(localeProviderFactory.createLocaleProvider());
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
protected TextProvider getTextProvider(Class clazz) {
|
||||
return new TextProviderSupport(clazz, localeProviderFactory.createLocaleProvider(), localizedTextProvider);
|
||||
}
|
||||
|
||||
protected TextProvider getTextProvider(ResourceBundle bundle) {
|
||||
return new TextProviderSupport(bundle, localeProviderFactory.createLocaleProvider(), localizedTextProvider);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* Copyright 2002-2007,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.
|
||||
@@ -15,57 +15,12 @@
|
||||
*/
|
||||
package com.opensymphony.xwork2;
|
||||
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
/**
|
||||
* This factory enables users to provide and correctly initialize a custom TextProvider.
|
||||
*
|
||||
* @author Oleg Gorobets
|
||||
* @author Rene Gielen
|
||||
*/
|
||||
public class TextProviderFactory {
|
||||
public interface TextProviderFactory {
|
||||
|
||||
private TextProvider textProvider;
|
||||
TextProvider createInstance(Class clazz);
|
||||
|
||||
@Inject
|
||||
public void setTextProvider(TextProvider textProvider) {
|
||||
this.textProvider = textProvider;
|
||||
}
|
||||
|
||||
public TextProvider createInstance(Class clazz, LocaleProvider provider) {
|
||||
TextProvider instance = getTextProvider(clazz, provider);
|
||||
if (instance instanceof ResourceBundleTextProvider) {
|
||||
((ResourceBundleTextProvider) instance).setClazz(clazz);
|
||||
((ResourceBundleTextProvider) instance).setLocaleProvider(provider);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public TextProvider createInstance(ResourceBundle bundle, LocaleProvider provider) {
|
||||
TextProvider instance = getTextProvider(bundle, provider);
|
||||
if (instance instanceof ResourceBundleTextProvider) {
|
||||
((ResourceBundleTextProvider) instance).setBundle(bundle);
|
||||
((ResourceBundleTextProvider) instance).setLocaleProvider(provider);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
protected TextProvider getTextProvider(Class clazz, LocaleProvider provider) {
|
||||
if (this.textProvider == null) {
|
||||
return new TextProviderSupport(clazz, provider);
|
||||
} else {
|
||||
return textProvider;
|
||||
}
|
||||
}
|
||||
|
||||
private TextProvider getTextProvider(ResourceBundle bundle, LocaleProvider provider) {
|
||||
if (this.textProvider == null) {
|
||||
return new TextProviderSupport(bundle, provider);
|
||||
} else {
|
||||
return textProvider;
|
||||
}
|
||||
}
|
||||
TextProvider createInstance(ResourceBundle bundle);
|
||||
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
package com.opensymphony.xwork2;
|
||||
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.util.LocalizedTextUtil;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
|
||||
import java.util.*;
|
||||
@@ -30,15 +29,10 @@ import java.util.*;
|
||||
*/
|
||||
public class TextProviderSupport implements ResourceBundleTextProvider {
|
||||
|
||||
private Class clazz;
|
||||
private LocaleProvider localeProvider;
|
||||
private ResourceBundle bundle;
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
public TextProviderSupport() {
|
||||
}
|
||||
protected Class clazz;
|
||||
protected LocaleProvider localeProvider;
|
||||
protected ResourceBundle bundle;
|
||||
protected LocalizedTextProvider localizedTextProvider;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
@@ -46,9 +40,10 @@ public class TextProviderSupport implements ResourceBundleTextProvider {
|
||||
* @param clazz a clazz to use for reading the resource bundle.
|
||||
* @param provider a locale provider.
|
||||
*/
|
||||
public TextProviderSupport(Class clazz, LocaleProvider provider) {
|
||||
public TextProviderSupport(Class clazz, LocaleProvider provider, LocalizedTextProvider localizedTextProvider) {
|
||||
this.clazz = clazz;
|
||||
this.localeProvider = provider;
|
||||
this.localizedTextProvider = localizedTextProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -57,14 +52,16 @@ public class TextProviderSupport implements ResourceBundleTextProvider {
|
||||
* @param bundle the resource bundle.
|
||||
* @param provider a locale provider.
|
||||
*/
|
||||
public TextProviderSupport(ResourceBundle bundle, LocaleProvider provider) {
|
||||
public TextProviderSupport(ResourceBundle bundle, LocaleProvider provider, LocalizedTextProvider localizedTextProvider) {
|
||||
this.bundle = bundle;
|
||||
this.localeProvider = provider;
|
||||
this.localizedTextProvider = localizedTextProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bundle the resource bundle.
|
||||
*/
|
||||
@Override
|
||||
public void setBundle(ResourceBundle bundle) {
|
||||
this.bundle = bundle;
|
||||
}
|
||||
@@ -72,19 +69,28 @@ public class TextProviderSupport implements ResourceBundleTextProvider {
|
||||
/**
|
||||
* @param clazz a clazz to use for reading the resource bundle.
|
||||
*/
|
||||
@Override
|
||||
public void setClazz(Class clazz) {
|
||||
this.clazz = clazz;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param localeProvider a locale provider.
|
||||
*/
|
||||
@Inject
|
||||
@Override
|
||||
public void setLocaleProvider(LocaleProvider localeProvider) {
|
||||
this.localeProvider = localeProvider;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setLocaleProviderFactory(LocaleProviderFactory localeProviderFactory) {
|
||||
this.localeProvider = localeProviderFactory.createLocaleProvider();
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setLocalizedTextProvider(LocalizedTextProvider localizedTextProvider) {
|
||||
this.localizedTextProvider = localizedTextProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a key is available in the resource bundles associated with this action.
|
||||
@@ -97,9 +103,9 @@ public class TextProviderSupport implements ResourceBundleTextProvider {
|
||||
public boolean hasKey(String key) {
|
||||
String message;
|
||||
if (clazz != null) {
|
||||
message = LocalizedTextUtil.findText(clazz, key, getLocale(), null, new Object[0] );
|
||||
message = localizedTextProvider.findText(clazz, key, getLocale(), null, new Object[0] );
|
||||
} else {
|
||||
message = LocalizedTextUtil.findText(bundle, key, getLocale(), null, new Object[0]);
|
||||
message = localizedTextProvider.findText(bundle, key, getLocale(), null, new Object[0]);
|
||||
}
|
||||
return message != null;
|
||||
}
|
||||
@@ -201,9 +207,9 @@ public class TextProviderSupport implements ResourceBundleTextProvider {
|
||||
public String getText(String key, String defaultValue, List<?> args) {
|
||||
Object[] argsArray = ((args != null && !args.equals(Collections.emptyList())) ? args.toArray() : null);
|
||||
if (clazz != null) {
|
||||
return LocalizedTextUtil.findText(clazz, key, getLocale(), defaultValue, argsArray);
|
||||
return localizedTextProvider.findText(clazz, key, getLocale(), defaultValue, argsArray);
|
||||
} else {
|
||||
return LocalizedTextUtil.findText(bundle, key, getLocale(), defaultValue, argsArray);
|
||||
return localizedTextProvider.findText(bundle, key, getLocale(), defaultValue, argsArray);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,9 +228,9 @@ public class TextProviderSupport implements ResourceBundleTextProvider {
|
||||
*/
|
||||
public String getText(String key, String defaultValue, String[] args) {
|
||||
if (clazz != null) {
|
||||
return LocalizedTextUtil.findText(clazz, key, getLocale(), defaultValue, args);
|
||||
return localizedTextProvider.findText(clazz, key, getLocale(), defaultValue, args);
|
||||
} else {
|
||||
return LocalizedTextUtil.findText(bundle, key, getLocale(), defaultValue, args);
|
||||
return localizedTextProvider.findText(bundle, key, getLocale(), defaultValue, args);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,9 +258,9 @@ public class TextProviderSupport implements ResourceBundleTextProvider {
|
||||
locale = getLocale();
|
||||
}
|
||||
if (clazz != null) {
|
||||
return LocalizedTextUtil.findText(clazz, key, locale, defaultValue, argsArray, stack);
|
||||
return localizedTextProvider.findText(clazz, key, locale, defaultValue, argsArray, stack);
|
||||
} else {
|
||||
return LocalizedTextUtil.findText(bundle, key, locale, defaultValue, argsArray, stack);
|
||||
return localizedTextProvider.findText(bundle, key, locale, defaultValue, argsArray, stack);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,9 +288,9 @@ public class TextProviderSupport implements ResourceBundleTextProvider {
|
||||
locale = getLocale();
|
||||
}
|
||||
if (clazz != null) {
|
||||
return LocalizedTextUtil.findText(clazz, key, locale, defaultValue, args, stack);
|
||||
return localizedTextProvider.findText(clazz, key, locale, defaultValue, args, stack);
|
||||
} else {
|
||||
return LocalizedTextUtil.findText(bundle, key, locale, defaultValue, args, stack);
|
||||
return localizedTextProvider.findText(bundle, key, locale, defaultValue, args, stack);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -304,7 +310,7 @@ public class TextProviderSupport implements ResourceBundleTextProvider {
|
||||
* @return a resource bundle
|
||||
*/
|
||||
public ResourceBundle getTexts(String aBundleName) {
|
||||
return LocalizedTextUtil.findResourceBundle(aBundleName, getLocale());
|
||||
return localizedTextProvider.findResourceBundle(aBundleName, getLocale());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import com.opensymphony.xwork2.config.Configuration;
|
||||
import com.opensymphony.xwork2.config.ConfigurationManager;
|
||||
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Simple facade to make using XWork standalone easier
|
||||
*/
|
||||
public class XWork {
|
||||
|
||||
ConfigurationManager configurationManager;
|
||||
|
||||
public XWork() {
|
||||
this(new ConfigurationManager());
|
||||
}
|
||||
|
||||
public XWork(ConfigurationManager mgr) {
|
||||
this.configurationManager = mgr;
|
||||
}
|
||||
|
||||
public void setLoggerFactory(LoggerFactory factory) {
|
||||
LoggerFactory.setLoggerFactory(factory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes an action
|
||||
*
|
||||
* @param namespace The namespace
|
||||
* @param name The action name
|
||||
* @param method The method name
|
||||
* @throws XWorkException If anything goes wrong
|
||||
*/
|
||||
public void executeAction(String namespace, String name, String method) throws XWorkException {
|
||||
Map<String, Object> extraContext = Collections.emptyMap();
|
||||
executeAction(namespace, name, method, extraContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes an action with extra context information
|
||||
*
|
||||
* @param namespace The namespace
|
||||
* @param name The action name
|
||||
* @param method The method name
|
||||
* @param extraContext A map of extra context information
|
||||
* @throws XWorkException If anything goes wrong
|
||||
*/
|
||||
public void executeAction(String namespace, String name, String method, Map<String, Object> extraContext) throws XWorkException {
|
||||
Configuration config = configurationManager.getConfiguration();
|
||||
try {
|
||||
ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(
|
||||
namespace, name, method, extraContext, true, false);
|
||||
|
||||
proxy.execute();
|
||||
} catch (Exception e) {
|
||||
throw new XWorkException(e);
|
||||
} finally {
|
||||
ActionContext.setContext(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,29 +3,46 @@ package com.opensymphony.xwork2;
|
||||
/**
|
||||
* Constants used across framework
|
||||
*/
|
||||
@Deprecated
|
||||
public final class XWorkConstants {
|
||||
|
||||
@Deprecated
|
||||
public static final String COLLECTION_CONVERTER = "collectionConverter";
|
||||
@Deprecated
|
||||
public static final String DATE_CONVERTER = "dateConverter";
|
||||
@Deprecated
|
||||
public static final String NUMBER_CONVERTER = "numberConverter";
|
||||
@Deprecated
|
||||
public static final String STRING_CONVERTER = "stringConverter";
|
||||
@Deprecated
|
||||
public static final String ARRAY_CONVERTER = "arrayConverter";
|
||||
@Deprecated
|
||||
public static final String DEV_MODE = "devMode";
|
||||
@Deprecated
|
||||
public static final String LOG_MISSING_PROPERTIES = "logMissingProperties";
|
||||
@Deprecated
|
||||
public static final String ENABLE_OGNL_EXPRESSION_CACHE = "enableOGNLExpressionCache";
|
||||
@Deprecated
|
||||
public static final String ENABLE_OGNL_EVAL_EXPRESSION = "enableOGNLEvalExpression";
|
||||
@Deprecated
|
||||
public static final String RELOAD_XML_CONFIGURATION = "reloadXmlConfiguration";
|
||||
@Deprecated
|
||||
public static final String ALLOW_STATIC_METHOD_ACCESS = "allowStaticMethodAccess";
|
||||
@Deprecated
|
||||
public static final String XWORK_LOGGER_FACTORY = "xwork.loggerFactory";
|
||||
|
||||
@Deprecated
|
||||
public static final String OGNL_EXCLUDED_CLASSES = "ognlExcludedClasses";
|
||||
@Deprecated
|
||||
public static final String OGNL_EXCLUDED_PACKAGE_NAME_PATTERNS = "ognlExcludedPackageNamePatterns";
|
||||
@Deprecated
|
||||
public static final String OGNL_EXCLUDED_PACKAGE_NAMES = "ognlExcludedPackageNames";
|
||||
|
||||
@Deprecated
|
||||
public static final String ADDITIONAL_EXCLUDED_PATTERNS = "additionalExcludedPatterns";
|
||||
@Deprecated
|
||||
public static final String ADDITIONAL_ACCEPTED_PATTERNS = "additionalAcceptedPatterns";
|
||||
|
||||
@Deprecated
|
||||
public static final String OVERRIDE_EXCLUDED_PATTERNS = "overrideExcludedPatterns";
|
||||
@Deprecated
|
||||
public static final String OVERRIDE_ACCEPTED_PATTERNS = "overrideAcceptedPatterns";
|
||||
|
||||
}
|
||||
|
||||
@@ -47,10 +47,6 @@ public class ConfigurationManager {
|
||||
private boolean providersChanged = false;
|
||||
private boolean reloadConfigs = true; // for the first time
|
||||
|
||||
public ConfigurationManager() {
|
||||
this("xwork");
|
||||
}
|
||||
|
||||
public ConfigurationManager(String name) {
|
||||
this.defaultFrameworkBeanName = name;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ package com.opensymphony.xwork2.config.entities;
|
||||
import com.opensymphony.xwork2.interceptor.Interceptor;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <code>InterceptorMapping</code>
|
||||
@@ -30,10 +32,16 @@ public class InterceptorMapping implements Serializable {
|
||||
|
||||
private String name;
|
||||
private Interceptor interceptor;
|
||||
private final Map<String, String> params;
|
||||
|
||||
public InterceptorMapping(String name, Interceptor interceptor) {
|
||||
this(name, interceptor, new HashMap<String, String>());
|
||||
}
|
||||
|
||||
public InterceptorMapping(String name, Interceptor interceptor, Map<String, String> params) {
|
||||
this.name = name;
|
||||
this.interceptor = interceptor;
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
@@ -44,6 +52,10 @@ public class InterceptorMapping implements Serializable {
|
||||
return interceptor;
|
||||
}
|
||||
|
||||
public Map<String, String> getParams() {
|
||||
return params;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
@@ -65,7 +77,7 @@ public class InterceptorMapping implements Serializable {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "InterceptorMapping: [" + name + "] => [" + interceptor.getClass().getName() + ']';
|
||||
return "InterceptorMapping: [" + name + "] => [" + interceptor.getClass().getName() + "] with params [" + params + "]" ;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -53,7 +53,6 @@ public class DefaultConfiguration implements Configuration {
|
||||
|
||||
protected static final Logger LOG = LogManager.getLogger(DefaultConfiguration.class);
|
||||
|
||||
|
||||
// Programmatic Action Configurations
|
||||
protected Map<String, PackageConfig> packageContexts = new LinkedHashMap<>();
|
||||
protected RuntimeConfiguration runtimeConfiguration;
|
||||
@@ -66,7 +65,7 @@ public class DefaultConfiguration implements Configuration {
|
||||
ObjectFactory objectFactory;
|
||||
|
||||
public DefaultConfiguration() {
|
||||
this("xwork");
|
||||
this(Container.DEFAULT_NAME);
|
||||
}
|
||||
|
||||
public DefaultConfiguration(String defaultBeanName) {
|
||||
@@ -260,9 +259,14 @@ public class DefaultConfiguration implements Configuration {
|
||||
builder.factory(TypeConverter.class, XWorkConstants.NUMBER_CONVERTER, NumberConverter.class, Scope.SINGLETON);
|
||||
builder.factory(TypeConverter.class, XWorkConstants.STRING_CONVERTER, StringConverter.class, Scope.SINGLETON);
|
||||
|
||||
builder.factory(TextParser.class, OgnlTextParser.class, Scope.SINGLETON);
|
||||
builder.factory(TextProvider.class, "system", DefaultTextProvider.class, Scope.SINGLETON);
|
||||
|
||||
builder.factory(LocalizedTextProvider.class, StrutsLocalizedTextProvider.class, Scope.SINGLETON);
|
||||
builder.factory(TextProviderFactory.class, StrutsTextProviderFactory.class, Scope.SINGLETON);
|
||||
builder.factory(LocaleProviderFactory.class, DefaultLocaleProviderFactory.class, Scope.SINGLETON);
|
||||
|
||||
builder.factory(TextParser.class, OgnlTextParser.class, Scope.SINGLETON);
|
||||
|
||||
builder.factory(ObjectTypeDeterminer.class, DefaultObjectTypeDeterminer.class, Scope.SINGLETON);
|
||||
builder.factory(PropertyAccessor.class, CompoundRoot.class.getName(), CompoundRootAccessor.class, Scope.SINGLETON);
|
||||
builder.factory(OgnlUtil.class, Scope.SINGLETON);
|
||||
@@ -270,10 +274,12 @@ public class DefaultConfiguration implements Configuration {
|
||||
builder.factory(ValueSubstitutor.class, EnvsValueSubstitutor.class, Scope.SINGLETON);
|
||||
|
||||
builder.constant(XWorkConstants.DEV_MODE, "false");
|
||||
builder.constant(StrutsConstants.STRUTS_DEVMODE, "false");
|
||||
builder.constant(XWorkConstants.LOG_MISSING_PROPERTIES, "false");
|
||||
builder.constant(XWorkConstants.ENABLE_OGNL_EVAL_EXPRESSION, "false");
|
||||
builder.constant(XWorkConstants.ENABLE_OGNL_EXPRESSION_CACHE, "true");
|
||||
builder.constant(XWorkConstants.RELOAD_XML_CONFIGURATION, "false");
|
||||
builder.constant(StrutsConstants.STRUTS_I18N_RELOAD, "false");
|
||||
|
||||
return builder.create(true);
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import com.opensymphony.xwork2.interceptor.Interceptor;
|
||||
import com.opensymphony.xwork2.util.location.Location;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.message.ParameterizedMessage;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -43,7 +44,6 @@ public class InterceptorBuilder {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(InterceptorBuilder.class);
|
||||
|
||||
|
||||
/**
|
||||
* Builds a list of interceptors referenced by the refName in the supplied PackageConfig (InterceptorMapping object).
|
||||
*
|
||||
@@ -67,13 +67,11 @@ public class InterceptorBuilder {
|
||||
InterceptorConfig config = (InterceptorConfig) referencedConfig;
|
||||
Interceptor inter;
|
||||
try {
|
||||
|
||||
inter = objectFactory.buildInterceptor(config, refParams);
|
||||
result.add(new InterceptorMapping(refName, inter));
|
||||
result.add(new InterceptorMapping(refName, inter, refParams));
|
||||
} catch (ConfigurationException ex) {
|
||||
LOG.warn("Unable to load config class {} at {} probably due to a missing jar, which might be fine if you never plan to use the {} interceptor",
|
||||
config.getClassName(), ex.getLocation(), config.getName());
|
||||
LOG.error("Unable to load config class {}", config.getClassName(), ex);
|
||||
LOG.warn(new ParameterizedMessage("Unable to load config class {} at {} probably due to a missing jar, which might be fine if you never plan to use the {} interceptor",
|
||||
config.getClassName(), ex.getLocation(), config.getName()), ex);
|
||||
}
|
||||
|
||||
} else if (referencedConfig instanceof InterceptorStackConfig) {
|
||||
|
||||
+13
-5
@@ -2,6 +2,10 @@ package com.opensymphony.xwork2.config.providers;
|
||||
|
||||
import com.opensymphony.xwork2.ActionProxyFactory;
|
||||
import com.opensymphony.xwork2.DefaultActionProxyFactory;
|
||||
import com.opensymphony.xwork2.DefaultLocaleProviderFactory;
|
||||
import com.opensymphony.xwork2.LocaleProviderFactory;
|
||||
import com.opensymphony.xwork2.StrutsTextProviderFactory;
|
||||
import com.opensymphony.xwork2.TextProviderFactory;
|
||||
import com.opensymphony.xwork2.factory.DefaultUnknownHandlerFactory;
|
||||
import com.opensymphony.xwork2.factory.UnknownHandlerFactory;
|
||||
import com.opensymphony.xwork2.ognl.accessor.HttpParametersPropertyAccessor;
|
||||
@@ -9,16 +13,13 @@ import com.opensymphony.xwork2.ognl.accessor.ParameterPropertyAccessor;
|
||||
import com.opensymphony.xwork2.security.AcceptedPatternsChecker;
|
||||
import com.opensymphony.xwork2.security.DefaultAcceptedPatternsChecker;
|
||||
import com.opensymphony.xwork2.security.DefaultExcludedPatternsChecker;
|
||||
import com.opensymphony.xwork2.DefaultLocaleProvider;
|
||||
import com.opensymphony.xwork2.DefaultTextProvider;
|
||||
import com.opensymphony.xwork2.DefaultUnknownHandlerManager;
|
||||
import com.opensymphony.xwork2.security.ExcludedPatternsChecker;
|
||||
import com.opensymphony.xwork2.FileManager;
|
||||
import com.opensymphony.xwork2.FileManagerFactory;
|
||||
import com.opensymphony.xwork2.LocaleProvider;
|
||||
import com.opensymphony.xwork2.ObjectFactory;
|
||||
import com.opensymphony.xwork2.TextProvider;
|
||||
import com.opensymphony.xwork2.TextProviderSupport;
|
||||
import com.opensymphony.xwork2.UnknownHandlerManager;
|
||||
import com.opensymphony.xwork2.XWorkConstants;
|
||||
import com.opensymphony.xwork2.config.Configuration;
|
||||
@@ -70,6 +71,8 @@ import com.opensymphony.xwork2.ognl.accessor.XWorkListPropertyAccessor;
|
||||
import com.opensymphony.xwork2.ognl.accessor.XWorkMapPropertyAccessor;
|
||||
import com.opensymphony.xwork2.ognl.accessor.XWorkMethodAccessor;
|
||||
import com.opensymphony.xwork2.util.CompoundRoot;
|
||||
import com.opensymphony.xwork2.LocalizedTextProvider;
|
||||
import com.opensymphony.xwork2.util.StrutsLocalizedTextProvider;
|
||||
import com.opensymphony.xwork2.util.OgnlTextParser;
|
||||
import com.opensymphony.xwork2.util.PatternMatcher;
|
||||
import com.opensymphony.xwork2.util.TextParser;
|
||||
@@ -177,9 +180,12 @@ public class XWorkConfigurationProvider implements ConfigurationProvider {
|
||||
.factory(NullHandler.class, Object.class.getName(), InstantiatingNullHandler.class, Scope.SINGLETON)
|
||||
.factory(ActionValidatorManager.class, AnnotationActionValidatorManager.class, Scope.SINGLETON)
|
||||
.factory(ActionValidatorManager.class, "no-annotations", DefaultActionValidatorManager.class, Scope.SINGLETON)
|
||||
|
||||
.factory(TextProvider.class, "system", DefaultTextProvider.class, Scope.SINGLETON)
|
||||
.factory(TextProvider.class, TextProviderSupport.class, Scope.SINGLETON)
|
||||
.factory(LocaleProvider.class, DefaultLocaleProvider.class, Scope.SINGLETON)
|
||||
.factory(LocalizedTextProvider.class, StrutsLocalizedTextProvider.class, Scope.SINGLETON)
|
||||
.factory(TextProviderFactory.class, StrutsTextProviderFactory.class, Scope.SINGLETON)
|
||||
.factory(LocaleProviderFactory.class, DefaultLocaleProviderFactory.class, Scope.SINGLETON)
|
||||
|
||||
.factory(OgnlUtil.class, Scope.SINGLETON)
|
||||
.factory(CollectionConverter.class, Scope.SINGLETON)
|
||||
.factory(ArrayConverter.class, Scope.SINGLETON)
|
||||
@@ -194,6 +200,8 @@ public class XWorkConfigurationProvider implements ConfigurationProvider {
|
||||
;
|
||||
|
||||
props.setProperty(StrutsConstants.STRUTS_ENABLE_DYNAMIC_METHOD_INVOCATION, Boolean.FALSE.toString());
|
||||
props.setProperty(StrutsConstants.STRUTS_I18N_RELOAD, Boolean.FALSE.toString());
|
||||
props.setProperty(StrutsConstants.STRUTS_DEVMODE, Boolean.FALSE.toString());
|
||||
props.setProperty(XWorkConstants.DEV_MODE, Boolean.FALSE.toString());
|
||||
props.setProperty(XWorkConstants.LOG_MISSING_PROPERTIES, Boolean.FALSE.toString());
|
||||
props.setProperty(XWorkConstants.ENABLE_OGNL_EXPRESSION_CACHE, Boolean.TRUE.toString());
|
||||
|
||||
+2
-2
@@ -757,7 +757,7 @@ public class XmlConfigurationProvider implements ConfigurationProvider {
|
||||
{
|
||||
// if <result ...>something</result> then we add a parameter of 'something' as this is the most used result param
|
||||
if (resultElement.getChildNodes().getLength() >= 1) {
|
||||
resultParams = new LinkedHashMap<String, String>();
|
||||
resultParams = new LinkedHashMap<>();
|
||||
|
||||
String paramName = config.getDefaultResultParam();
|
||||
if (paramName != null) {
|
||||
@@ -775,7 +775,7 @@ public class XmlConfigurationProvider implements ConfigurationProvider {
|
||||
resultParams.put(paramName, val);
|
||||
}
|
||||
} else {
|
||||
LOG.warn("No default parameter defined for result [{}] of type [{}] ", config.getName(), config.getClassName());
|
||||
LOG.debug("No default parameter defined for result [{}] of type [{}] ", config.getName(), config.getClassName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+15
@@ -1,3 +1,18 @@
|
||||
/*
|
||||
* 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.conversion;
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,4 +14,13 @@ public interface TypeConverterCreator {
|
||||
*/
|
||||
TypeConverter createTypeConverter(String className) throws Exception;
|
||||
|
||||
/**
|
||||
* Creates {@link TypeConverter} from given class
|
||||
*
|
||||
* @param clazz convert class
|
||||
* @return instance of {@link TypeConverter}
|
||||
* @throws Exception when cannot create/cast to {@link TypeConverter}
|
||||
*/
|
||||
TypeConverter createTypeConverter(Class<?> clazz) throws Exception;
|
||||
|
||||
}
|
||||
|
||||
+25
-6
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package com.opensymphony.xwork2.conversion.annotations;
|
||||
|
||||
import com.opensymphony.xwork2.conversion.impl.XWorkBasicConverter;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
@@ -77,11 +79,17 @@ import java.lang.annotation.Target;
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>converter</td>
|
||||
* <td>either this or value</td>
|
||||
* <td>DEPRECATED: either this or value</td>
|
||||
* <td> </td>
|
||||
* <td>The class name of the TypeConverter to be used as converter.</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>converterClass</td>
|
||||
* <td>either this or value</td>
|
||||
* <td> </td>
|
||||
* <td>The class of the TypeConverter to be used as converter. XWorkBasicConverter by default.</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>value</td>
|
||||
* <td>either converter or this</td>
|
||||
* <td> </td>
|
||||
@@ -106,27 +114,27 @@ import java.lang.annotation.Target;
|
||||
*
|
||||
* private HashMap keyValues = null;
|
||||
*
|
||||
* @TypeConversion(type = ConversionType.APPLICATION, converter = "com.opensymphony.xwork2.util.XWorkBasicConverter")
|
||||
* @TypeConversion(type = ConversionType.APPLICATION)
|
||||
* public void setConvertInt( String convertInt ) {
|
||||
* this.convertInt = convertInt;
|
||||
* }
|
||||
*
|
||||
* @TypeConversion(converter = "com.opensymphony.xwork2.util.XWorkBasicConverter")
|
||||
* @TypeConversion(converterClass = XWorkBasicConverter.class)
|
||||
* public void setConvertDouble( String convertDouble ) {
|
||||
* this.convertDouble = convertDouble;
|
||||
* }
|
||||
*
|
||||
* @TypeConversion(rule = ConversionRule.COLLECTION, converter = "java.util.String")
|
||||
* @TypeConversion(rule = ConversionRule.COLLECTION, converterClass = String.class)
|
||||
* public void setUsers( List users ) {
|
||||
* this.users = users;
|
||||
* }
|
||||
*
|
||||
* @TypeConversion(rule = ConversionRule.MAP, converter = "java.math.BigInteger")
|
||||
* @TypeConversion(rule = ConversionRule.MAP, converterClass = BigInteger.class)
|
||||
* public void setKeyValues( HashMap keyValues ) {
|
||||
* this.keyValues = keyValues;
|
||||
* }
|
||||
*
|
||||
* @TypeConversion(type = ConversionType.APPLICATION, property = "java.util.Date", converter = "com.opensymphony.xwork2.util.XWorkBasicConverter")
|
||||
* @TypeConversion(type = ConversionType.APPLICATION, property = "java.util.Date", converterClass = XWorkBasicConverter.class)
|
||||
* public String execute() throws Exception {
|
||||
* return SUCCESS;
|
||||
* }
|
||||
@@ -175,9 +183,20 @@ public @interface TypeConversion {
|
||||
* Note: This can not be used with ConversionRule.KEY_PROPERTY!
|
||||
*
|
||||
* @return class of the TypeConverter to be used as converter
|
||||
* @deprecated user {@link #converterClass()} instead
|
||||
*/
|
||||
@Deprecated
|
||||
String converter() default "";
|
||||
|
||||
/**
|
||||
* The class of the TypeConverter to be used as converter.
|
||||
*
|
||||
* Note: This can not be used with ConversionRule.KEY_PROPERTY!
|
||||
*
|
||||
* @return class of the TypeConverter to be used as converter
|
||||
*/
|
||||
Class<?> converterClass() default XWorkBasicConverter.class;
|
||||
|
||||
/**
|
||||
* If used with ConversionRule.KEY_PROPERTY specify a value here!
|
||||
*
|
||||
|
||||
+28
-8
@@ -8,6 +8,7 @@ import com.opensymphony.xwork2.conversion.annotations.ConversionRule;
|
||||
import com.opensymphony.xwork2.conversion.annotations.ConversionType;
|
||||
import com.opensymphony.xwork2.conversion.annotations.TypeConversion;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
@@ -34,28 +35,43 @@ public class DefaultConversionAnnotationProcessor implements ConversionAnnotatio
|
||||
}
|
||||
|
||||
public void process(Map<String, Object> mapping, TypeConversion tc, String key) {
|
||||
LOG.debug("TypeConversion [{}] with key: [{}]", tc.converter(), key);
|
||||
LOG.debug("TypeConversion [{}/{}] with key: [{}]", tc.converter(), tc.converterClass(), key);
|
||||
if (key == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (tc.type() == ConversionType.APPLICATION) {
|
||||
converterHolder.addDefaultMapping(key, converterCreator.createTypeConverter(tc.converter()));
|
||||
if (StringUtils.isNoneEmpty(tc.converter())) {
|
||||
converterHolder.addDefaultMapping(key, converterCreator.createTypeConverter(tc.converter()));
|
||||
} else {
|
||||
converterHolder.addDefaultMapping(key, converterCreator.createTypeConverter(tc.converterClass()));
|
||||
}
|
||||
} else {
|
||||
if (tc.rule() == ConversionRule.KEY_PROPERTY || tc.rule() == ConversionRule.CREATE_IF_NULL) {
|
||||
mapping.put(key, tc.value());
|
||||
}
|
||||
//for properties of classes
|
||||
else if (tc.rule() != ConversionRule.ELEMENT || tc.rule() == ConversionRule.KEY || tc.rule() == ConversionRule.COLLECTION) {
|
||||
mapping.put(key, converterCreator.createTypeConverter(tc.converter()));
|
||||
if (StringUtils.isNoneEmpty(tc.converter())) {
|
||||
mapping.put(key, converterCreator.createTypeConverter(tc.converter()));
|
||||
} else {
|
||||
mapping.put(key, converterCreator.createTypeConverter(tc.converterClass()));
|
||||
}
|
||||
}
|
||||
//for keys of Maps
|
||||
else if (tc.rule() == ConversionRule.KEY) {
|
||||
Class converterClass = Thread.currentThread().getContextClassLoader().loadClass(tc.converter());
|
||||
Class<?> converterClass;
|
||||
if (StringUtils.isNoneEmpty(tc.converter())) {
|
||||
converterClass = Thread.currentThread().getContextClassLoader().loadClass(tc.converter());
|
||||
//check if the converter is a type converter if it is one
|
||||
//then just put it in the map as is. Otherwise
|
||||
//put a value in for the type converter of the class
|
||||
} else {
|
||||
converterClass = tc.converterClass();
|
||||
}
|
||||
|
||||
LOG.debug("Converter class: [{}]", converterClass);
|
||||
//check if the converter is a type converter if it is one
|
||||
//then just put it in the map as is. Otherwise
|
||||
//put a value in for the type converter of the class
|
||||
|
||||
if (converterClass.isAssignableFrom(TypeConverter.class)) {
|
||||
mapping.put(key, converterCreator.createTypeConverter(tc.converter()));
|
||||
} else {
|
||||
@@ -65,7 +81,11 @@ public class DefaultConversionAnnotationProcessor implements ConversionAnnotatio
|
||||
}
|
||||
//elements(values) of maps / lists
|
||||
else {
|
||||
mapping.put(key, Thread.currentThread().getContextClassLoader().loadClass(tc.converter()));
|
||||
if (StringUtils.isNoneEmpty(tc.converter())) {
|
||||
mapping.put(key, Thread.currentThread().getContextClassLoader().loadClass(tc.converter()));
|
||||
} else {
|
||||
mapping.put(key, tc.converterClass());
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
||||
+18
-6
@@ -1,3 +1,18 @@
|
||||
/*
|
||||
* 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.conversion.impl;
|
||||
|
||||
import com.opensymphony.xwork2.XWorkException;
|
||||
@@ -16,9 +31,6 @@ import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* TODO lukaszlenart: add a comment
|
||||
*/
|
||||
public class DefaultConversionPropertiesProcessor implements ConversionPropertiesProcessor {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(DefaultConversionPropertiesProcessor.class);
|
||||
@@ -59,11 +71,11 @@ public class DefaultConversionPropertiesProcessor implements ConversionPropertie
|
||||
String key = (String) entry.getKey();
|
||||
|
||||
try {
|
||||
TypeConverter _typeConverter = converterCreator.createTypeConverter((String) entry.getValue());
|
||||
TypeConverter typeConverter = converterCreator.createTypeConverter((String) entry.getValue());
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("\t{}:{} [treated as TypeConverter {}]", key, entry.getValue(), _typeConverter);
|
||||
LOG.debug("\t{}:{} [treated as TypeConverter {}]", key, entry.getValue(), typeConverter);
|
||||
}
|
||||
converterHolder.addDefaultMapping(key, _typeConverter);
|
||||
converterHolder.addDefaultMapping(key, typeConverter);
|
||||
} catch (Exception e) {
|
||||
LOG.error("Conversion registration error", e);
|
||||
}
|
||||
|
||||
+3
-1
@@ -32,6 +32,7 @@ package com.opensymphony.xwork2.conversion.impl;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.LocaleProvider;
|
||||
import com.opensymphony.xwork2.LocaleProviderFactory;
|
||||
import com.opensymphony.xwork2.conversion.TypeConverter;
|
||||
import com.opensymphony.xwork2.inject.Container;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
@@ -349,7 +350,8 @@ public abstract class DefaultTypeConverter implements TypeConverter {
|
||||
locale = (Locale) context.get(ActionContext.LOCALE);
|
||||
}
|
||||
if (locale == null) {
|
||||
locale = container.getInstance(LocaleProvider.class).getLocale();
|
||||
LocaleProviderFactory localeProviderFactory = container.getInstance(LocaleProviderFactory.class);
|
||||
locale = localeProviderFactory.createLocaleProvider().getLocale();
|
||||
}
|
||||
return locale;
|
||||
}
|
||||
|
||||
+9
-1
@@ -19,8 +19,16 @@ public class DefaultTypeConverterCreator implements TypeConverterCreator {
|
||||
}
|
||||
|
||||
public TypeConverter createTypeConverter(String className) throws Exception {
|
||||
// type converters are used across users
|
||||
Object obj = objectFactory.buildBean(className, null);
|
||||
return getTypeConverter(obj);
|
||||
}
|
||||
|
||||
public TypeConverter createTypeConverter(Class<?> clazz) throws Exception {
|
||||
Object obj = objectFactory.buildBean(clazz, null);
|
||||
return getTypeConverter(obj);
|
||||
}
|
||||
|
||||
protected TypeConverter getTypeConverter(Object obj) {
|
||||
if (obj instanceof TypeConverter) {
|
||||
return (TypeConverter) obj;
|
||||
|
||||
|
||||
@@ -1,32 +1,43 @@
|
||||
package com.opensymphony.xwork2.conversion.impl;
|
||||
|
||||
import com.opensymphony.xwork2.XWorkException;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.lang.reflect.Member;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.NumberFormat;
|
||||
import java.text.ParsePosition;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
public class NumberConverter extends DefaultTypeConverter {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(NumberConverter.class);
|
||||
|
||||
public Object convertValue(Map<String, Object> context, Object target, Member member, String propertyName, Object value, Class toType) {
|
||||
if (value instanceof String) {
|
||||
String stringValue = String.valueOf(value);
|
||||
|
||||
if (toType == BigDecimal.class) {
|
||||
return new BigDecimal((String) value);
|
||||
return convertToBigDecimal(context, stringValue);
|
||||
} else if (toType == BigInteger.class) {
|
||||
return new BigInteger((String) value);
|
||||
return new BigInteger(stringValue);
|
||||
} else if (toType == Double.class || toType == double.class) {
|
||||
return convertToDouble(context, stringValue);
|
||||
} else if (toType == Float.class || toType == float.class) {
|
||||
return convertToFloat(context, stringValue);
|
||||
} else if (toType.isPrimitive()) {
|
||||
Object convertedValue = super.convertValue(context, value, toType);
|
||||
String stringValue = (String) value;
|
||||
|
||||
if (!isInRange((Number) convertedValue, stringValue, toType))
|
||||
throw new XWorkException("Overflow or underflow casting: \"" + stringValue + "\" into class " + convertedValue.getClass().getName());
|
||||
|
||||
return convertedValue;
|
||||
} else {
|
||||
String stringValue = (String) value;
|
||||
if (!toType.isPrimitive() && (stringValue == null || stringValue.length() == 0)) {
|
||||
if (!toType.isPrimitive() && stringValue.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
NumberFormat numFormat = NumberFormat.getInstance(getLocale(context));
|
||||
@@ -59,6 +70,97 @@ public class NumberConverter extends DefaultTypeConverter {
|
||||
return super.convertValue(context, value, toType);
|
||||
}
|
||||
|
||||
protected Object convertToBigDecimal(Map<String, Object> context, String stringValue) {
|
||||
Locale locale = getLocale(context);
|
||||
|
||||
NumberFormat format = getNumberFormat(locale);
|
||||
if (format instanceof DecimalFormat) {
|
||||
((DecimalFormat) format).setParseBigDecimal(true);
|
||||
char separator = ((DecimalFormat) format).getDecimalFormatSymbols().getGroupingSeparator();
|
||||
stringValue = normalize(stringValue, separator);
|
||||
}
|
||||
|
||||
LOG.debug("Trying to convert a value {} with locale {} to BigDecimal", stringValue, locale);
|
||||
ParsePosition parsePosition = new ParsePosition(0);
|
||||
Number number = format.parse(stringValue, parsePosition);
|
||||
|
||||
if (parsePosition.getIndex() != stringValue.length()) {
|
||||
throw new XWorkException("Unparseable number: \"" + stringValue + "\" at position " + parsePosition.getIndex());
|
||||
}
|
||||
|
||||
return number;
|
||||
}
|
||||
|
||||
protected Object convertToDouble(Map<String, Object> context, String stringValue) {
|
||||
Locale locale = getLocale(context);
|
||||
|
||||
NumberFormat format = getNumberFormat(locale);
|
||||
if (format instanceof DecimalFormat) {
|
||||
char separator = ((DecimalFormat) format).getDecimalFormatSymbols().getGroupingSeparator();
|
||||
stringValue = normalize(stringValue, separator);
|
||||
}
|
||||
|
||||
LOG.debug("Trying to convert a value {} with locale {} to Double", stringValue, locale);
|
||||
ParsePosition parsePosition = new ParsePosition(0);
|
||||
Number number = format.parse(stringValue, parsePosition);
|
||||
|
||||
if (parsePosition.getIndex() != stringValue.length()) {
|
||||
throw new XWorkException("Unparseable number: \"" + stringValue + "\" at position " + parsePosition.getIndex());
|
||||
}
|
||||
|
||||
if (!isInRange(number, stringValue, Double.class)) {
|
||||
throw new XWorkException("Overflow or underflow converting: \"" + stringValue + "\" into class " + number.getClass().getName());
|
||||
}
|
||||
|
||||
if (number != null) {
|
||||
return number.doubleValue();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected Object convertToFloat(Map<String, Object> context, String stringValue) {
|
||||
Locale locale = getLocale(context);
|
||||
|
||||
NumberFormat format = getNumberFormat(locale);
|
||||
if (format instanceof DecimalFormat) {
|
||||
char separator = ((DecimalFormat) format).getDecimalFormatSymbols().getGroupingSeparator();
|
||||
stringValue = normalize(stringValue, separator);
|
||||
}
|
||||
|
||||
LOG.debug("Trying to convert a value {} with locale {} to Float", stringValue, locale);
|
||||
ParsePosition parsePosition = new ParsePosition(0);
|
||||
Number number = format.parse(stringValue, parsePosition);
|
||||
|
||||
if (parsePosition.getIndex() != stringValue.length()) {
|
||||
throw new XWorkException("Unparseable number: \"" + stringValue + "\" at position " + parsePosition.getIndex());
|
||||
}
|
||||
|
||||
if (!isInRange(number, stringValue, Float.class)) {
|
||||
throw new XWorkException("Overflow or underflow converting: \"" + stringValue + "\" into class " + number.getClass().getName());
|
||||
}
|
||||
|
||||
if (number != null) {
|
||||
return number.floatValue();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected NumberFormat getNumberFormat(Locale locale) {
|
||||
NumberFormat format = NumberFormat.getNumberInstance(locale);
|
||||
format.setGroupingUsed(true);
|
||||
return format;
|
||||
}
|
||||
|
||||
protected String normalize(String strValue, char separator) {
|
||||
// this is a hack as \160 isn't the same as " " (an empty space)
|
||||
if (separator == 160) {
|
||||
strValue = strValue.replaceAll(" ", String.valueOf(separator));
|
||||
}
|
||||
return strValue;
|
||||
}
|
||||
|
||||
protected boolean isInRange(Number value, String stringValue, Class toType) {
|
||||
Number bigValue = null;
|
||||
Number lowerBound = null;
|
||||
@@ -103,7 +205,7 @@ public class NumberConverter extends DefaultTypeConverter {
|
||||
return true;
|
||||
}
|
||||
|
||||
return ((Comparable)bigValue).compareTo(lowerBound) >= 0 && ((Comparable)bigValue).compareTo(upperBound) <= 0;
|
||||
return ((Comparable) bigValue).compareTo(lowerBound) >= 0 && ((Comparable) bigValue).compareTo(upperBound) <= 0;
|
||||
}
|
||||
|
||||
private boolean isIntegerType(Class type) {
|
||||
|
||||
@@ -2,58 +2,46 @@ package com.opensymphony.xwork2.conversion.impl;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Member;
|
||||
import java.math.BigDecimal;
|
||||
import java.text.DateFormat;
|
||||
import java.text.NumberFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
public class StringConverter extends DefaultTypeConverter {
|
||||
|
||||
@Override
|
||||
public Object convertValue(Map<String, Object> context, Object target, Member member, String propertyName, Object value, Class toType) {
|
||||
String result = null;
|
||||
String result;
|
||||
|
||||
if (value instanceof int[]) {
|
||||
int[] x = (int[]) value;
|
||||
List<Integer> intArray = new ArrayList<>(x.length);
|
||||
if (value.getClass().isArray()) {
|
||||
int length = Array.getLength(value);
|
||||
List<String> converted = new ArrayList<>(length);
|
||||
|
||||
for (int aX : x) {
|
||||
intArray.add(Integer.valueOf(aX));
|
||||
for (int i = 0; i < length; i++) {
|
||||
Object o = Array.get(value, i);
|
||||
converted.add(convertToString(getLocale(context), o));
|
||||
}
|
||||
|
||||
result = StringUtils.join(intArray, ", ");
|
||||
} else if (value instanceof long[]) {
|
||||
long[] x = (long[]) value;
|
||||
List<Long> longArray = new ArrayList<>(x.length);
|
||||
result = StringUtils.join(converted, ", ");
|
||||
} else if(value.getClass().isAssignableFrom(Collection.class)) {
|
||||
Collection<?> colValue = (Collection) value;
|
||||
List<String> converted = new ArrayList<>(colValue.hashCode());
|
||||
|
||||
for (long aX : x) {
|
||||
longArray.add(Long.valueOf(aX));
|
||||
for (Object o : colValue) {
|
||||
converted.add(convertToString(getLocale(context), o));
|
||||
}
|
||||
|
||||
result = StringUtils.join(longArray, ", ");
|
||||
} else if (value instanceof double[]) {
|
||||
double[] x = (double[]) value;
|
||||
List<Double> doubleArray = new ArrayList<>(x.length);
|
||||
|
||||
for (double aX : x) {
|
||||
doubleArray.add(new Double(aX));
|
||||
}
|
||||
|
||||
result = StringUtils.join(doubleArray, ", ");
|
||||
} else if (value instanceof boolean[]) {
|
||||
boolean[] x = (boolean[]) value;
|
||||
List<Boolean> booleanArray = new ArrayList<>(x.length);
|
||||
|
||||
for (boolean aX : x) {
|
||||
booleanArray.add(new Boolean(aX));
|
||||
}
|
||||
|
||||
result = StringUtils.join(booleanArray, ", ");
|
||||
result = StringUtils.join(converted, ", ");
|
||||
} else if (value instanceof Date) {
|
||||
DateFormat df = null;
|
||||
DateFormat df;
|
||||
if (value instanceof java.sql.Time) {
|
||||
df = DateFormat.getTimeInstance(DateFormat.MEDIUM, getLocale(context));
|
||||
} else if (value instanceof java.sql.Timestamp) {
|
||||
@@ -65,9 +53,24 @@ public class StringConverter extends DefaultTypeConverter {
|
||||
df = DateFormat.getDateInstance(DateFormat.SHORT, getLocale(context));
|
||||
}
|
||||
result = df.format(value);
|
||||
} else if (value instanceof String[]) {
|
||||
result = StringUtils.join((String[]) value, ", ");
|
||||
} else {
|
||||
result = convertToString(getLocale(context), value);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected String convertToString(Locale locale, Object value) {
|
||||
if (Number.class.isInstance(value)) {
|
||||
NumberFormat format = NumberFormat.getNumberInstance(locale);
|
||||
format.setGroupingUsed(false);
|
||||
if (Double.class.isInstance(value) || BigDecimal.class.isInstance(value)) {
|
||||
format.setMinimumFractionDigits(1);
|
||||
}
|
||||
return format.format(value);
|
||||
} else {
|
||||
return String.valueOf(value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -173,8 +173,7 @@ public class XWorkConverter extends DefaultTypeConverter {
|
||||
|
||||
@Inject
|
||||
public void setConversionPropertiesProcessor(ConversionPropertiesProcessor propertiesProcessor) {
|
||||
// note: this file is deprecated
|
||||
propertiesProcessor.process("xwork-default-conversion.properties");
|
||||
propertiesProcessor.processRequired("struts-default-conversion.properties");
|
||||
propertiesProcessor.process("xwork-conversion.properties");
|
||||
}
|
||||
|
||||
@@ -194,7 +193,8 @@ public class XWorkConverter extends DefaultTypeConverter {
|
||||
}
|
||||
|
||||
public static String getConversionErrorMessage(String propertyName, ValueStack stack) {
|
||||
String defaultMessage = LocalizedTextUtil.findDefaultText("xwork.default.invalid.fieldvalue",
|
||||
LocalizedTextProvider localizedTextProvider = ActionContext.getContext().getContainer().getInstance(LocalizedTextProvider.class);
|
||||
String defaultMessage = localizedTextProvider.findDefaultText("xwork.default.invalid.fieldvalue",
|
||||
ActionContext.getContext().getLocale(),
|
||||
new Object[]{
|
||||
propertyName
|
||||
@@ -487,9 +487,9 @@ public class XWorkConverter extends DefaultTypeConverter {
|
||||
}
|
||||
if (LOG.isDebugEnabled()) {
|
||||
if (StringUtils.isEmpty(tc.key())) {
|
||||
LOG.debug("WARNING! key of @TypeConversion [{}] applied to [{}] is empty!", tc.converter(), clazz.getName());
|
||||
LOG.debug("WARNING! key of @TypeConversion [{}/{}] applied to [{}] is empty!", tc.converter(), tc.converterClass(), clazz.getName());
|
||||
} else {
|
||||
LOG.debug("TypeConversion [{}] with key: [{}]", tc.converter(), tc.key());
|
||||
LOG.debug("TypeConversion [{}/{}] with key: [{}]", tc.converter(), tc.converterClass(), tc.key());
|
||||
}
|
||||
}
|
||||
annotationProcessor.process(mapping, tc, tc.key());
|
||||
|
||||
@@ -5,7 +5,10 @@ import com.opensymphony.xwork2.config.ConfigurationException;
|
||||
import com.opensymphony.xwork2.config.entities.InterceptorConfig;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.interceptor.Interceptor;
|
||||
import com.opensymphony.xwork2.interceptor.WithLazyParams;
|
||||
import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@@ -15,6 +18,8 @@ import java.util.Map;
|
||||
*/
|
||||
public class DefaultInterceptorFactory implements InterceptorFactory {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(DefaultInterceptorFactory.class);
|
||||
|
||||
private ObjectFactory objectFactory;
|
||||
private ReflectionProvider reflectionProvider;
|
||||
|
||||
@@ -40,7 +45,12 @@ public class DefaultInterceptorFactory implements InterceptorFactory {
|
||||
try {
|
||||
// interceptor instances are long-lived and used across user sessions, so don't try to pass in any extra context
|
||||
Object o = objectFactory.buildBean(interceptorClassName, null);
|
||||
reflectionProvider.setProperties(params, o);
|
||||
if (o instanceof WithLazyParams) {
|
||||
LOG.debug("Interceptor {} is marked with interface {} and params will be set during action invocation",
|
||||
interceptorClassName, WithLazyParams.class.getName());
|
||||
} else {
|
||||
reflectionProvider.setProperties(params, o);
|
||||
}
|
||||
|
||||
if (o instanceof Interceptor) {
|
||||
Interceptor interceptor = (Interceptor) o;
|
||||
|
||||
@@ -21,6 +21,7 @@ package com.opensymphony.xwork2.inject.util;
|
||||
*
|
||||
* @author crazybob@google.com (Bob Lee)
|
||||
*/
|
||||
@Deprecated
|
||||
public class Strings {
|
||||
|
||||
/**
|
||||
@@ -43,6 +44,7 @@ public class Strings {
|
||||
* converted to uppercase
|
||||
* @throws NullPointerException if <tt>s</tt> is null
|
||||
*/
|
||||
@Deprecated
|
||||
public static String capitalize(String s) {
|
||||
if (s.length() == 0)
|
||||
return s;
|
||||
|
||||
@@ -23,7 +23,7 @@ import com.opensymphony.xwork2.config.entities.ActionConfig;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.util.ClearableValueStack;
|
||||
import com.opensymphony.xwork2.util.Evaluated;
|
||||
import com.opensymphony.xwork2.util.LocalizedTextUtil;
|
||||
import com.opensymphony.xwork2.LocalizedTextProvider;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
import com.opensymphony.xwork2.util.ValueStackFactory;
|
||||
import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
|
||||
@@ -94,11 +94,12 @@ public class AliasInterceptor extends AbstractInterceptor {
|
||||
protected String aliasesKey = DEFAULT_ALIAS_KEY;
|
||||
|
||||
protected ValueStackFactory valueStackFactory;
|
||||
static boolean devMode = false;
|
||||
protected LocalizedTextProvider localizedTextProvider;
|
||||
protected boolean devMode = false;
|
||||
|
||||
@Inject(XWorkConstants.DEV_MODE)
|
||||
public static void setDevMode(String mode) {
|
||||
devMode = "true".equals(mode);
|
||||
public void setDevMode(String mode) {
|
||||
this.devMode = Boolean.parseBoolean(mode);
|
||||
}
|
||||
|
||||
@Inject
|
||||
@@ -106,6 +107,11 @@ public class AliasInterceptor extends AbstractInterceptor {
|
||||
this.valueStackFactory = valueStackFactory;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setLocalizedTextProvider(LocalizedTextProvider localizedTextProvider) {
|
||||
this.localizedTextProvider = localizedTextProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Sets the name of the action parameter to look for the alias map.
|
||||
@@ -173,7 +179,7 @@ public class AliasInterceptor extends AbstractInterceptor {
|
||||
newStack.setValue(alias, value.get());
|
||||
} catch (RuntimeException e) {
|
||||
if (devMode) {
|
||||
String developerNotification = LocalizedTextUtil.findText(ParametersInterceptor.class, "devmode.notification", ActionContext.getContext().getLocale(), "Developer Notification:\n{0}", new Object[]{
|
||||
String developerNotification = localizedTextProvider.findText(ParametersInterceptor.class, "devmode.notification", ActionContext.getContext().getLocale(), "Developer Notification:\n{0}", new Object[]{
|
||||
"Unexpected Exception caught setting '" + entry.getKey() + "' on '" + action.getClass() + ": " + e.getMessage()
|
||||
});
|
||||
LOG.error(developerNotification);
|
||||
|
||||
@@ -20,7 +20,9 @@ import com.opensymphony.xwork2.ActionInvocation;
|
||||
import com.opensymphony.xwork2.Result;
|
||||
import com.opensymphony.xwork2.Unchainable;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.util.ProxyUtil;
|
||||
import com.opensymphony.xwork2.util.CompoundRoot;
|
||||
import com.opensymphony.xwork2.util.TextParseUtil;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
@@ -160,7 +162,12 @@ public class ChainingInterceptor extends AbstractInterceptor {
|
||||
Map<String, Object> ctxMap = invocation.getInvocationContext().getContextMap();
|
||||
for (Object object : list) {
|
||||
if (shouldCopy(object)) {
|
||||
reflectionProvider.copy(object, invocation.getAction(), ctxMap, prepareExcludes(), includes);
|
||||
Object action = invocation.getAction();
|
||||
Class<?> editable = null;
|
||||
if(ProxyUtil.isProxy(action)) {
|
||||
editable = ProxyUtil.ultimateTargetClass(action);
|
||||
}
|
||||
reflectionProvider.copy(object, action, ctxMap, prepareExcludes(), includes, editable);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -210,12 +217,21 @@ public class ChainingInterceptor extends AbstractInterceptor {
|
||||
return excludes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the list of parameter names to exclude from copying (all others will be included).
|
||||
*
|
||||
* @param excludes the excludes list as comma separated String
|
||||
*/
|
||||
public void setExcludes(String excludes) {
|
||||
this.excludes = TextParseUtil.commaDelimitedStringToSet(excludes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the list of parameter names to exclude from copying (all others will be included).
|
||||
*
|
||||
* @param excludes the excludes list
|
||||
*/
|
||||
public void setExcludes(Collection<String> excludes) {
|
||||
public void setExcludesCollection(Collection<String> excludes) {
|
||||
this.excludes = excludes;
|
||||
}
|
||||
|
||||
@@ -228,12 +244,22 @@ public class ChainingInterceptor extends AbstractInterceptor {
|
||||
return includes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the list of parameter names to include when copying (all others will be excluded).
|
||||
*
|
||||
* @param includes the includes list as comma separated String
|
||||
*/
|
||||
public void setIncludes(String includes) {
|
||||
this.includes = TextParseUtil.commaDelimitedStringToSet(includes);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the list of parameter names to include when copying (all others will be excluded).
|
||||
*
|
||||
* @param includes the includes list
|
||||
*/
|
||||
public void setIncludes(Collection<String> includes) {
|
||||
public void setIncludesCollection(Collection<String> includes) {
|
||||
this.includes = includes;
|
||||
}
|
||||
|
||||
|
||||
+4
-5
@@ -19,10 +19,9 @@ import com.opensymphony.xwork2.Action;
|
||||
import com.opensymphony.xwork2.ActionInvocation;
|
||||
import com.opensymphony.xwork2.interceptor.annotations.InputConfig;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.reflect.MethodUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import com.opensymphony.xwork2.util.AnnotationUtils;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* <!-- START SNIPPET: description -->
|
||||
@@ -207,11 +206,11 @@ public class DefaultWorkflowInterceptor extends MethodFilterInterceptor {
|
||||
*/
|
||||
protected String processInputConfig(final Object action, final String method, final String currentResultName) throws Exception {
|
||||
String resultName = currentResultName;
|
||||
InputConfig annotation = action.getClass().getMethod(method, EMPTY_CLASS_ARRAY).getAnnotation(InputConfig.class);
|
||||
InputConfig annotation = MethodUtils.getAnnotation(action.getClass().getMethod(method, EMPTY_CLASS_ARRAY),
|
||||
InputConfig.class ,true,true);
|
||||
if (annotation != null) {
|
||||
if (StringUtils.isNotEmpty(annotation.methodName())) {
|
||||
Method m = action.getClass().getMethod(annotation.methodName());
|
||||
resultName = (String) m.invoke(action);
|
||||
resultName = (String) MethodUtils.invokeMethod(action, true, annotation.methodName());
|
||||
} else {
|
||||
resultName = annotation.resultName();
|
||||
}
|
||||
|
||||
+1
-1
@@ -153,7 +153,7 @@ import java.util.Map;
|
||||
*/
|
||||
public class ExceptionMappingInterceptor extends AbstractInterceptor {
|
||||
|
||||
protected static final Logger LOG = LogManager.getLogger(ExceptionMappingInterceptor.class);
|
||||
private static final Logger LOG = LogManager.getLogger(ExceptionMappingInterceptor.class);
|
||||
|
||||
protected Logger categoryLogger;
|
||||
protected boolean logEnabled = false;
|
||||
|
||||
@@ -70,7 +70,8 @@ import java.util.Set;
|
||||
* @see com.opensymphony.xwork2.validator.ValidationInterceptor
|
||||
*/
|
||||
public abstract class MethodFilterInterceptor extends AbstractInterceptor {
|
||||
protected transient Logger log = LogManager.getLogger(getClass());
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(MethodFilterInterceptor.class);
|
||||
|
||||
protected Set<String> excludeMethods = Collections.emptySet();
|
||||
protected Set<String> includeMethods = Collections.emptySet();
|
||||
@@ -104,7 +105,7 @@ public abstract class MethodFilterInterceptor extends AbstractInterceptor {
|
||||
// ValidationInterceptor
|
||||
boolean applyMethod = MethodFilterInterceptorUtil.applyMethod(excludeMethods, includeMethods, method);
|
||||
if (!applyMethod) {
|
||||
log.debug("Skipping Interceptor... Method [{}] found in exclude list.", method);
|
||||
LOG.debug("Skipping Interceptor... Method [{}] found in exclude list.", method);
|
||||
}
|
||||
return applyMethod;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ package com.opensymphony.xwork2.interceptor;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.ActionInvocation;
|
||||
import com.opensymphony.xwork2.TextProvider;
|
||||
import com.opensymphony.xwork2.XWorkConstants;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.security.AcceptedPatternsChecker;
|
||||
@@ -218,15 +219,19 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
|
||||
}
|
||||
|
||||
protected void notifyDeveloperParameterException(Object action, String property, String message) {
|
||||
String developerNotification = LocalizedTextUtil.findText(ParametersInterceptor.class, "devmode.notification",
|
||||
ActionContext.getContext().getLocale(), "Developer Notification:\n{0}",
|
||||
new Object[]{
|
||||
"Unexpected Exception caught setting '" + property + "' on '" + action.getClass() + ": " + message
|
||||
}
|
||||
);
|
||||
String developerNotification = "Unexpected Exception caught setting '" + property + "' on '" + action.getClass() + ": " + message;
|
||||
if (action instanceof TextProvider) {
|
||||
TextProvider tp = (TextProvider) action;
|
||||
developerNotification = tp.getText("devmode.notification",
|
||||
"Developer Notification:\n{0}",
|
||||
new String[]{ developerNotification }
|
||||
);
|
||||
}
|
||||
|
||||
LOG.error(developerNotification);
|
||||
// see https://issues.apache.org/jira/browse/WW-4066
|
||||
|
||||
if (action instanceof ValidationAware) {
|
||||
// see https://issues.apache.org/jira/browse/WW-4066
|
||||
Collection<String> messages = ((ValidationAware) action).getActionMessages();
|
||||
messages.add(message);
|
||||
((ValidationAware) action).setActionMessages(messages);
|
||||
|
||||
+10
-3
@@ -22,7 +22,7 @@ import com.opensymphony.xwork2.config.entities.ActionConfig;
|
||||
import com.opensymphony.xwork2.config.entities.Parameterizable;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.util.ClearableValueStack;
|
||||
import com.opensymphony.xwork2.util.LocalizedTextUtil;
|
||||
import com.opensymphony.xwork2.LocalizedTextProvider;
|
||||
import com.opensymphony.xwork2.util.TextParseUtil;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
import com.opensymphony.xwork2.util.ValueStackFactory;
|
||||
@@ -93,6 +93,7 @@ public class StaticParametersInterceptor extends AbstractInterceptor {
|
||||
private static final Logger LOG = LogManager.getLogger(StaticParametersInterceptor.class);
|
||||
|
||||
private ValueStackFactory valueStackFactory;
|
||||
private LocalizedTextProvider localizedTextProvider;
|
||||
|
||||
@Inject
|
||||
public void setValueStackFactory(ValueStackFactory valueStackFactory) {
|
||||
@@ -102,7 +103,12 @@ public class StaticParametersInterceptor extends AbstractInterceptor {
|
||||
@Inject(XWorkConstants.DEV_MODE)
|
||||
public void setDevMode(String mode) {
|
||||
devMode = BooleanUtils.toBoolean(mode);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setLocalizedTextProvider(LocalizedTextProvider localizedTextProvider) {
|
||||
this.localizedTextProvider = localizedTextProvider;
|
||||
}
|
||||
|
||||
public void setParse(String value) {
|
||||
this.parse = BooleanUtils.toBoolean(value);
|
||||
@@ -168,7 +174,8 @@ public class StaticParametersInterceptor extends AbstractInterceptor {
|
||||
newStack.setValue(entry.getKey(), val);
|
||||
} catch (RuntimeException e) {
|
||||
if (devMode) {
|
||||
String developerNotification = LocalizedTextUtil.findText(ParametersInterceptor.class, "devmode.notification", ActionContext.getContext().getLocale(), "Developer Notification:\n{0}", new Object[]{
|
||||
|
||||
String developerNotification = localizedTextProvider.findText(ParametersInterceptor.class, "devmode.notification", ActionContext.getContext().getLocale(), "Developer Notification:\n{0}", new Object[]{
|
||||
"Unexpected Exception caught setting '" + entry.getKey() + "' on '" + action.getClass() + ": " + e.getMessage()
|
||||
});
|
||||
LOG.error(developerNotification);
|
||||
|
||||
@@ -91,9 +91,13 @@ import org.apache.logging.log4j.Logger;
|
||||
*
|
||||
* @author Jason Carreira
|
||||
* @author Claus Ibsen
|
||||
*
|
||||
* @deprecated will be dropped with next major release (2.6)
|
||||
*/
|
||||
@Deprecated
|
||||
public class TimerInterceptor extends AbstractInterceptor {
|
||||
protected static final Logger LOG = LogManager.getLogger(TimerInterceptor.class);
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(TimerInterceptor.class);
|
||||
|
||||
protected Logger categoryLogger;
|
||||
protected String logCategory;
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.opensymphony.xwork2.interceptor;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.ognl.OgnlUtil;
|
||||
import com.opensymphony.xwork2.util.TextParseUtil;
|
||||
import com.opensymphony.xwork2.util.TextParser;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Interceptors marked with this interface won't be fully initialised during initialisation.
|
||||
* Appropriated params will be injected just before usage of the interceptor.
|
||||
*
|
||||
* Please be aware that in such case {@link Interceptor#init()} method must be prepared for this.
|
||||
*
|
||||
* @since 2.5.9
|
||||
*/
|
||||
public interface WithLazyParams {
|
||||
|
||||
class LazyParamInjector {
|
||||
|
||||
protected OgnlUtil ognlUtil;
|
||||
protected TextParser textParser;
|
||||
protected ReflectionProvider reflectionProvider;
|
||||
|
||||
private final TextParseUtil.ParsedValueEvaluator valueEvaluator;
|
||||
|
||||
public LazyParamInjector(final ValueStack valueStack) {
|
||||
valueEvaluator = new TextParseUtil.ParsedValueEvaluator() {
|
||||
public Object evaluate(String parsedValue) {
|
||||
return valueStack.findValue(parsedValue); // no asType !!!
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setTextParser(TextParser textParser) {
|
||||
this.textParser = textParser;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setReflectionProvider(ReflectionProvider reflectionProvider) {
|
||||
this.reflectionProvider = reflectionProvider;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setOgnlUtil(OgnlUtil ognlUtil) {
|
||||
this.ognlUtil = ognlUtil;
|
||||
}
|
||||
|
||||
public Interceptor injectParams(Interceptor interceptor, Map<String, String> params, ActionContext invocationContext) {
|
||||
for (Map.Entry<String, String> entry : params.entrySet()) {
|
||||
Object paramValue = textParser.evaluate(new char[]{ '$' }, entry.getValue(), valueEvaluator, TextParser.DEFAULT_LOOP_COUNT);
|
||||
ognlUtil.setProperty(entry.getKey(), paramValue, interceptor, invocationContext.getContextMap());
|
||||
}
|
||||
|
||||
return interceptor;
|
||||
}
|
||||
}
|
||||
}
|
||||
+19
-13
@@ -19,7 +19,7 @@ import com.opensymphony.xwork2.ActionInvocation;
|
||||
import com.opensymphony.xwork2.XWorkException;
|
||||
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
|
||||
import com.opensymphony.xwork2.interceptor.PreResultListener;
|
||||
import com.opensymphony.xwork2.util.AnnotationUtils;
|
||||
import org.apache.commons.lang3.reflect.MethodUtils;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
@@ -112,17 +112,19 @@ public class AnnotationWorkflowInterceptor extends AbstractInterceptor implement
|
||||
public String intercept(ActionInvocation invocation) throws Exception {
|
||||
final Object action = invocation.getAction();
|
||||
invocation.addPreResultListener(this);
|
||||
List<Method> methods = new ArrayList<>(AnnotationUtils.getAnnotatedMethods(action.getClass(), Before.class));
|
||||
List<Method> methods = new ArrayList<>(MethodUtils.getMethodsListWithAnnotation(action.getClass(), Before.class,
|
||||
true, true));
|
||||
if (methods.size() > 0) {
|
||||
// methods are only sorted by priority
|
||||
Collections.sort(methods, new Comparator<Method>() {
|
||||
public int compare(Method method1, Method method2) {
|
||||
return comparePriorities(method1.getAnnotation(Before.class).priority(),
|
||||
method2.getAnnotation(Before.class).priority());
|
||||
return comparePriorities(MethodUtils.getAnnotation(method1, Before.class, true,
|
||||
true).priority(), MethodUtils.getAnnotation(method2, Before.class, true,
|
||||
true).priority());
|
||||
}
|
||||
});
|
||||
for (Method m : methods) {
|
||||
final String resultCode = (String) m.invoke(action, (Object[]) null);
|
||||
final String resultCode = (String) MethodUtils.invokeMethod(action, true, m.getName());
|
||||
if (resultCode != null) {
|
||||
// shortcircuit execution
|
||||
return resultCode;
|
||||
@@ -133,18 +135,20 @@ public class AnnotationWorkflowInterceptor extends AbstractInterceptor implement
|
||||
String invocationResult = invocation.invoke();
|
||||
|
||||
// invoke any @After methods
|
||||
methods = new ArrayList<Method>(AnnotationUtils.getAnnotatedMethods(action.getClass(), After.class));
|
||||
methods = new ArrayList<Method>(MethodUtils.getMethodsListWithAnnotation(action.getClass(), After.class,
|
||||
true, true));
|
||||
|
||||
if (methods.size() > 0) {
|
||||
// methods are only sorted by priority
|
||||
Collections.sort(methods, new Comparator<Method>() {
|
||||
public int compare(Method method1, Method method2) {
|
||||
return comparePriorities(method1.getAnnotation(After.class).priority(),
|
||||
method2.getAnnotation(After.class).priority());
|
||||
return comparePriorities(MethodUtils.getAnnotation(method1, After.class, true,
|
||||
true).priority(), MethodUtils.getAnnotation(method2, After.class, true,
|
||||
true).priority());
|
||||
}
|
||||
});
|
||||
for (Method m : methods) {
|
||||
m.invoke(action, (Object[]) null);
|
||||
MethodUtils.invokeMethod(action, true, m.getName());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,19 +172,21 @@ public class AnnotationWorkflowInterceptor extends AbstractInterceptor implement
|
||||
*/
|
||||
public void beforeResult(ActionInvocation invocation, String resultCode) {
|
||||
Object action = invocation.getAction();
|
||||
List<Method> methods = new ArrayList<Method>(AnnotationUtils.getAnnotatedMethods(action.getClass(), BeforeResult.class));
|
||||
List<Method> methods = new ArrayList<Method>(MethodUtils.getMethodsListWithAnnotation(action.getClass(),
|
||||
BeforeResult.class, true, true));
|
||||
|
||||
if (methods.size() > 0) {
|
||||
// methods are only sorted by priority
|
||||
Collections.sort(methods, new Comparator<Method>() {
|
||||
public int compare(Method method1, Method method2) {
|
||||
return comparePriorities(method1.getAnnotation(BeforeResult.class).priority(),
|
||||
method2.getAnnotation(BeforeResult.class).priority());
|
||||
return comparePriorities(MethodUtils.getAnnotation(method1, BeforeResult.class, true,
|
||||
true).priority(), MethodUtils.getAnnotation(method2, BeforeResult.class,
|
||||
true, true).priority());
|
||||
}
|
||||
});
|
||||
for (Method m : methods) {
|
||||
try {
|
||||
m.invoke(action, (Object[]) null);
|
||||
MethodUtils.invokeMethod(action, true, m.getName());
|
||||
} catch (Exception e) {
|
||||
throw new XWorkException(e);
|
||||
}
|
||||
|
||||
@@ -16,16 +16,15 @@
|
||||
package com.opensymphony.xwork2.mock;
|
||||
|
||||
import com.opensymphony.xwork2.ActionInvocation;
|
||||
import com.opensymphony.xwork2.interceptor.Interceptor;
|
||||
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
|
||||
import org.junit.Assert;
|
||||
|
||||
|
||||
/**
|
||||
* Mock for an {@link com.opensymphony.xwork2.interceptor.Interceptor}.
|
||||
*
|
||||
* @author Jason Carreira
|
||||
*/
|
||||
public class MockInterceptor implements Interceptor {
|
||||
public class MockInterceptor extends AbstractInterceptor {
|
||||
|
||||
private static final long serialVersionUID = 2692551676567227756L;
|
||||
|
||||
@@ -57,12 +56,6 @@ public class MockInterceptor implements Interceptor {
|
||||
return foo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to let an interceptor clean up any resources it has allocated.
|
||||
*/
|
||||
public void destroy() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
@@ -101,13 +94,6 @@ public class MockInterceptor implements Interceptor {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after an Interceptor is created, but before any requests are processed using the intercept() methodName. This
|
||||
* gives the Interceptor a chance to initialize any needed resources.
|
||||
*/
|
||||
public void init() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows the Interceptor to do some processing on the request before and/or after the rest of the processing of the
|
||||
* request by the DefaultActionInvocation or to short-circuit the processing and just return a String return code.
|
||||
|
||||
@@ -70,7 +70,12 @@ public class OgnlReflectionProvider implements ReflectionProvider {
|
||||
|
||||
public void copy(Object from, Object to, Map<String, Object> context,
|
||||
Collection<String> exclusions, Collection<String> inclusions) {
|
||||
ognlUtil.copy(from, to, context, exclusions, inclusions);
|
||||
copy(from, to, context, exclusions, inclusions, null);
|
||||
}
|
||||
|
||||
public void copy(Object from, Object to, Map<String, Object> context,
|
||||
Collection<String> exclusions, Collection<String> inclusions, Class<?> editable) {
|
||||
ognlUtil.copy(from, to, context, exclusions, inclusions, editable);
|
||||
}
|
||||
|
||||
public Object getRealTarget(String property, Map<String, Object> context, Object root)
|
||||
|
||||
@@ -28,6 +28,7 @@ import ognl.*;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
|
||||
import java.beans.BeanInfo;
|
||||
import java.beans.IntrospectionException;
|
||||
@@ -49,6 +50,7 @@ import java.util.regex.Pattern;
|
||||
public class OgnlUtil {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(OgnlUtil.class);
|
||||
|
||||
private ConcurrentMap<String, Object> expressions = new ConcurrentHashMap<>();
|
||||
private final ConcurrentMap<Class, BeanInfo> beanInfoCache = new ConcurrentHashMap<>();
|
||||
private TypeConverter defaultConverter;
|
||||
@@ -57,12 +59,13 @@ public class OgnlUtil {
|
||||
private boolean enableExpressionCache = true;
|
||||
private boolean enableEvalExpression;
|
||||
|
||||
private Set<Class<?>> excludedClasses = new HashSet<>();
|
||||
private Set<Pattern> excludedPackageNamePatterns = new HashSet<>();
|
||||
private Set<String> excludedPackageNames = new HashSet<>();
|
||||
private Set<Class<?>> excludedClasses = Collections.emptySet();
|
||||
private Set<Pattern> excludedPackageNamePatterns = Collections.emptySet();
|
||||
private Set<String> excludedPackageNames = Collections.emptySet();
|
||||
|
||||
private Container container;
|
||||
private boolean allowStaticMethodAccess;
|
||||
private boolean disallowProxyMemberAccess;
|
||||
|
||||
@Inject
|
||||
public void setXWorkConverter(XWorkConverter conv) {
|
||||
@@ -90,27 +93,35 @@ public class OgnlUtil {
|
||||
|
||||
@Inject(value = XWorkConstants.OGNL_EXCLUDED_CLASSES, required = false)
|
||||
public void setExcludedClasses(String commaDelimitedClasses) {
|
||||
Set<String> classes = TextParseUtil.commaDelimitedStringToSet(commaDelimitedClasses);
|
||||
for (String className : classes) {
|
||||
Set<String> classNames = TextParseUtil.commaDelimitedStringToSet(commaDelimitedClasses);
|
||||
Set<Class<?>> classes = new HashSet<>();
|
||||
|
||||
for (String className : classNames) {
|
||||
try {
|
||||
excludedClasses.add(Class.forName(className));
|
||||
classes.add(Class.forName(className));
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new ConfigurationException("Cannot load excluded class: " + className, e);
|
||||
}
|
||||
}
|
||||
|
||||
excludedClasses = Collections.unmodifiableSet(classes);
|
||||
}
|
||||
|
||||
@Inject(value = XWorkConstants.OGNL_EXCLUDED_PACKAGE_NAME_PATTERNS, required = false)
|
||||
public void setExcludedPackageNamePatterns(String commaDelimitedPackagePatterns) {
|
||||
Set<String> packagePatterns = TextParseUtil.commaDelimitedStringToSet(commaDelimitedPackagePatterns);
|
||||
Set<Pattern> packageNamePatterns = new HashSet<>();
|
||||
|
||||
for (String pattern : packagePatterns) {
|
||||
excludedPackageNamePatterns.add(Pattern.compile(pattern));
|
||||
packageNamePatterns.add(Pattern.compile(pattern));
|
||||
}
|
||||
|
||||
excludedPackageNamePatterns = Collections.unmodifiableSet(packageNamePatterns);
|
||||
}
|
||||
|
||||
@Inject(value = XWorkConstants.OGNL_EXCLUDED_PACKAGE_NAMES, required = false)
|
||||
public void setExcludedPackageNames(String commaDelimitedPackageNames) {
|
||||
excludedPackageNames = TextParseUtil.commaDelimitedStringToSet(commaDelimitedPackageNames);
|
||||
excludedPackageNames = Collections.unmodifiableSet(TextParseUtil.commaDelimitedStringToSet(commaDelimitedPackageNames));
|
||||
}
|
||||
|
||||
public Set<Class<?>> getExcludedClasses() {
|
||||
@@ -135,6 +146,15 @@ public class OgnlUtil {
|
||||
this.allowStaticMethodAccess = Boolean.parseBoolean(allowStaticMethodAccess);
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_DISALLOW_PROXY_MEMBER_ACCESS, required = false)
|
||||
public void setDisallowProxyMemberAccess(String disallowProxyMemberAccess) {
|
||||
this.disallowProxyMemberAccess = Boolean.parseBoolean(disallowProxyMemberAccess);
|
||||
}
|
||||
|
||||
public boolean isDisallowProxyMemberAccess() {
|
||||
return disallowProxyMemberAccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the object's properties using the default type converter, defaulting to not throw
|
||||
* exceptions for problems setting the properties.
|
||||
@@ -441,6 +461,24 @@ public class OgnlUtil {
|
||||
* note if exclusions AND inclusions are supplied and not null nothing will get copied.
|
||||
*/
|
||||
public void copy(final Object from, final Object to, final Map<String, Object> context, Collection<String> exclusions, Collection<String> inclusions) {
|
||||
copy(from, to, context, exclusions, inclusions, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the properties in the object "from" and sets them in the object "to"
|
||||
* only setting properties defined in the given "editable" class (or interface)
|
||||
* using specified type converter, or {@link com.opensymphony.xwork2.conversion.impl.XWorkConverter} if none
|
||||
* is specified.
|
||||
*
|
||||
* @param from the source object
|
||||
* @param to the target object
|
||||
* @param context the action context we're running under
|
||||
* @param exclusions collection of method names to excluded from copying ( can be null)
|
||||
* @param inclusions collection of method names to included copying (can be null)
|
||||
* note if exclusions AND inclusions are supplied and not null nothing will get copied.
|
||||
* @param editable the class (or interface) to restrict property setting to
|
||||
*/
|
||||
public void copy(final Object from, final Object to, final Map<String, Object> context, Collection<String> exclusions, Collection<String> inclusions, Class<?> editable) {
|
||||
if (from == null || to == null) {
|
||||
LOG.warn("Attempting to copy from or to a null source. This is illegal and is bein skipped. This may be due to an error in an OGNL expression, action chaining, or some other event.");
|
||||
return;
|
||||
@@ -457,7 +495,12 @@ public class OgnlUtil {
|
||||
|
||||
try {
|
||||
fromPds = getPropertyDescriptors(from);
|
||||
toPds = getPropertyDescriptors(to);
|
||||
if (editable != null) {
|
||||
toPds = getPropertyDescriptors(editable);
|
||||
}
|
||||
else {
|
||||
toPds = getPropertyDescriptors(to);
|
||||
}
|
||||
} catch (IntrospectionException e) {
|
||||
LOG.error("An error occurred", e);
|
||||
return;
|
||||
@@ -647,6 +690,7 @@ public class OgnlUtil {
|
||||
memberAccess.setExcludedClasses(excludedClasses);
|
||||
memberAccess.setExcludedPackageNamePatterns(excludedPackageNamePatterns);
|
||||
memberAccess.setExcludedPackageNames(excludedPackageNames);
|
||||
memberAccess.setDisallowProxyMemberAccess(disallowProxyMemberAccess);
|
||||
|
||||
return Ognl.createDefaultContext(root, resolver, defaultConverter, memberAccess);
|
||||
}
|
||||
|
||||
@@ -52,19 +52,20 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
|
||||
|
||||
public static final String THROW_EXCEPTION_ON_FAILURE = OgnlValueStack.class.getName() + ".throwExceptionOnFailure";
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(OgnlValueStack.class);
|
||||
|
||||
private static final long serialVersionUID = 370737852934925530L;
|
||||
|
||||
private static final String MAP_IDENTIFIER_KEY = "com.opensymphony.xwork2.util.OgnlValueStack.MAP_IDENTIFIER_KEY";
|
||||
private static final Logger LOG = LogManager.getLogger(OgnlValueStack.class);
|
||||
|
||||
CompoundRoot root;
|
||||
transient Map<String, Object> context;
|
||||
Class defaultType;
|
||||
Map<Object, Object> overrides;
|
||||
transient OgnlUtil ognlUtil;
|
||||
transient SecurityMemberAccess securityMemberAccess;
|
||||
protected CompoundRoot root;
|
||||
protected transient Map<String, Object> context;
|
||||
protected Class defaultType;
|
||||
protected Map<Object, Object> overrides;
|
||||
protected transient OgnlUtil ognlUtil;
|
||||
protected transient SecurityMemberAccess securityMemberAccess;
|
||||
|
||||
private transient XWorkConverter converter;
|
||||
|
||||
private boolean devMode;
|
||||
private boolean logMissingProperties;
|
||||
|
||||
@@ -83,6 +84,7 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
|
||||
securityMemberAccess.setExcludedClasses(ognlUtil.getExcludedClasses());
|
||||
securityMemberAccess.setExcludedPackageNamePatterns(ognlUtil.getExcludedPackageNamePatterns());
|
||||
securityMemberAccess.setExcludedPackageNames(ognlUtil.getExcludedPackageNames());
|
||||
securityMemberAccess.setDisallowProxyMemberAccess(ognlUtil.isDisallowProxyMemberAccess());
|
||||
}
|
||||
|
||||
protected void setRoot(XWorkConverter xworkConverter, CompoundRootAccessor accessor, CompoundRoot compoundRoot,
|
||||
@@ -189,7 +191,7 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
|
||||
context.remove(REPORT_ERRORS_ON_NO_PROP);
|
||||
}
|
||||
|
||||
private void handleRuntimeException(String expr, Object value, boolean throwExceptionOnFailure, RuntimeException re) {
|
||||
protected void handleRuntimeException(String expr, Object value, boolean throwExceptionOnFailure, RuntimeException re) {
|
||||
if (throwExceptionOnFailure) {
|
||||
String message = ErrorMessageBuilder.create()
|
||||
.errorSettingExpressionWithValue(expr, value)
|
||||
@@ -200,7 +202,7 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
|
||||
}
|
||||
}
|
||||
|
||||
private void handleOgnlException(String expr, Object value, boolean throwExceptionOnFailure, OgnlException e) {
|
||||
protected void handleOgnlException(String expr, Object value, boolean throwExceptionOnFailure, OgnlException e) {
|
||||
boolean shouldLog = shouldLogMissingPropertyWarning(e);
|
||||
String msg = null;
|
||||
if (throwExceptionOnFailure || shouldLog) {
|
||||
@@ -242,7 +244,7 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
|
||||
}
|
||||
}
|
||||
|
||||
private void setupExceptionOnFailure(boolean throwExceptionOnFailure) {
|
||||
protected void setupExceptionOnFailure(boolean throwExceptionOnFailure) {
|
||||
if (throwExceptionOnFailure) {
|
||||
context.put(THROW_EXCEPTION_ON_FAILURE, true);
|
||||
}
|
||||
@@ -255,7 +257,7 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
|
||||
return tryFindValue(expr);
|
||||
}
|
||||
|
||||
private Object handleOtherException(String expr, boolean throwExceptionOnFailure, Exception e) {
|
||||
protected Object handleOtherException(String expr, boolean throwExceptionOnFailure, Exception e) {
|
||||
logLookupFailure(expr, e);
|
||||
|
||||
if (throwExceptionOnFailure)
|
||||
@@ -322,7 +324,7 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
|
||||
return tryFindValue(expr, asType);
|
||||
}
|
||||
|
||||
private Object handleOgnlException(String expr, boolean throwExceptionOnFailure, OgnlException e) {
|
||||
protected Object handleOgnlException(String expr, boolean throwExceptionOnFailure, OgnlException e) {
|
||||
Object ret = findInContext(expr);
|
||||
if (ret == null) {
|
||||
if (shouldLogMissingPropertyWarning(e)) {
|
||||
@@ -335,7 +337,7 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
|
||||
return ret;
|
||||
}
|
||||
|
||||
private boolean shouldLogMissingPropertyWarning(OgnlException e) {
|
||||
protected boolean shouldLogMissingPropertyWarning(OgnlException e) {
|
||||
return (e instanceof NoSuchPropertyException || e instanceof MethodFailedException)
|
||||
&& devMode && logMissingProperties;
|
||||
}
|
||||
@@ -359,7 +361,7 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
|
||||
return ognlUtil.getValue(expr, context, root, asType);
|
||||
}
|
||||
|
||||
private Object findInContext(String name) {
|
||||
protected Object findInContext(String name) {
|
||||
return getContext().get(name);
|
||||
}
|
||||
|
||||
|
||||
@@ -38,11 +38,11 @@ import java.util.Set;
|
||||
*/
|
||||
public class OgnlValueStackFactory implements ValueStackFactory {
|
||||
|
||||
private XWorkConverter xworkConverter;
|
||||
private CompoundRootAccessor compoundRootAccessor;
|
||||
private TextProvider textProvider;
|
||||
private Container container;
|
||||
private boolean allowStaticMethodAccess;
|
||||
protected XWorkConverter xworkConverter;
|
||||
protected CompoundRootAccessor compoundRootAccessor;
|
||||
protected TextProvider textProvider;
|
||||
protected Container container;
|
||||
protected boolean allowStaticMethodAccess;
|
||||
|
||||
@Inject
|
||||
public void setXWorkConverter(XWorkConverter converter) {
|
||||
|
||||
@@ -15,12 +15,12 @@
|
||||
*/
|
||||
package com.opensymphony.xwork2.ognl;
|
||||
|
||||
import com.opensymphony.xwork2.util.ProxyUtil;
|
||||
import ognl.DefaultMemberAccess;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.*;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -41,6 +41,7 @@ public class SecurityMemberAccess extends DefaultMemberAccess {
|
||||
private Set<Class<?>> excludedClasses = Collections.emptySet();
|
||||
private Set<Pattern> excludedPackageNamePatterns = Collections.emptySet();
|
||||
private Set<String> excludedPackageNames = Collections.emptySet();
|
||||
private boolean disallowProxyMemberAccess;
|
||||
|
||||
public SecurityMemberAccess(boolean method) {
|
||||
super(false);
|
||||
@@ -53,6 +54,8 @@ public class SecurityMemberAccess extends DefaultMemberAccess {
|
||||
|
||||
@Override
|
||||
public boolean isAccessible(Map context, Object target, Member member, String propertyName) {
|
||||
LOG.debug("Checking access for [target: {}, member: {}, property: {}]", target, member, propertyName);
|
||||
|
||||
if (checkEnumAccess(target, member)) {
|
||||
LOG.trace("Allowing access to enum: {}", target);
|
||||
return true;
|
||||
@@ -83,6 +86,11 @@ public class SecurityMemberAccess extends DefaultMemberAccess {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (disallowProxyMemberAccess && ProxyUtil.isProxyMember(member, target)) {
|
||||
LOG.warn("Access to proxy [{}] is blocked!", member);
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean allow = true;
|
||||
if (!checkStaticMethodAccess(member)) {
|
||||
LOG.warn("Access to static [{}] is blocked!", member);
|
||||
@@ -205,4 +213,8 @@ public class SecurityMemberAccess extends DefaultMemberAccess {
|
||||
public void setExcludedPackageNames(Set<String> excludedPackageNames) {
|
||||
this.excludedPackageNames = excludedPackageNames;
|
||||
}
|
||||
|
||||
public void setDisallowProxyMemberAccess(boolean disallowProxyMemberAccess) {
|
||||
this.disallowProxyMemberAccess = disallowProxyMemberAccess;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +57,7 @@ public class SpringObjectFactory extends ObjectFactory implements ApplicationCon
|
||||
* This is temporary solution, after validating can be removed
|
||||
* @since 2.3.18
|
||||
*/
|
||||
@Deprecated
|
||||
private boolean enableAopSupport = false;
|
||||
|
||||
@Inject(value="applicationContextPath",required=false)
|
||||
|
||||
@@ -0,0 +1,576 @@
|
||||
package com.opensymphony.xwork2.util;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.LocalizedTextProvider;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.message.ParameterizedMessage;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.MissingResourceException;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
abstract class AbstractLocalizedTextProvider implements LocalizedTextProvider {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(AbstractLocalizedTextProvider.class);
|
||||
|
||||
public static final String XWORK_MESSAGES_BUNDLE = "com/opensymphony/xwork2/xwork-messages";
|
||||
public static final String STRUTS_MESSAGES_BUNDLE = "org/apache/struts2/struts-messages";
|
||||
|
||||
private static final String TOMCAT_RESOURCE_ENTRIES_FIELD = "resourceEntries";
|
||||
private final String RELOADED = "com.opensymphony.xwork2.util.LocalizedTextProvider.reloaded";
|
||||
|
||||
protected final ConcurrentMap<String, ResourceBundle> bundlesMap = new ConcurrentHashMap<>();
|
||||
protected boolean devMode = false;
|
||||
protected boolean reloadBundles = false;
|
||||
|
||||
private final ConcurrentMap<MessageFormatKey, MessageFormat> messageFormats = new ConcurrentHashMap<>();
|
||||
private final ConcurrentMap<Integer, List<String>> classLoaderMap = new ConcurrentHashMap<>();
|
||||
private final Set<String> missingBundles = Collections.synchronizedSet(new HashSet<String>());
|
||||
private final ConcurrentMap<Integer, ClassLoader> delegatedClassLoaderMap = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Add's the bundle to the internal list of default bundles.
|
||||
* If the bundle already exists in the list it will be re-added.
|
||||
*
|
||||
* @param resourceBundleName the name of the bundle to add.
|
||||
*/
|
||||
@Override
|
||||
public void addDefaultResourceBundle(String resourceBundleName) {
|
||||
//make sure this doesn't get added more than once
|
||||
final ClassLoader ccl = getCurrentThreadContextClassLoader();
|
||||
synchronized (XWORK_MESSAGES_BUNDLE) {
|
||||
List<String> bundles = classLoaderMap.get(ccl.hashCode());
|
||||
if (bundles == null) {
|
||||
bundles = new CopyOnWriteArrayList<>();
|
||||
classLoaderMap.put(ccl.hashCode(), bundles);
|
||||
}
|
||||
bundles.remove(resourceBundleName);
|
||||
bundles.add(0, resourceBundleName);
|
||||
}
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Added default resource bundle '{}' to default resource bundles for the following classloader '{}'", resourceBundleName, ccl.toString());
|
||||
}
|
||||
}
|
||||
|
||||
protected List<String> getCurrentBundleNames() {
|
||||
return classLoaderMap.get(getCurrentThreadContextClassLoader().hashCode());
|
||||
}
|
||||
|
||||
protected ClassLoader getCurrentThreadContextClassLoader() {
|
||||
return Thread.currentThread().getContextClassLoader();
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_CUSTOM_I18N_RESOURCES, required = false)
|
||||
public void setCustomI18NResources(String bundles) {
|
||||
if (bundles != null && bundles.length() > 0) {
|
||||
StringTokenizer customBundles = new StringTokenizer(bundles, ", ");
|
||||
|
||||
while (customBundles.hasMoreTokens()) {
|
||||
String name = customBundles.nextToken();
|
||||
try {
|
||||
LOG.trace("Loading global messages from [{}]", name);
|
||||
addDefaultResourceBundle(name);
|
||||
} catch (Exception e) {
|
||||
LOG.error(new ParameterizedMessage("Could not find messages file {}.properties. Skipping", name), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a localized message for the specified key, aTextName. Neither the key nor the
|
||||
* message is evaluated.
|
||||
*
|
||||
* @param aTextName the message key
|
||||
* @param locale the locale the message should be for
|
||||
* @return a localized message based on the specified key, or null if no localized message can be found for it
|
||||
*/
|
||||
@Override
|
||||
public String findDefaultText(String aTextName, Locale locale) {
|
||||
List<String> localList = getCurrentBundleNames();
|
||||
|
||||
for (String bundleName : localList) {
|
||||
ResourceBundle bundle = findResourceBundle(bundleName, locale);
|
||||
if (bundle != null) {
|
||||
reloadBundles();
|
||||
try {
|
||||
return bundle.getString(aTextName);
|
||||
} catch (MissingResourceException e) {
|
||||
// will be logged when not found in any bundle
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (devMode) {
|
||||
LOG.warn("Missing key [{}] in bundles [{}]!", aTextName, localList);
|
||||
} else {
|
||||
LOG.debug("Missing key [{}] in bundles [{}]!", aTextName, localList);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a localized message for the specified key, aTextName, substituting variables from the
|
||||
* array of params into the message. Neither the key nor the message is evaluated.
|
||||
*
|
||||
* @param aTextName the message key
|
||||
* @param locale the locale the message should be for
|
||||
* @param params an array of objects to be substituted into the message text
|
||||
* @return A formatted message based on the specified key, or null if no localized message can be found for it
|
||||
*/
|
||||
@Override
|
||||
public String findDefaultText(String aTextName, Locale locale, Object[] params) {
|
||||
String defaultText = findDefaultText(aTextName, locale);
|
||||
if (defaultText != null) {
|
||||
MessageFormat mf = buildMessageFormat(defaultText, locale);
|
||||
return formatWithNullDetection(mf, params);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Finds a localized text message for the given key, aTextName, in the specified resource
|
||||
* bundle.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If a message is found, it will also be interpolated. Anything within <code>${...}</code>
|
||||
* will be treated as an OGNL expression and evaluated as such.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If a message is <b>not</b> found a WARN log will be logged.
|
||||
* </p>
|
||||
*
|
||||
* @param bundle the bundle
|
||||
* @param aTextName the key
|
||||
* @param locale the locale
|
||||
* @param defaultMessage the default message to use if no message was found in the bundle
|
||||
* @param args arguments for the message formatter.
|
||||
* @param valueStack the OGNL value stack.
|
||||
* @return the localized text, or null if none can be found and no defaultMessage is provided
|
||||
*/
|
||||
@Override
|
||||
public String findText(ResourceBundle bundle, String aTextName, Locale locale, String defaultMessage, Object[] args,
|
||||
ValueStack valueStack) {
|
||||
try {
|
||||
reloadBundles(valueStack.getContext());
|
||||
|
||||
String message = TextParseUtil.translateVariables(bundle.getString(aTextName), valueStack);
|
||||
MessageFormat mf = buildMessageFormat(message, locale);
|
||||
|
||||
return formatWithNullDetection(mf, args);
|
||||
} catch (MissingResourceException ex) {
|
||||
if (devMode) {
|
||||
LOG.warn("Missing key [{}] in bundle [{}]!", aTextName, bundle);
|
||||
} else {
|
||||
LOG.debug("Missing key [{}] in bundle [{}]!", aTextName, bundle);
|
||||
}
|
||||
}
|
||||
|
||||
GetDefaultMessageReturnArg result = getDefaultMessage(aTextName, locale, valueStack, args, defaultMessage);
|
||||
if (unableToFindTextForKey(result)) {
|
||||
LOG.warn("Unable to find text for key '{}' in ResourceBundles for locale '{}'", aTextName, locale);
|
||||
}
|
||||
return result != null ? result.message : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param classLoader a {@link ClassLoader} to look up the bundle from if none can be found on the current thread's classloader
|
||||
*/
|
||||
public void setDelegatedClassLoader(final ClassLoader classLoader) {
|
||||
synchronized (bundlesMap) {
|
||||
delegatedClassLoaderMap.put(getCurrentThreadContextClassLoader().hashCode(), classLoader);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bundleName Removes the bundle from any cached "misses"
|
||||
*/
|
||||
public void clearBundle(final String bundleName) {
|
||||
bundlesMap.remove(getCurrentThreadContextClassLoader().hashCode() + bundleName);
|
||||
}
|
||||
|
||||
protected void reloadBundles() {
|
||||
reloadBundles(ActionContext.getContext() != null ? ActionContext.getContext().getContextMap() : null);
|
||||
}
|
||||
|
||||
protected void reloadBundles(Map<String, Object> context) {
|
||||
if (reloadBundles) {
|
||||
try {
|
||||
Boolean reloaded;
|
||||
if (context != null) {
|
||||
reloaded = (Boolean) ObjectUtils.defaultIfNull(context.get(RELOADED), Boolean.FALSE);
|
||||
} else {
|
||||
reloaded = Boolean.FALSE;
|
||||
}
|
||||
if (!reloaded) {
|
||||
bundlesMap.clear();
|
||||
try {
|
||||
clearMap(ResourceBundle.class, null, "cacheList");
|
||||
} catch (NoSuchFieldException e) {
|
||||
// happens in IBM JVM, that has a different ResourceBundle impl
|
||||
// it has a 'cache' member
|
||||
clearMap(ResourceBundle.class, null, "cache");
|
||||
}
|
||||
|
||||
// now, for the true and utter hack, if we're running in tomcat, clear
|
||||
// it's class loader resource cache as well.
|
||||
clearTomcatCache();
|
||||
if (context != null) {
|
||||
context.put(RELOADED, true);
|
||||
}
|
||||
LOG.debug("Resource bundles reloaded");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("Could not reload resource bundles", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void clearTomcatCache() {
|
||||
ClassLoader loader = getCurrentThreadContextClassLoader();
|
||||
// no need for compilation here.
|
||||
Class cl = loader.getClass();
|
||||
|
||||
try {
|
||||
if ("org.apache.catalina.loader.WebappClassLoader".equals(cl.getName())) {
|
||||
clearMap(cl, loader, TOMCAT_RESOURCE_ENTRIES_FIELD);
|
||||
} else {
|
||||
LOG.debug("Class loader {} is not tomcat loader.", cl.getName());
|
||||
}
|
||||
} catch (NoSuchFieldException nsfe) {
|
||||
if ("org.apache.catalina.loader.WebappClassLoaderBase".equals(cl.getSuperclass().getName())) {
|
||||
LOG.debug("Base class {} doesn't contain '{}' field, trying with parent!", cl.getName(), TOMCAT_RESOURCE_ENTRIES_FIELD, nsfe);
|
||||
try {
|
||||
clearMap(cl.getSuperclass(), loader, TOMCAT_RESOURCE_ENTRIES_FIELD);
|
||||
} catch (Exception e) {
|
||||
LOG.warn("Couldn't clear tomcat cache using {}", cl.getSuperclass().getName(), e);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.warn("Couldn't clear tomcat cache", cl.getName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private void clearMap(Class cl, Object obj, String name)
|
||||
throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
|
||||
|
||||
Field field = cl.getDeclaredField(name);
|
||||
field.setAccessible(true);
|
||||
|
||||
Object cache = field.get(obj);
|
||||
|
||||
synchronized (cache) {
|
||||
Class ccl = cache.getClass();
|
||||
Method clearMethod = ccl.getMethod("clear");
|
||||
clearMethod.invoke(cache);
|
||||
}
|
||||
}
|
||||
|
||||
protected MessageFormat buildMessageFormat(String pattern, Locale locale) {
|
||||
MessageFormatKey key = new MessageFormatKey(pattern, locale);
|
||||
MessageFormat format = messageFormats.get(key);
|
||||
if (format == null) {
|
||||
format = new MessageFormat(pattern);
|
||||
format.setLocale(locale);
|
||||
format.applyPattern(pattern);
|
||||
messageFormats.put(key, format);
|
||||
}
|
||||
|
||||
return format;
|
||||
}
|
||||
|
||||
protected String formatWithNullDetection(MessageFormat mf, Object[] args) {
|
||||
String message = mf.format(args);
|
||||
if ("null".equals(message)) {
|
||||
return null;
|
||||
} else {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_I18N_RELOAD, required = false)
|
||||
public void setReloadBundles(String reloadBundles) {
|
||||
this.reloadBundles = Boolean.parseBoolean(reloadBundles);
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_DEVMODE, required = false)
|
||||
public void setDevMode(String devMode) {
|
||||
this.devMode = Boolean.parseBoolean(devMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the given resource bundle by it's name.
|
||||
* <p>
|
||||
* Will use <code>Thread.currentThread().getContextClassLoader()</code> as the classloader.
|
||||
* </p>
|
||||
*
|
||||
* @param aBundleName the name of the bundle (usually it's FQN classname).
|
||||
* @param locale the locale.
|
||||
* @return the bundle, <tt>null</tt> if not found.
|
||||
*/
|
||||
@Override
|
||||
public ResourceBundle findResourceBundle(String aBundleName, Locale locale) {
|
||||
ClassLoader classLoader = getCurrentThreadContextClassLoader();
|
||||
String key = createMissesKey(String.valueOf(classLoader.hashCode()), aBundleName, locale);
|
||||
|
||||
if (missingBundles.contains(key)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ResourceBundle bundle = null;
|
||||
try {
|
||||
if (bundlesMap.containsKey(key)) {
|
||||
bundle = bundlesMap.get(key);
|
||||
} else {
|
||||
bundle = ResourceBundle.getBundle(aBundleName, locale, classLoader);
|
||||
bundlesMap.putIfAbsent(key, bundle);
|
||||
}
|
||||
} catch (MissingResourceException ex) {
|
||||
if (delegatedClassLoaderMap.containsKey(classLoader.hashCode())) {
|
||||
try {
|
||||
if (bundlesMap.containsKey(key)) {
|
||||
bundle = bundlesMap.get(key);
|
||||
} else {
|
||||
bundle = ResourceBundle.getBundle(aBundleName, locale, delegatedClassLoaderMap.get(classLoader.hashCode()));
|
||||
bundlesMap.putIfAbsent(key, bundle);
|
||||
}
|
||||
} catch (MissingResourceException e) {
|
||||
LOG.debug("Missing resource bundle [{}]!", aBundleName, e);
|
||||
missingBundles.add(key);
|
||||
}
|
||||
} else {
|
||||
LOG.debug("Missing resource bundle [{}]!", aBundleName);
|
||||
missingBundles.add(key);
|
||||
}
|
||||
}
|
||||
return bundle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all the internal lists.
|
||||
*
|
||||
* @deprecated used only in tests
|
||||
*/
|
||||
@Deprecated
|
||||
public void reset() {
|
||||
// no-op
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if we found the text in the bundles.
|
||||
*
|
||||
* @param result the result so far
|
||||
* @return <tt>true</tt> if we could <b>not</b> find the text, <tt>false</tt> if the text was found (=success).
|
||||
*/
|
||||
protected boolean unableToFindTextForKey(GetDefaultMessageReturnArg result) {
|
||||
if (result == null || result.message == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// did we find it in the bundle, then no problem?
|
||||
if (result.foundInBundle) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// not found in bundle
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a key to used for lookup/storing in the bundle misses cache.
|
||||
*
|
||||
* @param prefix the prefix for the returning String - it is supposed to be the ClassLoader hash code.
|
||||
* @param aBundleName the name of the bundle (usually it's FQN classname).
|
||||
* @param locale the locale.
|
||||
* @return the key to use for lookup/storing in the bundle misses cache.
|
||||
*/
|
||||
private String createMissesKey(String prefix, String aBundleName, Locale locale) {
|
||||
return prefix + aBundleName + "_" + locale.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the default message.
|
||||
*/
|
||||
protected GetDefaultMessageReturnArg getDefaultMessage(String key, Locale locale, ValueStack valueStack, Object[] args,
|
||||
String defaultMessage) {
|
||||
GetDefaultMessageReturnArg result = null;
|
||||
boolean found = true;
|
||||
|
||||
if (key != null) {
|
||||
String message = findDefaultText(key, locale);
|
||||
|
||||
if (message == null) {
|
||||
message = defaultMessage;
|
||||
found = false; // not found in bundles
|
||||
}
|
||||
|
||||
// defaultMessage may be null
|
||||
if (message != null) {
|
||||
MessageFormat mf = buildMessageFormat(TextParseUtil.translateVariables(message, valueStack), locale);
|
||||
|
||||
String msg = formatWithNullDetection(mf, args);
|
||||
result = new GetDefaultMessageReturnArg(msg, found);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the message from the named resource bundle.
|
||||
*/
|
||||
protected String getMessage(String bundleName, Locale locale, String key, ValueStack valueStack, Object[] args) {
|
||||
ResourceBundle bundle = findResourceBundle(bundleName, locale);
|
||||
if (bundle == null) {
|
||||
return null;
|
||||
}
|
||||
if (valueStack != null)
|
||||
reloadBundles(valueStack.getContext());
|
||||
try {
|
||||
String message = bundle.getString(key);
|
||||
if (valueStack != null)
|
||||
message = TextParseUtil.translateVariables(bundle.getString(key), valueStack);
|
||||
MessageFormat mf = buildMessageFormat(message, locale);
|
||||
return formatWithNullDetection(mf, args);
|
||||
} catch (MissingResourceException e) {
|
||||
if (devMode) {
|
||||
LOG.warn("Missing key [{}] in bundle [{}]!", key, bundleName);
|
||||
} else {
|
||||
LOG.debug("Missing key [{}] in bundle [{}]!", key, bundleName);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverse up class hierarchy looking for message. Looks at class, then implemented interface,
|
||||
* before going up hierarchy.
|
||||
*
|
||||
* @return the message
|
||||
*/
|
||||
protected String findMessage(Class clazz, String key, String indexedKey, Locale locale, Object[] args, Set<String> checked,
|
||||
ValueStack valueStack) {
|
||||
if (checked == null) {
|
||||
checked = new TreeSet<>();
|
||||
} else if (checked.contains(clazz.getName())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// look in properties of this class
|
||||
String msg = getMessage(clazz.getName(), locale, key, valueStack, args);
|
||||
|
||||
if (msg != null) {
|
||||
return msg;
|
||||
}
|
||||
|
||||
if (indexedKey != null) {
|
||||
msg = getMessage(clazz.getName(), locale, indexedKey, valueStack, args);
|
||||
|
||||
if (msg != null) {
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
|
||||
// look in properties of implemented interfaces
|
||||
Class[] interfaces = clazz.getInterfaces();
|
||||
|
||||
for (Class anInterface : interfaces) {
|
||||
msg = getMessage(anInterface.getName(), locale, key, valueStack, args);
|
||||
|
||||
if (msg != null) {
|
||||
return msg;
|
||||
}
|
||||
|
||||
if (indexedKey != null) {
|
||||
msg = getMessage(anInterface.getName(), locale, indexedKey, valueStack, args);
|
||||
|
||||
if (msg != null) {
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// traverse up hierarchy
|
||||
if (clazz.isInterface()) {
|
||||
interfaces = clazz.getInterfaces();
|
||||
|
||||
for (Class anInterface : interfaces) {
|
||||
msg = findMessage(anInterface, key, indexedKey, locale, args, checked, valueStack);
|
||||
|
||||
if (msg != null) {
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!clazz.equals(Object.class) && !clazz.isPrimitive()) {
|
||||
return findMessage(clazz.getSuperclass(), key, indexedKey, locale, args, checked, valueStack);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static class MessageFormatKey {
|
||||
String pattern;
|
||||
Locale locale;
|
||||
|
||||
MessageFormatKey(String pattern, Locale locale) {
|
||||
this.pattern = pattern;
|
||||
this.locale = locale;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
MessageFormatKey that = (MessageFormatKey) o;
|
||||
|
||||
if (pattern != null ? !pattern.equals(that.pattern) : that.pattern != null) return false;
|
||||
return locale != null ? locale.equals(that.locale) : that.locale == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = pattern != null ? pattern.hashCode() : 0;
|
||||
result = 31 * result + (locale != null ? locale.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
static class GetDefaultMessageReturnArg {
|
||||
String message;
|
||||
boolean foundInBundle;
|
||||
|
||||
public GetDefaultMessageReturnArg(String message, boolean foundInBundle) {
|
||||
this.message = message;
|
||||
this.foundInBundle = foundInBundle;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,15 +15,10 @@
|
||||
*/
|
||||
package com.opensymphony.xwork2.util;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -48,8 +43,8 @@ public class AnnotationUtils {
|
||||
* Adds all fields with the specified Annotation of class clazz and its superclasses to allFields
|
||||
*
|
||||
* @param annotationClass the {@link Annotation}s to find
|
||||
* @param clazz The {@link Class} to inspect
|
||||
* @param allFields list of all fields
|
||||
* @param clazz The {@link Class} to inspect
|
||||
* @param allFields list of all fields
|
||||
*/
|
||||
public static void addAllFields(Class<? extends Annotation> annotationClass, Class clazz, List<Field> allFields) {
|
||||
|
||||
@@ -61,7 +56,7 @@ public class AnnotationUtils {
|
||||
|
||||
for (Field field : fields) {
|
||||
Annotation ann = field.getAnnotation(annotationClass);
|
||||
if (ann!=null) {
|
||||
if (ann != null) {
|
||||
allFields.add(field);
|
||||
}
|
||||
}
|
||||
@@ -72,8 +67,8 @@ public class AnnotationUtils {
|
||||
* Adds all methods with the specified Annotation of class clazz and its superclasses to allFields
|
||||
*
|
||||
* @param annotationClass the {@link Annotation}s to find
|
||||
* @param clazz The {@link Class} to inspect
|
||||
* @param allMethods list of all methods
|
||||
* @param clazz The {@link Class} to inspect
|
||||
* @param allMethods list of all methods
|
||||
*/
|
||||
public static void addAllMethods(Class<? extends Annotation> annotationClass, Class clazz, List<Method> allMethods) {
|
||||
|
||||
@@ -85,7 +80,7 @@ public class AnnotationUtils {
|
||||
|
||||
for (Method method : methods) {
|
||||
Annotation ann = method.getAnnotation(annotationClass);
|
||||
if (ann!=null) {
|
||||
if (ann != null) {
|
||||
allMethods.add(method);
|
||||
}
|
||||
}
|
||||
@@ -93,8 +88,7 @@ public class AnnotationUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param clazz The {@link Class} to inspect
|
||||
* @param clazz The {@link Class} to inspect
|
||||
* @param allInterfaces list of all interfaces
|
||||
*/
|
||||
public static void addAllInterfaces(Class clazz, List<Class> allInterfaces) {
|
||||
@@ -107,50 +101,6 @@ public class AnnotationUtils {
|
||||
addAllInterfaces(clazz.getSuperclass(), allInterfaces);
|
||||
}
|
||||
|
||||
/**
|
||||
* For the given <code>Class</code> get a collection of the the {@link AnnotatedElement}s
|
||||
* that match the given <code>annotation</code>s or if no <code>annotation</code>s are
|
||||
* specified then return all of the annotated elements of the given <code>Class</code>.
|
||||
* Includes only the method level annotations.
|
||||
*
|
||||
* @param clazz The {@link Class} to inspect
|
||||
* @param annotation the {@link Annotation}s to find
|
||||
* @return A {@link Collection}<{@link AnnotatedElement}> containing all of the
|
||||
* method {@link AnnotatedElement}s matching the specified {@link Annotation}s
|
||||
*/
|
||||
public static Collection<Method> getAnnotatedMethods(Class clazz, Class<? extends Annotation>... annotation){
|
||||
Collection<Method> toReturn = new HashSet<>();
|
||||
|
||||
for (Method m : clazz.getMethods()) {
|
||||
if (ArrayUtils.isNotEmpty(annotation) && isAnnotatedBy(m, annotation)) {
|
||||
toReturn.add(m);
|
||||
} else if (ArrayUtils.isEmpty(annotation) && ArrayUtils.isNotEmpty(m.getAnnotations())) {
|
||||
toReturn.add(m);
|
||||
}
|
||||
}
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Varargs version of <code>AnnotatedElement.isAnnotationPresent()</code>
|
||||
* @param annotatedElement element to check
|
||||
* @param annotation the {@link Annotation}s to find
|
||||
* @return true is element is annotated by one of the annotation
|
||||
* @see AnnotatedElement
|
||||
*/
|
||||
public static boolean isAnnotatedBy(AnnotatedElement annotatedElement, Class<? extends Annotation>... annotation) {
|
||||
if (ArrayUtils.isEmpty(annotation)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for( Class<? extends Annotation> c : annotation ){
|
||||
if( annotatedElement.isAnnotationPresent(c) ) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the property name for a method.
|
||||
* This method is independent from property fields.
|
||||
@@ -179,10 +129,10 @@ public class AnnotationUtils {
|
||||
* Returns the annotation on the given class or the package of the class. This searchs up the
|
||||
* class hierarchy and the package hierarchy for the closest match.
|
||||
*
|
||||
* @param <T> class type
|
||||
* @param clazz The class to search for the annotation.
|
||||
* @param annotationClass The Class of the annotation.
|
||||
* @return The annotation or null.
|
||||
* @param <T> class type
|
||||
* @param clazz The class to search for the annotation.
|
||||
* @param annotationClass The Class of the annotation.
|
||||
* @return The annotation or null.
|
||||
*/
|
||||
public static <T extends Annotation> T findAnnotation(Class<?> clazz, Class<T> annotationClass) {
|
||||
T ann = clazz.getAnnotation(annotationClass);
|
||||
|
||||
@@ -20,12 +20,16 @@ package com.opensymphony.xwork2.util;
|
||||
import com.opensymphony.xwork2.XWorkException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.HashMap;
|
||||
import java.util.Vector;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
/**
|
||||
* This class is an utility class that will search through the classpath
|
||||
@@ -88,10 +92,24 @@ public class ClassPathFinder {
|
||||
} catch (URISyntaxException e) {
|
||||
continue;
|
||||
}
|
||||
File entry = new File(entryURI) ;
|
||||
Vector<String> results = checkEntries(entry.list(), entry, "");
|
||||
if (results != null ) {
|
||||
matches.addAll(results);
|
||||
File entry = new File(entryURI);
|
||||
if (entry.isFile() && entry.toString().endsWith(".jar")) {
|
||||
try {
|
||||
ZipInputStream zip = new ZipInputStream(new FileInputStream(entry));
|
||||
for (ZipEntry zipEntry = zip.getNextEntry(); zipEntry != null; zipEntry = zip.getNextEntry()) {
|
||||
boolean doesMatch = patternMatcher.match(new HashMap<String, String>(), zipEntry.getName(), compiledPattern);
|
||||
if (doesMatch) {
|
||||
matches.add(zipEntry.getName());
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
Vector<String> results = checkEntries(entry.list(), entry, "");
|
||||
if (results != null) {
|
||||
matches.addAll(results);
|
||||
}
|
||||
}
|
||||
}
|
||||
return matches;
|
||||
|
||||
@@ -0,0 +1,260 @@
|
||||
/*
|
||||
* $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.
|
||||
*/
|
||||
|
||||
package com.opensymphony.xwork2.util;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.ModelDriven;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
/**
|
||||
* Provides support for localization in the framework, it can be used to read only default bundles,
|
||||
* or it can search the class hierarchy to find proper bundles.
|
||||
*/
|
||||
public class GlobalLocalizedTextProvider extends AbstractLocalizedTextProvider {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(GlobalLocalizedTextProvider.class);
|
||||
|
||||
public GlobalLocalizedTextProvider() {
|
||||
addDefaultResourceBundle(XWORK_MESSAGES_BUNDLE);
|
||||
addDefaultResourceBundle(STRUTS_MESSAGES_BUNDLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link #findText(Class aClass, String aTextName, Locale locale, String defaultMessage, Object[] args)}
|
||||
* with aTextName as the default message.
|
||||
*
|
||||
* @param aClass class name
|
||||
* @param aTextName text name
|
||||
* @param locale the locale
|
||||
* @return the localized text, or null if none can be found and no defaultMessage is provided
|
||||
* @see #findText(Class aClass, String aTextName, Locale locale, String defaultMessage, Object[] args)
|
||||
*/
|
||||
@Override
|
||||
public String findText(Class aClass, String aTextName, Locale locale) {
|
||||
return findText(aClass, aTextName, locale, aTextName, new Object[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Finds a localized text message for the given key, aTextName. Both the key and the message
|
||||
* itself is evaluated as required. The following algorithm is used to find the requested
|
||||
* message:
|
||||
* </p>
|
||||
*
|
||||
* <ol>
|
||||
* <li>Look for message in aClass' class hierarchy.
|
||||
* <ol>
|
||||
* <li>Look for the message in a resource bundle for aClass</li>
|
||||
* <li>If not found, look for the message in a resource bundle for any implemented interface</li>
|
||||
* <li>If not found, traverse up the Class' hierarchy and repeat from the first sub-step</li>
|
||||
* </ol></li>
|
||||
* <li>If not found and aClass is a {@link ModelDriven} Action, then look for message in
|
||||
* the model's class hierarchy (repeat sub-steps listed above).</li>
|
||||
* <li>If not found, look for message in child property. This is determined by evaluating
|
||||
* the message key as an OGNL expression. For example, if the key is
|
||||
* <i>user.address.state</i>, then it will attempt to see if "user" can be resolved into an
|
||||
* object. If so, repeat the entire process fromthe beginning with the object's class as
|
||||
* aClass and "address.state" as the message key.</li>
|
||||
* <li>If not found, look for the message in aClass' package hierarchy.</li>
|
||||
* <li>If still not found, look for the message in the default resource bundles.</li>
|
||||
* <li>Return defaultMessage</li>
|
||||
* </ol>
|
||||
*
|
||||
* <p>
|
||||
* When looking for the message, if the key indexes a collection (e.g. user.phone[0]) and a
|
||||
* message for that specific key cannot be found, the general form will also be looked up
|
||||
* (i.e. user.phone[*]).
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If a message is found, it will also be interpolated. Anything within <code>${...}</code>
|
||||
* will be treated as an OGNL expression and evaluated as such.
|
||||
* </p>
|
||||
*
|
||||
* @param aClass the class whose name to use as the start point for the search
|
||||
* @param aTextName the key to find the text message for
|
||||
* @param locale the locale the message should be for
|
||||
* @param defaultMessage the message to be returned if no text message can be found in any
|
||||
* resource bundle
|
||||
* @param args arguments
|
||||
* resource bundle
|
||||
* @return the localized text, or null if none can be found and no defaultMessage is provided
|
||||
*/
|
||||
@Override
|
||||
public String findText(Class aClass, String aTextName, Locale locale, String defaultMessage, Object[] args) {
|
||||
ValueStack valueStack = ActionContext.getContext().getValueStack();
|
||||
return findText(aClass, aTextName, locale, defaultMessage, args, valueStack);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Finds a localized text message for the given key, aTextName. Both the key and the message
|
||||
* itself is evaluated as required. The following algorithm is used to find the requested
|
||||
* message:
|
||||
* </p>
|
||||
*
|
||||
* <ol>
|
||||
* <li>Look for message in aClass' class hierarchy.
|
||||
* <ol>
|
||||
* <li>Look for the message in a resource bundle for aClass</li>
|
||||
* <li>If not found, look for the message in a resource bundle for any implemented interface</li>
|
||||
* <li>If not found, traverse up the Class' hierarchy and repeat from the first sub-step</li>
|
||||
* </ol></li>
|
||||
* <li>If not found and aClass is a {@link ModelDriven} Action, then look for message in
|
||||
* the model's class hierarchy (repeat sub-steps listed above).</li>
|
||||
* <li>If not found, look for message in child property. This is determined by evaluating
|
||||
* the message key as an OGNL expression. For example, if the key is
|
||||
* <i>user.address.state</i>, then it will attempt to see if "user" can be resolved into an
|
||||
* object. If so, repeat the entire process fromthe beginning with the object's class as
|
||||
* aClass and "address.state" as the message key.</li>
|
||||
* <li>If not found, look for the message in aClass' package hierarchy.</li>
|
||||
* <li>If still not found, look for the message in the default resource bundles.</li>
|
||||
* <li>Return defaultMessage</li>
|
||||
* </ol>
|
||||
*
|
||||
* <p>
|
||||
* When looking for the message, if the key indexes a collection (e.g. user.phone[0]) and a
|
||||
* message for that specific key cannot be found, the general form will also be looked up
|
||||
* (i.e. user.phone[*]).
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If a message is found, it will also be interpolated. Anything within <code>${...}</code>
|
||||
* will be treated as an OGNL expression and evaluated as such.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If a message is <b>not</b> found a WARN log will be logged.
|
||||
* </p>
|
||||
*
|
||||
* @param aClass the class whose name to use as the start point for the search
|
||||
* @param aTextName the key to find the text message for
|
||||
* @param locale the locale the message should be for
|
||||
* @param defaultMessage the message to be returned if no text message can be found in any
|
||||
* resource bundle
|
||||
* @param args arguments
|
||||
* @param valueStack the value stack to use to evaluate expressions instead of the
|
||||
* one in the ActionContext ThreadLocal
|
||||
* @return the localized text, or null if none can be found and no defaultMessage is provided
|
||||
*/
|
||||
@Override
|
||||
public String findText(Class aClass, String aTextName, Locale locale, String defaultMessage, Object[] args, ValueStack valueStack) {
|
||||
String indexedTextName = null;
|
||||
if (aTextName == null) {
|
||||
LOG.warn("Trying to find text with null key!");
|
||||
aTextName = "";
|
||||
}
|
||||
// calculate indexedTextName (collection[*]) if applicable
|
||||
if (aTextName.contains("[")) {
|
||||
int i = -1;
|
||||
|
||||
indexedTextName = aTextName;
|
||||
|
||||
while ((i = indexedTextName.indexOf("[", i + 1)) != -1) {
|
||||
int j = indexedTextName.indexOf("]", i);
|
||||
String a = indexedTextName.substring(0, i);
|
||||
String b = indexedTextName.substring(j);
|
||||
indexedTextName = a + "[*" + b;
|
||||
}
|
||||
}
|
||||
|
||||
// get default
|
||||
GetDefaultMessageReturnArg result;
|
||||
if (indexedTextName == null) {
|
||||
result = getDefaultMessage(aTextName, locale, valueStack, args, defaultMessage);
|
||||
} else {
|
||||
result = getDefaultMessage(aTextName, locale, valueStack, args, null);
|
||||
if (result != null && result.message != null) {
|
||||
return result.message;
|
||||
}
|
||||
result = getDefaultMessage(indexedTextName, locale, valueStack, args, defaultMessage);
|
||||
}
|
||||
|
||||
// could we find the text, if not log a warn
|
||||
if (unableToFindTextForKey(result) && LOG.isDebugEnabled()) {
|
||||
String warn = "Unable to find text for key '" + aTextName + "' ";
|
||||
if (indexedTextName != null) {
|
||||
warn += " or indexed key '" + indexedTextName + "' ";
|
||||
}
|
||||
warn += "in class '" + aClass.getName() + "' and locale '" + locale + "'";
|
||||
LOG.debug(warn);
|
||||
}
|
||||
|
||||
return result != null ? result.message : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Finds a localized text message for the given key, aTextName, in the specified resource bundle
|
||||
* with aTextName as the default message.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If a message is found, it will also be interpolated. Anything within <code>${...}</code>
|
||||
* will be treated as an OGNL expression and evaluated as such.
|
||||
* </p>
|
||||
*
|
||||
* @param bundle a resource bundle name
|
||||
* @param aTextName text name
|
||||
* @param locale the locale
|
||||
* @return the localized text, or null if none can be found and no defaultMessage is provided
|
||||
* @see #findText(ResourceBundle, String, Locale, String, Object[])
|
||||
*/
|
||||
@Override
|
||||
public String findText(ResourceBundle bundle, String aTextName, Locale locale) {
|
||||
return findText(bundle, aTextName, locale, aTextName, new Object[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Finds a localized text message for the given key, aTextName, in the specified resource
|
||||
* bundle.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If a message is found, it will also be interpolated. Anything within <code>${...}</code>
|
||||
* will be treated as an OGNL expression and evaluated as such.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If a message is <b>not</b> found a WARN log will be logged.
|
||||
* </p>
|
||||
*
|
||||
* @param bundle the bundle
|
||||
* @param aTextName the key
|
||||
* @param locale the locale
|
||||
* @param defaultMessage the default message to use if no message was found in the bundle
|
||||
* @param args arguments for the message formatter.
|
||||
* @return the localized text, or null if none can be found and no defaultMessage is provided
|
||||
*/
|
||||
@Override
|
||||
public String findText(ResourceBundle bundle, String aTextName, Locale locale, String defaultMessage, Object[] args) {
|
||||
ValueStack valueStack = ActionContext.getContext().getValueStack();
|
||||
return findText(bundle, aTextName, locale, defaultMessage, args, valueStack);
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* Copyright 2017 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.util;
|
||||
|
||||
import org.apache.commons.lang3.reflect.ConstructorUtils;
|
||||
import org.apache.commons.lang3.reflect.FieldUtils;
|
||||
import org.apache.commons.lang3.reflect.MethodUtils;
|
||||
|
||||
import java.lang.reflect.*;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* <code>ProxyUtil</code>
|
||||
* <p>
|
||||
* Various utility methods dealing with proxies
|
||||
* </p>
|
||||
*
|
||||
*/
|
||||
public class ProxyUtil {
|
||||
private static final String SPRING_ADVISED_CLASS_NAME = "org.springframework.aop.framework.Advised";
|
||||
private static final String SPRING_SPRINGPROXY_CLASS_NAME = "org.springframework.aop.SpringProxy";
|
||||
private static final String SPRING_SINGLETONTARGETSOURCE_CLASS_NAME = "org.springframework.aop.target.SingletonTargetSource";
|
||||
private static final String SPRING_TARGETCLASSAWARE_CLASS_NAME = "org.springframework.aop.TargetClassAware";
|
||||
|
||||
private static final Map<Class<?>, Boolean> isProxyCache =
|
||||
new ConcurrentHashMap<>(256);
|
||||
private static final Map<Member, Boolean> isProxyMemberCache =
|
||||
new ConcurrentHashMap<>(256);
|
||||
|
||||
/**
|
||||
* Determine the ultimate target class of the given instance, traversing
|
||||
* not only a top-level proxy but any number of nested proxies as well —
|
||||
* as long as possible without side effects.
|
||||
* @param candidate the instance to check (might be a proxy)
|
||||
* @return the ultimate target class (or the plain class of the given
|
||||
* object as fallback; never {@code null})
|
||||
*/
|
||||
public static Class<?> ultimateTargetClass(Object candidate) {
|
||||
Class<?> result = null;
|
||||
if (isSpringAopProxy(candidate))
|
||||
result = springUltimateTargetClass(candidate);
|
||||
|
||||
if (result == null) {
|
||||
result = candidate.getClass();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given object is a proxy.
|
||||
* @param object the object to check
|
||||
*/
|
||||
public static boolean isProxy(Object object) {
|
||||
Class<?> clazz = object.getClass();
|
||||
Boolean flag = isProxyCache.get(clazz);
|
||||
if (flag != null) {
|
||||
return flag;
|
||||
}
|
||||
|
||||
boolean isProxy = isSpringAopProxy(object);
|
||||
|
||||
isProxyCache.put(clazz, isProxy);
|
||||
return isProxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given member is a proxy member of a proxy object.
|
||||
* @param member the member to check
|
||||
* @param object the object to check
|
||||
*/
|
||||
public static boolean isProxyMember(Member member, Object object) {
|
||||
if (!isProxy(object))
|
||||
return false;
|
||||
|
||||
Boolean flag = isProxyMemberCache.get(member);
|
||||
if (flag != null) {
|
||||
return flag;
|
||||
}
|
||||
|
||||
boolean isProxyMember = isSpringProxyMember(member);
|
||||
|
||||
isProxyMemberCache.put(member, isProxyMember);
|
||||
return isProxyMember;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the ultimate target class of the given spring bean instance, traversing
|
||||
* not only a top-level spring proxy but any number of nested spring proxies as well —
|
||||
* as long as possible without side effects, that is, just for singleton targets.
|
||||
* @param candidate the instance to check (might be a spring AOP proxy)
|
||||
* @return the ultimate target class (or the plain class of the given
|
||||
* object as fallback; never {@code null})
|
||||
*/
|
||||
private static Class<?> springUltimateTargetClass(Object candidate) {
|
||||
Object current = candidate;
|
||||
Class<?> result = null;
|
||||
while (null != current && implementsInterface(current.getClass(), SPRING_TARGETCLASSAWARE_CLASS_NAME)) {
|
||||
try {
|
||||
result = (Class<?>) MethodUtils.invokeMethod(current, "getTargetClass");
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
current = getSingletonTarget(current);
|
||||
}
|
||||
if (result == null) {
|
||||
Class<?> clazz = candidate.getClass();
|
||||
result = (isCglibProxyClass(clazz) ? clazz.getSuperclass() : candidate.getClass());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given object is a Spring proxy.
|
||||
* @param object the object to check
|
||||
*/
|
||||
private static boolean isSpringAopProxy(Object object) {
|
||||
Class<?> clazz = object.getClass();
|
||||
return (implementsInterface(clazz, SPRING_SPRINGPROXY_CLASS_NAME) && (Proxy.isProxyClass(clazz)
|
||||
|| isCglibProxyClass(clazz)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given member is a member of a spring proxy.
|
||||
* @param member the member to check
|
||||
*/
|
||||
private static boolean isSpringProxyMember(Member member) {
|
||||
try {
|
||||
Class<?> clazz = ClassLoaderUtil.loadClass(SPRING_ADVISED_CLASS_NAME, ProxyUtil.class);
|
||||
if (hasMember(clazz, member))
|
||||
return true;
|
||||
clazz = ClassLoaderUtil.loadClass(SPRING_TARGETCLASSAWARE_CLASS_NAME, ProxyUtil.class);
|
||||
if (hasMember(clazz, member))
|
||||
return true;
|
||||
clazz = ClassLoaderUtil.loadClass(SPRING_SPRINGPROXY_CLASS_NAME, ProxyUtil.class);
|
||||
if (hasMember(clazz, member))
|
||||
return true;
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the singleton target object behind the given spring proxy, if any.
|
||||
* @param candidate the (potential) spring proxy to check
|
||||
* @return the singleton target object, or {@code null} in any other case
|
||||
* (not a spring proxy, not an existing singleton target)
|
||||
*/
|
||||
private static Object getSingletonTarget(Object candidate) {
|
||||
try {
|
||||
if (implementsInterface(candidate.getClass(), SPRING_ADVISED_CLASS_NAME)) {
|
||||
Object targetSource = MethodUtils.invokeMethod(candidate, "getTargetSource");
|
||||
if (implementsInterface(targetSource.getClass(), SPRING_SINGLETONTARGETSOURCE_CLASS_NAME)) {
|
||||
return MethodUtils.invokeMethod(targetSource, "getTarget");
|
||||
}
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the specified class is a CGLIB-generated class.
|
||||
* @param clazz the class to check
|
||||
*/
|
||||
private static boolean isCglibProxyClass(Class<?> clazz) {
|
||||
return (clazz != null && clazz.getName().contains("$$"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given class implements an interface with a given class name.
|
||||
* @param clazz the class to check
|
||||
* @param ifaceClassName the interface class name to check
|
||||
*/
|
||||
private static boolean implementsInterface(Class<?> clazz, String ifaceClassName) {
|
||||
try {
|
||||
Class<?> ifaceClass = ClassLoaderUtil.loadClass(ifaceClassName, ProxyUtil.class);
|
||||
return ifaceClass.isAssignableFrom(clazz);
|
||||
} catch (ClassNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given class has a given member.
|
||||
* @param clazz the class to check
|
||||
* @param member the member to check
|
||||
*/
|
||||
private static boolean hasMember(Class<?> clazz, Member member) {
|
||||
if (member instanceof Method) {
|
||||
return null != MethodUtils.getMatchingMethod(clazz, member.getName(), ((Method) member).getParameterTypes());
|
||||
}
|
||||
if (member instanceof Field) {
|
||||
return null != FieldUtils.getField(clazz, member.getName(), true);
|
||||
}
|
||||
if (member instanceof Constructor) {
|
||||
return null != ConstructorUtils.getMatchingAccessibleConstructor(clazz, ((Constructor) member).getParameterTypes());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,422 @@
|
||||
/*
|
||||
* $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.
|
||||
*/
|
||||
|
||||
package com.opensymphony.xwork2.util;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.ActionInvocation;
|
||||
import com.opensymphony.xwork2.ModelDriven;
|
||||
import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
|
||||
import com.opensymphony.xwork2.util.reflection.ReflectionProviderFactory;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Locale;
|
||||
import java.util.MissingResourceException;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
/**
|
||||
* Provides support for localization in the framework, it can be used to read only default bundles,
|
||||
* or it can search the class hierarchy to find proper bundles.
|
||||
*/
|
||||
public class StrutsLocalizedTextProvider extends AbstractLocalizedTextProvider {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(StrutsLocalizedTextProvider.class);
|
||||
|
||||
/**
|
||||
* Clears the internal list of resource bundles.
|
||||
*
|
||||
* @deprecated used only in tests
|
||||
*/
|
||||
@Deprecated
|
||||
public static void clearDefaultResourceBundles() {
|
||||
// no-op
|
||||
}
|
||||
|
||||
public StrutsLocalizedTextProvider() {
|
||||
addDefaultResourceBundle(XWORK_MESSAGES_BUNDLE);
|
||||
addDefaultResourceBundle(STRUTS_MESSAGES_BUNDLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a {@link java.util.Locale} from a String of the form en_US_foo into a Locale
|
||||
* with language "en", country "US" and variant "foo". This will parse the output of
|
||||
* {@link java.util.Locale#toString()}.
|
||||
*
|
||||
* @param localeStr The locale String to parse.
|
||||
* @param defaultLocale The locale to use if localeStr is <tt>null</tt>.
|
||||
* @return requested Locale
|
||||
* @deprecated please use {@link org.apache.commons.lang3.LocaleUtils#toLocale(String)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static Locale localeFromString(String localeStr, Locale defaultLocale) {
|
||||
if ((localeStr == null) || (localeStr.trim().length() == 0) || ("_".equals(localeStr))) {
|
||||
if (defaultLocale != null) {
|
||||
return defaultLocale;
|
||||
}
|
||||
return Locale.getDefault();
|
||||
}
|
||||
|
||||
int index = localeStr.indexOf('_');
|
||||
if (index < 0) {
|
||||
return new Locale(localeStr);
|
||||
}
|
||||
|
||||
String language = localeStr.substring(0, index);
|
||||
if (index == localeStr.length()) {
|
||||
return new Locale(language);
|
||||
}
|
||||
|
||||
localeStr = localeStr.substring(index + 1);
|
||||
index = localeStr.indexOf('_');
|
||||
if (index < 0) {
|
||||
return new Locale(language, localeStr);
|
||||
}
|
||||
|
||||
String country = localeStr.substring(0, index);
|
||||
if (index == localeStr.length()) {
|
||||
return new Locale(language, country);
|
||||
}
|
||||
|
||||
localeStr = localeStr.substring(index + 1);
|
||||
return new Locale(language, country, localeStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link #findText(Class aClass, String aTextName, Locale locale, String defaultMessage, Object[] args)}
|
||||
* with aTextName as the default message.
|
||||
*
|
||||
* @param aClass class name
|
||||
* @param aTextName text name
|
||||
* @param locale the locale
|
||||
* @return the localized text, or null if none can be found and no defaultMessage is provided
|
||||
* @see #findText(Class aClass, String aTextName, Locale locale, String defaultMessage, Object[] args)
|
||||
*/
|
||||
@Override
|
||||
public String findText(Class aClass, String aTextName, Locale locale) {
|
||||
return findText(aClass, aTextName, locale, aTextName, new Object[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Finds a localized text message for the given key, aTextName. Both the key and the message
|
||||
* itself is evaluated as required. The following algorithm is used to find the requested
|
||||
* message:
|
||||
* </p>
|
||||
*
|
||||
* <ol>
|
||||
* <li>Look for message in aClass' class hierarchy.
|
||||
* <ol>
|
||||
* <li>Look for the message in a resource bundle for aClass</li>
|
||||
* <li>If not found, look for the message in a resource bundle for any implemented interface</li>
|
||||
* <li>If not found, traverse up the Class' hierarchy and repeat from the first sub-step</li>
|
||||
* </ol></li>
|
||||
* <li>If not found and aClass is a {@link ModelDriven} Action, then look for message in
|
||||
* the model's class hierarchy (repeat sub-steps listed above).</li>
|
||||
* <li>If not found, look for message in child property. This is determined by evaluating
|
||||
* the message key as an OGNL expression. For example, if the key is
|
||||
* <i>user.address.state</i>, then it will attempt to see if "user" can be resolved into an
|
||||
* object. If so, repeat the entire process fromthe beginning with the object's class as
|
||||
* aClass and "address.state" as the message key.</li>
|
||||
* <li>If not found, look for the message in aClass' package hierarchy.</li>
|
||||
* <li>If still not found, look for the message in the default resource bundles.</li>
|
||||
* <li>Return defaultMessage</li>
|
||||
* </ol>
|
||||
*
|
||||
* <p>
|
||||
* When looking for the message, if the key indexes a collection (e.g. user.phone[0]) and a
|
||||
* message for that specific key cannot be found, the general form will also be looked up
|
||||
* (i.e. user.phone[*]).
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If a message is found, it will also be interpolated. Anything within <code>${...}</code>
|
||||
* will be treated as an OGNL expression and evaluated as such.
|
||||
* </p>
|
||||
*
|
||||
* @param aClass the class whose name to use as the start point for the search
|
||||
* @param aTextName the key to find the text message for
|
||||
* @param locale the locale the message should be for
|
||||
* @param defaultMessage the message to be returned if no text message can be found in any
|
||||
* resource bundle
|
||||
* @param args arguments
|
||||
* resource bundle
|
||||
* @return the localized text, or null if none can be found and no defaultMessage is provided
|
||||
*/
|
||||
@Override
|
||||
public String findText(Class aClass, String aTextName, Locale locale, String defaultMessage, Object[] args) {
|
||||
ValueStack valueStack = ActionContext.getContext().getValueStack();
|
||||
return findText(aClass, aTextName, locale, defaultMessage, args, valueStack);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Finds a localized text message for the given key, aTextName. Both the key and the message
|
||||
* itself is evaluated as required. The following algorithm is used to find the requested
|
||||
* message:
|
||||
* </p>
|
||||
*
|
||||
* <ol>
|
||||
* <li>Look for message in aClass' class hierarchy.
|
||||
* <ol>
|
||||
* <li>Look for the message in a resource bundle for aClass</li>
|
||||
* <li>If not found, look for the message in a resource bundle for any implemented interface</li>
|
||||
* <li>If not found, traverse up the Class' hierarchy and repeat from the first sub-step</li>
|
||||
* </ol></li>
|
||||
* <li>If not found and aClass is a {@link ModelDriven} Action, then look for message in
|
||||
* the model's class hierarchy (repeat sub-steps listed above).</li>
|
||||
* <li>If not found, look for message in child property. This is determined by evaluating
|
||||
* the message key as an OGNL expression. For example, if the key is
|
||||
* <i>user.address.state</i>, then it will attempt to see if "user" can be resolved into an
|
||||
* object. If so, repeat the entire process fromthe beginning with the object's class as
|
||||
* aClass and "address.state" as the message key.</li>
|
||||
* <li>If not found, look for the message in aClass' package hierarchy.</li>
|
||||
* <li>If still not found, look for the message in the default resource bundles.</li>
|
||||
* <li>Return defaultMessage</li>
|
||||
* </ol>
|
||||
*
|
||||
* <p>
|
||||
* When looking for the message, if the key indexes a collection (e.g. user.phone[0]) and a
|
||||
* message for that specific key cannot be found, the general form will also be looked up
|
||||
* (i.e. user.phone[*]).
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If a message is found, it will also be interpolated. Anything within <code>${...}</code>
|
||||
* will be treated as an OGNL expression and evaluated as such.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If a message is <b>not</b> found a WARN log will be logged.
|
||||
* </p>
|
||||
*
|
||||
* @param aClass the class whose name to use as the start point for the search
|
||||
* @param aTextName the key to find the text message for
|
||||
* @param locale the locale the message should be for
|
||||
* @param defaultMessage the message to be returned if no text message can be found in any
|
||||
* resource bundle
|
||||
* @param args arguments
|
||||
* @param valueStack the value stack to use to evaluate expressions instead of the
|
||||
* one in the ActionContext ThreadLocal
|
||||
* @return the localized text, or null if none can be found and no defaultMessage is provided
|
||||
*/
|
||||
@Override
|
||||
public String findText(Class aClass, String aTextName, Locale locale, String defaultMessage, Object[] args,
|
||||
ValueStack valueStack) {
|
||||
String indexedTextName = null;
|
||||
if (aTextName == null) {
|
||||
LOG.warn("Trying to find text with null key!");
|
||||
aTextName = "";
|
||||
}
|
||||
// calculate indexedTextName (collection[*]) if applicable
|
||||
if (aTextName.contains("[")) {
|
||||
int i = -1;
|
||||
|
||||
indexedTextName = aTextName;
|
||||
|
||||
while ((i = indexedTextName.indexOf("[", i + 1)) != -1) {
|
||||
int j = indexedTextName.indexOf("]", i);
|
||||
String a = indexedTextName.substring(0, i);
|
||||
String b = indexedTextName.substring(j);
|
||||
indexedTextName = a + "[*" + b;
|
||||
}
|
||||
}
|
||||
|
||||
// search up class hierarchy
|
||||
String msg = findMessage(aClass, aTextName, indexedTextName, locale, args, null, valueStack);
|
||||
|
||||
if (msg != null) {
|
||||
return msg;
|
||||
}
|
||||
|
||||
if (ModelDriven.class.isAssignableFrom(aClass)) {
|
||||
ActionContext context = ActionContext.getContext();
|
||||
// search up model's class hierarchy
|
||||
ActionInvocation actionInvocation = context.getActionInvocation();
|
||||
|
||||
// ActionInvocation may be null if we're being run from a Sitemesh filter, so we won't get model texts if this is null
|
||||
if (actionInvocation != null) {
|
||||
Object action = actionInvocation.getAction();
|
||||
if (action instanceof ModelDriven) {
|
||||
Object model = ((ModelDriven) action).getModel();
|
||||
if (model != null) {
|
||||
msg = findMessage(model.getClass(), aTextName, indexedTextName, locale, args, null, valueStack);
|
||||
if (msg != null) {
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// nothing still? alright, search the package hierarchy now
|
||||
for (Class clazz = aClass;
|
||||
(clazz != null) && !clazz.equals(Object.class);
|
||||
clazz = clazz.getSuperclass()) {
|
||||
|
||||
String basePackageName = clazz.getName();
|
||||
while (basePackageName.lastIndexOf('.') != -1) {
|
||||
basePackageName = basePackageName.substring(0, basePackageName.lastIndexOf('.'));
|
||||
String packageName = basePackageName + ".package";
|
||||
msg = getMessage(packageName, locale, aTextName, valueStack, args);
|
||||
|
||||
if (msg != null) {
|
||||
return msg;
|
||||
}
|
||||
|
||||
if (indexedTextName != null) {
|
||||
msg = getMessage(packageName, locale, indexedTextName, valueStack, args);
|
||||
|
||||
if (msg != null) {
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// see if it's a child property
|
||||
int idx = aTextName.indexOf(".");
|
||||
|
||||
if (idx != -1) {
|
||||
String newKey = null;
|
||||
String prop = null;
|
||||
|
||||
if (aTextName.startsWith(XWorkConverter.CONVERSION_ERROR_PROPERTY_PREFIX)) {
|
||||
idx = aTextName.indexOf(".", XWorkConverter.CONVERSION_ERROR_PROPERTY_PREFIX.length());
|
||||
|
||||
if (idx != -1) {
|
||||
prop = aTextName.substring(XWorkConverter.CONVERSION_ERROR_PROPERTY_PREFIX.length(), idx);
|
||||
newKey = XWorkConverter.CONVERSION_ERROR_PROPERTY_PREFIX + aTextName.substring(idx + 1);
|
||||
}
|
||||
} else {
|
||||
prop = aTextName.substring(0, idx);
|
||||
newKey = aTextName.substring(idx + 1);
|
||||
}
|
||||
|
||||
if (prop != null) {
|
||||
Object obj = valueStack.findValue(prop);
|
||||
try {
|
||||
Object actionObj = ReflectionProviderFactory.getInstance().getRealTarget(prop, valueStack.getContext(), valueStack.getRoot());
|
||||
if (actionObj != null) {
|
||||
PropertyDescriptor propertyDescriptor = ReflectionProviderFactory.getInstance().getPropertyDescriptor(actionObj.getClass(), prop);
|
||||
|
||||
if (propertyDescriptor != null) {
|
||||
Class clazz = propertyDescriptor.getPropertyType();
|
||||
|
||||
if (clazz != null) {
|
||||
if (obj != null) {
|
||||
valueStack.push(obj);
|
||||
}
|
||||
msg = findText(clazz, newKey, locale, null, args);
|
||||
if (obj != null) {
|
||||
valueStack.pop();
|
||||
}
|
||||
if (msg != null) {
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.debug("unable to find property {}", prop, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get default
|
||||
GetDefaultMessageReturnArg result;
|
||||
if (indexedTextName == null) {
|
||||
result = getDefaultMessage(aTextName, locale, valueStack, args, defaultMessage);
|
||||
} else {
|
||||
result = getDefaultMessage(aTextName, locale, valueStack, args, null);
|
||||
if (result != null && result.message != null) {
|
||||
return result.message;
|
||||
}
|
||||
result = getDefaultMessage(indexedTextName, locale, valueStack, args, defaultMessage);
|
||||
}
|
||||
|
||||
// could we find the text, if not log a warn
|
||||
if (unableToFindTextForKey(result) && LOG.isDebugEnabled()) {
|
||||
String warn = "Unable to find text for key '" + aTextName + "' ";
|
||||
if (indexedTextName != null) {
|
||||
warn += " or indexed key '" + indexedTextName + "' ";
|
||||
}
|
||||
warn += "in class '" + aClass.getName() + "' and locale '" + locale + "'";
|
||||
LOG.debug(warn);
|
||||
}
|
||||
|
||||
return result != null ? result.message : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Finds a localized text message for the given key, aTextName, in the specified resource bundle
|
||||
* with aTextName as the default message.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If a message is found, it will also be interpolated. Anything within <code>${...}</code>
|
||||
* will be treated as an OGNL expression and evaluated as such.
|
||||
* </p>
|
||||
*
|
||||
* @param bundle a resource bundle name
|
||||
* @param aTextName text name
|
||||
* @param locale the locale
|
||||
* @return the localized text, or null if none can be found and no defaultMessage is provided
|
||||
* @see #findText(java.util.ResourceBundle, String, java.util.Locale, String, Object[])
|
||||
*/
|
||||
@Override
|
||||
public String findText(ResourceBundle bundle, String aTextName, Locale locale) {
|
||||
return findText(bundle, aTextName, locale, aTextName, new Object[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Finds a localized text message for the given key, aTextName, in the specified resource
|
||||
* bundle.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If a message is found, it will also be interpolated. Anything within <code>${...}</code>
|
||||
* will be treated as an OGNL expression and evaluated as such.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If a message is <b>not</b> found a WARN log will be logged.
|
||||
* </p>
|
||||
*
|
||||
* @param bundle the bundle
|
||||
* @param aTextName the key
|
||||
* @param locale the locale
|
||||
* @param defaultMessage the default message to use if no message was found in the bundle
|
||||
* @param args arguments for the message formatter.
|
||||
* @return the localized text, or null if none can be found and no defaultMessage is provided
|
||||
*/
|
||||
@Override
|
||||
public String findText(ResourceBundle bundle, String aTextName, Locale locale, String defaultMessage, Object[] args) {
|
||||
ValueStack valueStack = ActionContext.getContext().getValueStack();
|
||||
return findText(bundle, aTextName, locale, defaultMessage, args, valueStack);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -31,8 +31,6 @@ import java.util.*;
|
||||
*/
|
||||
public class TextParseUtil {
|
||||
|
||||
private static final int MAX_RECURSION = 1;
|
||||
|
||||
/**
|
||||
* Converts all instances of ${...}, and %{...} in <code>expression</code> to the value returned
|
||||
* by a call to {@link ValueStack#findValue(java.lang.String)}. If an item cannot
|
||||
@@ -108,7 +106,7 @@ public class TextParseUtil {
|
||||
* @return Converted object from variable translation.
|
||||
*/
|
||||
public static Object translateVariables(char open, String expression, ValueStack stack, Class asType, ParsedValueEvaluator evaluator) {
|
||||
return translateVariables(new char[]{open} , expression, stack, asType, evaluator, MAX_RECURSION);
|
||||
return translateVariables(new char[]{open} , expression, stack, asType, evaluator, TextParser.DEFAULT_LOOP_COUNT);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -122,7 +120,7 @@ public class TextParseUtil {
|
||||
* @return Converted object from variable translation.
|
||||
*/
|
||||
public static Object translateVariables(char[] openChars, String expression, ValueStack stack, Class asType, ParsedValueEvaluator evaluator) {
|
||||
return translateVariables(openChars, expression, stack, asType, evaluator, MAX_RECURSION);
|
||||
return translateVariables(openChars, expression, stack, asType, evaluator, TextParser.DEFAULT_LOOP_COUNT);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -178,7 +176,7 @@ public class TextParseUtil {
|
||||
* @return converted objects
|
||||
*/
|
||||
public static Collection<String> translateVariablesCollection(String expression, ValueStack stack, boolean excludeEmptyElements, ParsedValueEvaluator evaluator) {
|
||||
return translateVariablesCollection(new char[]{'$', '%'}, expression, stack, excludeEmptyElements, evaluator, MAX_RECURSION);
|
||||
return translateVariablesCollection(new char[]{'$', '%'}, expression, stack, excludeEmptyElements, evaluator, TextParser.DEFAULT_LOOP_COUNT);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,6 +6,8 @@ package com.opensymphony.xwork2.util;
|
||||
*/
|
||||
public interface TextParser {
|
||||
|
||||
int DEFAULT_LOOP_COUNT = 1;
|
||||
|
||||
Object evaluate(char[] openChars, String expression, TextParseUtil.ParsedValueEvaluator evaluator, int maxLoopCount);
|
||||
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ import com.opensymphony.xwork2.util.location.LocatableProperties;
|
||||
public class XWorkTestCaseHelper {
|
||||
|
||||
public static ConfigurationManager setUp() throws Exception {
|
||||
ConfigurationManager configurationManager = new ConfigurationManager();
|
||||
ConfigurationManager configurationManager = new ConfigurationManager(Container.DEFAULT_NAME);
|
||||
configurationManager.addContainerProvider(new XWorkConfigurationProvider());
|
||||
Configuration config = configurationManager.getConfiguration();
|
||||
Container container = config.getContainer();
|
||||
@@ -40,7 +40,7 @@ public class XWorkTestCaseHelper {
|
||||
ActionContext.setContext(new ActionContext(stack.getContext()));
|
||||
|
||||
// clear out localization
|
||||
LocalizedTextUtil.reset();
|
||||
//container.getInstance(LocalizedTextUtil.class).reset();
|
||||
|
||||
|
||||
//ObjectFactory.setObjectFactory(container.getInstance(ObjectFactory.class));
|
||||
@@ -54,7 +54,7 @@ public class XWorkTestCaseHelper {
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Cannot clean old configuration", e);
|
||||
}
|
||||
configurationManager = new ConfigurationManager();
|
||||
configurationManager = new ConfigurationManager(Container.DEFAULT_NAME);
|
||||
configurationManager.addContainerProvider(new ContainerProvider() {
|
||||
public void destroy() {}
|
||||
public void init(Configuration configuration) throws ConfigurationException {}
|
||||
|
||||
@@ -2,11 +2,11 @@ package com.opensymphony.xwork2.util.fs;
|
||||
|
||||
import com.opensymphony.xwork2.FileManager;
|
||||
import com.opensymphony.xwork2.FileManagerFactory;
|
||||
import com.opensymphony.xwork2.XWorkConstants;
|
||||
import com.opensymphony.xwork2.inject.Container;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
@@ -19,6 +19,7 @@ public class DefaultFileManagerFactory implements FileManagerFactory {
|
||||
private static final Logger LOG = LogManager.getLogger(DefaultFileManagerFactory.class);
|
||||
|
||||
private boolean reloadingConfigs;
|
||||
private FileManagerHolder fileManagerHolder;
|
||||
private FileManager systemFileManager;
|
||||
private Container container;
|
||||
|
||||
@@ -32,20 +33,27 @@ public class DefaultFileManagerFactory implements FileManagerFactory {
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
@Inject(value = XWorkConstants.RELOAD_XML_CONFIGURATION, required = false)
|
||||
@Inject(value = StrutsConstants.STRUTS_CONFIGURATION_XML_RELOAD, required = false)
|
||||
public void setReloadingConfigs(String reloadingConfigs) {
|
||||
this.reloadingConfigs = Boolean.parseBoolean(reloadingConfigs);
|
||||
}
|
||||
|
||||
public FileManager getFileManager() {
|
||||
if (fileManagerHolder != null) {
|
||||
return fileManagerHolder.getFileManager();
|
||||
}
|
||||
|
||||
FileManager fileManager = lookupFileManager();
|
||||
if (fileManager != null) {
|
||||
LOG.debug("Using FileManager implementation [{}]", fileManager.getClass().getSimpleName());
|
||||
fileManager.setReloadingConfigs(reloadingConfigs);
|
||||
fileManagerHolder = new FileManagerHolder(fileManager);
|
||||
return fileManager;
|
||||
}
|
||||
|
||||
LOG.debug("Using default implementation of FileManager provided under name [system]: {}", systemFileManager.getClass().getSimpleName());
|
||||
systemFileManager.setReloadingConfigs(reloadingConfigs);
|
||||
fileManagerHolder = new FileManagerHolder(systemFileManager);
|
||||
return systemFileManager;
|
||||
}
|
||||
|
||||
@@ -77,4 +85,17 @@ public class DefaultFileManagerFactory implements FileManagerFactory {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static class FileManagerHolder {
|
||||
|
||||
private final FileManager fileManager;
|
||||
|
||||
public FileManagerHolder(FileManager fileManager) {
|
||||
this.fileManager = fileManager;
|
||||
}
|
||||
|
||||
public FileManager getFileManager() {
|
||||
return fileManager;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,7 +33,9 @@ import java.lang.reflect.Proxy;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:scott@atlassian.com">Scott Farquhar</a>
|
||||
* @deprecated will be dropped with next major release (2.6)
|
||||
*/
|
||||
@Deprecated
|
||||
public class ObjectProfiler {
|
||||
|
||||
/**
|
||||
@@ -135,6 +137,10 @@ public class ObjectProfiler {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated will be dropped with next major release (2.6)
|
||||
*/
|
||||
@Deprecated
|
||||
class TimerInvocationHandler implements InvocationHandler {
|
||||
protected Object target;
|
||||
|
||||
|
||||
@@ -35,7 +35,10 @@ import java.util.List;
|
||||
* @author <a href="mailto:mike@atlassian.com">Mike Cannon-Brookes</a>
|
||||
* @author <a href="mailto:scott@atlassian.com">Scott Farquhar</a>
|
||||
* @version $Date$ $Id$
|
||||
*
|
||||
* @deprecated will be dropped with next major release (2.6)
|
||||
*/
|
||||
@Deprecated
|
||||
public class ProfilingTimerBean implements java.io.Serializable {
|
||||
|
||||
private static final long serialVersionUID = -6180672043920208784L;
|
||||
|
||||
@@ -266,7 +266,10 @@ import org.apache.logging.log4j.Logger;
|
||||
* </p>
|
||||
*
|
||||
* <!-- END SNIPPET: profileLogFile -->
|
||||
*
|
||||
* @deprecated will be dropped with next major release (2.6)
|
||||
*/
|
||||
@Deprecated
|
||||
public class UtilTimerStack {
|
||||
|
||||
// A reference to the current ProfilingTimerBean
|
||||
|
||||
@@ -72,7 +72,23 @@ public interface ReflectionProvider {
|
||||
* note if exclusions AND inclusions are supplied and not null nothing will get copied.
|
||||
*/
|
||||
void copy(Object from, Object to, Map<String, Object> context, Collection<String> exclusions, Collection<String> inclusions);
|
||||
|
||||
|
||||
/**
|
||||
* Copies the properties in the object "from" and sets them in the object "to"
|
||||
* only setting properties defined in the given "editable" class (or interface)
|
||||
* using specified type converter, or {@link com.opensymphony.xwork2.conversion.impl.XWorkConverter} if none
|
||||
* is specified.
|
||||
*
|
||||
* @param from the source object
|
||||
* @param to the target object
|
||||
* @param context the action context we're running under
|
||||
* @param exclusions collection of method names to excluded from copying ( can be null)
|
||||
* @param inclusions collection of method names to included copying (can be null)
|
||||
* note if exclusions AND inclusions are supplied and not null nothing will get copied.
|
||||
* @param editable the class (or interface) to restrict property setting to
|
||||
*/
|
||||
void copy(Object from, Object to, Map<String, Object> context, Collection<String> exclusions, Collection<String> inclusions, Class<?> editable);
|
||||
|
||||
/**
|
||||
* Looks for the real target with the specified property given a root Object which may be a
|
||||
* CompoundRoot.
|
||||
|
||||
+7
-1
@@ -52,6 +52,7 @@ public class AnnotationActionValidatorManager implements ActionValidatorManager
|
||||
private ValidatorFileParser validatorFileParser;
|
||||
private FileManager fileManager;
|
||||
private boolean reloadingConfigs;
|
||||
private TextProviderFactory textProviderFactory;
|
||||
|
||||
@Inject
|
||||
public void setValidatorFactory(ValidatorFactory fac) {
|
||||
@@ -73,6 +74,11 @@ public class AnnotationActionValidatorManager implements ActionValidatorManager
|
||||
this.reloadingConfigs = Boolean.parseBoolean(reloadingConfigs);
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setTextProviderFactory(TextProviderFactory textProviderFactory) {
|
||||
this.textProviderFactory = textProviderFactory;
|
||||
}
|
||||
|
||||
public List<Validator> getValidators(Class clazz, String context) {
|
||||
return getValidators(clazz, context, null);
|
||||
}
|
||||
@@ -116,7 +122,7 @@ public class AnnotationActionValidatorManager implements ActionValidatorManager
|
||||
}
|
||||
|
||||
public void validate(Object object, String context, String method) throws ValidationException {
|
||||
ValidatorContext validatorContext = new DelegatingValidatorContext(object);
|
||||
ValidatorContext validatorContext = new DelegatingValidatorContext(object, textProviderFactory);
|
||||
validate(object, context, validatorContext, method);
|
||||
}
|
||||
|
||||
|
||||
+93
-30
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.opensymphony.xwork2.validator;
|
||||
|
||||
import com.opensymphony.xwork2.util.AnnotationUtils;
|
||||
import com.opensymphony.xwork2.validator.annotations.*;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@@ -24,8 +25,6 @@ import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* <code>AnnotationValidationConfigurationBuilder</code>
|
||||
@@ -36,9 +35,6 @@ import java.util.regex.Pattern;
|
||||
*/
|
||||
public class AnnotationValidationConfigurationBuilder {
|
||||
|
||||
private static final Pattern SETTER_PATTERN = Pattern.compile("set([A-Z][A-Za-z0-9]*)$");
|
||||
private static final Pattern GETTER_PATTERN = Pattern.compile("(get|is|has)([A-Z][A-Za-z0-9]*)$");
|
||||
|
||||
private ValidatorFactory validatorFactory;
|
||||
|
||||
public AnnotationValidationConfigurationBuilder(ValidatorFactory fac) {
|
||||
@@ -61,7 +57,7 @@ public class AnnotationValidationConfigurationBuilder {
|
||||
|
||||
if (o instanceof Method) {
|
||||
Method method = (Method) o;
|
||||
fieldName = resolvePropertyName(method);
|
||||
fieldName = AnnotationUtils.resolvePropertyName(method);
|
||||
methodName = method.getName();
|
||||
|
||||
annotations = method.getAnnotations();
|
||||
@@ -126,6 +122,14 @@ public class AnnotationValidationConfigurationBuilder {
|
||||
result.add(temp);
|
||||
}
|
||||
}
|
||||
// Process CrediCardValidator
|
||||
else if (a instanceof CreditCardValidator) {
|
||||
CreditCardValidator v = (CreditCardValidator) a;
|
||||
ValidatorConfig temp = processCreditCardValidatorAnnotation(v, fieldName, methodName);
|
||||
if (temp != null) {
|
||||
result.add(temp);
|
||||
}
|
||||
}
|
||||
// Process FieldExpressionValidator
|
||||
else if (a instanceof FieldExpressionValidator) {
|
||||
FieldExpressionValidator v = (FieldExpressionValidator) a;
|
||||
@@ -142,6 +146,14 @@ public class AnnotationValidationConfigurationBuilder {
|
||||
result.add(temp);
|
||||
}
|
||||
}
|
||||
// Process LongRangeFieldValidator
|
||||
else if (a instanceof LongRangeFieldValidator) {
|
||||
LongRangeFieldValidator v = (LongRangeFieldValidator) a;
|
||||
ValidatorConfig temp = processLongRangeFieldValidatorAnnotation(v, fieldName, methodName);
|
||||
if (temp != null) {
|
||||
result.add(temp);
|
||||
}
|
||||
}
|
||||
// Process ShortRangeFieldValidator
|
||||
else if (a instanceof ShortRangeFieldValidator) {
|
||||
ShortRangeFieldValidator v = (ShortRangeFieldValidator) a;
|
||||
@@ -267,6 +279,15 @@ public class AnnotationValidationConfigurationBuilder {
|
||||
}
|
||||
}
|
||||
}
|
||||
CreditCardValidator[] ccv = validations.creditCards();
|
||||
if (ccv != null) {
|
||||
for (CreditCardValidator v : ccv) {
|
||||
ValidatorConfig temp = processCreditCardValidatorAnnotation(v, fieldName, methodName);
|
||||
if (temp != null) {
|
||||
result.add(temp);
|
||||
}
|
||||
}
|
||||
}
|
||||
FieldExpressionValidator[] fev = validations.fieldExpressions();
|
||||
if (fev != null) {
|
||||
for (FieldExpressionValidator v : fev) {
|
||||
@@ -285,6 +306,15 @@ public class AnnotationValidationConfigurationBuilder {
|
||||
}
|
||||
}
|
||||
}
|
||||
LongRangeFieldValidator[] lrfv = validations.longRangeFields();
|
||||
if (irfv != null) {
|
||||
for (LongRangeFieldValidator v : lrfv) {
|
||||
ValidatorConfig temp = processLongRangeFieldValidatorAnnotation(v, fieldName, methodName);
|
||||
if (temp != null) {
|
||||
result.add(temp);
|
||||
}
|
||||
}
|
||||
}
|
||||
RegexFieldValidator[] rfv = validations.regexFields();
|
||||
if (rfv != null) {
|
||||
for (RegexFieldValidator v : rfv) {
|
||||
@@ -659,6 +689,41 @@ public class AnnotationValidationConfigurationBuilder {
|
||||
.build();
|
||||
}
|
||||
|
||||
private ValidatorConfig processLongRangeFieldValidatorAnnotation(LongRangeFieldValidator v, String fieldName, String methodName) {
|
||||
String validatorType = "long";
|
||||
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
|
||||
if (fieldName != null) {
|
||||
params.put("fieldName", fieldName);
|
||||
} else if (StringUtils.isNotEmpty(v.fieldName())) {
|
||||
params.put("fieldName", v.fieldName());
|
||||
}
|
||||
|
||||
if (v.min() != null && v.min().length() > 0) {
|
||||
params.put("min", v.min());
|
||||
}
|
||||
if (v.max() != null && v.max().length() > 0) {
|
||||
params.put("max", v.max());
|
||||
}
|
||||
if (StringUtils.isNotEmpty(v.maxExpression())) {
|
||||
params.put("maxExpression", v.maxExpression());
|
||||
}
|
||||
if (StringUtils.isNotEmpty(v.minExpression())) {
|
||||
params.put("minExpression", v.minExpression());
|
||||
}
|
||||
|
||||
validatorFactory.lookupRegisteredValidatorType(validatorType);
|
||||
return new ValidatorConfig.Builder(validatorType)
|
||||
.addParams(params)
|
||||
.addParam("methodName", methodName)
|
||||
.shortCircuit(v.shortCircuit())
|
||||
.defaultMessage(v.message())
|
||||
.messageKey(v.key())
|
||||
.messageParams(v.messageParams())
|
||||
.build();
|
||||
}
|
||||
|
||||
private ValidatorConfig processShortRangeFieldValidatorAnnotation(ShortRangeFieldValidator v, String fieldName, String methodName) {
|
||||
String validatorType = "short";
|
||||
|
||||
@@ -790,6 +855,28 @@ public class AnnotationValidationConfigurationBuilder {
|
||||
.build();
|
||||
}
|
||||
|
||||
private ValidatorConfig processCreditCardValidatorAnnotation(CreditCardValidator v, String fieldName, String methodName) {
|
||||
String validatorType = "creditcard";
|
||||
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
|
||||
if (fieldName != null) {
|
||||
params.put("fieldName", fieldName);
|
||||
} else if (StringUtils.isNotEmpty(v.fieldName())) {
|
||||
params.put("fieldName", v.fieldName());
|
||||
}
|
||||
|
||||
validatorFactory.lookupRegisteredValidatorType(validatorType);
|
||||
return new ValidatorConfig.Builder(validatorType)
|
||||
.addParams(params)
|
||||
.addParam("methodName", methodName)
|
||||
.shortCircuit(v.shortCircuit())
|
||||
.defaultMessage(v.message())
|
||||
.messageKey(v.key())
|
||||
.messageParams(v.messageParams())
|
||||
.build();
|
||||
}
|
||||
|
||||
private ValidatorConfig processDateRangeFieldValidatorAnnotation(DateRangeFieldValidator v, String fieldName, String methodName) {
|
||||
String validatorType = "date";
|
||||
|
||||
@@ -874,28 +961,4 @@ public class AnnotationValidationConfigurationBuilder {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the property name for a method.
|
||||
* This method is independant from property fields.
|
||||
*
|
||||
* @param method The method to get the property name for.
|
||||
* @return the property name for given method; null if non could be resolved.
|
||||
*/
|
||||
public String resolvePropertyName(Method method) {
|
||||
|
||||
Matcher matcher = SETTER_PATTERN.matcher(method.getName());
|
||||
if (matcher.matches() && method.getParameterTypes().length == 1) {
|
||||
String raw = matcher.group(1);
|
||||
return raw.substring(0, 1).toLowerCase() + raw.substring(1);
|
||||
}
|
||||
|
||||
matcher = GETTER_PATTERN.matcher(method.getName());
|
||||
if (matcher.matches() && method.getParameterTypes().length == 0) {
|
||||
String raw = matcher.group(2);
|
||||
return raw.substring(0, 1).toLowerCase() + raw.substring(1);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+11
-2
@@ -18,6 +18,7 @@ package com.opensymphony.xwork2.validator;
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.FileManager;
|
||||
import com.opensymphony.xwork2.FileManagerFactory;
|
||||
import com.opensymphony.xwork2.TextProviderFactory;
|
||||
import com.opensymphony.xwork2.XWorkConstants;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.util.ClassLoaderUtil;
|
||||
@@ -50,16 +51,19 @@ import java.util.*;
|
||||
*/
|
||||
public class DefaultActionValidatorManager implements ActionValidatorManager {
|
||||
|
||||
private final static Logger LOG = LogManager.getLogger(DefaultActionValidatorManager.class);
|
||||
|
||||
/** The file suffix for any validation file. */
|
||||
protected static final String VALIDATION_CONFIG_SUFFIX = "-validation.xml";
|
||||
|
||||
private final Map<String, List<ValidatorConfig>> validatorCache = Collections.synchronizedMap(new HashMap<String, List<ValidatorConfig>>());
|
||||
private final Map<String, List<ValidatorConfig>> validatorFileCache = Collections.synchronizedMap(new HashMap<String, List<ValidatorConfig>>());
|
||||
private final Logger LOG = LogManager.getLogger(DefaultActionValidatorManager.class);
|
||||
|
||||
private ValidatorFactory validatorFactory;
|
||||
private ValidatorFileParser validatorFileParser;
|
||||
private FileManager fileManager;
|
||||
private boolean reloadingConfigs;
|
||||
private TextProviderFactory textProviderFactory;
|
||||
|
||||
@Inject
|
||||
public void setValidatorFileParser(ValidatorFileParser parser) {
|
||||
@@ -81,6 +85,11 @@ public class DefaultActionValidatorManager implements ActionValidatorManager {
|
||||
this.reloadingConfigs = Boolean.parseBoolean(reloadingConfigs);
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setTextProviderFactory(TextProviderFactory textProviderFactory) {
|
||||
this.textProviderFactory = textProviderFactory;
|
||||
}
|
||||
|
||||
public synchronized List<Validator> getValidators(Class clazz, String context) {
|
||||
return getValidators(clazz, context, null);
|
||||
}
|
||||
@@ -118,7 +127,7 @@ public class DefaultActionValidatorManager implements ActionValidatorManager {
|
||||
}
|
||||
|
||||
public void validate(Object object, String context, String method) throws ValidationException {
|
||||
ValidatorContext validatorContext = new DelegatingValidatorContext(object);
|
||||
ValidatorContext validatorContext = new DelegatingValidatorContext(object, textProviderFactory);
|
||||
validate(object, context, validatorContext, method);
|
||||
}
|
||||
|
||||
|
||||
+25
-26
@@ -57,10 +57,10 @@ public class DelegatingValidatorContext implements ValidatorContext {
|
||||
*
|
||||
* @param object the object to use for validation (usually an Action).
|
||||
*/
|
||||
public DelegatingValidatorContext(Object object) {
|
||||
public DelegatingValidatorContext(Object object, TextProviderFactory textProviderFactory) {
|
||||
this.localeProvider = makeLocaleProvider(object);
|
||||
this.validationAware = makeValidationAware(object);
|
||||
this.textProvider = makeTextProvider(object, localeProvider);
|
||||
this.textProvider = makeTextProvider(object, textProviderFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,10 +68,13 @@ public class DelegatingValidatorContext implements ValidatorContext {
|
||||
* the validation context are created based on the class.
|
||||
*
|
||||
* @param clazz the class to initialize the context with.
|
||||
*
|
||||
* @deprecated will be removed, do not use!
|
||||
*/
|
||||
@Deprecated
|
||||
public DelegatingValidatorContext(Class clazz) {
|
||||
localeProvider = new ActionContextLocaleProvider();
|
||||
textProvider = new TextProviderFactory().createInstance(clazz, localeProvider);
|
||||
textProvider = new StrutsTextProviderFactory().createInstance(clazz);
|
||||
validationAware = new LoggingValidationAware(clazz);
|
||||
}
|
||||
|
||||
@@ -193,18 +196,11 @@ public class DelegatingValidatorContext implements ValidatorContext {
|
||||
return validationAware.hasFieldErrors();
|
||||
}
|
||||
|
||||
public static TextProvider makeTextProvider(Object object, LocaleProvider localeProvider) {
|
||||
public TextProvider makeTextProvider(Object object, TextProviderFactory textProviderFactory) {
|
||||
// the object argument passed through here will most probably be an ActionSupport descendant which does
|
||||
// implements TextProvider.
|
||||
if (object != null && object instanceof DelegatingValidatorContext) {
|
||||
return ((DelegatingValidatorContext) object).getTextProvider();
|
||||
} else if (object != null && localeProvider != null && localeProvider instanceof DelegatingValidatorContext) {
|
||||
return new CompositeTextProvider(new TextProvider[]{
|
||||
new TextProviderFactory().createInstance(object.getClass(), localeProvider),
|
||||
((DelegatingValidatorContext)localeProvider).getTextProvider()
|
||||
});
|
||||
} else if (localeProvider != null && localeProvider instanceof DelegatingValidatorContext) {
|
||||
return ((DelegatingValidatorContext)localeProvider).getTextProvider();
|
||||
}
|
||||
|
||||
if ((object != null) && (object instanceof TextProvider)) {
|
||||
@@ -213,20 +209,11 @@ public class DelegatingValidatorContext implements ValidatorContext {
|
||||
}
|
||||
return new CompositeTextProvider(new TextProvider[]{
|
||||
((TextProvider) object),
|
||||
new TextProviderSupport(object.getClass(), localeProvider)
|
||||
});
|
||||
} else if (localeProvider != null && localeProvider instanceof TextProvider) {
|
||||
if (localeProvider instanceof CompositeTextProvider) {
|
||||
return (CompositeTextProvider) localeProvider;
|
||||
}
|
||||
return new CompositeTextProvider(new TextProvider[]{
|
||||
((TextProvider) localeProvider),
|
||||
new TextProviderSupport(localeProvider.getClass(), localeProvider)
|
||||
textProviderFactory.createInstance(object.getClass())
|
||||
});
|
||||
} else {
|
||||
return new TextProviderFactory().createInstance(
|
||||
object != null ? object.getClass() : DelegatingValidatorContext.class,
|
||||
localeProvider);
|
||||
return textProviderFactory.createInstance(
|
||||
object != null ? object.getClass() : DelegatingValidatorContext.class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,18 +253,30 @@ public class DelegatingValidatorContext implements ValidatorContext {
|
||||
* An implementation of LocaleProvider which gets the locale from the action context.
|
||||
*/
|
||||
private static class ActionContextLocaleProvider implements LocaleProvider {
|
||||
|
||||
private LocaleProvider localeProvider;
|
||||
|
||||
private LocaleProvider getLocaleProvider() {
|
||||
if (localeProvider == null) {
|
||||
LocaleProviderFactory localeProviderFactory = ActionContext.getContext().getInstance(LocaleProviderFactory.class);
|
||||
localeProvider = localeProviderFactory.createLocaleProvider();
|
||||
}
|
||||
return localeProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locale getLocale() {
|
||||
return ActionContext.getContext().getInstance(LocaleProvider.class).getLocale();
|
||||
return getLocaleProvider().getLocale();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidLocaleString(String localeStr) {
|
||||
return ActionContext.getContext().getInstance(LocaleProvider.class).isValidLocaleString(localeStr);
|
||||
return getLocaleProvider().isValidLocaleString(localeStr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidLocale(Locale locale) {
|
||||
return ActionContext.getContext().getInstance(LocaleProvider.class).isValidLocale(locale);
|
||||
return getLocaleProvider().isValidLocale(locale);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -129,15 +129,15 @@ import org.apache.logging.log4j.Logger;
|
||||
*/
|
||||
public class ValidationInterceptor extends MethodFilterInterceptor {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(ValidationInterceptor.class);
|
||||
|
||||
private final static String VALIDATE_PREFIX = "validate";
|
||||
private final static String ALT_VALIDATE_PREFIX = "validateDo";
|
||||
|
||||
private boolean validateAnnotatedMethodOnly;
|
||||
|
||||
private ActionValidatorManager actionValidatorManager;
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(ValidationInterceptor.class);
|
||||
|
||||
private final static String VALIDATE_PREFIX = "validate";
|
||||
private final static String ALT_VALIDATE_PREFIX = "validateDo";
|
||||
|
||||
private boolean alwaysInvokeValidate = true;
|
||||
private boolean programmatic = true;
|
||||
private boolean declarative = true;
|
||||
@@ -212,8 +212,8 @@ public class ValidationInterceptor extends MethodFilterInterceptor {
|
||||
String context = this.getValidationContext(proxy);
|
||||
String method = proxy.getMethod();
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Validating {}/{} with method {}.", invocation.getProxy().getNamespace(), invocation.getProxy().getActionName(), method);
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Validating {}/{} with method {}.", invocation.getProxy().getNamespace(), invocation.getProxy().getActionName(), method);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.validator.annotations;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* This validator checks that a field is a valid credit card.
|
||||
*/
|
||||
@Target({ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface CreditCardValidator {
|
||||
|
||||
/**
|
||||
* @return The default error message for this validator.
|
||||
* NOTE: It is required to set a message, if you are not using the message key for 18n lookup!
|
||||
*/
|
||||
String message() default "";
|
||||
|
||||
/**
|
||||
* @return The message key to lookup for i18n.
|
||||
*/
|
||||
String key() default "";
|
||||
|
||||
/**
|
||||
* @return Additional params to be used to customize message - will be evaluated against the Value Stack
|
||||
*/
|
||||
String[] messageParams() default {};
|
||||
|
||||
/**
|
||||
* @return The optional fieldName for SIMPLE validator types.
|
||||
*/
|
||||
String fieldName() default "";
|
||||
|
||||
/**
|
||||
* If this is activated, the validator will be used as short-circuit.
|
||||
*
|
||||
* Adds the short-circuit='true' attribute value if <tt>true</tt>.
|
||||
*
|
||||
* @return true if validator will be used as short-circuit. Default is false.
|
||||
*/
|
||||
boolean shortCircuit() default false;
|
||||
|
||||
/**
|
||||
* @return The validation type for this field/method.
|
||||
*/
|
||||
ValidatorType type() default ValidatorType.FIELD;
|
||||
|
||||
}
|
||||
+194
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* 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.validator.annotations;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* <!-- START SNIPPET: description -->
|
||||
* This validator checks that a numeric field has a value within a specified range.
|
||||
* If neither min nor max is set, nothing will be done.
|
||||
* <!-- END SNIPPET: description -->
|
||||
*
|
||||
* <p><u>Annotation usage:</u></p>
|
||||
*
|
||||
* <!-- START SNIPPET: usage -->
|
||||
* <p>The annotation must be applied at method level.</p>
|
||||
* <!-- END SNIPPET: usage -->
|
||||
*
|
||||
* <p><u>Annotation parameters:</u></p>
|
||||
*
|
||||
* <!-- START SNIPPET: parameters -->
|
||||
* <table class='confluenceTable' summary=''>
|
||||
* <tr>
|
||||
* <th class='confluenceTh'> Parameter </th>
|
||||
* <th class='confluenceTh'> Required </th>
|
||||
* <th class='confluenceTh'> Default </th>
|
||||
* <th class='confluenceTh'> Notes </th>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td class='confluenceTd'>message</td>
|
||||
* <td class='confluenceTd'>yes</td>
|
||||
* <td class='confluenceTd'> </td>
|
||||
* <td class='confluenceTd'>field error message</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td class='confluenceTd'>key</td>
|
||||
* <td class='confluenceTd'>no</td>
|
||||
* <td class='confluenceTd'> </td>
|
||||
* <td class='confluenceTd'>i18n key from language specific properties file.</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td class='confluenceTd'>messageParams</td>
|
||||
* <td class='confluenceTd'>no</td>
|
||||
* <td class='confluenceTd'> </td>
|
||||
* <td class='confluenceTd'>Additional params to be used to customize message - will be evaluated against the Value Stack</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td class='confluenceTd'>fieldName</td>
|
||||
* <td class='confluenceTd'>no</td>
|
||||
* <td class='confluenceTd'> </td>
|
||||
* <td class='confluenceTd'> </td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td class='confluenceTd'>shortCircuit</td>
|
||||
* <td class='confluenceTd'>no</td>
|
||||
* <td class='confluenceTd'>false</td>
|
||||
* <td class='confluenceTd'>If this validator should be used as shortCircuit.</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td class='confluenceTd'>type</td>
|
||||
* <td class='confluenceTd'>yes</td>
|
||||
* <td class='confluenceTd'>ValidatorType.FIELD</td>
|
||||
* <td class='confluenceTd'>Enum value from ValidatorType. Either FIELD or SIMPLE can be used here.</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td class='confluenceTd'> min </td>
|
||||
* <td class='confluenceTd'> no </td>
|
||||
* <td class='confluenceTd'> </td>
|
||||
* <td class='confluenceTd'> Integer property. The minimum the number must be.</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td class='confluenceTd'> minExpression </td>
|
||||
* <td class='confluenceTd'> no </td>
|
||||
* <td class='confluenceTd'> </td>
|
||||
* <td class='confluenceTd'>OGNL expression used to obtain the minimum the number must be.</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td class='confluenceTd'> max </td>
|
||||
* <td class='confluenceTd'> no </td>
|
||||
* <td class='confluenceTd'> </td>
|
||||
* <td class='confluenceTd'> Integer property. The maximum number can be. Can be an expression.!</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td class='confluenceTd'> maxExpression </td>
|
||||
* <td class='confluenceTd'> no </td>
|
||||
* <td class='confluenceTd'> </td>
|
||||
* <td class='confluenceTd'>OGNL expression used to obtain the maximum number can be.</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td class='confluenceTd'>parse</td>
|
||||
* <td class='confluenceTd'>no</td>
|
||||
* <td class='confluenceTd'>false</td>
|
||||
* <td class='confluenceTd'>Enable parsing of min/max value.</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
*
|
||||
* <p>If neither <em>min</em> nor <em>max</em> is set, nothing will be done.</p>
|
||||
*
|
||||
* <p>The values for min and max must be inserted as String values so that "0" can be handled as a possible value.</p>
|
||||
* <!-- END SNIPPET: parameters -->
|
||||
*
|
||||
* <p><u>Example code:</u></p>
|
||||
*
|
||||
* <pre>
|
||||
* <!-- START SNIPPET: example -->
|
||||
* @LongRangeFieldValidator(message = "Default message", key = "i18n.key", shortCircuit = true, min = "0", max = "42")
|
||||
*
|
||||
* @LongRangeFieldValidator(message = "Default message", key = "i18n.key", shortCircuit = true, minExpression = "${minValue}", maxExpression = "${maxValue}")
|
||||
* <!-- END SNIPPET: example -->
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* @author Rainer Hermanns
|
||||
*/
|
||||
@Target({ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface LongRangeFieldValidator {
|
||||
|
||||
/**
|
||||
* Long property.
|
||||
*
|
||||
* @return The minimum the number must be.
|
||||
*/
|
||||
String min() default "";
|
||||
|
||||
/**
|
||||
* @return The minimum number can be defined as an expression
|
||||
*/
|
||||
String minExpression() default "";
|
||||
|
||||
/**
|
||||
* Long property.
|
||||
*
|
||||
* @return The maximum number can be.
|
||||
*/
|
||||
String max() default "";
|
||||
|
||||
/**
|
||||
* @return The maximum number can be defined as an expression
|
||||
*/
|
||||
String maxExpression() default "";
|
||||
|
||||
/**
|
||||
* @return The default error message for this validator.
|
||||
* NOTE: It is required to set a message, if you are not using the message key for 18n lookup!
|
||||
*/
|
||||
String message() default "";
|
||||
|
||||
/**
|
||||
* @return The message key to lookup for i18n.
|
||||
*/
|
||||
String key() default "";
|
||||
|
||||
/**
|
||||
* @return Additional params to be used to customize message - will be evaluated against the Value Stack
|
||||
*/
|
||||
String[] messageParams() default {};
|
||||
|
||||
/**
|
||||
* @return The optional fieldName for SIMPLE validator types.
|
||||
*/
|
||||
String fieldName() default "";
|
||||
|
||||
/**
|
||||
* If this is activated, the validator will be used as short-circuit.
|
||||
*
|
||||
* Adds the short-circuit='true' attribute value if <tt>true</tt>.
|
||||
*
|
||||
* @return true if validator will be used as short-circuit. Default is false.
|
||||
*/
|
||||
boolean shortCircuit() default false;
|
||||
|
||||
/**
|
||||
* @return The validation type for this field/method.
|
||||
*/
|
||||
ValidatorType type() default ValidatorType.FIELD;
|
||||
}
|
||||
@@ -129,6 +129,8 @@ import java.lang.annotation.Target;
|
||||
* {@StringLengthFieldValidator(type = ValidatorType.SIMPLE, trim = true, minLength="10" , maxLength = "12", fieldName = "needstringlength", message = "You must enter a stringlength.")},
|
||||
* intRangeFields =
|
||||
* { @IntRangeFieldValidator(type = ValidatorType.SIMPLE, fieldName = "intfield", min = "6", max = "10", message = "bar must be between ${min} and ${max}, current value is ${bar}.")},
|
||||
* longRangeFields =
|
||||
* { @LongRangeFieldValidator(type = ValidatorType.SIMPLE, fieldName = "intfield", min = "6", max = "10", message = "bar must be between ${min} and ${max}, current value is ${bar}.")},
|
||||
* shortRangeFields =
|
||||
* { @ShortRangeFieldValidator(type = ValidatorType.SIMPLE, fieldName = "shortfield", min = "1", max = "128", message = "bar must be between ${min} and ${max}, current value is ${bar}.")},
|
||||
* dateRangeFields =
|
||||
@@ -167,10 +169,14 @@ public @interface Validations {
|
||||
|
||||
EmailValidator[] emails() default {};
|
||||
|
||||
CreditCardValidator[] creditCards() default {};
|
||||
|
||||
FieldExpressionValidator[] fieldExpressions() default {};
|
||||
|
||||
IntRangeFieldValidator[] intRangeFields() default {};
|
||||
|
||||
LongRangeFieldValidator[] longRangeFields() default {};
|
||||
|
||||
RequiredFieldValidator[] requiredFields() default {};
|
||||
|
||||
RequiredStringValidator[] requiredStrings() default {};
|
||||
|
||||
+5
-1
@@ -1,6 +1,8 @@
|
||||
package com.opensymphony.xwork2.validator.validators;
|
||||
|
||||
import com.opensymphony.xwork2.validator.ValidationException;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
/**
|
||||
* <!-- START SNIPPET: javadoc -->
|
||||
@@ -29,6 +31,8 @@ import com.opensymphony.xwork2.validator.ValidationException;
|
||||
*/
|
||||
public class ConditionalVisitorFieldValidator extends VisitorFieldValidator {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(ConditionalVisitorFieldValidator.class);
|
||||
|
||||
private String expression;
|
||||
|
||||
public void setExpression(String expression) {
|
||||
@@ -75,7 +79,7 @@ public class ConditionalVisitorFieldValidator extends VisitorFieldValidator {
|
||||
if ((obj != null) && (obj instanceof Boolean)) {
|
||||
answer = (Boolean) obj;
|
||||
} else {
|
||||
log.warn("Got result of {} when trying to get Boolean.", obj);
|
||||
LOG.warn("Got result of {} when trying to get Boolean.", obj);
|
||||
}
|
||||
|
||||
return answer;
|
||||
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.validator.validators;
|
||||
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* CreditCardFieldValidator checks that a given String/Array/Collection field,
|
||||
* if not empty, is a valid credit card number.
|
||||
*/
|
||||
public class CreditCardValidator extends RegexFieldValidator {
|
||||
|
||||
public static final String CREDIT_CARD_PATTERN =
|
||||
"^(?:4[0-9]{12}(?:[0-9]{3})?" + // Visa
|
||||
"|(?:5[1-5][0-9]{2}" + // MasterCard
|
||||
"|222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}" +
|
||||
"|3[47][0-9]{13}" + // American Express
|
||||
"|3(?:0[0-5]|[68][0-9])[0-9]{11}" + // Diners Club
|
||||
"|6(?:011|5[0-9]{2})[0-9]{12}" + // Discover
|
||||
"|(?:2131|1800|35\\d{3})\\d{11}" + // JCB
|
||||
")$";
|
||||
|
||||
public CreditCardValidator() {
|
||||
setRegex(CREDIT_CARD_PATTERN);
|
||||
setCaseSensitive(false);
|
||||
}
|
||||
|
||||
protected void validateFieldValue(Object object, String value, String regexToUse) {
|
||||
super.validateFieldValue(object, StringUtils.deleteWhitespace(value), regexToUse);
|
||||
}
|
||||
|
||||
}
|
||||
+41
-14
@@ -18,6 +18,11 @@ package com.opensymphony.xwork2.validator.validators;
|
||||
|
||||
import com.opensymphony.xwork2.validator.ValidationException;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* <!-- START SNIPPET: javadoc -->
|
||||
@@ -86,7 +91,9 @@ import org.apache.commons.lang3.StringUtils;
|
||||
* @author Rene Gielen
|
||||
*/
|
||||
public class DoubleRangeFieldValidator extends FieldValidatorSupport {
|
||||
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(DoubleRangeFieldValidator.class);
|
||||
|
||||
private Double maxInclusive = null;
|
||||
private Double minInclusive = null;
|
||||
private Double minExclusive = null;
|
||||
@@ -99,14 +106,8 @@ public class DoubleRangeFieldValidator extends FieldValidatorSupport {
|
||||
|
||||
public void validate(Object object) throws ValidationException {
|
||||
String fieldName = getFieldName();
|
||||
Double value;
|
||||
try {
|
||||
Object obj = this.getFieldValue(fieldName, object);
|
||||
if (obj == null) {
|
||||
return;
|
||||
}
|
||||
value = Double.valueOf(obj.toString());
|
||||
} catch (NumberFormatException e) {
|
||||
Object obj = this.getFieldValue(fieldName, object);
|
||||
if (obj == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -115,11 +116,37 @@ public class DoubleRangeFieldValidator extends FieldValidatorSupport {
|
||||
Double maxExclusiveToUse = getMaxExclusive();
|
||||
Double minExclusiveToUse = getMinExclusive();
|
||||
|
||||
if ((maxInclusiveToUse != null && value.compareTo(maxInclusiveToUse) > 0) ||
|
||||
(minInclusiveToUse != null && value.compareTo(minInclusiveToUse) < 0) ||
|
||||
(maxExclusiveToUse != null && value.compareTo(maxExclusiveToUse) >= 0) ||
|
||||
(minExclusiveToUse != null && value.compareTo(minExclusiveToUse) <= 0)) {
|
||||
addFieldError(fieldName, object);
|
||||
if (obj.getClass().isArray()) {
|
||||
Object[] values = (Object[]) obj;
|
||||
validateCollection(maxInclusiveToUse, minInclusiveToUse, maxExclusiveToUse, minExclusiveToUse, Arrays.asList(values));
|
||||
} else if (Collection.class.isAssignableFrom(obj.getClass())) {
|
||||
Collection values = (Collection) obj;
|
||||
validateCollection(maxInclusiveToUse, minInclusiveToUse, maxExclusiveToUse, minExclusiveToUse, values);
|
||||
} else {
|
||||
validateValue(obj, maxInclusiveToUse, minInclusiveToUse, maxExclusiveToUse, minExclusiveToUse);
|
||||
}
|
||||
}
|
||||
|
||||
protected void validateCollection(Double maxInclusiveToUse, Double minInclusiveToUse, Double maxExclusiveToUse, Double minExclusiveToUse, Collection values) {
|
||||
for (Object objValue : values) {
|
||||
validateValue(objValue, maxInclusiveToUse, minInclusiveToUse, maxExclusiveToUse, minExclusiveToUse);
|
||||
}
|
||||
}
|
||||
|
||||
protected void validateValue(Object obj, Double maxInclusiveToUse, Double minInclusiveToUse, Double maxExclusiveToUse, Double minExclusiveToUse) {
|
||||
try {
|
||||
setCurrentValue(obj);
|
||||
Double value = Double.valueOf(obj.toString());
|
||||
if ((maxInclusiveToUse != null && value.compareTo(maxInclusiveToUse) > 0) ||
|
||||
(minInclusiveToUse != null && value.compareTo(minInclusiveToUse) < 0) ||
|
||||
(maxExclusiveToUse != null && value.compareTo(maxExclusiveToUse) >= 0) ||
|
||||
(minExclusiveToUse != null && value.compareTo(minExclusiveToUse) <= 0)) {
|
||||
addFieldError(getFieldName(), value);
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
LOG.debug("Cannot validate value {} - not a Double", e);
|
||||
} finally {
|
||||
setCurrentValue(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ package com.opensymphony.xwork2.validator.validators;
|
||||
* The regular expression used to validate that the string is an email address is:
|
||||
*
|
||||
* <pre>
|
||||
* \\b^['_a-z0-9-\\+]+(\\.['_a-z0-9-\\+]+)*@[a-z0-9-]+(\\.[a-z0-9-]+)*\\.([a-z]{2}|aero|arpa|asia|biz|com|coop|edu|gov|info|int|jobs|mil|mobi|museum|name|nato|net|org|pro|tel|travel|xxx|tech|cat)$\\b
|
||||
* \\b^['_a-z0-9-\\+]+(\\.['_a-z0-9-\\+]+)*@[a-z0-9-]+(\\.[a-z0-9-]+)*\\.([a-z]{2,6})$\\b
|
||||
* </pre>
|
||||
*
|
||||
* You can also specify expression, caseSensitive and trim params as a OGNL expression, see the example below.
|
||||
@@ -78,9 +78,7 @@ package com.opensymphony.xwork2.validator.validators;
|
||||
*/
|
||||
public class EmailValidator extends RegexFieldValidator {
|
||||
|
||||
// see XW-371
|
||||
public static final String EMAIL_ADDRESS_PATTERN =
|
||||
"\\b^['_a-z0-9-\\+]+(\\.['_a-z0-9-\\+]+)*@[a-z0-9-]+(\\.[a-z0-9-]+)*\\.([a-z]{2}|aero|arpa|asia|biz|com|coop|edu|gov|info|int|jobs|mil|mobi|museum|name|nato|net|org|pro|tel|travel|xxx|tech|cat)$\\b";
|
||||
public static final String EMAIL_ADDRESS_PATTERN = "\\b^['_a-z0-9-\\+]+(\\.['_a-z0-9-\\+]+)*@[a-z0-9-]+(\\.[a-z0-9-]+)*\\.([a-z]{2,6})$\\b";
|
||||
|
||||
public EmailValidator() {
|
||||
setRegex(EMAIL_ADDRESS_PATTERN);
|
||||
|
||||
+6
-2
@@ -16,6 +16,8 @@
|
||||
package com.opensymphony.xwork2.validator.validators;
|
||||
|
||||
import com.opensymphony.xwork2.validator.ValidationException;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
/**
|
||||
* <!-- START SNIPPET: javadoc -->
|
||||
@@ -44,6 +46,8 @@ import com.opensymphony.xwork2.validator.ValidationException;
|
||||
*/
|
||||
public class ExpressionValidator extends ValidatorSupport {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(ExpressionValidator.class);
|
||||
|
||||
private String expression;
|
||||
|
||||
public void setExpression(String expression) {
|
||||
@@ -69,11 +73,11 @@ public class ExpressionValidator extends ValidatorSupport {
|
||||
if ((obj != null) && (obj instanceof Boolean)) {
|
||||
answer = (Boolean) obj;
|
||||
} else {
|
||||
log.warn("Got result of [{}] when trying to get Boolean.", obj);
|
||||
LOG.warn("Got result of [{}] when trying to get Boolean.", obj);
|
||||
}
|
||||
|
||||
if (!answer) {
|
||||
log.debug("Validation failed on expression [{}] with validated object [{}]", expression, object);
|
||||
LOG.debug("Validation failed on expression [{}] with validated object [{}]", expression, object);
|
||||
addActionError(object);
|
||||
}
|
||||
}
|
||||
|
||||
+6
-2
@@ -16,6 +16,8 @@
|
||||
package com.opensymphony.xwork2.validator.validators;
|
||||
|
||||
import com.opensymphony.xwork2.validator.ValidationException;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
|
||||
/**
|
||||
@@ -55,6 +57,8 @@ import com.opensymphony.xwork2.validator.ValidationException;
|
||||
*/
|
||||
public class FieldExpressionValidator extends FieldValidatorSupport {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(FieldExpressionValidator.class);
|
||||
|
||||
private String expression;
|
||||
|
||||
public void setExpression(String expression) {
|
||||
@@ -82,10 +86,10 @@ public class FieldExpressionValidator extends FieldValidatorSupport {
|
||||
if ((obj != null) && (obj instanceof Boolean)) {
|
||||
answer = (Boolean) obj;
|
||||
} else {
|
||||
log.warn("Got result of {} when trying to get Boolean.", obj);
|
||||
LOG.warn("Got result of {} when trying to get Boolean.", obj);
|
||||
}
|
||||
|
||||
if (!answer.booleanValue()) {
|
||||
if (!answer) {
|
||||
addFieldError(fieldName, object);
|
||||
}
|
||||
}
|
||||
|
||||
+13
-4
@@ -17,16 +17,17 @@ package com.opensymphony.xwork2.validator.validators;
|
||||
|
||||
import com.opensymphony.xwork2.validator.FieldValidator;
|
||||
|
||||
|
||||
/**
|
||||
* Base class for field validators.
|
||||
*
|
||||
* @author Jason Carreira
|
||||
* You can access fieldName and its currentValue in a message using expression, e.g.
|
||||
* "Wrong value ${currentValue} for ${fieldName}"
|
||||
*/
|
||||
public abstract class FieldValidatorSupport extends ValidatorSupport implements FieldValidator {
|
||||
|
||||
private String fieldName;
|
||||
private String type;
|
||||
protected String fieldName;
|
||||
protected String type;
|
||||
protected Object currentValue;
|
||||
|
||||
public void setFieldName(String fieldName) {
|
||||
this.fieldName = fieldName;
|
||||
@@ -45,4 +46,12 @@ public abstract class FieldValidatorSupport extends ValidatorSupport implements
|
||||
public String getValidatorType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public Object getCurrentValue() {
|
||||
return currentValue;
|
||||
}
|
||||
|
||||
void setCurrentValue(Object currentValue) {
|
||||
this.currentValue = currentValue;
|
||||
}
|
||||
}
|
||||
|
||||
+43
-23
@@ -20,6 +20,8 @@ import org.apache.logging.log4j.LogManager;
|
||||
import com.opensymphony.xwork2.validator.ValidationException;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Base class for range based validators. Use this class to develop any other custom range validators.
|
||||
*/
|
||||
@@ -40,25 +42,45 @@ public abstract class RangeValidatorSupport<T extends Comparable> extends FieldV
|
||||
|
||||
public void validate(Object object) throws ValidationException {
|
||||
Object obj = getFieldValue(getFieldName(), object);
|
||||
Comparable<T> value = (Comparable<T>) obj;
|
||||
|
||||
// if there is no value - don't do comparison
|
||||
// if a value is required, a required validator should be added to the field
|
||||
if (value == null) {
|
||||
if (obj == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
T min = getMin();
|
||||
T max = getMax();
|
||||
|
||||
if (obj.getClass().isArray()) {
|
||||
Object[] values = (Object[]) obj;
|
||||
for (Object objValue : values) {
|
||||
validateValue(object, (Comparable<T>) objValue, min, max);
|
||||
}
|
||||
} else if (Collection.class.isAssignableFrom(obj.getClass())) {
|
||||
Collection<?> values = (Collection<?>) obj;
|
||||
for (Object objValue : values) {
|
||||
validateValue(object, (Comparable<T>) objValue, min, max);
|
||||
}
|
||||
} else {
|
||||
validateValue(object, (Comparable<T>) obj, min, max);
|
||||
}
|
||||
}
|
||||
|
||||
protected void validateValue(Object object, Comparable<T> value, T min, T max) {
|
||||
setCurrentValue(value);
|
||||
|
||||
// only check for a minimum value if the min parameter is set
|
||||
T minComparatorValue = getMin();
|
||||
if ((minComparatorValue != null) && (value.compareTo(minComparatorValue) < 0)) {
|
||||
if ((min != null) && (value.compareTo(min) < 0)) {
|
||||
addFieldError(getFieldName(), object);
|
||||
}
|
||||
|
||||
// only check for a maximum value if the max parameter is set
|
||||
T maxComparatorValue = getMax();
|
||||
if ((maxComparatorValue != null) && (value.compareTo(maxComparatorValue) > 0)) {
|
||||
if ((max != null) && (value.compareTo(max) > 0)) {
|
||||
addFieldError(getFieldName(), object);
|
||||
}
|
||||
|
||||
setCurrentValue(null);
|
||||
}
|
||||
|
||||
public void setMin(T min) {
|
||||
@@ -66,13 +88,11 @@ public abstract class RangeValidatorSupport<T extends Comparable> extends FieldV
|
||||
}
|
||||
|
||||
public T getMin() {
|
||||
if (min != null) {
|
||||
return min;
|
||||
} else if (StringUtils.isNotEmpty(minExpression)) {
|
||||
return (T) parse(minExpression, type);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
return getT(min, minExpression, type);
|
||||
}
|
||||
|
||||
public T getMax() {
|
||||
return getT(max, maxExpression, type);
|
||||
}
|
||||
|
||||
public void setMinExpression(String minExpression) {
|
||||
@@ -84,19 +104,19 @@ public abstract class RangeValidatorSupport<T extends Comparable> extends FieldV
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
public T getMax() {
|
||||
if (max != null) {
|
||||
return max;
|
||||
} else if (StringUtils.isNotEmpty(maxExpression)) {
|
||||
return (T) parse(maxExpression, type);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void setMaxExpression(String maxExpression) {
|
||||
LOG.debug("${maxExpression} was defined as [{}]", maxExpression);
|
||||
this.maxExpression = maxExpression;
|
||||
}
|
||||
|
||||
protected T getT(T minMax, String minMaxExpression, Class<T> toType) {
|
||||
if (minMax != null) {
|
||||
return minMax;
|
||||
} else if (StringUtils.isNotEmpty(minMaxExpression)) {
|
||||
return (T) parse(minMaxExpression, toType);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+35
-11
@@ -16,11 +16,17 @@
|
||||
|
||||
package com.opensymphony.xwork2.validator.validators;
|
||||
|
||||
import com.opensymphony.xwork2.ObjectFactory;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import com.opensymphony.xwork2.validator.ValidationException;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -89,9 +95,9 @@ public class RegexFieldValidator extends FieldValidatorSupport {
|
||||
private String regex;
|
||||
private String regexExpression;
|
||||
private Boolean caseSensitive = true;
|
||||
private String caseSensitiveExpression = "";
|
||||
private String caseSensitiveExpression = EMPTY_STRING;
|
||||
private Boolean trim = true;
|
||||
private String trimExpression = "";
|
||||
private String trimExpression = EMPTY_STRING;
|
||||
|
||||
public void validate(Object object) throws ValidationException {
|
||||
String fieldName = getFieldName();
|
||||
@@ -102,17 +108,30 @@ public class RegexFieldValidator extends FieldValidatorSupport {
|
||||
LOG.debug("Defined regexp as [{}]", regexToUse);
|
||||
|
||||
if (value == null || regexToUse == null) {
|
||||
LOG.debug("Either value is empty (please use a required validator) or regex is empty");
|
||||
return;
|
||||
}
|
||||
|
||||
// XW-375 - must be a string
|
||||
if (!(value instanceof String)) {
|
||||
return;
|
||||
if (value.getClass().isArray()) {
|
||||
Object[] values = (Object[]) value;
|
||||
for (Object objValue: values) {
|
||||
validateFieldValue(object, Objects.toString(objValue, EMPTY_STRING), regexToUse);
|
||||
}
|
||||
} else if (Collection.class.isAssignableFrom(value.getClass())) {
|
||||
Collection values = (Collection) value;
|
||||
for (Object objValue : values) {
|
||||
validateFieldValue(object, Objects.toString(objValue, EMPTY_STRING), regexToUse);
|
||||
}
|
||||
} else {
|
||||
validateFieldValue(object, Objects.toString(value, EMPTY_STRING), regexToUse);
|
||||
}
|
||||
}
|
||||
|
||||
protected void validateFieldValue(Object object, String value, String regexToUse) {
|
||||
// string must not be empty
|
||||
String str = ((String) value).trim();
|
||||
String str = value.trim();
|
||||
if (str.length() == 0) {
|
||||
LOG.debug("Value is empty, please use a required validator");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -124,14 +143,19 @@ public class RegexFieldValidator extends FieldValidatorSupport {
|
||||
pattern = Pattern.compile(regexToUse, Pattern.CASE_INSENSITIVE);
|
||||
}
|
||||
|
||||
String compare = (String) value;
|
||||
if ( isTrimed() ) {
|
||||
String compare = value;
|
||||
if (isTrimed()) {
|
||||
compare = compare.trim();
|
||||
}
|
||||
Matcher matcher = pattern.matcher( compare );
|
||||
|
||||
if (!matcher.matches()) {
|
||||
addFieldError(fieldName, object);
|
||||
try {
|
||||
setCurrentValue(compare);
|
||||
Matcher matcher = pattern.matcher(compare);
|
||||
if (!matcher.matches()) {
|
||||
addFieldError(fieldName, object);
|
||||
}
|
||||
} finally {
|
||||
setCurrentValue(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+7
@@ -17,6 +17,9 @@ package com.opensymphony.xwork2.validator.validators;
|
||||
|
||||
import com.opensymphony.xwork2.validator.ValidationException;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.Collection;
|
||||
|
||||
|
||||
/**
|
||||
* <!-- START SNIPPET: javadoc -->
|
||||
@@ -63,6 +66,10 @@ public class RequiredFieldValidator extends FieldValidatorSupport {
|
||||
|
||||
if (value == null) {
|
||||
addFieldError(fieldName, object);
|
||||
} else if (value.getClass().isArray() && Array.getLength(value) == 0) {
|
||||
addFieldError(fieldName, object);
|
||||
} else if (Collection.class.isAssignableFrom(value.getClass()) && ((Collection) value).size() == 0) {
|
||||
addFieldError(fieldName, object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+40
-9
@@ -17,6 +17,7 @@ package com.opensymphony.xwork2.validator.validators;
|
||||
|
||||
import com.opensymphony.xwork2.validator.ValidationException;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* <!-- START SNIPPET: javadoc -->
|
||||
@@ -85,21 +86,51 @@ public class RequiredStringValidator extends FieldValidatorSupport {
|
||||
}
|
||||
|
||||
public void validate(Object object) throws ValidationException {
|
||||
String fieldName = getFieldName();
|
||||
Object value = this.getFieldValue(fieldName, object);
|
||||
Object fieldValue = this.getFieldValue(getFieldName(), object);
|
||||
|
||||
if (!(value instanceof String)) {
|
||||
addFieldError(fieldName, object);
|
||||
if (fieldValue == null) {
|
||||
addFieldError(getFieldName(), object);
|
||||
return;
|
||||
}
|
||||
|
||||
if (fieldValue.getClass().isArray()) {
|
||||
Object[] values = (Object[]) fieldValue;
|
||||
for (Object value : values) {
|
||||
validateValue(object, value);
|
||||
}
|
||||
} else if (Collection.class.isAssignableFrom(fieldValue.getClass())) {
|
||||
Collection values = (Collection) fieldValue;
|
||||
for (Object value : values) {
|
||||
validateValue(object, value);
|
||||
}
|
||||
} else {
|
||||
String s = (String) value;
|
||||
validateValue(object, fieldValue);
|
||||
}
|
||||
}
|
||||
|
||||
if (trim) {
|
||||
s = s.trim();
|
||||
protected void validateValue(Object object, Object fieldValue) {
|
||||
try {
|
||||
setCurrentValue(fieldValue);
|
||||
if (fieldValue == null) {
|
||||
addFieldError(getFieldName(), object);
|
||||
return;
|
||||
}
|
||||
|
||||
if (s.length() == 0) {
|
||||
addFieldError(fieldName, object);
|
||||
if (fieldValue instanceof String) {
|
||||
String stingValue = (String) fieldValue;
|
||||
|
||||
if (trim) {
|
||||
stingValue = stingValue.trim();
|
||||
}
|
||||
|
||||
if (stingValue.length() == 0) {
|
||||
addFieldError(getFieldName(), object);
|
||||
}
|
||||
} else {
|
||||
addFieldError(getFieldName(), object);
|
||||
}
|
||||
} finally {
|
||||
setCurrentValue(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+43
-11
@@ -17,6 +17,11 @@ package com.opensymphony.xwork2.validator.validators;
|
||||
|
||||
import com.opensymphony.xwork2.validator.ValidationException;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* <!-- START SNIPPET: javadoc -->
|
||||
@@ -84,6 +89,8 @@ import org.apache.commons.lang3.StringUtils;
|
||||
*/
|
||||
public class StringLengthFieldValidator extends FieldValidatorSupport {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(StringLengthFieldValidator.class);
|
||||
|
||||
private boolean trim = true;
|
||||
private int maxLength = -1;
|
||||
private int minLength = -1;
|
||||
@@ -138,17 +145,37 @@ public class StringLengthFieldValidator extends FieldValidatorSupport {
|
||||
}
|
||||
|
||||
public void validate(Object object) throws ValidationException {
|
||||
String fieldName = getFieldName();
|
||||
String val = (String) getFieldValue(fieldName, object);
|
||||
Object fieldValue = getFieldValue(fieldName, object);
|
||||
|
||||
if (StringUtils.isEmpty(val)) {
|
||||
// use a required validator for these
|
||||
if (fieldValue == null) {
|
||||
LOG.debug("Value for field {} is null, use a required validator", getFieldName());
|
||||
} else if (fieldValue.getClass().isArray()) {
|
||||
Object[] values = (Object[]) fieldValue;
|
||||
for (Object value : values) {
|
||||
validateValue(object, value);
|
||||
}
|
||||
} else if (Collection.class.isAssignableFrom(fieldValue.getClass())) {
|
||||
Collection values = (Collection) fieldValue;
|
||||
for (Object value : values) {
|
||||
validateValue(object, value);
|
||||
}
|
||||
} else {
|
||||
validateValue(object, fieldValue);
|
||||
}
|
||||
}
|
||||
|
||||
protected void validateValue(Object object, Object value) {
|
||||
String stringValue = Objects.toString(value, "");
|
||||
|
||||
if (StringUtils.isEmpty(stringValue)) {
|
||||
LOG.debug("Value is empty, use a required validator");
|
||||
return;
|
||||
}
|
||||
|
||||
if (isTrim()) {
|
||||
val = val.trim();
|
||||
if (val.length() <= 0) {
|
||||
// use a required validator
|
||||
stringValue = stringValue.trim();
|
||||
if (StringUtils.isEmpty(stringValue)) {
|
||||
LOG.debug("Value is empty, use a required validator");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -156,10 +183,15 @@ public class StringLengthFieldValidator extends FieldValidatorSupport {
|
||||
int minLengthToUse = getMinLength();
|
||||
int maxLengthToUse = getMaxLength();
|
||||
|
||||
if ((minLengthToUse > -1) && (val.length() < minLengthToUse)) {
|
||||
addFieldError(fieldName, object);
|
||||
} else if ((maxLengthToUse > -1) && (val.length() > maxLengthToUse)) {
|
||||
addFieldError(fieldName, object);
|
||||
try {
|
||||
setCurrentValue(stringValue);
|
||||
if ((minLengthToUse > -1) && (stringValue.length() < minLengthToUse)) {
|
||||
addFieldError(fieldName, object);
|
||||
} else if ((maxLengthToUse > -1) && (stringValue.length() > maxLengthToUse)) {
|
||||
addFieldError(fieldName, object);
|
||||
}
|
||||
} finally {
|
||||
setCurrentValue(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@ import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
@@ -65,19 +67,46 @@ public class URLValidator extends FieldValidatorSupport {
|
||||
private Pattern urlPattern = Pattern.compile(DEFAULT_URL_REGEX, Pattern.CASE_INSENSITIVE);
|
||||
|
||||
public void validate(Object object) throws ValidationException {
|
||||
String fieldName = getFieldName();
|
||||
Object value = this.getFieldValue(fieldName, object);
|
||||
Object value = getFieldValue(fieldName, object);
|
||||
|
||||
// if there is no value - don't do comparison
|
||||
// if a value is required, a required validator should be added to the field
|
||||
if (value == null || value.toString().length() == 0) {
|
||||
String stringValue = Objects.toString(value, EMPTY_STRING).trim();
|
||||
if (stringValue.length() == 0) {
|
||||
LOG.debug("Value for field {} is empty, won't ba validated, please use a required validator", fieldName);
|
||||
return;
|
||||
}
|
||||
|
||||
String stringValue = String.valueOf(value).trim();
|
||||
if (value.getClass().isArray()) {
|
||||
Object[] values = (Object[]) value;
|
||||
for (Object objValue : values) {
|
||||
LOG.debug("Validating element of array: {}", objValue);
|
||||
validateValue(object, objValue);
|
||||
}
|
||||
} else if (Collection.class.isAssignableFrom(value.getClass())) {
|
||||
Collection values = (Collection) value;
|
||||
for (Object objValue : values) {
|
||||
LOG.debug("Validating element of collection: {}", objValue);
|
||||
validateValue(object, objValue);
|
||||
}
|
||||
} else {
|
||||
LOG.debug("Validating field: {}", value);
|
||||
validateValue(object, value);
|
||||
}
|
||||
}
|
||||
|
||||
if (!(value.getClass().equals(String.class)) || !getUrlPattern().matcher(stringValue).matches()) {
|
||||
addFieldError(fieldName, object);
|
||||
protected void validateValue(Object object, Object value) {
|
||||
String stringValue = Objects.toString(value, EMPTY_STRING).trim();
|
||||
if (stringValue.length() == 0) {
|
||||
LOG.debug("Value for field {} is empty, won't ba validated, please use a required validator", fieldName);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setCurrentValue(value);
|
||||
if (!(value.getClass().equals(String.class)) || !getUrlPattern().matcher(stringValue).matches()) {
|
||||
addFieldError(fieldName, object);
|
||||
}
|
||||
} finally {
|
||||
setCurrentValue(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+16
-6
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package com.opensymphony.xwork2.validator.validators;
|
||||
|
||||
import com.opensymphony.xwork2.TextProviderFactory;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.util.TextParseUtil;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
import com.opensymphony.xwork2.validator.*;
|
||||
@@ -35,16 +37,24 @@ import java.util.List;
|
||||
*/
|
||||
public abstract class ValidatorSupport implements Validator, ShortCircuitableValidator {
|
||||
|
||||
protected final Logger log = LogManager.getLogger(this.getClass());
|
||||
private static final Logger LOG = LogManager.getLogger(ValidatorSupport.class);
|
||||
|
||||
public static final String EMPTY_STRING = "";
|
||||
|
||||
protected String defaultMessage = "";
|
||||
protected String messageKey;
|
||||
private ValidatorContext validatorContext;
|
||||
private boolean shortCircuit;
|
||||
private String type;
|
||||
private String[] messageParameters;
|
||||
protected ValueStack stack;
|
||||
|
||||
protected String defaultMessage = "";
|
||||
protected String messageKey;
|
||||
protected ValueStack stack;
|
||||
protected TextProviderFactory textProviderFactory;
|
||||
|
||||
@Inject
|
||||
public void setTextProviderFactory(TextProviderFactory textProviderFactory) {
|
||||
this.textProviderFactory = textProviderFactory;
|
||||
}
|
||||
|
||||
public void setValueStack(ValueStack stack) {
|
||||
this.stack = stack;
|
||||
@@ -76,7 +86,7 @@ public abstract class ValidatorSupport implements Validator, ShortCircuitableVal
|
||||
defaultMessage = messageKey;
|
||||
}
|
||||
if (validatorContext == null) {
|
||||
validatorContext = new DelegatingValidatorContext(object);
|
||||
validatorContext = new DelegatingValidatorContext(object, textProviderFactory);
|
||||
}
|
||||
List<Object> parsedMessageParameters = null;
|
||||
if (messageParameters != null) {
|
||||
@@ -89,7 +99,7 @@ public abstract class ValidatorSupport implements Validator, ShortCircuitableVal
|
||||
} catch (Exception e) {
|
||||
// if there's an exception in parsing, we'll just treat the expression itself as the
|
||||
// parameter
|
||||
log.warn("exception while parsing message parameter [{}]", messageParameter, e);
|
||||
LOG.warn("exception while parsing message parameter [{}]", messageParameter, e);
|
||||
parsedMessageParameters.add(messageParameter);
|
||||
}
|
||||
}
|
||||
|
||||
+26
-5
@@ -16,14 +16,20 @@
|
||||
package com.opensymphony.xwork2.validator.validators;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.CompositeTextProvider;
|
||||
import com.opensymphony.xwork2.TextProvider;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
import com.opensymphony.xwork2.validator.ActionValidatorManager;
|
||||
import com.opensymphony.xwork2.validator.DelegatingValidatorContext;
|
||||
import com.opensymphony.xwork2.validator.ValidationException;
|
||||
import com.opensymphony.xwork2.validator.ValidatorContext;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
@@ -81,6 +87,8 @@ import java.util.Collection;
|
||||
*/
|
||||
public class VisitorFieldValidator extends FieldValidatorSupport {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(VisitorFieldValidator.class);
|
||||
|
||||
private String context;
|
||||
private boolean appendPrefix = true;
|
||||
private ActionValidatorManager actionValidatorManager;
|
||||
@@ -121,7 +129,7 @@ public class VisitorFieldValidator extends FieldValidatorSupport {
|
||||
String fieldName = getFieldName();
|
||||
Object value = this.getFieldValue(fieldName, object);
|
||||
if (value == null) {
|
||||
log.warn("The visited object is null, VisitorValidator will not be able to handle validation properly. Please make sure the visited object is not null for VisitorValidator to function properly");
|
||||
LOG.warn("The visited object is null, VisitorValidator will not be able to handle validation properly. Please make sure the visited object is not null for VisitorValidator to function properly");
|
||||
return;
|
||||
}
|
||||
ValueStack stack = ActionContext.getContext().getValueStack();
|
||||
@@ -166,24 +174,37 @@ public class VisitorFieldValidator extends FieldValidatorSupport {
|
||||
ValidatorContext validatorContext;
|
||||
|
||||
if (appendPrefix) {
|
||||
validatorContext = new AppendingValidatorContext(getValidatorContext(), o, fieldName, getMessage(o));
|
||||
ValidatorContext parent = getValidatorContext();
|
||||
validatorContext = new AppendingValidatorContext(parent, createTextProvider(o, parent), fieldName, getMessage(o));
|
||||
} else {
|
||||
ValidatorContext parent = getValidatorContext();
|
||||
validatorContext = new DelegatingValidatorContext(parent, DelegatingValidatorContext.makeTextProvider(o, parent), parent);
|
||||
CompositeTextProvider textProvider = createTextProvider(o, parent);
|
||||
validatorContext = new DelegatingValidatorContext(parent, textProvider, parent);
|
||||
}
|
||||
|
||||
actionValidatorManager.validate(o, visitorContext, validatorContext);
|
||||
stack.pop();
|
||||
}
|
||||
|
||||
private CompositeTextProvider createTextProvider(Object o, ValidatorContext parent) {
|
||||
List<TextProvider> textProviders = new LinkedList<>();
|
||||
if (o instanceof TextProvider) {
|
||||
textProviders.add((TextProvider) o);
|
||||
} else {
|
||||
textProviders.add(textProviderFactory.createInstance(o.getClass()));
|
||||
}
|
||||
textProviders.add(parent);
|
||||
|
||||
return new CompositeTextProvider(textProviders);
|
||||
}
|
||||
|
||||
public static class AppendingValidatorContext extends DelegatingValidatorContext {
|
||||
private String field;
|
||||
private String message;
|
||||
private ValidatorContext parent;
|
||||
|
||||
public AppendingValidatorContext(ValidatorContext parent, Object object, String field, String message) {
|
||||
super(parent, makeTextProvider(object, parent), parent);
|
||||
public AppendingValidatorContext(ValidatorContext parent, TextProvider textProvider, String field, String message) {
|
||||
super(parent, textProvider, parent);
|
||||
|
||||
this.field = field;
|
||||
this.message = message;
|
||||
|
||||
@@ -139,6 +139,13 @@ public final class StrutsConstants {
|
||||
*/
|
||||
public static final String STRUTS_MULTIPART_PARSER = "struts.multipart.parser";
|
||||
|
||||
/**
|
||||
* A global switch to disable support for multipart requests
|
||||
*/
|
||||
public static final String STRUTS_MULTIPART_ENABLED = "struts.multipart.enabled";
|
||||
|
||||
public static final String STRUTS_MULTIPART_VALIDATION_REGEX = "struts.multipart.validationRegex";
|
||||
|
||||
/** How Spring should autowire. Valid values are 'name', 'type', 'auto', and 'constructor' */
|
||||
public static final String STRUTS_OBJECTFACTORY_SPRING_AUTOWIRE = "struts.objectFactory.spring.autoWire";
|
||||
|
||||
@@ -152,6 +159,7 @@ public final class StrutsConstants {
|
||||
public static final String STRUTS_OBJECTFACTORY_SPRING_USE_CLASS_CACHE = "struts.objectFactory.spring.useClassCache";
|
||||
|
||||
/** Uses different logic to construct beans, see https://issues.apache.org/jira/browse/WW-4110 */
|
||||
@Deprecated
|
||||
public static final String STRUTS_OBJECTFACTORY_SPRING_ENABLE_AOP_SUPPORT = "struts.objectFactory.spring.enableAopSupport";
|
||||
|
||||
/** Whether or not XSLT templates should not be cached */
|
||||
@@ -200,9 +208,16 @@ public final class StrutsConstants {
|
||||
/** XWork default text provider */
|
||||
public static final String STRUTS_XWORKTEXTPROVIDER = "struts.xworkTextProvider";
|
||||
|
||||
/** The {@link com.opensymphony.xwork2.LocaleProvider} implementation class */
|
||||
/**
|
||||
* The {@link com.opensymphony.xwork2.LocaleProvider} implementation class
|
||||
* @deprecated use {@link StrutsConstants#STRUTS_LOCALE_PROVIDER_FACTORY} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public static final String STRUTS_LOCALE_PROVIDER = "struts.localeProvider";
|
||||
|
||||
/** The {@link com.opensymphony.xwork2.LocaleProviderFactory} implementation class */
|
||||
public static final String STRUTS_LOCALE_PROVIDER_FACTORY = "struts.localeProviderFactory";
|
||||
|
||||
/** The name of the parameter to create when mapping an id (used by some action mappers) */
|
||||
public static final String STRUTS_ID_PARAMETER_NAME = "struts.mapper.idParameterName";
|
||||
|
||||
@@ -306,4 +321,10 @@ public final class StrutsConstants {
|
||||
public static final String STRUTS_CONTENT_TYPE_MATCHER = "struts.contentTypeMatcher";
|
||||
|
||||
public static final String STRUTS_SMI_METHOD_REGEX = "struts.strictMethodInvocation.methodRegex";
|
||||
|
||||
public static final String STRUTS_TEXT_PROVIDER_FACTORY = "struts.textProviderFactory";
|
||||
|
||||
public static final String STRUTS_LOCALIZED_TEXT_PROVIDER = "struts.localizedTextProvider";
|
||||
|
||||
public static final String STRUTS_DISALLOW_PROXY_MEMBER_ACCESS = "struts.disallowProxyMemberAccess";
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user