mirror of
https://github.com/apache/struts.git
synced 2026-08-10 17:16:58 +00:00
Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 05ca1ff087 | |||
| 199f356669 | |||
| 084c66723d | |||
| 4c37011020 | |||
| fbb31ee65b | |||
| c094052969 | |||
| 84164c422e | |||
| 8b11a3873a | |||
| 115fef2af7 | |||
| 0c9f9c3400 | |||
| 3e65ec1687 | |||
| 59932a5179 | |||
| 4746a49d17 | |||
| 059514d6ca | |||
| 5648721d07 | |||
| e3dff7691e | |||
| 38c63334a4 | |||
| fe7959e5d8 | |||
| d454384a4b | |||
| 1c3cab2e6f | |||
| b1e9eae01c | |||
| 804e1546f1 | |||
| f0b24d17da | |||
| 4d8108e766 | |||
| 14c3a14c8f |
+1
-1
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-parent</artifactId>
|
||||
<version>6.0.0-RC2</version>
|
||||
<version>6.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>struts2-apps</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
@@ -24,12 +24,12 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-apps</artifactId>
|
||||
<version>6.0.0-RC2</version>
|
||||
<version>6.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-rest-showcase</artifactId>
|
||||
<packaging>war</packaging>
|
||||
<version>6.0.0-RC2</version>
|
||||
<version>6.0.0-SNAPSHOT</version>
|
||||
<name>Struts 2 Rest Showcase Webapp</name>
|
||||
<description>Struts 2 Rest Showcase Example</description>
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-apps</artifactId>
|
||||
<version>6.0.0-RC2</version>
|
||||
<version>6.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-showcase</artifactId>
|
||||
|
||||
+14
-16
@@ -27,22 +27,24 @@ import org.apache.struts2.convention.annotation.Namespace;
|
||||
import org.apache.struts2.convention.annotation.ParentPackage;
|
||||
import org.apache.struts2.convention.annotation.Result;
|
||||
import org.apache.struts2.interceptor.validation.SkipValidation;
|
||||
import org.hibernate.validator.constraints.Email;
|
||||
import org.hibernate.validator.constraints.NotBlank;
|
||||
import org.hibernate.validator.constraints.ScriptAssert;
|
||||
import org.hibernate.validator.constraints.URL;
|
||||
|
||||
import javax.validation.constraints.*;
|
||||
import javax.validation.constraints.Email;
|
||||
import javax.validation.constraints.Max;
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Pattern;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* <!-- START SNIPPET: beanValidatationExample -->
|
||||
*/
|
||||
// <!-- START SNIPPET: beanValidationExample -->
|
||||
@Namespace("/bean-validation")
|
||||
@ParentPackage("bean-validation")
|
||||
@Action(results = {
|
||||
@Result(name = "input", location = "bean-validation.jsp"),
|
||||
@Result(name = "success", location = "/WEB-INF/validation/successFieldValidatorsExample.jsp")
|
||||
@Result(name = "input", location = "bean-validation.jsp"),
|
||||
@Result(name = "success", location = "/WEB-INF/validation/successFieldValidatorsExample.jsp")
|
||||
})
|
||||
@FieldMatch(first = "fieldExpressionValidatorField", second = "requiredValidatorField", message = "requiredValidatorField and fieldExpressionValidatorField are not matching")
|
||||
@ScriptAssert(lang = "javascript", script = "_this.dateValidatorField != null && _this.dateValidatorField.before(new java.util.Date())", message = "Date need to before now")
|
||||
@@ -82,10 +84,10 @@ public class BeanValidationExampleAction extends ActionSupport {
|
||||
private String fieldExpressionValidatorField = null;
|
||||
|
||||
@Action(value = "bean-validation", results = {
|
||||
@Result(name = "success", location = "bean-validation.jsp")
|
||||
@Result(name = "success", location = "bean-validation.jsp")
|
||||
})
|
||||
@SkipValidation
|
||||
public String beanValidation(){
|
||||
public String beanValidation() {
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
@@ -150,7 +152,7 @@ public class BeanValidationExampleAction extends ActionSupport {
|
||||
}
|
||||
|
||||
public void setFieldExpressionValidatorField(
|
||||
String fieldExpressionValidatorField) {
|
||||
String fieldExpressionValidatorField) {
|
||||
this.fieldExpressionValidatorField = fieldExpressionValidatorField;
|
||||
}
|
||||
|
||||
@@ -163,8 +165,4 @@ public class BeanValidationExampleAction extends ActionSupport {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <!-- END SNIPPET: beanValidatationExample -->
|
||||
*/
|
||||
|
||||
|
||||
// <!-- END SNIPPET: beanValidationExample -->
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-parent</artifactId>
|
||||
<version>6.0.0-RC2</version>
|
||||
<version>6.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-assembly</artifactId>
|
||||
|
||||
+3
-3
@@ -29,7 +29,7 @@
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-bom</artifactId>
|
||||
<version>6.0.0-RC2</version>
|
||||
<version>6.0.0-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<name>Struts 2 Bill of Materials</name>
|
||||
@@ -44,7 +44,7 @@
|
||||
</licenses>
|
||||
|
||||
<properties>
|
||||
<struts-version.version>6.0.0-RC2</struts-version.version>
|
||||
<struts-version.version>6.0.0-SNAPSHOT</struts-version.version>
|
||||
<maven.site.skip>true</maven.site.skip>
|
||||
<maven.site.deploy.skip>true</maven.site.deploy.skip>
|
||||
</properties>
|
||||
@@ -175,7 +175,7 @@
|
||||
</dependencyManagement>
|
||||
|
||||
<scm>
|
||||
<tag>STRUTS_6_0_0_RC2</tag>
|
||||
<tag>HEAD</tag>
|
||||
<connection>scm:git:https://gitbox.apache.org/repos/asf/struts.git</connection>
|
||||
<developerConnection>scm:git:https://gitbox.apache.org/repos/asf/struts.git</developerConnection>
|
||||
<url>https://github.com/apache/struts/</url>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-osgi-bundles</artifactId>
|
||||
<version>6.0.0-RC2</version>
|
||||
<version>6.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-osgi-admin-bundle</artifactId>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-osgi-bundles</artifactId>
|
||||
<version>6.0.0-RC2</version>
|
||||
<version>6.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-osgi-demo-bundle</artifactId>
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-parent</artifactId>
|
||||
<version>6.0.0-RC2</version>
|
||||
<version>6.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-osgi-bundles</artifactId>
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-parent</artifactId>
|
||||
<version>6.0.0-RC2</version>
|
||||
<version>6.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>struts2-core</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
@@ -28,6 +28,10 @@ import com.opensymphony.xwork2.conversion.*;
|
||||
import com.opensymphony.xwork2.conversion.impl.*;
|
||||
import com.opensymphony.xwork2.factory.*;
|
||||
import com.opensymphony.xwork2.inject.*;
|
||||
import com.opensymphony.xwork2.ognl.BeanInfoCacheFactory;
|
||||
import com.opensymphony.xwork2.ognl.DefaultOgnlBeanInfoCacheFactory;
|
||||
import com.opensymphony.xwork2.ognl.DefaultOgnlExpressionCacheFactory;
|
||||
import com.opensymphony.xwork2.ognl.ExpressionCacheFactory;
|
||||
import com.opensymphony.xwork2.ognl.OgnlReflectionProvider;
|
||||
import com.opensymphony.xwork2.ognl.OgnlUtil;
|
||||
import com.opensymphony.xwork2.ognl.OgnlValueStackFactory;
|
||||
@@ -79,30 +83,37 @@ public class DefaultConfiguration implements Configuration {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public PackageConfig getPackageConfig(String name) {
|
||||
return packageContexts.get(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UnknownHandlerConfig> getUnknownHandlerStack() {
|
||||
return unknownHandlerStack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUnknownHandlerStack(List<UnknownHandlerConfig> unknownHandlerStack) {
|
||||
this.unknownHandlerStack = unknownHandlerStack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getPackageConfigNames() {
|
||||
return packageContexts.keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, PackageConfig> getPackageConfigs() {
|
||||
return packageContexts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getLoadedFileNames() {
|
||||
return loadedFileNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RuntimeConfiguration getRuntimeConfiguration() {
|
||||
return runtimeConfiguration;
|
||||
}
|
||||
@@ -110,10 +121,12 @@ public class DefaultConfiguration implements Configuration {
|
||||
/**
|
||||
* @return the container
|
||||
*/
|
||||
@Override
|
||||
public Container getContainer() {
|
||||
return container;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPackageConfig(String name, PackageConfig packageContext) {
|
||||
PackageConfig check = packageContexts.get(name);
|
||||
if (check != null) {
|
||||
@@ -131,6 +144,7 @@ public class DefaultConfiguration implements Configuration {
|
||||
packageContexts.put(name, packageContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PackageConfig removePackageConfig(String packageName) {
|
||||
return packageContexts.remove(packageName);
|
||||
}
|
||||
@@ -138,11 +152,13 @@ public class DefaultConfiguration implements Configuration {
|
||||
/**
|
||||
* Allows the configuration to clean up any resources used
|
||||
*/
|
||||
@Override
|
||||
public void destroy() {
|
||||
packageContexts.clear();
|
||||
loadedFileNames.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rebuildRuntimeConfiguration() {
|
||||
runtimeConfiguration = buildRuntimeConfiguration();
|
||||
}
|
||||
@@ -151,10 +167,12 @@ public class DefaultConfiguration implements Configuration {
|
||||
* Calls the ConfigurationProviderFactory.getConfig() to tell it to reload the configuration and then calls
|
||||
* buildRuntimeConfiguration().
|
||||
*
|
||||
* @param providers list of ContainerProvider
|
||||
* @return list of package providers
|
||||
*
|
||||
* @throws ConfigurationException in case of any configuration errors
|
||||
*/
|
||||
@Override
|
||||
public synchronized List<PackageProvider> reloadContainer(List<ContainerProvider> providers) throws ConfigurationException {
|
||||
packageContexts.clear();
|
||||
loadedFileNames.clear();
|
||||
@@ -172,6 +190,7 @@ public class DefaultConfiguration implements Configuration {
|
||||
props.setConstants(builder);
|
||||
|
||||
builder.factory(Configuration.class, new Factory<Configuration>() {
|
||||
@Override
|
||||
public Configuration create(Context context) throws Exception {
|
||||
return DefaultConfiguration.this;
|
||||
}
|
||||
@@ -279,6 +298,8 @@ public class DefaultConfiguration implements Configuration {
|
||||
|
||||
builder.factory(ObjectTypeDeterminer.class, DefaultObjectTypeDeterminer.class, Scope.SINGLETON);
|
||||
builder.factory(PropertyAccessor.class, CompoundRoot.class.getName(), CompoundRootAccessor.class, Scope.SINGLETON);
|
||||
builder.factory(ExpressionCacheFactory.class, "defaultOgnlExpressionCacheFactory", DefaultOgnlExpressionCacheFactory.class, Scope.SINGLETON);
|
||||
builder.factory(BeanInfoCacheFactory.class, "defaultOgnlBeanInfoCacheFactory", DefaultOgnlBeanInfoCacheFactory.class, Scope.SINGLETON);
|
||||
builder.factory(OgnlUtil.class, Scope.SINGLETON);
|
||||
|
||||
builder.factory(ValueSubstitutor.class, EnvsValueSubstitutor.class, Scope.SINGLETON);
|
||||
@@ -417,10 +438,10 @@ public class DefaultConfiguration implements Configuration {
|
||||
|
||||
private static class RuntimeConfigurationImpl implements RuntimeConfiguration {
|
||||
|
||||
private Map<String, Map<String, ActionConfig>> namespaceActionConfigs;
|
||||
private Map<String, ActionConfigMatcher> namespaceActionConfigMatchers;
|
||||
private NamespaceMatcher namespaceMatcher;
|
||||
private Map<String, String> namespaceConfigs;
|
||||
private final Map<String, Map<String, ActionConfig>> namespaceActionConfigs;
|
||||
private final Map<String, ActionConfigMatcher> namespaceActionConfigMatchers;
|
||||
private final NamespaceMatcher namespaceMatcher;
|
||||
private final Map<String, String> namespaceConfigs;
|
||||
|
||||
public RuntimeConfigurationImpl(Map<String, Map<String, ActionConfig>> namespaceActionConfigs,
|
||||
Map<String, String> namespaceConfigs,
|
||||
@@ -448,6 +469,7 @@ public class DefaultConfiguration implements Configuration {
|
||||
* @param namespace the namespace for the action or null for the empty namespace, ""
|
||||
* @return the configuration information for action requested
|
||||
*/
|
||||
@Override
|
||||
public ActionConfig getActionConfig(String namespace, String name) {
|
||||
ActionConfig config = findActionConfigInNamespace(namespace, name);
|
||||
|
||||
@@ -503,6 +525,7 @@ public class DefaultConfiguration implements Configuration {
|
||||
*
|
||||
* @return a Map of namespace - > Map of ActionConfig objects, with the key being the action name
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Map<String, ActionConfig>> getActionConfigs() {
|
||||
return namespaceActionConfigs;
|
||||
}
|
||||
|
||||
+17
@@ -26,6 +26,8 @@ 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.BeanInfoCacheFactory;
|
||||
import com.opensymphony.xwork2.ognl.ExpressionCacheFactory;
|
||||
import com.opensymphony.xwork2.ognl.accessor.HttpParametersPropertyAccessor;
|
||||
import com.opensymphony.xwork2.ognl.accessor.ParameterPropertyAccessor;
|
||||
import com.opensymphony.xwork2.security.AcceptedPatternsChecker;
|
||||
@@ -56,6 +58,9 @@ import com.opensymphony.xwork2.conversion.impl.DateConverter;
|
||||
import com.opensymphony.xwork2.conversion.impl.DefaultConversionAnnotationProcessor;
|
||||
import com.opensymphony.xwork2.conversion.impl.DefaultConversionFileProcessor;
|
||||
import com.opensymphony.xwork2.security.NotExcludedAcceptedPatternsChecker;
|
||||
import org.apache.struts2.components.date.DateFormatter;
|
||||
import org.apache.struts2.components.date.DateTimeFormatterAdapter;
|
||||
import org.apache.struts2.components.date.SimpleDateFormatAdapter;
|
||||
import org.apache.struts2.conversion.StrutsConversionPropertiesProcessor;
|
||||
import com.opensymphony.xwork2.conversion.impl.DefaultObjectTypeDeterminer;
|
||||
import org.apache.struts2.conversion.StrutsTypeConverterCreator;
|
||||
@@ -91,6 +96,8 @@ 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.ognl.DefaultOgnlBeanInfoCacheFactory;
|
||||
import com.opensymphony.xwork2.ognl.DefaultOgnlExpressionCacheFactory;
|
||||
import com.opensymphony.xwork2.util.StrutsLocalizedTextProvider;
|
||||
import com.opensymphony.xwork2.util.OgnlTextParser;
|
||||
import com.opensymphony.xwork2.util.PatternMatcher;
|
||||
@@ -127,19 +134,24 @@ import java.util.Set;
|
||||
|
||||
public class StrutsDefaultConfigurationProvider implements ConfigurationProvider {
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Configuration configuration) throws ConfigurationException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadPackages() throws ConfigurationException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsReload() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(ContainerBuilder builder, LocatableProperties props)
|
||||
throws ConfigurationException {
|
||||
|
||||
@@ -205,6 +217,8 @@ public class StrutsDefaultConfigurationProvider implements ConfigurationProvider
|
||||
.factory(TextProviderFactory.class, StrutsTextProviderFactory.class, Scope.SINGLETON)
|
||||
.factory(LocaleProviderFactory.class, DefaultLocaleProviderFactory.class, Scope.SINGLETON)
|
||||
|
||||
.factory(ExpressionCacheFactory.class, "defaultOgnlExpressionCacheFactory", DefaultOgnlExpressionCacheFactory.class, Scope.SINGLETON)
|
||||
.factory(BeanInfoCacheFactory.class, "defaultOgnlBeanInfoCacheFactory", DefaultOgnlBeanInfoCacheFactory.class, Scope.SINGLETON)
|
||||
.factory(OgnlUtil.class, Scope.SINGLETON)
|
||||
.factory(CollectionConverter.class, Scope.SINGLETON)
|
||||
.factory(ArrayConverter.class, Scope.SINGLETON)
|
||||
@@ -218,6 +232,9 @@ public class StrutsDefaultConfigurationProvider implements ConfigurationProvider
|
||||
, Scope.SINGLETON)
|
||||
|
||||
.factory(ValueSubstitutor.class, EnvsValueSubstitutor.class, Scope.SINGLETON)
|
||||
|
||||
.factory(DateFormatter.class, "simpleDateFormatter", SimpleDateFormatAdapter.class, Scope.SINGLETON)
|
||||
.factory(DateFormatter.class, "dateTimeFormatter", DateTimeFormatterAdapter.class, Scope.SINGLETON)
|
||||
;
|
||||
|
||||
props.setProperty(StrutsConstants.STRUTS_ENABLE_DYNAMIC_METHOD_INVOCATION, Boolean.FALSE.toString());
|
||||
|
||||
+2
-1
@@ -38,7 +38,8 @@ import java.lang.annotation.Target;
|
||||
*
|
||||
* <p>
|
||||
* Application wide conversion:<br>
|
||||
* The conversion rules will be assembled within the <code>xwork-conversion.properties</code> file within the classpath root.
|
||||
* The conversion rules will be assembled within the <code>struts-conversion.properties</code> or
|
||||
* <code>xwork-conversion.properties</code> (deprecated) file within the classpath root.
|
||||
* Set type to: <code>type = ConversionType.APPLICATION</code>
|
||||
* </p>
|
||||
* <!-- END SNIPPET: description -->
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2022 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.ognl;
|
||||
|
||||
/**
|
||||
* A proxy interface to be used with Struts DI mechanism
|
||||
*/
|
||||
public interface BeanInfoCacheFactory<Key, Value> extends OgnlCacheFactory <Key, Value> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2022 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.ognl;
|
||||
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
|
||||
/**
|
||||
* Default OGNL Cache factory implementation.
|
||||
*
|
||||
* Currently used for BeanInfo cache creation.
|
||||
*
|
||||
* @param <Key> The type for the cache key entries
|
||||
* @param <Value> The type for the cache value entries
|
||||
*/
|
||||
public class DefaultOgnlBeanInfoCacheFactory<Key, Value> extends DefaultOgnlCacheFactory<Key, Value>
|
||||
implements BeanInfoCacheFactory<Key, Value> {
|
||||
|
||||
@Override
|
||||
@Inject(value = StrutsConstants.STRUTS_OGNL_BEANINFO_CACHE_MAXSIZE, required = false)
|
||||
protected void setCacheMaxSize(String maxSize) {
|
||||
super.setCacheMaxSize(maxSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Inject(value = StrutsConstants.STRUTS_OGNL_BEANINFO_CACHE_LRU_MODE, required = false)
|
||||
protected void setUseLRUCache(String useLRUMode) {
|
||||
super.setUseLRUCache(useLRUMode);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2022 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.ognl;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
|
||||
/**
|
||||
* Default OGNL Cache factory implementation.
|
||||
*
|
||||
* Currently used for Expression cache and BeanInfo cache creation.
|
||||
*
|
||||
* @param <Key> The type for the cache key entries
|
||||
* @param <Value> The type for the cache value entries
|
||||
*/
|
||||
public class DefaultOgnlCacheFactory<Key, Value> implements OgnlCacheFactory<Key, Value> {
|
||||
|
||||
private final AtomicBoolean useLRUCache = new AtomicBoolean(false);
|
||||
private final AtomicInteger cacheMaxSize = new AtomicInteger(25000);
|
||||
|
||||
@Override
|
||||
public OgnlCache<Key, Value> buildOgnlCache() {
|
||||
return buildOgnlCache(getCacheMaxSize(), 16, 0.75f, getUseLRUCache());
|
||||
}
|
||||
|
||||
@Override
|
||||
public OgnlCache<Key, Value> buildOgnlCache(int evictionLimit, int initialCapacity, float loadFactor, boolean lruCache) {
|
||||
if (lruCache) {
|
||||
return new OgnlLRUCache<>(evictionLimit, initialCapacity, loadFactor);
|
||||
} else {
|
||||
return new OgnlDefaultCache<>(evictionLimit, initialCapacity, loadFactor);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCacheMaxSize() {
|
||||
return cacheMaxSize.get();
|
||||
}
|
||||
|
||||
protected void setCacheMaxSize(String maxSize) {
|
||||
cacheMaxSize.set(Integer.parseInt(maxSize));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getUseLRUCache() {
|
||||
return useLRUCache.get();
|
||||
}
|
||||
|
||||
protected void setUseLRUCache(String useLRUMode) {
|
||||
useLRUCache.set(BooleanUtils.toBoolean(useLRUMode));
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2022 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.ognl;
|
||||
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
|
||||
/**
|
||||
* Default OGNL Expression Cache factory implementation.
|
||||
*
|
||||
* Currently used for Expression cache creation.
|
||||
*
|
||||
* @param <Key> The type for the cache key entries
|
||||
* @param <Value> The type for the cache value entries
|
||||
*/
|
||||
public class DefaultOgnlExpressionCacheFactory<Key, Value> extends DefaultOgnlCacheFactory<Key, Value>
|
||||
implements ExpressionCacheFactory<Key, Value> {
|
||||
|
||||
@Override
|
||||
@Inject(value = StrutsConstants.STRUTS_OGNL_EXPRESSION_CACHE_MAXSIZE, required = false)
|
||||
protected void setCacheMaxSize(String maxSize) {
|
||||
super.setCacheMaxSize(maxSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Inject(value = StrutsConstants.STRUTS_OGNL_EXPRESSION_CACHE_LRU_MODE, required = false)
|
||||
protected void setUseLRUCache(String useLRUMode) {
|
||||
super.setUseLRUCache(useLRUMode);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2022 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.ognl;
|
||||
|
||||
/**
|
||||
* A proxy interface to be used with Struts DI mechanism
|
||||
*/
|
||||
public interface ExpressionCacheFactory<Key, Value> extends OgnlCacheFactory <Key, Value> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2022 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.ognl;
|
||||
|
||||
/**
|
||||
* A basic cache interface for use with OGNL processing (such as Expression, BeanInfo).
|
||||
* All OGNL caches will have an eviction limit, but setting an extremely high value can
|
||||
* simulate an "effectively unlimited" cache.
|
||||
*
|
||||
* @param <Key> The type for the cache key entries
|
||||
* @param <Value> The type for the cache value entries
|
||||
*/
|
||||
public interface OgnlCache<Key, Value> {
|
||||
|
||||
public Value get(Key key);
|
||||
|
||||
public void put(Key key, Value value);
|
||||
|
||||
public void putIfAbsent(Key key, Value value);
|
||||
|
||||
public int size();
|
||||
|
||||
public void clear();
|
||||
|
||||
public int getEvictionLimit();
|
||||
|
||||
public void setEvictionLimit(int cacheEvictionLimit);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2022 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.ognl;
|
||||
|
||||
/**
|
||||
* Used by {@link com.opensymphony.xwork2.ognl.OgnlUtil} to create appropriate OGNL
|
||||
* caches based on configuration.
|
||||
*
|
||||
* @param <Key> The type for the cache key entries
|
||||
* @param <Value> The type for the cache value entries
|
||||
*/
|
||||
interface OgnlCacheFactory<Key, Value> {
|
||||
OgnlCache<Key, Value> buildOgnlCache();
|
||||
OgnlCache<Key, Value> buildOgnlCache(int evictionLimit, int initialCapacity, float loadFactor, boolean lruCache);
|
||||
int getCacheMaxSize();
|
||||
boolean getUseLRUCache();
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright 2022 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.ognl;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* Default OGNL cache implementation.
|
||||
*
|
||||
* Setting a very high eviction limit simulates an unlimited cache.
|
||||
* Setting too low an eviction limit will make the cache ineffective.
|
||||
*
|
||||
* @param <Key> The type for the cache key entries
|
||||
* @param <Value> The type for the cache value entries
|
||||
*/
|
||||
public class OgnlDefaultCache<Key, Value> implements OgnlCache<Key, Value> {
|
||||
|
||||
private final ConcurrentHashMap<Key, Value> ognlCache;
|
||||
private final AtomicInteger cacheEvictionLimit = new AtomicInteger(25000);
|
||||
|
||||
public OgnlDefaultCache(int evictionLimit, int initialCapacity, float loadFactor) {
|
||||
this.cacheEvictionLimit.set(evictionLimit);
|
||||
ognlCache = new ConcurrentHashMap<>(initialCapacity, loadFactor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value get(Key key) {
|
||||
return ognlCache.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(Key key, Value value) {
|
||||
ognlCache.put(key, value);
|
||||
this.clearIfEvictionLimitExceeded();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putIfAbsent(Key key, Value value) {
|
||||
ognlCache.putIfAbsent(key, value);
|
||||
this.clearIfEvictionLimitExceeded();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return ognlCache.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
ognlCache.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEvictionLimit() {
|
||||
return this.cacheEvictionLimit.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEvictionLimit(int cacheEvictionLimit) {
|
||||
this.cacheEvictionLimit.set(cacheEvictionLimit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the cache if the eviction limit has been exceeded.
|
||||
*/
|
||||
private void clearIfEvictionLimitExceeded() {
|
||||
if (ognlCache.size() > cacheEvictionLimit.get()) {
|
||||
ognlCache.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2022 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.ognl;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* A basic OGNL LRU cache implementation.
|
||||
*
|
||||
* The implementation utilizes a {@link Collections#synchronizedMap(java.util.Map)}
|
||||
* backed by a {@link LinkedHashMap}. May be replaced by a more efficient implementation in the future.
|
||||
*
|
||||
* Setting too low an eviction limit will produce more overhead than value.
|
||||
* Setting too high an eviction limit may also produce more overhead than value.
|
||||
* An appropriate eviction limit will need to be determined on an individual application basis.
|
||||
*
|
||||
* @param <Key> The type for the cache key entries
|
||||
* @param <Value> The type for the cache value entries
|
||||
*/
|
||||
public class OgnlLRUCache<Key, Value> implements OgnlCache<Key, Value> {
|
||||
|
||||
private final Map<Key, Value> ognlLRUCache;
|
||||
private final AtomicInteger cacheEvictionLimit = new AtomicInteger(2500);
|
||||
|
||||
public OgnlLRUCache(int evictionLimit, int initialCapacity, float loadFactor) {
|
||||
this.cacheEvictionLimit.set(evictionLimit);
|
||||
// Access-order mode selected (order mode true in LinkedHashMap constructor).
|
||||
ognlLRUCache = Collections.synchronizedMap (new LinkedHashMap<Key, Value>(initialCapacity, loadFactor, true) {
|
||||
@Override
|
||||
protected boolean removeEldestEntry(Map.Entry<Key,Value> eldest) {
|
||||
return (this.size() > cacheEvictionLimit.get());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value get(Key key) {
|
||||
return ognlLRUCache.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(Key key, Value value) {
|
||||
ognlLRUCache.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putIfAbsent(Key key, Value value) {
|
||||
ognlLRUCache.putIfAbsent(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return ognlLRUCache.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
ognlLRUCache.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEvictionLimit() {
|
||||
return this.cacheEvictionLimit.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEvictionLimit(int cacheEvictionLimit) {
|
||||
this.cacheEvictionLimit.set(cacheEvictionLimit);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -38,8 +38,6 @@ import java.beans.Introspector;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -57,8 +55,8 @@ public class OgnlUtil {
|
||||
// Flag used to reduce flooding logs with WARNs about using DevMode excluded packages
|
||||
private final AtomicBoolean warnReported = new AtomicBoolean(false);
|
||||
|
||||
private final ConcurrentMap<String, Object> expressions = new ConcurrentHashMap<>();
|
||||
private final ConcurrentMap<Class<?>, BeanInfo> beanInfoCache = new ConcurrentHashMap<>();
|
||||
private final OgnlCache<String, Object> expressionCache;
|
||||
private final OgnlCache<Class<?>, BeanInfo> beanInfoCache;
|
||||
private TypeConverter defaultConverter;
|
||||
|
||||
private boolean devMode;
|
||||
@@ -78,7 +76,31 @@ public class OgnlUtil {
|
||||
private boolean allowStaticMethodAccess;
|
||||
private boolean disallowProxyMemberAccess;
|
||||
|
||||
/**
|
||||
* Construct a new OgnlUtil instance for use with the framework
|
||||
*
|
||||
* @deprecated It is recommended to utilize the {@link OgnlUtil#OgnlUtil(com.opensymphony.xwork2.ognl.ExpressionCacheFactory, com.opensymphony.xwork2.ognl.BeanInfoCacheFactory) method instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public OgnlUtil() {
|
||||
this(null, null); // Instantiate default Expression and BeanInfo caches (null factories)
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new OgnlUtil instance for use with the framework, with optional
|
||||
* cache factories for OGNL Expression and BeanInfo caches.
|
||||
*
|
||||
* NOTE: Although the extension points are defined for the optional cache factories, developer-defined overrides do
|
||||
* do not appear to function at this time (it always appears to instantiate the default factories).
|
||||
* Construction injectors do not allow the optional flag, so the definitions must be defined.
|
||||
*
|
||||
* @param ognlExpressionCacheFactory factory for Expression cache instance. If null, it uses a default
|
||||
* @param ognlBeanInfoCacheFactory factory for BeanInfo cache instance. If null, it uses a default
|
||||
*/
|
||||
public OgnlUtil(
|
||||
@Inject(value = StrutsConstants.STRUTS_OGNL_EXPRESSION_CACHE_FACTORY, required = false) ExpressionCacheFactory<String, Object> ognlExpressionCacheFactory,
|
||||
@Inject(value = StrutsConstants.STRUTS_OGNL_BEANINFO_CACHE_FACTORY, required = false) BeanInfoCacheFactory<Class<?>, BeanInfo> ognlBeanInfoCacheFactory
|
||||
) {
|
||||
excludedClasses = Collections.unmodifiableSet(new HashSet<>());
|
||||
excludedPackageNamePatterns = Collections.unmodifiableSet(new HashSet<>());
|
||||
excludedPackageNames = Collections.unmodifiableSet(new HashSet<>());
|
||||
@@ -86,6 +108,12 @@ public class OgnlUtil {
|
||||
devModeExcludedClasses = Collections.unmodifiableSet(new HashSet<>());
|
||||
devModeExcludedPackageNamePatterns = Collections.unmodifiableSet(new HashSet<>());
|
||||
devModeExcludedPackageNames = Collections.unmodifiableSet(new HashSet<>());
|
||||
|
||||
OgnlCacheFactory<String, Object> ognlExpressionCacheFactory1 = (ognlExpressionCacheFactory != null ? ognlExpressionCacheFactory : new DefaultOgnlExpressionCacheFactory<>());
|
||||
OgnlCacheFactory<Class<?>, BeanInfo> ognlBeanInfoCacheFactory1 = (ognlBeanInfoCacheFactory != null ? ognlBeanInfoCacheFactory : new DefaultOgnlBeanInfoCacheFactory<>());
|
||||
|
||||
this.expressionCache = ognlExpressionCacheFactory1.buildOgnlCache();
|
||||
this.beanInfoCache = ognlBeanInfoCacheFactory1.buildOgnlCache();
|
||||
}
|
||||
|
||||
@Inject
|
||||
@@ -103,6 +131,16 @@ public class OgnlUtil {
|
||||
enableExpressionCache = BooleanUtils.toBoolean(cache);
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_OGNL_EXPRESSION_CACHE_MAXSIZE, required = false)
|
||||
protected void setExpressionCacheMaxSize(String maxSize) {
|
||||
expressionCache.setEvictionLimit(Integer.parseInt(maxSize));
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_OGNL_BEANINFO_CACHE_MAXSIZE, required = false)
|
||||
protected void setBeanInfoCacheMaxSize(String maxSize) {
|
||||
beanInfoCache.setEvictionLimit(Integer.parseInt(maxSize));
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_OGNL_ENABLE_EVAL_EXPRESSION, required = false)
|
||||
protected void setEnableEvalExpression(String evalExpression) {
|
||||
this.enableEvalExpression = BooleanUtils.toBoolean(evalExpression);
|
||||
@@ -273,7 +311,7 @@ public class OgnlUtil {
|
||||
* @since 2.5.21
|
||||
*/
|
||||
public void clearExpressionCache() {
|
||||
expressions.clear();
|
||||
expressionCache.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -284,7 +322,7 @@ public class OgnlUtil {
|
||||
* @since 2.5.21
|
||||
*/
|
||||
public int expressionCacheSize() {
|
||||
return expressions.size();
|
||||
return expressionCache.size();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -532,11 +570,11 @@ public class OgnlUtil {
|
||||
private <T> Object compileAndExecute(String expression, Map<String, Object> context, OgnlTask<T> task) throws OgnlException {
|
||||
Object tree;
|
||||
if (enableExpressionCache) {
|
||||
tree = expressions.get(expression);
|
||||
tree = expressionCache.get(expression);
|
||||
if (tree == null) {
|
||||
tree = Ognl.parseExpression(expression);
|
||||
checkEnableEvalExpression(tree, context);
|
||||
expressions.putIfAbsent(expression, tree);
|
||||
expressionCache.putIfAbsent(expression, tree);
|
||||
}
|
||||
} else {
|
||||
tree = Ognl.parseExpression(expression);
|
||||
@@ -549,11 +587,11 @@ public class OgnlUtil {
|
||||
private <T> Object compileAndExecuteMethod(String expression, Map<String, Object> context, OgnlTask<T> task) throws OgnlException {
|
||||
Object tree;
|
||||
if (enableExpressionCache) {
|
||||
tree = expressions.get(expression);
|
||||
tree = expressionCache.get(expression);
|
||||
if (tree == null) {
|
||||
tree = Ognl.parseExpression(expression);
|
||||
checkSimpleMethod(tree, context);
|
||||
expressions.putIfAbsent(expression, tree);
|
||||
expressionCache.putIfAbsent(expression, tree);
|
||||
}
|
||||
} else {
|
||||
tree = Ognl.parseExpression(expression);
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
package org.apache.struts2;
|
||||
|
||||
import org.apache.struts2.components.date.DateFormatter;
|
||||
import org.apache.struts2.dispatcher.mapper.CompositeActionMapper;
|
||||
|
||||
/**
|
||||
@@ -252,6 +253,47 @@ public final class StrutsConstants {
|
||||
/** Throw RuntimeException when a property is not found, or the evaluation of the expression fails */
|
||||
public static final String STRUTS_EL_THROW_EXCEPTION = "struts.el.throwExceptionOnFailure";
|
||||
|
||||
/**
|
||||
* Specifies an OGNL expression cache factory implementation. A default implementation is provided, but
|
||||
* could be replaced by a custom one if desired.
|
||||
*
|
||||
* @since 2.6
|
||||
*/
|
||||
public static final String STRUTS_OGNL_EXPRESSION_CACHE_FACTORY = "struts.ognl.expressionCacheFactory";
|
||||
|
||||
/**
|
||||
* Specifies an OGNL BeanInfo cache factory implementation. A default implementation is provided, but
|
||||
* could be replaced by a custom one if desired.
|
||||
*
|
||||
* @since 2.6
|
||||
*/
|
||||
public static final String STRUTS_OGNL_BEANINFO_CACHE_FACTORY = "struts.ognl.beanInfoCacheFactory";
|
||||
|
||||
/**
|
||||
* Specifies a maximum number of cached BeanInfo used by OgnlUtility. Not specified/set by default. If
|
||||
* a positive integer is specified, it will set a limit whose behaviour depends on whether the
|
||||
* normal (default) cache or optional LRU cache is in place.
|
||||
*
|
||||
* For the normal (default) cache, exceeding the maximum will cause the entire cache to flush (clear).
|
||||
* For the optional LRU cache, once the maximum is reached, the least-recently-used (LRU) entry will be
|
||||
* removed when a new entry needs to be added (cache is fully-utilized).
|
||||
*
|
||||
* @since 2.6
|
||||
*/
|
||||
public static final String STRUTS_OGNL_BEANINFO_CACHE_MAXSIZE = "struts.ognl.beanInfoCacheMaxSize";
|
||||
|
||||
/**
|
||||
* Set the cache mode of the BeanInfo cache used by OgnlUtility. A value of true means enable
|
||||
* least-recently-used (LRU) mode, a value of false (or any non-true value) means to use the
|
||||
* default cache.
|
||||
*
|
||||
* Note: When enabling LRU cache mode you must also set a maximum size (via {@link #STRUTS_OGNL_BEANINFO_CACHE_MAXSIZE})
|
||||
* for it to be effective. Otherwise, there is no condition to evict a LRU entry (cache has no limit).
|
||||
*
|
||||
* @since 2.6
|
||||
*/
|
||||
public static final String STRUTS_OGNL_BEANINFO_CACHE_LRU_MODE = "struts.ognl.beanInfoCacheLRUMode";
|
||||
|
||||
/**
|
||||
* Logs properties that are not found (very verbose)
|
||||
* @since 2.6
|
||||
@@ -277,6 +319,31 @@ public final class StrutsConstants {
|
||||
*/
|
||||
public static final String STRUTS_ENABLE_OGNL_EXPRESSION_CACHE = STRUTS_OGNL_ENABLE_EXPRESSION_CACHE;
|
||||
|
||||
/**
|
||||
* Specifies a maximum number of cached parsed OGNL expressions. Not specified/set by default. If
|
||||
* a positive integer is specified, it will set a limit whose behaviour depends on whether the
|
||||
* normal (default) cache or optional LRU cache is in place.
|
||||
*
|
||||
* For the normal (default) cache, exceeding the maximum will cause the entire cache to flush (clear).
|
||||
* For the optional LRU cache, once the maximum is reached, the least-recently-used (LRU) entry will be
|
||||
* removed when a new entry needs to be added (cache is fully-utilized).
|
||||
*
|
||||
* @since 2.6
|
||||
*/
|
||||
public static final String STRUTS_OGNL_EXPRESSION_CACHE_MAXSIZE = "struts.ognl.expressionCacheMaxSize";
|
||||
|
||||
/**
|
||||
* Set the cache mode of the parsed OGNL expression cache. A value of true means enable
|
||||
* least-recently-used (LRU) mode, a value of false (or any non-true value) means to use the
|
||||
* default cache.
|
||||
*
|
||||
* Note: When enabling LRU cache mode you must also set a maximum size (via {@link #STRUTS_OGNL_EXPRESSION_CACHE_MAXSIZE})
|
||||
* for it to be effective. Otherwise, there is no condition to evict a LRU entry (cache has no limit).
|
||||
*
|
||||
* @since 2.6
|
||||
*/
|
||||
public static final String STRUTS_OGNL_EXPRESSION_CACHE_LRU_MODE = "struts.ognl.expressionCacheLRUMode";
|
||||
|
||||
/**
|
||||
* Enables evaluation of OGNL expressions
|
||||
* @since 2.6
|
||||
@@ -384,4 +451,6 @@ public final class StrutsConstants {
|
||||
public static final String STRUTS_CHAINING_COPY_MESSAGES = "struts.chaining.copyMessages";
|
||||
public static final String STRUTS_OBJECT_FACTORY_CLASSLOADER = "struts.objectFactory.classloader";
|
||||
|
||||
/** See {@link org.apache.struts2.components.Date#setDateFormatter(DateFormatter)} */
|
||||
public static final String STRUTS_DATE_FORMATTER = "struts.date.formatter";
|
||||
}
|
||||
|
||||
@@ -75,7 +75,8 @@ public class Checkbox extends UIBean {
|
||||
}
|
||||
|
||||
if (submitUnchecked != null) {
|
||||
addParameter("submitUnchecked", findValue(submitUnchecked, Boolean.class));
|
||||
Object parsedValue = findValue(submitUnchecked, Boolean.class);
|
||||
addParameter("submitUnchecked", parsedValue == null ? Boolean.valueOf(submitUnchecked) : parsedValue);
|
||||
} else {
|
||||
addParameter("submitUnchecked", false);
|
||||
}
|
||||
|
||||
@@ -384,9 +384,8 @@ public class Component {
|
||||
return expression;
|
||||
}
|
||||
} else {
|
||||
expression = stripExpression(expression);
|
||||
|
||||
return getStack().findValue(expression, toType, throwExceptionOnELFailure);
|
||||
String strippedExpression = stripExpression(expression);
|
||||
return getStack().findValue(strippedExpression, toType, throwExceptionOnELFailure);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,29 +18,29 @@
|
||||
*/
|
||||
package org.apache.struts2.components;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.TextProvider;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.struts2.components.date.DateFormatter;
|
||||
import org.apache.struts2.views.annotations.StrutsTag;
|
||||
import org.apache.struts2.views.annotations.StrutsTagAttribute;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.FormatStyle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <!-- START SNIPPET: javadoc -->
|
||||
*
|
||||
* <p>
|
||||
* Format Date object in different ways.
|
||||
* <p>
|
||||
* The date tag will allow you to format a Date in a quick and easy way.
|
||||
@@ -59,6 +59,12 @@ import java.util.List;
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* <b>Note</b>: Since Struts 2.6 a new Java 8 API has been used to format the Date, it's based on
|
||||
* <a href="https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html">DateTimeFormatter</a>
|
||||
* which uses a bit different patterns.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Configurable attributes are:
|
||||
* </p>
|
||||
*
|
||||
@@ -130,8 +136,8 @@ import java.util.List;
|
||||
* <td>if one is not found DateFormat.MEDIUM format will be used</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
*
|
||||
*
|
||||
* <p>
|
||||
* <p>
|
||||
* <!-- END SNIPPET: javadoc -->
|
||||
*
|
||||
* <p><b>Examples</b></p>
|
||||
@@ -145,12 +151,12 @@ import java.util.List;
|
||||
* </pre>
|
||||
*
|
||||
* <code>Date</code>
|
||||
*
|
||||
*/
|
||||
@StrutsTag(name="date", tldBodyContent="empty", tldTagClass="org.apache.struts2.views.jsp.DateTag", description="Render a formatted date.")
|
||||
@StrutsTag(name = "date", tldBodyContent = "empty", tldTagClass = "org.apache.struts2.views.jsp.DateTag", description = "Render a formatted date.")
|
||||
public class Date extends ContextBean {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(Date.class);
|
||||
|
||||
/**
|
||||
* Property name to fall back when no format is specified
|
||||
*/
|
||||
@@ -202,17 +208,18 @@ public class Date extends ContextBean {
|
||||
|
||||
private String timezone;
|
||||
|
||||
private DateFormatter dateFormatter;
|
||||
|
||||
public Date(ValueStack stack) {
|
||||
super(stack);
|
||||
}
|
||||
|
||||
private TextProvider findProviderInStack() {
|
||||
for (Object o : getStack().getRoot()) {
|
||||
if (o instanceof TextProvider) {
|
||||
return (TextProvider) o;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
/**
|
||||
* An instance of {@link DateFormatter}
|
||||
*/
|
||||
@Inject
|
||||
public void setDateFormatter(DateFormatter dateFormatter) {
|
||||
this.dateFormatter = dateFormatter;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -280,6 +287,8 @@ public class Date extends ContextBean {
|
||||
|
||||
@Override
|
||||
public boolean end(Writer writer, String body) {
|
||||
TextProvider textProvider = findProviderInStack();
|
||||
|
||||
ZonedDateTime date = null;
|
||||
final ZoneId tz = getTimeZone();
|
||||
// find the name on the valueStack
|
||||
@@ -292,26 +301,27 @@ public class Date extends ContextBean {
|
||||
date = Instant.ofEpochMilli((long) dateObject).atZone(tz);
|
||||
} else if (dateObject instanceof LocalDateTime) {
|
||||
date = ((LocalDateTime) dateObject).atZone(tz);
|
||||
} else if (dateObject instanceof LocalDate) {
|
||||
date = ((LocalDate) dateObject).atStartOfDay(tz);
|
||||
} else if (dateObject instanceof Instant) {
|
||||
date = ((Instant) dateObject).atZone(tz);
|
||||
} else {
|
||||
if (devMode) {
|
||||
TextProvider tp = findProviderInStack();
|
||||
String developerNotification = "";
|
||||
if (tp != null) {
|
||||
developerNotification = findProviderInStack().getText(
|
||||
"devmode.notification",
|
||||
"Developer Notification:\n{0}",
|
||||
new String[]{
|
||||
"Expression [" + name + "] passed to <s:date/> tag which was evaluated to [" + dateObject + "]("
|
||||
+ (dateObject != null ? dateObject.getClass() : "null") + ") isn't supported!"
|
||||
}
|
||||
if (textProvider != null) {
|
||||
developerNotification = textProvider.getText(
|
||||
"devmode.notification",
|
||||
"Developer Notification:\n{0}",
|
||||
new String[]{
|
||||
"Expression [" + name + "] passed to <s:date/> tag which was evaluated to [" + dateObject + "]("
|
||||
+ (dateObject != null ? dateObject.getClass() : "null") + ") isn't supported!"
|
||||
}
|
||||
);
|
||||
}
|
||||
LOG.warn(developerNotification);
|
||||
} else {
|
||||
LOG.debug("Expression [{}] passed to <s:date/> tag which was evaluated to [{}]({}) isn't supported!",
|
||||
name, dateObject, (dateObject != null ? dateObject.getClass() : "null"));
|
||||
name, dateObject, (dateObject != null ? dateObject.getClass() : "null"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -321,33 +331,11 @@ public class Date extends ContextBean {
|
||||
}
|
||||
String msg;
|
||||
if (date != null) {
|
||||
TextProvider tp = findProviderInStack();
|
||||
if (tp != null) {
|
||||
if (textProvider != null) {
|
||||
if (nice) {
|
||||
msg = formatTime(tp, date);
|
||||
msg = formatTime(textProvider, date);
|
||||
} else {
|
||||
DateTimeFormatter dtf;
|
||||
if (format == null) {
|
||||
String globalFormat = null;
|
||||
|
||||
// if the format is not specified, fall back using the
|
||||
// defined property DATETAG_PROPERTY
|
||||
globalFormat = tp.getText(DATETAG_PROPERTY);
|
||||
|
||||
// if tp.getText can not find the property then the
|
||||
// returned string is the same as input =
|
||||
// DATETAG_PROPERTY
|
||||
if (globalFormat != null
|
||||
&& !DATETAG_PROPERTY.equals(globalFormat)) {
|
||||
dtf = DateTimeFormatter.ofPattern(globalFormat, ActionContext.getContext().getLocale());
|
||||
} else {
|
||||
dtf = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)
|
||||
.withLocale(ActionContext.getContext().getLocale());
|
||||
}
|
||||
} else {
|
||||
dtf = DateTimeFormatter.ofPattern(format, ActionContext.getContext().getLocale());
|
||||
}
|
||||
msg = dtf.format(date);
|
||||
msg = formatDate(textProvider, date);
|
||||
}
|
||||
if (msg != null) {
|
||||
try {
|
||||
@@ -365,6 +353,20 @@ public class Date extends ContextBean {
|
||||
return super.end(writer, "");
|
||||
}
|
||||
|
||||
private String formatDate(TextProvider textProvider, ZonedDateTime date) {
|
||||
String useFormat = format;
|
||||
if (useFormat == null) {
|
||||
// if the format is not specified, fall back using the defined property DATETAG_PROPERTY
|
||||
useFormat = textProvider.getText(DATETAG_PROPERTY);
|
||||
if (DATETAG_PROPERTY.equals(useFormat)) {
|
||||
// if tp.getText can not find the property then the
|
||||
// returned string is the same as input = DATETAG_PROPERTY
|
||||
useFormat = null;
|
||||
}
|
||||
}
|
||||
return dateFormatter.format(date, useFormat);
|
||||
}
|
||||
|
||||
private ZoneId getTimeZone() {
|
||||
ZoneId tz = ZoneId.systemDefault();
|
||||
if (timezone != null) {
|
||||
@@ -378,17 +380,26 @@ public class Date extends ContextBean {
|
||||
return tz;
|
||||
}
|
||||
|
||||
@StrutsTagAttribute(description="Date or DateTime format pattern", rtexprvalue=false)
|
||||
private TextProvider findProviderInStack() {
|
||||
for (Object o : getStack().getRoot()) {
|
||||
if (o instanceof TextProvider) {
|
||||
return (TextProvider) o;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@StrutsTagAttribute(description = "Date or DateTime format pattern")
|
||||
public void setFormat(String format) {
|
||||
this.format = format;
|
||||
}
|
||||
|
||||
@StrutsTagAttribute(description="Whether to print out the date nicely", type="Boolean", defaultValue="false")
|
||||
@StrutsTagAttribute(description = "Whether to print out the date nicely", type = "Boolean", defaultValue = "false")
|
||||
public void setNice(boolean nice) {
|
||||
this.nice = nice;
|
||||
}
|
||||
|
||||
@StrutsTagAttribute(description = "The specific timezone in which to format the date", required = false)
|
||||
@StrutsTagAttribute(description = "The specific timezone in which to format the date")
|
||||
public void setTimezone(String timezone) {
|
||||
this.timezone = timezone;
|
||||
}
|
||||
@@ -400,7 +411,7 @@ public class Date extends ContextBean {
|
||||
return name;
|
||||
}
|
||||
|
||||
@StrutsTagAttribute(description="The date value to format", required=true)
|
||||
@StrutsTagAttribute(description = "The date value to format", required = true)
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@@ -702,11 +702,13 @@ public abstract class UIBean extends Component {
|
||||
}
|
||||
|
||||
if (requiredLabel != null) {
|
||||
addParameter("required", findValue(requiredLabel, Boolean.class));
|
||||
Object parsedValue = findValue(requiredLabel, Boolean.class);
|
||||
addParameter("required", parsedValue == null ? Boolean.valueOf(requiredLabel) : parsedValue);
|
||||
}
|
||||
|
||||
if (disabled != null) {
|
||||
addParameter("disabled", findValue(disabled, Boolean.class));
|
||||
Object parsedValue = findValue(disabled, Boolean.class);
|
||||
addParameter("disabled", parsedValue == null ? Boolean.valueOf(disabled) : parsedValue);
|
||||
}
|
||||
|
||||
if (tabindex != null) {
|
||||
@@ -886,9 +888,9 @@ public abstract class UIBean extends Component {
|
||||
this.addParameter("tooltipDelay", findString(this.tooltipDelay));
|
||||
|
||||
if (this.javascriptTooltip != null) {
|
||||
Boolean jsTooltips = (Boolean) findValue(this.javascriptTooltip, Boolean.class);
|
||||
Object jsTooltips = findValue(this.javascriptTooltip, Boolean.class);
|
||||
//TODO use a Boolean model when tooltipConfig is dropped
|
||||
this.addParameter("jsTooltipEnabled", jsTooltips.toString());
|
||||
this.addParameter("jsTooltipEnabled", jsTooltips == null ? this.javascriptTooltip : jsTooltips.toString());
|
||||
|
||||
if (form != null)
|
||||
form.addParameter("hasTooltip", jsTooltips);
|
||||
@@ -968,7 +970,7 @@ public abstract class UIBean extends Component {
|
||||
// 1] UI component's tooltipConfig attribute OR
|
||||
// 2] <param name="tooltip" value="" /> param tag value attribute
|
||||
|
||||
result = new LinkedHashMap<>((Map) tooltipConfigObj);
|
||||
result = new LinkedHashMap<String, String>((Map) tooltipConfigObj);
|
||||
} else if (tooltipConfigObj instanceof String) {
|
||||
|
||||
// we get this if its configured using
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.components.date;
|
||||
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
|
||||
/**
|
||||
* Allows defines a wrapper around different formatting APIs, like old SimpleDateFormat
|
||||
* and new DateTimeFormatter introduced in Java 8 Date/Time API
|
||||
* <p>
|
||||
* New instance will be injected using {@link org.apache.struts2.StrutsConstants#STRUTS_DATE_FORMATTER}
|
||||
*/
|
||||
public interface DateFormatter {
|
||||
|
||||
/**
|
||||
* Formats provided temporal with the given format
|
||||
*
|
||||
* @param temporal Java 8 {@link TemporalAccessor}
|
||||
* @param format implementation specific format
|
||||
* @return a string representation of the formatted `temporal`
|
||||
*/
|
||||
String format(TemporalAccessor temporal, String format);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.components.date;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.FormatStyle;
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
import java.util.Locale;
|
||||
|
||||
public class DateTimeFormatterAdapter implements DateFormatter {
|
||||
|
||||
@Override
|
||||
public String format(TemporalAccessor temporal, String format) {
|
||||
DateTimeFormatter dtf;
|
||||
Locale locale = ActionContext.getContext().getLocale();
|
||||
if (format == null) {
|
||||
dtf = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)
|
||||
.withLocale(locale);
|
||||
} else {
|
||||
dtf = DateTimeFormatter.ofPattern(format, locale);
|
||||
}
|
||||
return dtf.format(temporal);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.components.date;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
public class SimpleDateFormatAdapter implements DateFormatter {
|
||||
|
||||
@Override
|
||||
public String format(TemporalAccessor temporal, String format) {
|
||||
DateFormat df;
|
||||
Locale locale = ActionContext.getContext().getLocale();
|
||||
if (format == null) {
|
||||
df = SimpleDateFormat.getDateInstance(DateFormat.MEDIUM, locale);
|
||||
} else {
|
||||
df = new SimpleDateFormat(format, locale);
|
||||
}
|
||||
return df.format(new Date(Instant.from(temporal).toEpochMilli()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -23,6 +23,8 @@ import com.opensymphony.xwork2.LocaleProviderFactory;
|
||||
import com.opensymphony.xwork2.LocalizedTextProvider;
|
||||
import com.opensymphony.xwork2.TextProviderFactory;
|
||||
import com.opensymphony.xwork2.factory.UnknownHandlerFactory;
|
||||
import com.opensymphony.xwork2.ognl.BeanInfoCacheFactory;
|
||||
import com.opensymphony.xwork2.ognl.ExpressionCacheFactory;
|
||||
import com.opensymphony.xwork2.security.AcceptedPatternsChecker;
|
||||
import com.opensymphony.xwork2.security.ExcludedPatternsChecker;
|
||||
import com.opensymphony.xwork2.FileManager;
|
||||
@@ -54,13 +56,12 @@ import com.opensymphony.xwork2.util.PatternMatcher;
|
||||
import com.opensymphony.xwork2.util.TextParser;
|
||||
import com.opensymphony.xwork2.util.ValueStackFactory;
|
||||
import com.opensymphony.xwork2.util.location.LocatableProperties;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import com.opensymphony.xwork2.util.reflection.ReflectionContextFactory;
|
||||
import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
|
||||
import com.opensymphony.xwork2.validator.ActionValidatorManager;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
import org.apache.struts2.components.UrlRenderer;
|
||||
import org.apache.struts2.components.date.DateFormatter;
|
||||
import org.apache.struts2.dispatcher.DispatcherErrorHandler;
|
||||
import org.apache.struts2.dispatcher.StaticContentLoader;
|
||||
import org.apache.struts2.dispatcher.mapper.ActionMapper;
|
||||
@@ -367,6 +368,7 @@ import org.apache.struts2.views.util.UrlHelper;
|
||||
*/
|
||||
public class StrutsBeanSelectionProvider extends AbstractBeanSelectionProvider {
|
||||
|
||||
@Override
|
||||
public void register(ContainerBuilder builder, LocatableProperties props) {
|
||||
alias(ObjectFactory.class, StrutsConstants.STRUTS_OBJECTFACTORY, builder, props);
|
||||
alias(ActionFactory.class, StrutsConstants.STRUTS_OBJECTFACTORY_ACTIONFACTORY, builder, props);
|
||||
@@ -422,6 +424,11 @@ public class StrutsBeanSelectionProvider extends AbstractBeanSelectionProvider {
|
||||
alias(NotExcludedAcceptedPatternsChecker.class, StrutsConstants.STRUTS_NOT_EXCLUDED_ACCEPTED_PATTERNS_CHECKER
|
||||
, builder, props, Scope.SINGLETON);
|
||||
|
||||
alias(DateFormatter.class, StrutsConstants.STRUTS_DATE_FORMATTER, builder, props, Scope.SINGLETON);
|
||||
|
||||
alias(ExpressionCacheFactory.class, StrutsConstants.STRUTS_OGNL_EXPRESSION_CACHE_FACTORY, builder, props, Scope.SINGLETON);
|
||||
alias(BeanInfoCacheFactory.class, StrutsConstants.STRUTS_OGNL_BEANINFO_CACHE_FACTORY, builder, props, Scope.SINGLETON);
|
||||
|
||||
switchDevMode(props);
|
||||
}
|
||||
|
||||
|
||||
@@ -144,6 +144,8 @@ public class ConstantConfig {
|
||||
private Boolean disallowProxyMemberAccess;
|
||||
private Integer ognlAutoGrowthCollectionLimit;
|
||||
private String staticContentPath;
|
||||
private BeanConfig expressionCacheFactory;
|
||||
private BeanConfig beaninfoCacheFactory;
|
||||
|
||||
protected String beanConfToString(BeanConfig beanConf) {
|
||||
return beanConf == null ? null : beanConf.getName();
|
||||
@@ -274,6 +276,8 @@ public class ConstantConfig {
|
||||
map.put(StrutsConstants.STRUTS_DISALLOW_PROXY_MEMBER_ACCESS, Objects.toString(disallowProxyMemberAccess, null));
|
||||
map.put(StrutsConstants.STRUTS_OGNL_AUTO_GROWTH_COLLECTION_LIMIT, Objects.toString(ognlAutoGrowthCollectionLimit, null));
|
||||
map.put(StrutsConstants.STRUTS_UI_STATIC_CONTENT_PATH, Objects.toString(staticContentPath, StaticContentLoader.DEFAULT_STATIC_CONTENT_PATH));
|
||||
map.put(StrutsConstants.STRUTS_OGNL_EXPRESSION_CACHE_FACTORY, beanConfToString(expressionCacheFactory));
|
||||
map.put(StrutsConstants.STRUTS_OGNL_BEANINFO_CACHE_FACTORY, beanConfToString(beaninfoCacheFactory));
|
||||
|
||||
return map;
|
||||
}
|
||||
@@ -1341,4 +1345,28 @@ public class ConstantConfig {
|
||||
public void setStaticContentPath(String staticContentPath) {
|
||||
this.staticContentPath = StaticContentLoader.Validator.validateStaticContentPath(staticContentPath);
|
||||
}
|
||||
|
||||
public BeanConfig getExpressionCacheFactory() {
|
||||
return expressionCacheFactory;
|
||||
}
|
||||
|
||||
public void setExpressionCacheFactory(BeanConfig expressionCacheFactory) {
|
||||
this.expressionCacheFactory = expressionCacheFactory;
|
||||
}
|
||||
|
||||
public void setExpressionCacheFactory(Class<?> clazz) {
|
||||
this.expressionCacheFactory = new BeanConfig(clazz, clazz.getName());
|
||||
}
|
||||
|
||||
public BeanConfig getBeaninfoCacheFactory() {
|
||||
return beaninfoCacheFactory;
|
||||
}
|
||||
|
||||
public void setBeaninfoCacheFactory(BeanConfig beaninfoCacheFactory) {
|
||||
this.beaninfoCacheFactory = beaninfoCacheFactory;
|
||||
}
|
||||
|
||||
public void setBeaninfoCacheFactory(Class<?> clazz) {
|
||||
this.beaninfoCacheFactory = new BeanConfig(clazz, clazz.getName());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,25 +45,23 @@ import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
|
||||
* </p>
|
||||
*
|
||||
* <ul>
|
||||
*
|
||||
* <li>{@link ServletContextAware}</li>
|
||||
*
|
||||
* <li>{@link org.apache.struts2.action.ServletContextAware}</li>
|
||||
* <li>{@link ServletRequestAware}</li>
|
||||
*
|
||||
* <li>{@link org.apache.struts2.action.ServletRequestAware}</li>
|
||||
* <li>{@link ServletResponseAware}</li>
|
||||
*
|
||||
* <li>{@link org.apache.struts2.action.ServletResponseAware}</li>
|
||||
* <li>{@link ParameterAware} - deprecated since 2.5.4, please use {@link HttpParametersAware}</li>
|
||||
*
|
||||
* <li>{@link HttpParametersAware}</li>
|
||||
*
|
||||
* <li>{@link org.apache.struts2.action.ParametersAware}</li>
|
||||
* <li>{@link RequestAware}</li>
|
||||
*
|
||||
* <li>{@link org.apache.struts2.action.ServletRequestAware}</li>
|
||||
* <li>{@link SessionAware}</li>
|
||||
*
|
||||
* <li>{@link org.apache.struts2.action.SessionAware}</li>
|
||||
* <li>{@link ApplicationAware}</li>
|
||||
*
|
||||
* <li>{@link org.apache.struts2.action.ApplicationAware}</li>
|
||||
* <li>{@link PrincipalAware}</li>
|
||||
*
|
||||
* <li>{@link org.apache.struts2.action.PrincipalAware}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <!-- END SNIPPET: description -->
|
||||
@@ -101,13 +99,19 @@ import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
|
||||
* </pre>
|
||||
*
|
||||
* @see ServletContextAware
|
||||
* @see org.apache.struts2.action.ServletContextAware
|
||||
* @see ServletRequestAware
|
||||
* @see org.apache.struts2.action.ServletRequestAware
|
||||
* @see ServletResponseAware
|
||||
* @see org.apache.struts2.action.ServletResponseAware
|
||||
* @see ParameterAware
|
||||
* @see ParametersAware
|
||||
* @see org.apache.struts2.action.ParametersAware
|
||||
* @see SessionAware
|
||||
* @see org.apache.struts2.action.SessionAware
|
||||
* @see ApplicationAware
|
||||
* @see org.apache.struts2.action.ApplicationAware
|
||||
* @see PrincipalAware
|
||||
* @see org.apache.struts2.action.PrincipalAware
|
||||
*/
|
||||
public class ServletConfigInterceptor extends AbstractInterceptor implements StrutsStatics {
|
||||
|
||||
@@ -159,7 +163,7 @@ public class ServletConfigInterceptor extends AbstractInterceptor implements Str
|
||||
if (action instanceof ApplicationAware) {
|
||||
((ApplicationAware) action).setApplication(context.getApplication());
|
||||
}
|
||||
|
||||
|
||||
if (action instanceof org.apache.struts2.action.ApplicationAware) {
|
||||
((org.apache.struts2.action.ApplicationAware) action).withApplication(context.getApplication());
|
||||
}
|
||||
@@ -167,7 +171,7 @@ public class ServletConfigInterceptor extends AbstractInterceptor implements Str
|
||||
if (action instanceof SessionAware) {
|
||||
((SessionAware) action).setSession(context.getSession());
|
||||
}
|
||||
|
||||
|
||||
if (action instanceof org.apache.struts2.action.SessionAware) {
|
||||
((org.apache.struts2.action.SessionAware) action).withSession(context.getSession());
|
||||
}
|
||||
@@ -179,7 +183,7 @@ public class ServletConfigInterceptor extends AbstractInterceptor implements Str
|
||||
if (action instanceof PrincipalAware) {
|
||||
HttpServletRequest request = context.getServletRequest();
|
||||
if(request != null) {
|
||||
// We are in servtlet environment, so principal information resides in HttpServletRequest
|
||||
// We are in servlet environment, so principal information resides in HttpServletRequest
|
||||
((PrincipalAware) action).setPrincipalProxy(new ServletPrincipalProxy(request));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,6 +229,37 @@ struts.ognl.logMissingProperties=false
|
||||
### if the application generates a lot of different expressions
|
||||
struts.ognl.enableExpressionCache=true
|
||||
|
||||
### Specify the OGNL expression cache factory and BeanInfo cache factory to use.
|
||||
### Currently, the default implementations are used, but can be replaced with custom ones if desired.
|
||||
struts.ognl.expressionCacheFactory=defaultOgnlExpressionCacheFactory
|
||||
struts.ognl.beanInfoCacheFactory=defaultOgnlBeanInfoCacheFactory
|
||||
|
||||
### Specify a limit to the number of entries in the OGNL expressionCache.
|
||||
### For the standard expressionCache mode, when the limit is exceeded the entire cache's
|
||||
### content will be cleared (can help prevent memory leaks).
|
||||
### For expressionCacheLRUMode true, the limit will ensure the cache does not exceed
|
||||
### that size, dropping the oldest (least-recently-used) expressions to add new ones.
|
||||
### NOTE: If not set, the default is 25000, which may be excessive.
|
||||
# struts.ognl.expressionCacheMaxSize=1000
|
||||
|
||||
### Indicates if the OGNL expressionCache should use LRU mode.
|
||||
### NOTE: When true, make sure to set the expressionCacheMaxSize to a reasonable value
|
||||
### for your application. Otherwise the default limit will never (practically) be reached.
|
||||
# struts.ognl.expressionCacheLRUMode=false
|
||||
|
||||
### Specify a limit to the number of entries in the OGNL beanInfoCache.
|
||||
### For the standard beanInfoCache mode, when the limit is exceeded the entire cache's
|
||||
### content will be cleared (can help prevent memory leaks).
|
||||
### For beanInfoCacheLRUMode true, the limit will ensure the cache does not exceed
|
||||
### that size, dropping the oldest (least-recently-used) expressions to add new ones.
|
||||
### NOTE: If not set, the default is 25000, which may be excessive.
|
||||
# struts.ognl.beanInfoCacheMaxSize=1000
|
||||
|
||||
### Indicates if the OGNL beanInfoCache should use LRU mode.
|
||||
### NOTE: When true, make sure to set the beanInfoCacheMaxSize to a reasonable value
|
||||
### for your application. Otherwise the default limit will never (practically) be reached.
|
||||
# struts.ognl.beanInfoCacheLRUMode=false
|
||||
|
||||
### Indicates if Dispatcher should handle unexpected exceptions by calling sendError()
|
||||
### or simply rethrow it as a ServletException to allow future processing by other frameworks like Spring Security
|
||||
struts.handle.exception=true
|
||||
@@ -243,4 +274,10 @@ struts.handle.exception=true
|
||||
### NOTE: The sample line below is *INTENTIONALLY* commented out, as this feature is disabled by default.
|
||||
# struts.ognl.expressionMaxLength=256
|
||||
|
||||
### Defines which named instance of DateFormatter to use, there are two instances:
|
||||
### - simpleDateFormatter (based on SimpleDateFormat)
|
||||
### - dateTimeFormatter (based on Java 8 Date/Time API)
|
||||
### These formatters are using a slightly different patterns, please check JavaDocs of both and more details is in WW-5016
|
||||
struts.date.formatter=dateTimeFormatter
|
||||
|
||||
### END SNIPPET: complete_file
|
||||
|
||||
@@ -216,7 +216,7 @@
|
||||
<bean type="com.opensymphony.xwork2.UnknownHandlerManager" class="com.opensymphony.xwork2.DefaultUnknownHandlerManager" name="struts" />
|
||||
|
||||
<bean type="org.apache.struts2.dispatcher.DispatcherErrorHandler" name="struts" class="org.apache.struts2.dispatcher.DefaultDispatcherErrorHandler" />
|
||||
|
||||
|
||||
<!-- Silly workarounds for OGNL since there is currently no way to flush its internal caches -->
|
||||
<bean type="ognl.PropertyAccessor" name="java.util.ArrayList" class="com.opensymphony.xwork2.ognl.accessor.XWorkListPropertyAccessor" />
|
||||
<bean type="ognl.PropertyAccessor" name="java.util.HashSet" class="com.opensymphony.xwork2.ognl.accessor.XWorkCollectionPropertyAccessor" />
|
||||
@@ -228,6 +228,12 @@
|
||||
|
||||
<bean type="com.opensymphony.xwork2.config.providers.ValueSubstitutor" class="com.opensymphony.xwork2.config.providers.EnvsValueSubstitutor" scope="singleton"/>
|
||||
|
||||
<bean type="org.apache.struts2.components.date.DateFormatter" name="simpleDateFormatter" class="org.apache.struts2.components.date.SimpleDateFormatAdapter" scope="singleton"/>
|
||||
<bean type="org.apache.struts2.components.date.DateFormatter" name="dateTimeFormatter" class="org.apache.struts2.components.date.DateTimeFormatterAdapter" scope="singleton"/>
|
||||
|
||||
<bean type="com.opensymphony.xwork2.ognl.OgnlCacheFactory" name="ognlExpressionCacheFactory" class="com.opensymphony.xwork2.ognl.DefaultOgnlExpressionCacheFactory" scope="singleton"/>
|
||||
<bean type="com.opensymphony.xwork2.ognl.OgnlCacheFactory" name="ognlBeanInfoCacheFactory" class="com.opensymphony.xwork2.ognl.DefaultOgnlBeanInfoCacheFactory" scope="singleton"/>
|
||||
|
||||
<package name="struts-default" abstract="true">
|
||||
<result-types>
|
||||
<result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/>
|
||||
|
||||
@@ -35,6 +35,7 @@ import com.opensymphony.xwork2.util.Owner;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
import com.opensymphony.xwork2.util.location.LocatableProperties;
|
||||
import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
|
||||
import java.beans.BeanInfo;
|
||||
import ognl.InappropriateExpressionException;
|
||||
import ognl.MethodFailedException;
|
||||
import ognl.NoSuchPropertyException;
|
||||
@@ -129,6 +130,61 @@ public class OgnlUtilTest extends XWorkTestCase {
|
||||
assertSame(expr0, expr2);
|
||||
}
|
||||
|
||||
public void testCacheEnabledMaxSize() throws OgnlException {
|
||||
ognlUtil.setEnableExpressionCache("true");
|
||||
ognlUtil.setExpressionCacheMaxSize("1");
|
||||
Object expr0 = ognlUtil.compile("test");
|
||||
Object expr2 = ognlUtil.compile("test");
|
||||
assertSame(expr0, expr2);
|
||||
assertEquals("Expression cache size should be at its limit", 1, ognlUtil.expressionCacheSize());
|
||||
// Next epxression cached should cause the cache to clear (exceeding maximum sized).
|
||||
Object expr3 = ognlUtil.compile("test1");
|
||||
assertEquals("Expression cache should be empty", 0, ognlUtil.expressionCacheSize());
|
||||
Object expr4 = ognlUtil.compile("test1");
|
||||
Object expr5 = ognlUtil.compile("test1");
|
||||
assertEquals("Expression cache size should still be at its limit", 1, ognlUtil.expressionCacheSize());
|
||||
assertNotSame("2nd test expression cache attempt will exceed size and force clear, but somehow they match ?", expr3, expr4);
|
||||
assertSame(expr4, expr5);
|
||||
// Next epxression cached should cause the cache to clear (exceeding maximum sized).
|
||||
Object expr6 = ognlUtil.compile("test");
|
||||
assertEquals("Expression cache should be empty", 0, ognlUtil.expressionCacheSize());
|
||||
Object expr7 = ognlUtil.compile("test");
|
||||
Object expr8 = ognlUtil.compile("test");
|
||||
assertNotSame("2nd test expression cache attempt will exceed size and force clear, but somehow they match ?", expr6, expr7);
|
||||
assertSame(expr7, expr8);
|
||||
assertEquals("Expression LRU cache size should still be at its limit", 1, ognlUtil.expressionCacheSize());
|
||||
assertNotSame("1st test expression identical after ejection from LRU cache ?", expr5, expr0);
|
||||
}
|
||||
|
||||
public void testLRUCacheEnabled() throws OgnlException {
|
||||
// Force usage of LRU cache factories for the OgnlUtil instance
|
||||
this.ognlUtil = generateOgnlUtilInstanceWithDefaultLRUCacheFactories();
|
||||
ognlUtil.setEnableExpressionCache("true");
|
||||
Object expr0 = ognlUtil.compile("test");
|
||||
Object expr2 = ognlUtil.compile("test");
|
||||
assertSame(expr0, expr2);
|
||||
}
|
||||
|
||||
public void testLRUCacheEnabledMaxSize() throws OgnlException {
|
||||
// Force usage of LRU cache factories for the OgnlUtil instance
|
||||
this.ognlUtil = generateOgnlUtilInstanceWithDefaultLRUCacheFactories();
|
||||
ognlUtil.setEnableExpressionCache("true");
|
||||
ognlUtil.setExpressionCacheMaxSize("1");
|
||||
Object expr0 = ognlUtil.compile("test");
|
||||
Object expr2 = ognlUtil.compile("test");
|
||||
assertSame(expr0, expr2);
|
||||
assertEquals("Expression LRU cache size should be at its limit", 1, ognlUtil.expressionCacheSize());
|
||||
Object expr3 = ognlUtil.compile("test1");
|
||||
Object expr4 = ognlUtil.compile("test1");
|
||||
assertSame(expr3, expr4);
|
||||
assertEquals("Expression LRU cache size should still be at its limit", 1, ognlUtil.expressionCacheSize());
|
||||
Object expr5 = ognlUtil.compile("test");
|
||||
Object expr6 = ognlUtil.compile("test");
|
||||
assertSame(expr5, expr6);
|
||||
assertEquals("Expression LRU cache size should still be at its limit", 1, ognlUtil.expressionCacheSize());
|
||||
assertNotSame("1st test expression identical after ejection from LRU cache ?", expr5, expr0);
|
||||
}
|
||||
|
||||
public void testExpressionIsCachedIrrespectiveOfItsExecutionStatus() throws OgnlException {
|
||||
Foo foo = new Foo();
|
||||
OgnlContext context = (OgnlContext) ognlUtil.createDefaultContext(foo);
|
||||
@@ -149,6 +205,30 @@ public class OgnlUtilTest extends XWorkTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
public void testExpressionIsLRUCachedIrrespectiveOfItsExecutionStatus() throws OgnlException {
|
||||
// Force usage of LRU cache factories for the OgnlUtil instance
|
||||
this.ognlUtil = generateOgnlUtilInstanceWithDefaultLRUCacheFactories();
|
||||
ognlUtil.setContainer(container); // Must be explicitly set as the generated OgnlUtil instance has no container
|
||||
ognlUtil.setEnableExpressionCache("true");
|
||||
Foo foo = new Foo();
|
||||
OgnlContext context = (OgnlContext) ognlUtil.createDefaultContext(foo);
|
||||
|
||||
// Expression which executes with success
|
||||
try {
|
||||
ognlUtil.getValue("@com.opensymphony.xwork2.ognl.OgnlUtilTest@STATIC_FINAL_PUBLIC_ATTRIBUTE", context, foo);
|
||||
assertEquals("Successfully executed expression must have been cached", ognlUtil.expressionCacheSize(), 1);
|
||||
} catch (Exception ex) {
|
||||
fail("Expression execution should have succeeded here. Exception: " + ex);
|
||||
}
|
||||
// Expression which executes with failure
|
||||
try {
|
||||
ognlUtil.getValue("@com.opensymphony.xwork2.ognl.OgnlUtilTest@STATIC_PRIVATE_ATTRIBUTE", context, foo);
|
||||
fail("Expression execution should have failed here");
|
||||
} catch (Exception ex) {
|
||||
assertEquals("Expression with failed execution must have been cached nevertheless", ognlUtil.expressionCacheSize(), 2);
|
||||
}
|
||||
}
|
||||
|
||||
public void testMethodExpressionIsCachedIrrespectiveOfItsExecutionStatus() throws Exception {
|
||||
Foo foo = new Foo();
|
||||
OgnlContext context = (OgnlContext) ognlUtil.createDefaultContext(foo);
|
||||
@@ -193,6 +273,32 @@ public class OgnlUtilTest extends XWorkTestCase {
|
||||
assertTrue("Expression cache empty after usage ?", ognlUtil.expressionCacheSize() > 0);
|
||||
}
|
||||
|
||||
public void testClearExpressionLRUCache() throws OgnlException {
|
||||
// Force usage of LRU cache factories for the OgnlUtil instance
|
||||
this.ognlUtil = generateOgnlUtilInstanceWithDefaultLRUCacheFactories();
|
||||
ognlUtil.setEnableExpressionCache("true");
|
||||
// Test that the expression cache is functioning as expected.
|
||||
Object expr0 = ognlUtil.compile("test");
|
||||
Object expr1 = ognlUtil.compile("test");
|
||||
Object expr2 = ognlUtil.compile("test");
|
||||
// Cache in effect, so expr0, expr1, expr2 should be the same.
|
||||
assertSame(expr0, expr1);
|
||||
assertSame(expr0, expr2);
|
||||
assertTrue("Expression cache empty before clear ?", ognlUtil.expressionCacheSize() > 0);
|
||||
// Clear the Epxression cache and confirm subsequent requests are new.
|
||||
ognlUtil.clearExpressionCache();
|
||||
assertEquals("Expression cache not empty after clear ?", 0, ognlUtil.expressionCacheSize());
|
||||
Object expr3 = ognlUtil.compile("test");
|
||||
Object expr4 = ognlUtil.compile("test");
|
||||
Object expr5 = ognlUtil.compile("test");
|
||||
// Cache cleared, expr3 should be a new instance.
|
||||
assertNotSame(expr0, expr3);
|
||||
// Cache still in effect, so expr3, expr4, expr5 should be the same.
|
||||
assertSame(expr3, expr4);
|
||||
assertSame(expr3, expr5);
|
||||
assertTrue("Expression cache empty after usage ?", ognlUtil.expressionCacheSize() > 0);
|
||||
}
|
||||
|
||||
public void testClearBeanInfoCache() throws IntrospectionException {
|
||||
final TestBean1 testBean1 = new TestBean1();
|
||||
final TestBean2 testBean2 = new TestBean2();
|
||||
@@ -242,6 +348,79 @@ public class OgnlUtilTest extends XWorkTestCase {
|
||||
assertTrue("BeanInfo cache empty after usage ?", ognlUtil.beanInfoCacheSize() > 0);
|
||||
}
|
||||
|
||||
public void testBeanInfoCache() throws IntrospectionException {
|
||||
final TestBean1 testBean1 = new TestBean1();
|
||||
final TestBean2 testBean2 = new TestBean2();
|
||||
// Test that the BeanInfo cache is functioning as expected.
|
||||
Object beanInfo1_1 = ognlUtil.getBeanInfo(testBean1);
|
||||
Object beanInfo1_2 = ognlUtil.getBeanInfo(testBean1);
|
||||
Object beanInfo1_3 = ognlUtil.getBeanInfo(testBean1);
|
||||
// Cache in effect, so beanInfo1_1, beanInfo1_2, beanInfo1_3 should be the same.
|
||||
assertSame(beanInfo1_1, beanInfo1_2);
|
||||
assertSame(beanInfo1_1, beanInfo1_3);
|
||||
Object beanInfo2_1 = ognlUtil.getBeanInfo(testBean2);
|
||||
Object beanInfo2_2 = ognlUtil.getBeanInfo(testBean2);
|
||||
Object beanInfo2_3 = ognlUtil.getBeanInfo(testBean2);
|
||||
// Cache in effect, so beanInfo2_1, beanInfo2_2, beanInfo2_3 should be the same.
|
||||
assertSame(beanInfo2_1, beanInfo2_2);
|
||||
assertSame(beanInfo2_1, beanInfo2_3);
|
||||
// BeanInfo for TestBean1 and TestBean2 should always be different.
|
||||
assertNotSame(beanInfo1_1, beanInfo2_1);
|
||||
assertTrue("BeanInfo cache empty after usage ?", ognlUtil.beanInfoCacheSize() > 0);
|
||||
}
|
||||
|
||||
public void testBeanInfoLRUCache() throws IntrospectionException {
|
||||
// Force usage of LRU cache factories for the OgnlUtil instance
|
||||
this.ognlUtil = generateOgnlUtilInstanceWithDefaultLRUCacheFactories();
|
||||
final TestBean1 testBean1 = new TestBean1();
|
||||
final TestBean2 testBean2 = new TestBean2();
|
||||
// Test that the BeanInfo cache is functioning as expected.
|
||||
Object beanInfo1_1 = ognlUtil.getBeanInfo(testBean1);
|
||||
Object beanInfo1_2 = ognlUtil.getBeanInfo(testBean1);
|
||||
Object beanInfo1_3 = ognlUtil.getBeanInfo(testBean1);
|
||||
// Cache in effect, so beanInfo1_1, beanInfo1_2, beanInfo1_3 should be the same.
|
||||
assertSame(beanInfo1_1, beanInfo1_2);
|
||||
assertSame(beanInfo1_1, beanInfo1_3);
|
||||
Object beanInfo2_1 = ognlUtil.getBeanInfo(testBean2);
|
||||
Object beanInfo2_2 = ognlUtil.getBeanInfo(testBean2);
|
||||
Object beanInfo2_3 = ognlUtil.getBeanInfo(testBean2);
|
||||
// Cache in effect, so beanInfo2_1, beanInfo2_2, beanInfo2_3 should be the same.
|
||||
assertSame(beanInfo2_1, beanInfo2_2);
|
||||
assertSame(beanInfo2_1, beanInfo2_3);
|
||||
// BeanInfo for TestBean1 and TestBean2 should always be different.
|
||||
assertNotSame(beanInfo1_1, beanInfo2_1);
|
||||
assertTrue("BeanInfo cache empty after usage ?", ognlUtil.beanInfoCacheSize() > 0);
|
||||
}
|
||||
|
||||
public void testBeanInfoLRUCacheLimits() throws IntrospectionException {
|
||||
// Force usage of LRU cache factories for the OgnlUtil instance
|
||||
this.ognlUtil = generateOgnlUtilInstanceWithDefaultLRUCacheFactories();
|
||||
ognlUtil.setBeanInfoCacheMaxSize("1");
|
||||
final TestBean1 testBean1 = new TestBean1();
|
||||
final TestBean2 testBean2 = new TestBean2();
|
||||
// Test that the BeanInfo cache is functioning as expected.
|
||||
Object beanInfo1_1 = ognlUtil.getBeanInfo(testBean1);
|
||||
Object beanInfo1_2 = ognlUtil.getBeanInfo(testBean1);
|
||||
Object beanInfo1_3 = ognlUtil.getBeanInfo(testBean1);
|
||||
// Cache in effect, so beanInfo1_1, beanInfo1_2, beanInfo1_3 should be the same.
|
||||
assertSame(beanInfo1_1, beanInfo1_2);
|
||||
assertSame(beanInfo1_1, beanInfo1_3);
|
||||
Object beanInfo2_1 = ognlUtil.getBeanInfo(testBean2);
|
||||
Object beanInfo2_2 = ognlUtil.getBeanInfo(testBean2);
|
||||
Object beanInfo2_3 = ognlUtil.getBeanInfo(testBean2);
|
||||
// Cache in effect, so beanInfo2_1, beanInfo2_2, beanInfo2_3 should be the same.
|
||||
assertSame(beanInfo2_1, beanInfo2_2);
|
||||
assertSame(beanInfo2_1, beanInfo2_3);
|
||||
// BeanInfo for TestBean1 and TestBean2 should always be different.
|
||||
assertNotSame(beanInfo1_1, beanInfo2_1);
|
||||
assertTrue("BeanInfo cache empty after usage ?", ognlUtil.beanInfoCacheSize() > 0);
|
||||
assertEquals("BeanInfo LRU cache size should be at its limit", 1, ognlUtil.beanInfoCacheSize());
|
||||
// LRU cache should not contain TestBean1 beaninfo anymore. A new entry should exist in the cache.
|
||||
Object beanInfo1_4 = ognlUtil.getBeanInfo(testBean1);
|
||||
assertNotSame("BeanInfo dropped from LRU cache is the same as newly added ?", beanInfo1_1, beanInfo1_4);
|
||||
ognlUtil.setBeanInfoCacheMaxSize(String.valueOf(Integer.MAX_VALUE));
|
||||
}
|
||||
|
||||
public void testClearRuntimeCache() {
|
||||
// Confirm that no exceptions or failures arise when calling the convenience global clear method.
|
||||
OgnlUtil.clearRuntimeCache();
|
||||
@@ -1134,6 +1313,20 @@ public class OgnlUtilTest extends XWorkTestCase {
|
||||
internalTestOgnlUtilExclusionsImmutable(basicOgnlUtil);
|
||||
}
|
||||
|
||||
public void testDefaultOgnlUtilExclusionsAlternateConstructor() {
|
||||
OgnlUtil basicOgnlUtil = new OgnlUtil(null, null);
|
||||
|
||||
internalTestInitialEmptyOgnlUtilExclusions(basicOgnlUtil);
|
||||
internalTestOgnlUtilExclusionsImmutable(basicOgnlUtil);
|
||||
}
|
||||
|
||||
public void testDefaultOgnlUtilExclusionsAlternateConstructorPopulated() {
|
||||
OgnlUtil basicOgnlUtil = new OgnlUtil(new DefaultOgnlExpressionCacheFactory<String, Object>(), new DefaultOgnlBeanInfoCacheFactory<Class<?>, BeanInfo>());
|
||||
|
||||
internalTestInitialEmptyOgnlUtilExclusions(basicOgnlUtil);
|
||||
internalTestOgnlUtilExclusionsImmutable(basicOgnlUtil);
|
||||
}
|
||||
|
||||
public void testOgnlUtilExcludedAdditivity() {
|
||||
Set<Class<?>> excludedClasses;
|
||||
Set<Pattern> excludedPackageNamePatterns;
|
||||
@@ -1497,6 +1690,34 @@ public class OgnlUtilTest extends XWorkTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
public void testGetExcludedPackageNamesAlternateConstructor() {
|
||||
// Getter should return an immutable collection
|
||||
OgnlUtil util = new OgnlUtil(null, null);
|
||||
util.setExcludedPackageNames("java.lang,java.awt");
|
||||
assertEquals(util.getExcludedPackageNames().size(), 2);
|
||||
try {
|
||||
util.getExcludedPackageNames().clear();
|
||||
} catch (Exception ex) {
|
||||
assertTrue(ex instanceof UnsupportedOperationException);
|
||||
} finally {
|
||||
assertEquals(util.getExcludedPackageNames().size(), 2);
|
||||
}
|
||||
}
|
||||
|
||||
public void testGetExcludedPackageNamesAlternateConstructorPopulated() {
|
||||
// Getter should return an immutable collection
|
||||
OgnlUtil util = new OgnlUtil(new DefaultOgnlExpressionCacheFactory<String, Object>(), new DefaultOgnlBeanInfoCacheFactory<Class<?>, BeanInfo>());
|
||||
util.setExcludedPackageNames("java.lang,java.awt");
|
||||
assertEquals(util.getExcludedPackageNames().size(), 2);
|
||||
try {
|
||||
util.getExcludedPackageNames().clear();
|
||||
} catch (Exception ex) {
|
||||
assertTrue(ex instanceof UnsupportedOperationException);
|
||||
} finally {
|
||||
assertEquals(util.getExcludedPackageNames().size(), 2);
|
||||
}
|
||||
}
|
||||
|
||||
public void testGetExcludedClasses() {
|
||||
// Getter should return an immutable collection
|
||||
OgnlUtil util = new OgnlUtil();
|
||||
@@ -1511,6 +1732,34 @@ public class OgnlUtilTest extends XWorkTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
public void testGetExcludedClassesAlternateConstructor() {
|
||||
// Getter should return an immutable collection
|
||||
OgnlUtil util = new OgnlUtil(null, null);
|
||||
util.setExcludedClasses("java.lang.Runtime,java.lang.ProcessBuilder,java.net.URL");
|
||||
assertEquals(util.getExcludedClasses().size(), 3);
|
||||
try {
|
||||
util.getExcludedClasses().clear();
|
||||
} catch (Exception ex) {
|
||||
assertTrue(ex instanceof UnsupportedOperationException);
|
||||
} finally {
|
||||
assertEquals(util.getExcludedClasses().size(), 3);
|
||||
}
|
||||
}
|
||||
|
||||
public void testGetExcludedClassesAlternateConstructorPopulated() {
|
||||
// Getter should return an immutable collection
|
||||
OgnlUtil util = new OgnlUtil(new DefaultOgnlExpressionCacheFactory<String, Object>(), new DefaultOgnlBeanInfoCacheFactory<Class<?>, BeanInfo>());
|
||||
util.setExcludedClasses("java.lang.Runtime,java.lang.ProcessBuilder,java.net.URL");
|
||||
assertEquals(util.getExcludedClasses().size(), 3);
|
||||
try {
|
||||
util.getExcludedClasses().clear();
|
||||
} catch (Exception ex) {
|
||||
assertTrue(ex instanceof UnsupportedOperationException);
|
||||
} finally {
|
||||
assertEquals(util.getExcludedClasses().size(), 3);
|
||||
}
|
||||
}
|
||||
|
||||
public void testGetExcludedPackageNamePatterns() {
|
||||
// Getter should return an immutable collection
|
||||
OgnlUtil util = new OgnlUtil();
|
||||
@@ -1525,6 +1774,132 @@ public class OgnlUtilTest extends XWorkTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
public void testGetExcludedPackageNamePatternsAlternateConstructor() {
|
||||
// Getter should return an immutable collection
|
||||
OgnlUtil util = new OgnlUtil(null, null);
|
||||
util.setExcludedPackageNamePatterns("java.lang.");
|
||||
assertEquals(util.getExcludedPackageNamePatterns().size(), 1);
|
||||
try {
|
||||
util.getExcludedPackageNamePatterns().clear();
|
||||
} catch (Exception ex) {
|
||||
assertTrue(ex instanceof UnsupportedOperationException);
|
||||
} finally {
|
||||
assertEquals(util.getExcludedPackageNamePatterns().size(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
public void testGetExcludedPackageNamePatternsAlternateConstructorPopulated() {
|
||||
// Getter should return an immutable collection
|
||||
OgnlUtil util = new OgnlUtil(new DefaultOgnlExpressionCacheFactory<String, Object>(), new DefaultOgnlBeanInfoCacheFactory<Class<?>, BeanInfo>());
|
||||
util.setExcludedPackageNamePatterns("java.lang.");
|
||||
assertEquals(util.getExcludedPackageNamePatterns().size(), 1);
|
||||
try {
|
||||
util.getExcludedPackageNamePatterns().clear();
|
||||
} catch (Exception ex) {
|
||||
assertTrue(ex instanceof UnsupportedOperationException);
|
||||
} finally {
|
||||
assertEquals(util.getExcludedPackageNamePatterns().size(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
public void testOgnlUtilDefaultCacheClass() throws OgnlException {
|
||||
OgnlDefaultCache<Integer, String> defaultCache = new OgnlDefaultCache<>(2, 16, 0.75f);
|
||||
assertEquals("Initial evictionLimit did not match initial value", 2, defaultCache.getEvictionLimit());
|
||||
defaultCache.setEvictionLimit(3);
|
||||
assertEquals("Updated evictionLimit did not match updated value", 3, defaultCache.getEvictionLimit());
|
||||
String lookupResult = defaultCache.get(Integer.valueOf(0));
|
||||
assertNull("Lookup of empty cache returned non-null value ?", lookupResult);
|
||||
defaultCache.put(Integer.valueOf(0), "Zero");
|
||||
lookupResult = defaultCache.get(Integer.valueOf(0));
|
||||
assertEquals("Retrieved value does not match put value ?", "Zero", lookupResult);
|
||||
defaultCache.put(Integer.valueOf(1), "One");
|
||||
defaultCache.put(Integer.valueOf(2), "Two");
|
||||
assertEquals("Default cache not size evictionlimit after adding three values ?", defaultCache.getEvictionLimit(), defaultCache.size());
|
||||
lookupResult = defaultCache.get(Integer.valueOf(2));
|
||||
assertEquals("Retrieved value does not match put value ?", "Two", lookupResult);
|
||||
defaultCache.put(Integer.valueOf(3), "Three");
|
||||
assertEquals("Default cache not size zero after an add that exceeded the evection limit ?", 0, defaultCache.size());
|
||||
lookupResult = defaultCache.get(Integer.valueOf(0));
|
||||
assertNull("Lookup of value 0 (should have been evicted with everything) returned non-null value ?", lookupResult);
|
||||
lookupResult = defaultCache.get(Integer.valueOf(3));
|
||||
assertNull("Lookup of value 3 (should have been evicted with everything) returned non-null value ?", lookupResult);
|
||||
defaultCache.putIfAbsent(Integer.valueOf(2), "Two");
|
||||
lookupResult = defaultCache.get(Integer.valueOf(2));
|
||||
assertEquals("Retrieved value does not match put value ?", "Two", lookupResult);
|
||||
defaultCache.clear();
|
||||
assertEquals("Default cache not empty after clear ?", 0, defaultCache.size());
|
||||
}
|
||||
|
||||
public void testOgnlUtilLRUCacheClass() throws OgnlException {
|
||||
OgnlLRUCache<Integer, String> lruCache = new OgnlLRUCache<>(2, 16, 0.75f);
|
||||
assertEquals("Initial evictionLimit did not match initial value", 2, lruCache.getEvictionLimit());
|
||||
lruCache.setEvictionLimit(3);
|
||||
assertEquals("Updated evictionLimit did not match updated value", 3, lruCache.getEvictionLimit());
|
||||
String lookupResult = lruCache.get(Integer.valueOf(0));
|
||||
assertNull("Lookup of empty cache returned non-null value ?", lookupResult);
|
||||
lruCache.put(Integer.valueOf(0), "Zero");
|
||||
lookupResult = lruCache.get(Integer.valueOf(0));
|
||||
assertEquals("Retrieved value does not match put value ?", "Zero", lookupResult);
|
||||
lruCache.put(Integer.valueOf(1), "One");
|
||||
lruCache.put(Integer.valueOf(2), "Two");
|
||||
assertEquals("LRU cache not size evictionlimit after adding three values ?", lruCache.getEvictionLimit(), lruCache.size());
|
||||
lookupResult = lruCache.get(Integer.valueOf(2));
|
||||
assertEquals("Retrieved value does not match put value ?", "Two", lookupResult);
|
||||
lruCache.put(Integer.valueOf(3), "Three");
|
||||
assertEquals("LRU cache not size evictionlimit after adding values ?", lruCache.getEvictionLimit(), lruCache.size());
|
||||
lookupResult = lruCache.get(Integer.valueOf(0));
|
||||
assertNull("Lookup of value 0 (should have dropped off LRU cache) returned non-null value ?", lookupResult);
|
||||
lruCache.putIfAbsent(Integer.valueOf(2), "Two");
|
||||
lookupResult = lruCache.get(Integer.valueOf(2));
|
||||
assertEquals("Retrieved value does not match put value ?", "Two", lookupResult);
|
||||
lruCache.clear();
|
||||
assertEquals("LRU cache not empty after clear ?", 0, lruCache.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Unit test primarily for code coverage
|
||||
*/
|
||||
public void testOgnlDefaultCacheFactoryCoverage() {
|
||||
OgnlCache<String, Object> ognlCache;
|
||||
DefaultOgnlCacheFactory defaultOgnlCacheFactory = new DefaultOgnlCacheFactory<String, Object>();
|
||||
// Normal cache
|
||||
defaultOgnlCacheFactory.setCacheMaxSize("12");
|
||||
defaultOgnlCacheFactory.setUseLRUCache("false");
|
||||
ognlCache = defaultOgnlCacheFactory.buildOgnlCache();
|
||||
assertNotNull("No param build method result null ?", ognlCache);
|
||||
assertEquals("Eviction limit for cache mismatches limit for factory ?", 12, ognlCache.getEvictionLimit() );
|
||||
ognlCache = defaultOgnlCacheFactory.buildOgnlCache(6, 6, 0.75f, false);
|
||||
assertNotNull("No param build method result null ?", ognlCache);
|
||||
assertEquals("Eviction limit for cache mismatches limit for factory ?", 6, ognlCache.getEvictionLimit() );
|
||||
// LRU cache
|
||||
defaultOgnlCacheFactory.setCacheMaxSize("30");
|
||||
defaultOgnlCacheFactory.setUseLRUCache("true");
|
||||
ognlCache = defaultOgnlCacheFactory.buildOgnlCache();
|
||||
assertNotNull("No param build method result null ?", ognlCache);
|
||||
assertEquals("Eviction limit for cache mismatches limit for factory ?", 30, ognlCache.getEvictionLimit() );
|
||||
ognlCache = defaultOgnlCacheFactory.buildOgnlCache(15, 15, 0.75f, false);
|
||||
assertNotNull("No param build method result null ?", ognlCache);
|
||||
assertEquals("Eviction limit for cache mismatches limit for factory ?", 15, ognlCache.getEvictionLimit() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a new OgnlUtil instance (not configured by the {@link ContainerBuilder}) that can be used for
|
||||
* basic tests, with its Expression and BeanInfo factories set to LRU mode.
|
||||
*
|
||||
* @return OgnlUtil instance with LRU enabled Expression and BeanInfo factories
|
||||
*/
|
||||
private OgnlUtil generateOgnlUtilInstanceWithDefaultLRUCacheFactories() {
|
||||
final OgnlUtil result;
|
||||
final DefaultOgnlExpressionCacheFactory<String, Object> expressionFactory = new DefaultOgnlExpressionCacheFactory<>();
|
||||
final DefaultOgnlBeanInfoCacheFactory<Class<?>, BeanInfo> beanInfoFactory = new DefaultOgnlBeanInfoCacheFactory<>();
|
||||
expressionFactory.setUseLRUCache("true");
|
||||
expressionFactory.setCacheMaxSize("25");
|
||||
beanInfoFactory.setUseLRUCache("true");
|
||||
beanInfoFactory.setCacheMaxSize("25");
|
||||
result = new OgnlUtil(expressionFactory, beanInfoFactory);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void reloadTestContainerConfiguration(boolean devMode, boolean allowStaticMethod) {
|
||||
loadConfigurationProviders(new StubConfigurationProvider() {
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.components;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
import com.opensymphony.xwork2.util.ValueStackFactory;
|
||||
import org.apache.struts2.StrutsInternalTestCase;
|
||||
import org.apache.struts2.components.date.SimpleDateFormatAdapter;
|
||||
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Map;
|
||||
|
||||
public class DateTest extends StrutsInternalTestCase {
|
||||
|
||||
private Map<String, Object> context;
|
||||
private ValueStack stack;
|
||||
|
||||
public void testSupportSimpleDateTimeFormat() {
|
||||
// given
|
||||
Date date = new Date(stack);
|
||||
date.setDateFormatter(new SimpleDateFormatAdapter());
|
||||
|
||||
String format = "EEEE MMMM dd, hh:mm aa";
|
||||
java.util.Date now = new java.util.Date();
|
||||
|
||||
String expected = new SimpleDateFormat(format, ActionContext.getContext().getLocale()).format(now);
|
||||
context.put("myDate", now);
|
||||
|
||||
Writer writer = new StringWriter();
|
||||
|
||||
// when
|
||||
date.setFormat(format);
|
||||
date.setName("myDate");
|
||||
date.setNice(false);
|
||||
date.start(writer);
|
||||
date.end(writer, "");
|
||||
|
||||
// then
|
||||
assertEquals(expected, writer.toString());
|
||||
}
|
||||
|
||||
public void testDefaultFormat() {
|
||||
// given
|
||||
Date date = new Date(stack);
|
||||
date.setDateFormatter(new SimpleDateFormatAdapter());
|
||||
|
||||
java.util.Date now = new java.util.Date();
|
||||
|
||||
String expected = SimpleDateFormat.getDateInstance(DateFormat.MEDIUM, ActionContext.getContext().getLocale()).format(now);
|
||||
context.put("myDate", now);
|
||||
|
||||
Writer writer = new StringWriter();
|
||||
|
||||
// when
|
||||
date.setName("myDate");
|
||||
date.setNice(false);
|
||||
date.start(writer);
|
||||
date.end(writer, "");
|
||||
|
||||
// then
|
||||
assertEquals(expected, writer.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
stack = container.getInstance(ValueStackFactory.class).createValueStack();
|
||||
context = stack.getContext();
|
||||
}
|
||||
}
|
||||
@@ -19,35 +19,33 @@
|
||||
package org.apache.struts2.views.jsp.ui;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
|
||||
import org.apache.struts2.TestAction;
|
||||
import org.apache.struts2.components.Component;
|
||||
import org.apache.struts2.components.DateTextField;
|
||||
import org.apache.struts2.views.jsp.AbstractTagTest;
|
||||
import org.apache.struts2.views.jsp.DateTag;
|
||||
|
||||
import javax.servlet.jsp.JspException;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
import org.apache.struts2.components.Component;
|
||||
import org.apache.struts2.components.DateTextField;
|
||||
|
||||
/**
|
||||
* Unit test for {@link org.apache.struts2.components.Date}.
|
||||
*
|
||||
*/
|
||||
public class DateTagTest extends AbstractTagTest {
|
||||
|
||||
private DateTag tag;
|
||||
|
||||
public void testCustomFormat() throws Exception {
|
||||
public void testCustomFormatForDateTime() throws Exception {
|
||||
String format = "yyyy/MM/dd hh:mm:ss";
|
||||
Date now = new Date();
|
||||
String formatted = new SimpleDateFormat(format).format(now);
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
String formatted = DateTimeFormatter.ofPattern(format).format(now);
|
||||
context.put("myDate", now);
|
||||
|
||||
tag.setName("myDate");
|
||||
@@ -62,13 +60,55 @@ public class DateTagTest extends AbstractTagTest {
|
||||
freshTag.setPageContext(pageContext);
|
||||
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
}
|
||||
|
||||
public void testCustomFormatForLong() throws Exception {
|
||||
String format = "yyyy/MM/dd";
|
||||
long now = new Date().getTime();
|
||||
String formatted = DateTimeFormatter.ofPattern(format).format(Instant.ofEpochMilli(now).atZone(ZoneId.systemDefault()));
|
||||
context.put("myDate", now);
|
||||
|
||||
tag.setName("myDate");
|
||||
tag.setNice(false);
|
||||
tag.setFormat(format);
|
||||
tag.doStartTag();
|
||||
tag.doEndTag();
|
||||
assertEquals(formatted, writer.toString());
|
||||
|
||||
// Basic sanity check of clearTagStateForTagPoolingServers() behaviour for Struts Tags after doEndTag().
|
||||
DateTag freshTag = new DateTag();
|
||||
freshTag.setPageContext(pageContext);
|
||||
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
}
|
||||
|
||||
public void testCustomFormatForDate() throws Exception {
|
||||
String format = "yyyy/MM/dd";
|
||||
LocalDate now = LocalDate.now();
|
||||
String formatted = DateTimeFormatter.ofPattern(format).format(now);
|
||||
context.put("myDate", now);
|
||||
|
||||
tag.setName("myDate");
|
||||
tag.setNice(false);
|
||||
tag.setFormat(format);
|
||||
tag.doStartTag();
|
||||
tag.doEndTag();
|
||||
assertEquals(formatted, writer.toString());
|
||||
|
||||
// Basic sanity check of clearTagStateForTagPoolingServers() behaviour for Struts Tags after doEndTag().
|
||||
DateTag freshTag = new DateTag();
|
||||
freshTag.setPageContext(pageContext);
|
||||
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
}
|
||||
|
||||
public void testCustomFormat_clearTagStateSet() throws Exception {
|
||||
String format = "yyyy/MM/dd hh:mm:ss";
|
||||
Date now = new Date();
|
||||
String formatted = new SimpleDateFormat(format).format(now);
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
String formatted = DateTimeFormatter.ofPattern(format).format(now);
|
||||
context.put("myDate", now);
|
||||
|
||||
tag.setPerformClearTagStateForTagPoolingServers(true); // Explicitly request tag state clearing.
|
||||
@@ -86,13 +126,13 @@ public class DateTagTest extends AbstractTagTest {
|
||||
freshTag.setPageContext(pageContext);
|
||||
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
}
|
||||
|
||||
public void testCustomGlobalFormatFormat() throws Exception {
|
||||
String format = "yyyy/MM/dd hh:mm:ss";
|
||||
Date now = new Date();
|
||||
String formatted = new SimpleDateFormat(format).format(now);
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
String formatted = DateTimeFormatter.ofPattern(format).format(now);
|
||||
context.put("myDate", now);
|
||||
|
||||
((TestAction) action).setText(org.apache.struts2.components.Date.DATETAG_PROPERTY, format);
|
||||
@@ -106,10 +146,8 @@ public class DateTagTest extends AbstractTagTest {
|
||||
|
||||
public void testCustomFormatWithTimezone() throws Exception {
|
||||
String format = "yyyy/MM/dd hh:mm:ss";
|
||||
Date now = Calendar.getInstance(TimeZone.getTimeZone("GMT+1")).getTime();
|
||||
SimpleDateFormat sdf = new SimpleDateFormat(format);
|
||||
sdf.setTimeZone(TimeZone.getTimeZone("GMT+1"));
|
||||
String formatted = sdf.format(now);
|
||||
LocalDateTime now = LocalDateTime.now(ZoneId.of("GMT+1"));
|
||||
String formatted = DateTimeFormatter.ofPattern(format).format(now);
|
||||
context.put("myDate", now);
|
||||
|
||||
tag.setName("myDate");
|
||||
@@ -126,15 +164,13 @@ public class DateTagTest extends AbstractTagTest {
|
||||
freshTag.setPageContext(pageContext);
|
||||
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
}
|
||||
|
||||
public void testCustomFormatWithTimezone_clearTagStateSet() throws Exception {
|
||||
String format = "yyyy/MM/dd hh:mm:ss";
|
||||
Date now = Calendar.getInstance(TimeZone.getTimeZone("GMT+1")).getTime();
|
||||
SimpleDateFormat sdf = new SimpleDateFormat(format);
|
||||
sdf.setTimeZone(TimeZone.getTimeZone("GMT+1"));
|
||||
String formatted = sdf.format(now);
|
||||
LocalDateTime now = LocalDateTime.now(ZoneId.of("GMT+1"));
|
||||
String formatted = DateTimeFormatter.ofPattern(format).format(now);
|
||||
context.put("myDate", now);
|
||||
|
||||
tag.setPerformClearTagStateForTagPoolingServers(true); // Explicitly request tag state clearing.
|
||||
@@ -153,15 +189,13 @@ public class DateTagTest extends AbstractTagTest {
|
||||
freshTag.setPageContext(pageContext);
|
||||
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
}
|
||||
|
||||
public void testCustomFormatWithTimezoneAsExpression() throws Exception {
|
||||
String format = "yyyy/MM/dd hh:mm:ss";
|
||||
Date now = Calendar.getInstance(TimeZone.getTimeZone("GMT+2")).getTime();
|
||||
SimpleDateFormat sdf = new SimpleDateFormat(format);
|
||||
sdf.setTimeZone(TimeZone.getTimeZone("GMT+2"));
|
||||
String formatted = sdf.format(now);
|
||||
LocalDateTime now = LocalDateTime.now(ZoneId.of("GMT+2"));
|
||||
String formatted = DateTimeFormatter.ofPattern(format).format(now);
|
||||
context.put("myDate", now);
|
||||
context.put("myTimezone", "GMT+2");
|
||||
|
||||
@@ -178,15 +212,13 @@ public class DateTagTest extends AbstractTagTest {
|
||||
freshTag.setPageContext(pageContext);
|
||||
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
}
|
||||
|
||||
public void testCustomFormatWithTimezoneAsExpression_clearTagStateSet() throws Exception {
|
||||
String format = "yyyy/MM/dd hh:mm:ss";
|
||||
Date now = Calendar.getInstance(TimeZone.getTimeZone("GMT+2")).getTime();
|
||||
SimpleDateFormat sdf = new SimpleDateFormat(format);
|
||||
sdf.setTimeZone(TimeZone.getTimeZone("GMT+2"));
|
||||
String formatted = sdf.format(now);
|
||||
LocalDateTime now = LocalDateTime.now(ZoneId.of("GMT+2"));
|
||||
String formatted = DateTimeFormatter.ofPattern(format).format(now);
|
||||
context.put("myDate", now);
|
||||
context.put("myTimezone", "GMT+2");
|
||||
|
||||
@@ -206,13 +238,13 @@ public class DateTagTest extends AbstractTagTest {
|
||||
freshTag.setPageContext(pageContext);
|
||||
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
}
|
||||
|
||||
public void testCustomFormatCalendar() throws Exception {
|
||||
String format = "yyyy/MM/dd hh:mm:ss";
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
String formatted = new SimpleDateFormat(format).format(calendar.getTime());
|
||||
String formatted = DateTimeFormatter.ofPattern(format).format(calendar.toInstant().atZone(ZoneId.systemDefault()));
|
||||
context.put("myDate", calendar);
|
||||
|
||||
tag.setName("myDate");
|
||||
@@ -227,13 +259,13 @@ public class DateTagTest extends AbstractTagTest {
|
||||
freshTag.setPageContext(pageContext);
|
||||
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
}
|
||||
|
||||
public void testCustomFormatCalendar_clearTagStateSet() throws Exception {
|
||||
String format = "yyyy/MM/dd hh:mm:ss";
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
String formatted = new SimpleDateFormat(format).format(calendar.getTime());
|
||||
String formatted = DateTimeFormatter.ofPattern(format).format(calendar.toInstant().atZone(ZoneId.systemDefault()));
|
||||
context.put("myDate", calendar);
|
||||
|
||||
tag.setPerformClearTagStateForTagPoolingServers(true); // Explicitly request tag state clearing.
|
||||
@@ -251,13 +283,13 @@ public class DateTagTest extends AbstractTagTest {
|
||||
freshTag.setPageContext(pageContext);
|
||||
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
}
|
||||
|
||||
public void testCustomFormatLong() throws Exception {
|
||||
String format = "yyyy/MM/dd hh:mm:ss";
|
||||
Date date = new Date();
|
||||
String formatted = new SimpleDateFormat(format).format(date);
|
||||
String formatted = DateTimeFormatter.ofPattern(format).format(date.toInstant().atZone(ZoneId.systemDefault()));
|
||||
// long
|
||||
context.put("myDate", date.getTime());
|
||||
|
||||
@@ -273,13 +305,13 @@ public class DateTagTest extends AbstractTagTest {
|
||||
freshTag.setPageContext(pageContext);
|
||||
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
}
|
||||
|
||||
public void testCustomFormatLong_clearTagStateSet() throws Exception {
|
||||
String format = "yyyy/MM/dd hh:mm:ss";
|
||||
Date date = new Date();
|
||||
String formatted = new SimpleDateFormat(format).format(date);
|
||||
String formatted = DateTimeFormatter.ofPattern(format).format(date.toInstant().atZone(ZoneId.systemDefault()));
|
||||
// long
|
||||
context.put("myDate", date.getTime());
|
||||
|
||||
@@ -298,7 +330,7 @@ public class DateTagTest extends AbstractTagTest {
|
||||
freshTag.setPageContext(pageContext);
|
||||
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
}
|
||||
|
||||
public void testCustomFormatLocalDateTime() throws Exception {
|
||||
@@ -332,7 +364,7 @@ public class DateTagTest extends AbstractTagTest {
|
||||
public void testDefaultFormat() throws Exception {
|
||||
Date now = new Date();
|
||||
String formatted = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM,
|
||||
ActionContext.getContext().getLocale()).format(now);
|
||||
ActionContext.getContext().getLocale()).format(now);
|
||||
|
||||
context.put("myDate", now);
|
||||
tag.setName("myDate");
|
||||
@@ -346,13 +378,13 @@ public class DateTagTest extends AbstractTagTest {
|
||||
freshTag.setPageContext(pageContext);
|
||||
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
}
|
||||
|
||||
public void testDefaultFormat_clearTagStateSet() throws Exception {
|
||||
Date now = new Date();
|
||||
String formatted = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM,
|
||||
ActionContext.getContext().getLocale()).format(now);
|
||||
ActionContext.getContext().getLocale()).format(now);
|
||||
|
||||
context.put("myDate", now);
|
||||
tag.setPerformClearTagStateForTagPoolingServers(true); // Explicitly request tag state clearing.
|
||||
@@ -369,13 +401,13 @@ public class DateTagTest extends AbstractTagTest {
|
||||
freshTag.setPageContext(pageContext);
|
||||
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
}
|
||||
|
||||
public void testCustomFormatAndComponent() throws Exception {
|
||||
String format = "yyyy/MM/dd hh:mm:ss";
|
||||
Date now = new Date();
|
||||
String formatted = new SimpleDateFormat(format).format(now);
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
String formatted = DateTimeFormatter.ofPattern(format).format(now);
|
||||
context.put("myDate", now);
|
||||
|
||||
tag.setName("myDate");
|
||||
@@ -388,7 +420,7 @@ public class DateTagTest extends AbstractTagTest {
|
||||
org.apache.struts2.components.Date component = (org.apache.struts2.components.Date) tag.getComponent();
|
||||
assertEquals("myDate", component.getName());
|
||||
assertEquals(format, component.getFormat());
|
||||
assertEquals(false, component.isNice());
|
||||
assertFalse(component.isNice());
|
||||
|
||||
tag.doEndTag();
|
||||
|
||||
@@ -399,13 +431,13 @@ public class DateTagTest extends AbstractTagTest {
|
||||
freshTag.setPageContext(pageContext);
|
||||
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
}
|
||||
|
||||
public void testCustomFormatAndComponent_clearTagStateSet() throws Exception {
|
||||
String format = "yyyy/MM/dd hh:mm:ss";
|
||||
Date now = new Date();
|
||||
String formatted = new SimpleDateFormat(format).format(now);
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
String formatted = DateTimeFormatter.ofPattern(format).format(now);
|
||||
context.put("myDate", now);
|
||||
|
||||
tag.setPerformClearTagStateForTagPoolingServers(true); // Explicitly request tag state clearing.
|
||||
@@ -420,7 +452,7 @@ public class DateTagTest extends AbstractTagTest {
|
||||
org.apache.struts2.components.Date component = (org.apache.struts2.components.Date) tag.getComponent();
|
||||
assertEquals("myDate", component.getName());
|
||||
assertEquals(format, component.getFormat());
|
||||
assertEquals(false, component.isNice());
|
||||
assertFalse(component.isNice());
|
||||
|
||||
tag.doEndTag();
|
||||
|
||||
@@ -432,13 +464,13 @@ public class DateTagTest extends AbstractTagTest {
|
||||
freshTag.setPageContext(pageContext);
|
||||
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
}
|
||||
|
||||
public void testSetId() throws Exception {
|
||||
String format = "yyyy/MM/dd hh:mm:ss";
|
||||
Date now = new Date();
|
||||
String formatted = new SimpleDateFormat(format).format(now);
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
String formatted = DateTimeFormatter.ofPattern(format).format(now);
|
||||
context.put("myDate", now);
|
||||
|
||||
tag.setName("myDate");
|
||||
@@ -454,13 +486,13 @@ public class DateTagTest extends AbstractTagTest {
|
||||
freshTag.setPageContext(pageContext);
|
||||
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
}
|
||||
|
||||
public void testSetId_clearTagStateSet() throws Exception {
|
||||
String format = "yyyy/MM/dd hh:mm:ss";
|
||||
Date now = new Date();
|
||||
String formatted = new SimpleDateFormat(format).format(now);
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
String formatted = DateTimeFormatter.ofPattern(format).format(now);
|
||||
context.put("myDate", now);
|
||||
|
||||
tag.setPerformClearTagStateForTagPoolingServers(true); // Explicitly request tag state clearing.
|
||||
@@ -479,7 +511,7 @@ public class DateTagTest extends AbstractTagTest {
|
||||
freshTag.setPageContext(pageContext);
|
||||
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
}
|
||||
|
||||
public void testFutureNiceHour() throws Exception {
|
||||
@@ -501,7 +533,7 @@ public class DateTagTest extends AbstractTagTest {
|
||||
freshTag.setPageContext(pageContext);
|
||||
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
}
|
||||
|
||||
public void testFutureNiceHour_clearTagStateSet() throws Exception {
|
||||
@@ -526,7 +558,7 @@ public class DateTagTest extends AbstractTagTest {
|
||||
freshTag.setPageContext(pageContext);
|
||||
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
}
|
||||
|
||||
public void testPastNiceHour() throws Exception {
|
||||
@@ -548,7 +580,7 @@ public class DateTagTest extends AbstractTagTest {
|
||||
freshTag.setPageContext(pageContext);
|
||||
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
}
|
||||
|
||||
public void testPastNiceHour_clearTagStateSet() throws Exception {
|
||||
@@ -573,7 +605,7 @@ public class DateTagTest extends AbstractTagTest {
|
||||
freshTag.setPageContext(pageContext);
|
||||
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
}
|
||||
|
||||
public void testFutureNiceHourMinSec() throws Exception {
|
||||
@@ -596,7 +628,7 @@ public class DateTagTest extends AbstractTagTest {
|
||||
freshTag.setPageContext(pageContext);
|
||||
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
}
|
||||
|
||||
public void testFutureNiceHourMinSec_clearTagStateSet() throws Exception {
|
||||
@@ -622,7 +654,7 @@ public class DateTagTest extends AbstractTagTest {
|
||||
freshTag.setPageContext(pageContext);
|
||||
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
}
|
||||
|
||||
public void testPastNiceHourMin() throws Exception {
|
||||
@@ -645,7 +677,7 @@ public class DateTagTest extends AbstractTagTest {
|
||||
freshTag.setPageContext(pageContext);
|
||||
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
}
|
||||
|
||||
public void testPastNiceHourMin_clearTagStateSet() throws Exception {
|
||||
@@ -671,7 +703,7 @@ public class DateTagTest extends AbstractTagTest {
|
||||
freshTag.setPageContext(pageContext);
|
||||
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
}
|
||||
|
||||
public void testFutureLessOneMin() throws Exception {
|
||||
@@ -693,7 +725,7 @@ public class DateTagTest extends AbstractTagTest {
|
||||
freshTag.setPageContext(pageContext);
|
||||
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
}
|
||||
|
||||
public void testFutureLessOneMin_clearTagStateSet() throws Exception {
|
||||
@@ -718,7 +750,7 @@ public class DateTagTest extends AbstractTagTest {
|
||||
freshTag.setPageContext(pageContext);
|
||||
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
}
|
||||
|
||||
public void testFutureLessOneHour() throws Exception {
|
||||
@@ -740,7 +772,7 @@ public class DateTagTest extends AbstractTagTest {
|
||||
freshTag.setPageContext(pageContext);
|
||||
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
}
|
||||
|
||||
public void testFutureLessOneHour_clearTagStateSet() throws Exception {
|
||||
@@ -765,7 +797,7 @@ public class DateTagTest extends AbstractTagTest {
|
||||
freshTag.setPageContext(pageContext);
|
||||
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
}
|
||||
|
||||
public void testFutureLessOneYear() throws Exception {
|
||||
@@ -787,7 +819,7 @@ public class DateTagTest extends AbstractTagTest {
|
||||
freshTag.setPageContext(pageContext);
|
||||
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
}
|
||||
|
||||
public void testFutureLessOneYear_clearTagStateSet() throws Exception {
|
||||
@@ -812,7 +844,7 @@ public class DateTagTest extends AbstractTagTest {
|
||||
freshTag.setPageContext(pageContext);
|
||||
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
}
|
||||
|
||||
public void testFutureTwoYears() throws Exception {
|
||||
@@ -838,7 +870,7 @@ public class DateTagTest extends AbstractTagTest {
|
||||
freshTag.setPageContext(pageContext);
|
||||
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
}
|
||||
|
||||
public void testFutureTwoYears_clearTagStateSet() throws Exception {
|
||||
@@ -867,7 +899,7 @@ public class DateTagTest extends AbstractTagTest {
|
||||
freshTag.setPageContext(pageContext);
|
||||
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
}
|
||||
|
||||
public void testNoDateObjectInContext() throws Exception {
|
||||
@@ -884,7 +916,7 @@ public class DateTagTest extends AbstractTagTest {
|
||||
freshTag.setPageContext(pageContext);
|
||||
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
}
|
||||
|
||||
public void testNoDateObjectInContext_clearTagStateSet() throws Exception {
|
||||
@@ -904,7 +936,7 @@ public class DateTagTest extends AbstractTagTest {
|
||||
freshTag.setPageContext(pageContext);
|
||||
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -912,10 +944,8 @@ public class DateTagTest extends AbstractTagTest {
|
||||
* since that tag does not have its own unit tests, and it also appears to be
|
||||
* a broken tag. The code coverage tests can be moved if the tag is fixed, or
|
||||
* removed if the tag is dropped.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public void testDateTextFieldTag_artificialCoverageTest() throws Exception {
|
||||
public void testDateTextFieldTag_artificialCoverageTest() throws JspException {
|
||||
final String format = "yyyy/MM/dd hh:mm:ss";
|
||||
DateTextFieldTag dateTextFieldTag = createDateTextFieldTag();
|
||||
dateTextFieldTag.setFormat(format);
|
||||
@@ -939,20 +969,40 @@ public class DateTagTest extends AbstractTagTest {
|
||||
freshTag.setPageContext(pageContext);
|
||||
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
strutsBodyTagsAreReflectionEqual(dateTextFieldTag, freshTag));
|
||||
strutsBodyTagsAreReflectionEqual(dateTextFieldTag, freshTag));
|
||||
}
|
||||
|
||||
public void testNewJava8Format() throws Exception {
|
||||
String format = "EEEE MMMM dd, hh:mm a";
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
String formatted = DateTimeFormatter.ofPattern(format, ActionContext.getContext().getLocale()).format(now);
|
||||
context.put("myDate", now);
|
||||
|
||||
tag.setName("myDate");
|
||||
tag.setNice(false);
|
||||
tag.setFormat(format);
|
||||
tag.doStartTag();
|
||||
tag.doEndTag();
|
||||
assertEquals(formatted, writer.toString());
|
||||
|
||||
// Basic sanity check of clearTagStateForTagPoolingServers() behaviour for Struts Tags after doEndTag().
|
||||
DateTag freshTag = new DateTag();
|
||||
freshTag.setPageContext(pageContext);
|
||||
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to create a new {@link DateTextFieldTag} instance for code coverage tests.
|
||||
*
|
||||
* <p>
|
||||
* Note: There is no datetextfield.ftl template for the tag, so it does not appear that it can
|
||||
* actually be used in practice. We can perform basic coverage tests from within this
|
||||
* unit test class until the {@link DateTextFieldTag} is fixed or removed.
|
||||
*
|
||||
* actually be used in practice. We can perform basic coverage tests from within this
|
||||
* unit test class until the {@link DateTextFieldTag} is fixed or removed.
|
||||
*
|
||||
* @return a basic {@link DateTextFieldTag} instance
|
||||
* @throws Exception
|
||||
*/
|
||||
private DateTextFieldTag createDateTextFieldTag() throws Exception {
|
||||
private DateTextFieldTag createDateTextFieldTag() {
|
||||
DateTextFieldTag tag = new DateTextFieldTag();
|
||||
tag.setPageContext(pageContext);
|
||||
tag.setName("myDate");
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.0.0-RC2</version>
|
||||
<version>6.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-async-plugin</artifactId>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.0.0-RC2</version>
|
||||
<version>6.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -44,11 +44,6 @@
|
||||
<version>2.0.1.Final</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-beanutils</groupId>
|
||||
<artifactId>commons-beanutils</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.hibernate</groupId>
|
||||
<artifactId>hibernate-validator</artifactId>
|
||||
@@ -60,6 +55,17 @@
|
||||
<artifactId>javax.el</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- this library is excluded in the parent pom as it clashes with Easymock dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.objenesis</groupId>
|
||||
<artifactId>objenesis</artifactId>
|
||||
<version>3.2</version>
|
||||
</dependency>
|
||||
|
||||
<!--
|
||||
The Java EE API modules listed below are all marked @Deprecated(forRemoval=true), because they are scheduled
|
||||
@@ -94,4 +100,4 @@
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
</project>
|
||||
|
||||
+17
-4
@@ -20,17 +20,20 @@
|
||||
*/
|
||||
package org.apache.struts.beanvalidation.constraints.impl;
|
||||
|
||||
import org.apache.commons.beanutils.PropertyUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.struts.beanvalidation.constraints.FieldMatch;
|
||||
|
||||
import javax.validation.ConstraintValidator;
|
||||
import javax.validation.ConstraintValidatorContext;
|
||||
import java.beans.BeanInfo;
|
||||
import java.beans.Introspector;
|
||||
import java.beans.PropertyDescriptor;
|
||||
|
||||
public class FieldMatchValidator implements ConstraintValidator<FieldMatch, Object> {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(FieldMatchValidator.class);
|
||||
|
||||
private String firstFieldName;
|
||||
private String secondFieldName;
|
||||
|
||||
@@ -41,13 +44,23 @@ public class FieldMatchValidator implements ConstraintValidator<FieldMatch, Obje
|
||||
|
||||
public boolean isValid(final Object value, final ConstraintValidatorContext context) {
|
||||
try {
|
||||
final Object firstObj = PropertyUtils.getProperty(value, this.firstFieldName);
|
||||
final Object secondObj = PropertyUtils.getProperty(value, this.secondFieldName);
|
||||
final Object firstObj = readPropertyValue(value, this.firstFieldName);
|
||||
final Object secondObj = readPropertyValue(value, this.secondFieldName);
|
||||
return firstObj == null && secondObj == null || firstObj != null && firstObj.equals(secondObj);
|
||||
} catch (final Exception ex) {
|
||||
LOG.info("Error while getting values from object", ex);
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Object readPropertyValue(Object bean, String propertyName) throws Exception {
|
||||
BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());
|
||||
for (PropertyDescriptor descriptor : beanInfo.getPropertyDescriptors()) {
|
||||
if (propertyName.equals(descriptor.getName())) {
|
||||
return descriptor.getReadMethod().invoke(bean);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts.beanvalidation.constraints.impl;
|
||||
|
||||
import org.apache.struts.beanvalidation.constraints.FieldMatch;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import javax.validation.ConstraintValidatorContext;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class FieldMatchValidatorTest {
|
||||
|
||||
@Test
|
||||
public void matchingFields() {
|
||||
// given
|
||||
FieldMatchValidator validator = new FieldMatchValidator();
|
||||
validator.initialize(FieldMatchTestBean.class.getAnnotation(FieldMatch.class));
|
||||
|
||||
ConstraintValidatorContext context = Mockito.mock(ConstraintValidatorContext.class);
|
||||
|
||||
// when
|
||||
FieldMatchTestBean bean = new FieldMatchTestBean("12345678", "12345678");
|
||||
|
||||
boolean valid = validator.isValid(bean, context);
|
||||
|
||||
// then
|
||||
assertTrue(valid);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void notMatchingFields() {
|
||||
// given
|
||||
FieldMatchValidator validator = new FieldMatchValidator();
|
||||
validator.initialize(FieldMatchTestBean.class.getAnnotation(FieldMatch.class));
|
||||
|
||||
ConstraintValidatorContext context = Mockito.mock(ConstraintValidatorContext.class);
|
||||
|
||||
// when
|
||||
FieldMatchTestBean bean = new FieldMatchTestBean("12345678", "87654321");
|
||||
|
||||
boolean valid = validator.isValid(bean, context);
|
||||
|
||||
// then
|
||||
assertFalse(valid);
|
||||
}
|
||||
|
||||
@FieldMatch(first = "password", second = "repeatPassword")
|
||||
public static class FieldMatchTestBean {
|
||||
String password;
|
||||
String repeatPassword;
|
||||
|
||||
public FieldMatchTestBean(String password, String repeatPassword) {
|
||||
this.password = password;
|
||||
this.repeatPassword = repeatPassword;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public String getRepeatPassword() {
|
||||
return repeatPassword;
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -25,7 +25,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.0.0-RC2</version>
|
||||
<version>6.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-cdi-plugin</artifactId>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.0.0-RC2</version>
|
||||
<version>6.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-config-browser-plugin</artifactId>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.0.0-RC2</version>
|
||||
<version>6.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-convention-plugin</artifactId>
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.0.0-RC2</version>
|
||||
<version>6.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-dwr-plugin</artifactId>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.0.0-RC2</version>
|
||||
<version>6.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-embeddedjsp-plugin</artifactId>
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.0.0-RC2</version>
|
||||
<version>6.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-gxp-plugin</artifactId>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.0.0-RC2</version>
|
||||
<version>6.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-jasperreports-plugin</artifactId>
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.0.0-RC2</version>
|
||||
<version>6.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-javatemplates-plugin</artifactId>
|
||||
|
||||
@@ -28,7 +28,7 @@ import java.util.LinkedHashMap;
|
||||
*/
|
||||
public class Attributes extends LinkedHashMap<String, String> {
|
||||
|
||||
private static final long serialVersionUID = 4103241472140545630L;
|
||||
private static final long serialVersionUID = 4103241472140545630L;
|
||||
|
||||
public Attributes add(String key, String value) {
|
||||
return add(key, value, true);
|
||||
@@ -74,15 +74,17 @@ public class Attributes extends LinkedHashMap<String, String> {
|
||||
* @return this
|
||||
*/
|
||||
public Attributes addIfTrue(String attrName, Object paramValue) {
|
||||
if (paramValue != null) {
|
||||
if ((paramValue instanceof Boolean && ((Boolean) paramValue).booleanValue()) ||
|
||||
(Boolean.valueOf(paramValue.toString()).booleanValue())) {
|
||||
put(attrName, attrName);
|
||||
}
|
||||
if (paramValue != null && isTrue(paramValue)) {
|
||||
put(attrName, attrName);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private boolean isTrue(Object paramValue) {
|
||||
return (paramValue instanceof Boolean && (Boolean) paramValue)
|
||||
|| (Boolean.parseBoolean(paramValue.toString()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a key/value pair to the attributes, if the value is null, it will be set as an empty string.
|
||||
* Value is html encoded.
|
||||
|
||||
+8
-6
@@ -37,12 +37,14 @@ public class AnchorHandler extends AbstractTagHandler implements TagGenerator {
|
||||
Attributes attrs = new Attributes();
|
||||
|
||||
attrs.addIfExists("name", params.get("name"))
|
||||
.addIfExists("id", params.get("id"))
|
||||
.addIfExists("class", params.get("cssClass"))
|
||||
.addIfExists("style", params.get("cssStyle"))
|
||||
.addIfExists("href", params.get("href"), false)
|
||||
.addIfExists("title", params.get("title"))
|
||||
.addIfExists("tabindex", params.get("tabindex"));
|
||||
.addIfExists("id", params.get("id"))
|
||||
.addIfExists("class", params.get("cssClass"))
|
||||
.addIfExists("style", params.get("cssStyle"))
|
||||
.addIfExists("href", params.get("href"), false)
|
||||
.addIfTrue("disabled", params.get("disabled"))
|
||||
.addIfExists("title", params.get("title"))
|
||||
.addIfExists("tabindex", params.get("tabindex"));
|
||||
|
||||
start("a", attrs);
|
||||
String body = (String) params.get("body");
|
||||
Boolean escapeHtmlBody = (Boolean) params.get("escapeHtmlBody");
|
||||
|
||||
+22
-17
@@ -18,13 +18,14 @@
|
||||
*/
|
||||
package org.apache.struts2.views.java.simple;
|
||||
|
||||
import org.apache.struts2.views.java.Attributes;
|
||||
import org.apache.struts2.views.java.TagGenerator;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.text.StringEscapeUtils;
|
||||
import org.apache.struts2.views.java.Attributes;
|
||||
import org.apache.struts2.views.java.TagGenerator;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public class CheckboxHandler extends AbstractTagHandler implements TagGenerator {
|
||||
public void generate() throws IOException {
|
||||
@@ -35,29 +36,33 @@ public class CheckboxHandler extends AbstractTagHandler implements TagGenerator
|
||||
String id = (String) params.get("id");
|
||||
String name = (String) params.get("name");
|
||||
Object disabled = params.get("disabled");
|
||||
boolean submitUnchecked = Boolean.parseBoolean(Objects.toString(params.get("submitUnchecked"), "false"));
|
||||
|
||||
attrs.add("type", "checkbox")
|
||||
.add("name", name)
|
||||
.add("value", fieldValue)
|
||||
.addIfTrue("checked", params.get("nameValue"))
|
||||
.addIfTrue("readonly", params.get("readonly"))
|
||||
.addIfTrue("disabled", disabled)
|
||||
.addIfExists("tabindex", params.get("tabindex"))
|
||||
.addIfExists("id", id)
|
||||
.addIfExists("class", params.get("cssClass"))
|
||||
.addIfExists("style", params.get("cssStyle"))
|
||||
.addIfExists("title", params.get("title"));
|
||||
.add("name", name)
|
||||
.add("value", fieldValue)
|
||||
.addIfTrue("checked", params.get("nameValue"))
|
||||
.addIfTrue("readonly", params.get("readonly"))
|
||||
.addIfTrue("disabled", disabled)
|
||||
.addIfExists("tabindex", params.get("tabindex"))
|
||||
.addIfExists("id", id)
|
||||
.addIfExists("class", params.get("cssClass"))
|
||||
.addIfExists("style", params.get("cssStyle"))
|
||||
.addIfExists("title", params.get("title"));
|
||||
start("input", attrs);
|
||||
end("input");
|
||||
|
||||
//hidden input
|
||||
attrs = new Attributes();
|
||||
attrs.add("type", "hidden")
|
||||
if (submitUnchecked) {
|
||||
//hidden input
|
||||
attrs = new Attributes();
|
||||
|
||||
attrs.add("type", "hidden")
|
||||
.add("id", "__checkbox_" + StringUtils.defaultString(StringEscapeUtils.escapeHtml4(id)))
|
||||
.add("name", "__checkbox_" + StringUtils.defaultString(StringEscapeUtils.escapeHtml4(name)))
|
||||
.add("value", "__checkbox_" + StringUtils.defaultString(StringEscapeUtils.escapeHtml4(fieldValue)))
|
||||
.addIfTrue("disabled", disabled);
|
||||
start("input", attrs);
|
||||
end("input");
|
||||
start("input", attrs);
|
||||
end("input");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-1
@@ -40,9 +40,13 @@ public class LinkHandler extends AbstractTagHandler implements TagGenerator {
|
||||
.addIfExists("referrerpolicy", params.get("referrerpolicy"))
|
||||
.addIfExists("type", params.get("type"))
|
||||
.addIfExists("as", params.get("as"))
|
||||
.addIfExists("disabled", params.get("disabled"))
|
||||
.addIfExists("title", params.get("title"));
|
||||
|
||||
// see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-disabled
|
||||
if ("stylesheet".equals(params.get("rel"))) {
|
||||
attrs.addIfTrue("disabled", params.get("disabled"));
|
||||
}
|
||||
|
||||
start("link", attrs);
|
||||
end("link");
|
||||
}
|
||||
|
||||
+9
-8
@@ -18,9 +18,9 @@
|
||||
*/
|
||||
package org.apache.struts2.views.java.simple;
|
||||
|
||||
import org.apache.struts2.views.java.TagGenerator;
|
||||
import org.apache.struts2.views.java.Attributes;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.struts2.views.java.Attributes;
|
||||
import org.apache.struts2.views.java.TagGenerator;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
@@ -34,12 +34,13 @@ public class ResetHandler extends AbstractTagHandler implements TagGenerator {
|
||||
boolean isButton = "button".equals(params.get("type"));
|
||||
|
||||
attrs.addDefaultToEmpty("name", params.get("name"))
|
||||
.add("type", "reset")
|
||||
.addIfExists("value", params.get("nameValue"))
|
||||
.addIfExists("tabindex", params.get("tabindex"))
|
||||
.addIfExists("id", params.get("id"))
|
||||
.addIfExists("class", params.get("cssClass"))
|
||||
.addIfExists("style", params.get("cssStyle"));
|
||||
.add("type", "reset")
|
||||
.addIfExists("value", params.get("nameValue"))
|
||||
.addIfTrue("disabled", params.get("disabled"))
|
||||
.addIfExists("tabindex", params.get("tabindex"))
|
||||
.addIfExists("id", params.get("id"))
|
||||
.addIfExists("class", params.get("cssClass"))
|
||||
.addIfExists("style", params.get("cssStyle"));
|
||||
|
||||
if (!isButton)
|
||||
attrs.addIfExists("title", params.get("title"));
|
||||
|
||||
+1
-1
@@ -43,7 +43,7 @@ public class AnchorTest extends AbstractTest {
|
||||
theme.renderTag(getTagName(), context);
|
||||
theme.renderTag(getTagName() + "-close", context);
|
||||
String output = writer.getBuffer().toString();
|
||||
String expected = s("<a name='name_' id='id_' class='class' style='style' href='http://sometest.com?ab=10' title='title' tabindex='1'></a>");
|
||||
String expected = s("<a name='name_' id='id_' class='class' style='style' href='http://sometest.com?ab=10' disabled='disabled' title='title' tabindex='1'></a>");
|
||||
assertEquals(expected, output);
|
||||
}
|
||||
|
||||
|
||||
+22
-4
@@ -29,7 +29,7 @@ public class CheckboxTest extends AbstractCommonAttributesTest {
|
||||
|
||||
public void testRenderCheckbox() {
|
||||
tag.setName("name_");
|
||||
tag.setDisabled("true");
|
||||
tag.setDisabled("false");
|
||||
tag.setTabindex("1");
|
||||
tag.setId("id_");
|
||||
tag.setCssClass("class");
|
||||
@@ -41,20 +41,38 @@ public class CheckboxTest extends AbstractCommonAttributesTest {
|
||||
map.putAll(tag.getParameters());
|
||||
theme.renderTag(getTagName(), context);
|
||||
String output = writer.getBuffer().toString();
|
||||
String expected = s("<input type='checkbox' name='name_' value='xyz' tabindex='1' id='id_' class='class' style='style' title='title'></input><input type='hidden' id='__checkbox_id_' name='__checkbox_name_' value='__checkbox_xyz'></input>");
|
||||
String expected = s("<input type='checkbox' name='name_' value='xyz' tabindex='1' id='id_' class='class' style='style' title='title'></input>");
|
||||
assertEquals(expected, output);
|
||||
}
|
||||
|
||||
public void testRenderUncheckCheckbox() {
|
||||
tag.setName("name_");
|
||||
tag.setDisabled("true");
|
||||
tag.setTabindex("1");
|
||||
tag.setId("id_");
|
||||
tag.setCssClass("class");
|
||||
tag.setCssStyle("style");
|
||||
tag.setTitle("title");
|
||||
tag.setFieldValue("xyz");
|
||||
tag.setSubmitUnchecked("true");
|
||||
|
||||
tag.evaluateParams();
|
||||
map.putAll(tag.getParameters());
|
||||
theme.renderTag(getTagName(), context);
|
||||
String output = writer.getBuffer().toString();
|
||||
String expected = s("<input type='checkbox' name='name_' value='xyz' disabled='disabled' tabindex='1' id='id_' class='class' style='style' title='title'></input><input type='hidden' id='__checkbox_id_' name='__checkbox_name_' value='__checkbox_xyz' disabled='disabled'></input>");
|
||||
assertEquals(expected, output);
|
||||
}
|
||||
|
||||
public void testRenderCheckboxWithNameValue() {
|
||||
tag.setName("name_");
|
||||
tag.setValue("%{someValue}");
|
||||
tag.setDisabled("true");
|
||||
|
||||
tag.evaluateParams();
|
||||
map.putAll(tag.getParameters());
|
||||
theme.renderTag(getTagName(), context);
|
||||
String output = writer.getBuffer().toString();
|
||||
String expected = s("<input type='checkbox' name='name_' value='true' checked='checked' id='name_'></input><input type='hidden' id='__checkbox_name_' name='__checkbox_name_' value='__checkbox_true'></input>");
|
||||
String expected = s("<input type='checkbox' name='name_' value='true' checked='checked' id='name_'></input>");
|
||||
assertEquals(expected, output);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -43,7 +43,7 @@ public class FileTest extends AbstractCommonAttributesTest {
|
||||
map.putAll(tag.getParameters());
|
||||
theme.renderTag(getTagName(), context);
|
||||
String output = writer.getBuffer().toString();
|
||||
String expected = s("<input name='name' type='file' size='10' value='val1' accept='accept_' tabindex='1' id='id1' class='class1' style='style1' title='title'></input>");
|
||||
String expected = s("<input name='name' type='file' size='10' value='val1' disabled='disabled' accept='accept_' tabindex='1' id='id1' class='class1' style='style1' title='title'></input>");
|
||||
assertEquals(expected, output);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -41,7 +41,7 @@ public class HiddenTest extends AbstractTest {
|
||||
map.putAll(tag.getParameters());
|
||||
theme.renderTag(getTagName(), context);
|
||||
String output = writer.getBuffer().toString();
|
||||
String expected = s("<input name='name' type='hidden' value='val1' id='id1' class='class1' style='style1'></input>");
|
||||
String expected = s("<input name='name' type='hidden' value='val1' disabled='disabled' id='id1' class='class1' style='style1'></input>");
|
||||
assertEquals(expected, output);
|
||||
}
|
||||
|
||||
|
||||
+48
-15
@@ -31,7 +31,7 @@ public class LinkTest extends AbstractTest{
|
||||
|
||||
private static final String NONCE_VAL = "r4andom";
|
||||
|
||||
public void testRenderScriptTag() {
|
||||
public void testRenderLinkTag() {
|
||||
tag.setHref("testhref");
|
||||
tag.setHreflang("test");
|
||||
tag.setRel("module");
|
||||
@@ -41,27 +41,60 @@ public class LinkTest extends AbstractTest{
|
||||
tag.setCrossorigin("same-origin");
|
||||
tag.setType("anonymous");
|
||||
tag.setAs("test");
|
||||
tag.setDisabled("disabled_");
|
||||
tag.setDisabled("true");
|
||||
tag.setTitle("test");
|
||||
|
||||
tag.evaluateParams();
|
||||
map.putAll(tag.getParameters());
|
||||
theme.renderTag(getTagName(), context);
|
||||
String s = writer.getBuffer().toString();
|
||||
String output = writer.getBuffer().toString();
|
||||
|
||||
assertTrue("Incorrect href attribute for link tag", s.contains("href=\"testhref\""));
|
||||
assertTrue("Incorrect hreflang attribute for link tag", s.contains("hreflang=\"test\""));
|
||||
assertTrue("Incorrect rel attribute for link tag", s.contains("rel=\"module\""));
|
||||
assertTrue("Incorrect media attribute for link tag", s.contains("media=\"foo\""));
|
||||
assertTrue("Incorrect referrerpolicy attribute for link tag", s.contains("referrerpolicy=\"test\""));
|
||||
assertTrue("Incorrect sizes attribute for link tag", s.contains("sizes=\"foo\""));
|
||||
assertTrue("Incorrect crossorigin attribute for link tag", s.contains("crossorigin=\"same-origin\""));
|
||||
assertTrue("Incorrect type attribute for link tag", s.contains("type=\"anonymous\""));
|
||||
assertTrue("Incorrect as attribute for link tag", s.contains("as=\"test\""));
|
||||
assertTrue("Non-existent disabled attribute for link tag", s.contains("disabled=\"disabled_\""));
|
||||
assertTrue("Incorrect title attribute for link tag", s.contains("title=\"test\""));
|
||||
assertTrue("Incorrect nonce attribute for link tag", s.contains("nonce=\"" + NONCE_VAL+"\""));
|
||||
assertTrue("Incorrect href attribute for link tag", output.contains(s("href='testhref'")));
|
||||
assertTrue("Incorrect hreflang attribute for link tag", output.contains(s("hreflang='test'")));
|
||||
assertTrue("Incorrect rel attribute for link tag", output.contains(s("rel='module'")));
|
||||
assertTrue("Incorrect media attribute for link tag", output.contains(s("media='foo'")));
|
||||
assertTrue("Incorrect referrerpolicy attribute for link tag", output.contains(s("referrerpolicy='test'")));
|
||||
assertTrue("Incorrect sizes attribute for link tag", output.contains(s("sizes='foo'")));
|
||||
assertTrue("Incorrect crossorigin attribute for link tag", output.contains(s("crossorigin='same-origin'")));
|
||||
assertTrue("Incorrect type attribute for link tag", output.contains(s("type='anonymous'")));
|
||||
assertTrue("Incorrect as attribute for link tag", output.contains(s("as='test'")));
|
||||
assertFalse("Non-existent disabled attribute for link tag", output.contains(s("disabled='disabled'")));
|
||||
assertTrue("Incorrect title attribute for link tag", output.contains(s("title='test'")));
|
||||
assertTrue("Incorrect nonce attribute for link tag", output.contains(s("nonce='" + NONCE_VAL+"'")));
|
||||
}
|
||||
|
||||
public void testRenderLinkTagAsStylesheet() {
|
||||
tag.setHref("testhref");
|
||||
tag.setHreflang("test");
|
||||
tag.setRel("stylesheet");
|
||||
tag.setMedia("foo");
|
||||
tag.setReferrerpolicy("test");
|
||||
tag.setSizes("foo");
|
||||
tag.setCrossorigin("same-origin");
|
||||
tag.setType("anonymous");
|
||||
tag.setAs("test");
|
||||
tag.setDisabled("true");
|
||||
tag.setTitle("test");
|
||||
|
||||
tag.evaluateParams();
|
||||
map.putAll(tag.getParameters());
|
||||
theme.renderTag(getTagName(), context);
|
||||
String output = writer.getBuffer().toString();
|
||||
|
||||
assertTrue("Incorrect href attribute for link tag", output.contains(s("href='testhref'")));
|
||||
assertTrue("Incorrect hreflang attribute for link tag", output.contains(s("hreflang='test'")));
|
||||
assertTrue("Incorrect rel attribute for link tag", output.contains(s("rel='stylesheet'")));
|
||||
assertTrue("Incorrect media attribute for link tag", output.contains(s("media='foo'")));
|
||||
assertTrue("Incorrect referrerpolicy attribute for link tag", output.contains(s("referrerpolicy='test'")));
|
||||
assertTrue("Incorrect sizes attribute for link tag", output.contains(s("sizes='foo'")));
|
||||
assertTrue("Incorrect crossorigin attribute for link tag", output.contains(s("crossorigin='same-origin'")));
|
||||
assertTrue("Incorrect type attribute for link tag", output.contains(s("type='anonymous'")));
|
||||
assertTrue("Incorrect as attribute for link tag", output.contains(s("as='test'")));
|
||||
assertTrue("Incorrect disabled attribute for link tag", output.contains(s("disabled='disabled'")));
|
||||
assertTrue("Incorrect title attribute for link tag", output.contains(s("title='test'")));
|
||||
assertTrue("Incorrect nonce attribute for link tag", output.contains(s("nonce='" + NONCE_VAL+"'")));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UIBean getUIBean() throws Exception {
|
||||
return tag;
|
||||
|
||||
+3
-5
@@ -24,11 +24,10 @@ import org.apache.struts2.components.Password;
|
||||
import org.apache.struts2.components.UIBean;
|
||||
|
||||
public class PasswordTest extends AbstractCommonAttributesTest {
|
||||
|
||||
private Password tag;
|
||||
private boolean showPassword;
|
||||
|
||||
public void testRenderPassword() throws Exception {
|
||||
this.showPassword = false;
|
||||
super.setUp();
|
||||
this.tag = new Password(stack, request, response);
|
||||
|
||||
@@ -47,12 +46,11 @@ public class PasswordTest extends AbstractCommonAttributesTest {
|
||||
map.putAll(tag.getParameters());
|
||||
theme.renderTag(getTagName(), context);
|
||||
String output = writer.getBuffer().toString();
|
||||
String expected = s("<input name='name' type='password' size='10' tabindex='1' id='id1' class='class1' style='style1' title='title'></input>");
|
||||
String expected = s("<input name='name' type='password' size='10' disabled='disabled' tabindex='1' id='id1' class='class1' style='style1' title='title'></input>");
|
||||
assertEquals(expected, output);
|
||||
}
|
||||
|
||||
public void testRenderPasswordShowIt() throws Exception {
|
||||
this.showPassword = true;
|
||||
super.setUp();
|
||||
this.tag = new Password(stack, request, response);
|
||||
|
||||
@@ -71,7 +69,7 @@ public class PasswordTest extends AbstractCommonAttributesTest {
|
||||
map.putAll(tag.getParameters());
|
||||
theme.renderTag(getTagName(), context);
|
||||
String output = writer.getBuffer().toString();
|
||||
String expected = s("<input value='val1' name='name' type='password' size='10' tabindex='1' id='id1' class='class1' style='style1' title='title'></input>");
|
||||
String expected = s("<input value='val1' name='name' type='password' size='10' disabled='disabled' tabindex='1' id='id1' class='class1' style='style1' title='title'></input>");
|
||||
assertEquals(expected, output);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ public class SelectTest extends AbstractCommonAttributesTest {
|
||||
map.putAll(tag.getParameters());
|
||||
theme.renderTag(getTagName(), context);
|
||||
String output = writer.getBuffer().toString();
|
||||
String expected = s("<select name='name_' size='10' tabindex='1' id='id_' class='class' style='style' title='title'></select>");
|
||||
String expected = s("<select name='name_' size='10' disabled='disabled' tabindex='1' id='id_' class='class' style='style' title='title'></select>");
|
||||
assertEquals(expected, output);
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -47,7 +47,7 @@ public class SubmitTest extends AbstractCommonAttributesTest {
|
||||
map.putAll(tag.getParameters());
|
||||
theme.renderTag(getTagName() + "-close", context);
|
||||
String output = writer.getBuffer().toString();
|
||||
String expected = s("<button name='name' type='submit' value='val1' tabindex='1' id='id1' class='class1' style='style1'><span>hey hey hey, here I go now</span></button>");
|
||||
String expected = s("<button name='name' type='submit' value='val1' disabled='disabled' tabindex='1' id='id1' class='class1' style='style1'><span>hey hey hey, here I go now</span></button>");
|
||||
assertEquals(expected, output);
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ public class SubmitTest extends AbstractCommonAttributesTest {
|
||||
theme.renderTag(getTagName(), context);
|
||||
theme.renderTag(getTagName() + "-close", context);
|
||||
String output = writer.getBuffer().toString();
|
||||
String expected = s("<button name='name' type='submit' value='val1' tabindex='1' id='id1' class='class1' style='style1'>Just as soon as I belong, than its time I disappear</button>");
|
||||
String expected = s("<button name='name' type='submit' value='val1' disabled='disabled' tabindex='1' id='id1' class='class1' style='style1'>Just as soon as I belong, than its time I disappear</button>");
|
||||
assertEquals(expected, output);
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ public class SubmitTest extends AbstractCommonAttributesTest {
|
||||
theme.renderTag(getTagName(), context);
|
||||
theme.renderTag(getTagName() + "-close", context);
|
||||
String output = writer.getBuffer().toString();
|
||||
String expected = s("<input name='name' type='submit' value='val1' tabindex='1' id='id1' class='class1' style='style1'></input>");
|
||||
String expected = s("<input name='name' type='submit' value='val1' disabled='disabled' tabindex='1' id='id1' class='class1' style='style1'></input>");
|
||||
assertEquals(expected, output);
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ public class SubmitTest extends AbstractCommonAttributesTest {
|
||||
theme.renderTag(getTagName(), context);
|
||||
theme.renderTag(getTagName() + "-close", context);
|
||||
String output = writer.getBuffer().toString();
|
||||
String expected = s("<input name='name' type='submit' value='val1' tabindex='1' id='id1' class='class1' style='style1'></input>");
|
||||
String expected = s("<input name='name' type='submit' value='val1' disabled='disabled' tabindex='1' id='id1' class='class1' style='style1'></input>");
|
||||
assertEquals(expected, output);
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -44,7 +44,7 @@ public class TextAreaTest extends AbstractCommonAttributesTest {
|
||||
map.putAll(tag.getParameters());
|
||||
theme.renderTag(getTagName(), context);
|
||||
String output = writer.getBuffer().toString();
|
||||
String expected = s("<textarea name='name' cols='2' rows='1' tabindex='1' id='id1' class='class1' style='style1' title='title'>val1</textarea>");
|
||||
String expected = s("<textarea name='name' cols='2' rows='1' disabled='disabled' tabindex='1' id='id1' class='class1' style='style1' title='title'>val1</textarea>");
|
||||
assertEquals(expected, output);
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ public class TextAreaTest extends AbstractCommonAttributesTest {
|
||||
map.putAll(tag.getParameters());
|
||||
theme.renderTag(getTagName(), context);
|
||||
String output = writer.getBuffer().toString();
|
||||
String expected = s("<textarea name='' cols='' rows='' tabindex='1' id='id1' class='class1' style='style1' title='title'>val1</textarea>");
|
||||
String expected = s("<textarea name='' cols='' rows='' disabled='disabled' tabindex='1' id='id1' class='class1' style='style1' title='title'>val1</textarea>");
|
||||
assertEquals(expected, output);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -44,7 +44,7 @@ public class TextFieldTest extends AbstractCommonAttributesTest {
|
||||
map.putAll(tag.getParameters());
|
||||
theme.renderTag(getTagName(), context);
|
||||
String output = writer.getBuffer().toString();
|
||||
String expected = s("<input type='text' name='name' size='10' maxlength='11' value='val1' tabindex='1' id='id1' class='class1' style='style1' title='title'></input>");
|
||||
String expected = s("<input type='text' name='name' size='10' maxlength='11' value='val1' disabled='disabled' tabindex='1' id='id1' class='class1' style='style1' title='title'></input>");
|
||||
assertEquals(expected, output);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.0.0-RC2</version>
|
||||
<version>6.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-jfreechart-plugin</artifactId>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.0.0-RC2</version>
|
||||
<version>6.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-json-plugin</artifactId>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.0.0-RC2</version>
|
||||
<version>6.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-junit-plugin</artifactId>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.0.0-RC2</version>
|
||||
<version>6.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-osgi-plugin</artifactId>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.0.0-RC2</version>
|
||||
<version>6.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-oval-plugin</artifactId>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.0.0-RC2</version>
|
||||
<version>6.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-pell-multipart-plugin</artifactId>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.0.0-RC2</version>
|
||||
<version>6.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-plexus-plugin</artifactId>
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-parent</artifactId>
|
||||
<version>6.0.0-RC2</version>
|
||||
<version>6.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.0.0-RC2</version>
|
||||
<version>6.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-portlet-mocks-plugin</artifactId>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.0.0-RC2</version>
|
||||
<version>6.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-portlet-tiles-plugin</artifactId>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.0.0-RC2</version>
|
||||
<version>6.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-portlet-plugin</artifactId>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.0.0-RC2</version>
|
||||
<version>6.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-rest-plugin</artifactId>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.0.0-RC2</version>
|
||||
<version>6.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-sitemesh-plugin</artifactId>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.0.0-RC2</version>
|
||||
<version>6.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-spring-plugin</artifactId>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.0.0-RC2</version>
|
||||
<version>6.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-testng-plugin</artifactId>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.0.0-RC2</version>
|
||||
<version>6.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-tiles-plugin</artifactId>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.0.0-RC2</version>
|
||||
<version>6.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-velocity-plugin</artifactId>
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>struts2-parent</artifactId>
|
||||
<version>6.0.0-RC2</version>
|
||||
<version>6.0.0-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>Struts 2</name>
|
||||
<url>http://struts.apache.org/</url>
|
||||
@@ -51,7 +51,7 @@
|
||||
<connection>scm:git:https://gitbox.apache.org/repos/asf/struts.git</connection>
|
||||
<developerConnection>scm:git:https://gitbox.apache.org/repos/asf/struts.git</developerConnection>
|
||||
<url>https://github.com/apache/struts/</url>
|
||||
<tag>STRUTS_6_0_0_RC2</tag>
|
||||
<tag>HEAD</tag>
|
||||
</scm>
|
||||
|
||||
<issueManagement>
|
||||
@@ -104,14 +104,14 @@
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.build.outputTimestamp>2022-01-29T16:32:23Z</project.build.outputTimestamp>
|
||||
<project.build.outputTimestamp>2022-02-24T06:11:22Z</project.build.outputTimestamp>
|
||||
<java.version>1.8</java.version>
|
||||
|
||||
<!-- dependency versions in alphanumeric order -->
|
||||
<asm.version>9.2</asm.version>
|
||||
<jackson.version>2.10.5</jackson.version>
|
||||
<jackson-databind.version>2.10.5.1</jackson-databind.version>
|
||||
<log4j2.version>2.17.1</log4j2.version>
|
||||
<log4j2.version>2.17.2</log4j2.version>
|
||||
<ognl.version>3.3.2</ognl.version>
|
||||
<slf4j.version>1.7.32</slf4j.version>
|
||||
<spring.platformVersion>4.3.30.RELEASE</spring.platformVersion>
|
||||
@@ -933,12 +933,6 @@
|
||||
<artifactId>commons-collections4</artifactId>
|
||||
<version>4.4</version>
|
||||
</dependency>
|
||||
<!-- it's a dependency of Velocity defined here to override the transitive one -->
|
||||
<dependency>
|
||||
<groupId>commons-collections</groupId>
|
||||
<artifactId>commons-collections</artifactId>
|
||||
<version>3.2.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-fileupload</groupId>
|
||||
<artifactId>commons-fileupload</artifactId>
|
||||
@@ -1081,7 +1075,7 @@
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>3.3.3</version>
|
||||
<version>4.3.1</version>
|
||||
<exclusions>
|
||||
<!-- The mockito-core artifact and easymock artifact use different versions of objenesis (2.6 vs 3.1).
|
||||
Excluding the older version here to pass enforcer. When next upgrading mockito-core, confirm whether this exclusion is still required. -->
|
||||
@@ -1134,7 +1128,7 @@
|
||||
<dependency>
|
||||
<groupId>com.thoughtworks.xstream</groupId>
|
||||
<artifactId>xstream</artifactId>
|
||||
<version>1.4.18</version>
|
||||
<version>1.4.19</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>io.github.x-stream</groupId>
|
||||
|
||||
Reference in New Issue
Block a user