mirror of
https://github.com/apache/struts.git
synced 2026-09-05 13:49:39 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1b2f1857d0 |
Vendored
BIN
Binary file not shown.
+18
-4
@@ -1,4 +1,18 @@
|
||||
wrapperVersion=3.3.4
|
||||
distributionType=bin
|
||||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.16/apache-maven-3.9.16-bin.zip
|
||||
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.4/maven-wrapper-3.3.4.jar
|
||||
# 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.
|
||||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.6/apache-maven-3.9.6-bin.zip
|
||||
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-parent</artifactId>
|
||||
<version>6.12.0-SNAPSHOT</version>
|
||||
<version>6.9.0</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.12.0-SNAPSHOT</version>
|
||||
<version>6.9.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-rest-showcase</artifactId>
|
||||
<packaging>war</packaging>
|
||||
<version>6.12.0-SNAPSHOT</version>
|
||||
<version>6.9.0</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.12.0-SNAPSHOT</version>
|
||||
<version>6.9.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-showcase</artifactId>
|
||||
@@ -167,7 +167,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-failsafe-plugin</artifactId>
|
||||
<version>3.5.6</version>
|
||||
<version>3.5.5</version>
|
||||
<configuration>
|
||||
<includes>
|
||||
<include>it.org.apache.struts2.showcase.*Test</include>
|
||||
|
||||
+2
-2
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-parent</artifactId>
|
||||
<version>6.12.0-SNAPSHOT</version>
|
||||
<version>6.9.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-assembly</artifactId>
|
||||
@@ -106,7 +106,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<version>3.8.0</version>
|
||||
<version>3.7.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>make-assembly</id>
|
||||
|
||||
+2
-2
@@ -25,7 +25,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-parent</artifactId>
|
||||
<version>6.12.0-SNAPSHOT</version>
|
||||
<version>6.9.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-bom</artifactId>
|
||||
@@ -43,7 +43,7 @@
|
||||
</licenses>
|
||||
|
||||
<properties>
|
||||
<struts-version.version>6.12.0-SNAPSHOT</struts-version.version>
|
||||
<struts-version.version>6.9.0</struts-version.version>
|
||||
<maven.site.skip>true</maven.site.skip>
|
||||
<maven.site.deploy.skip>true</maven.site.deploy.skip>
|
||||
</properties>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-osgi-bundles</artifactId>
|
||||
<version>6.12.0-SNAPSHOT</version>
|
||||
<version>6.9.0</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.12.0-SNAPSHOT</version>
|
||||
<version>6.9.0</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.12.0-SNAPSHOT</version>
|
||||
<version>6.9.0</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.12.0-SNAPSHOT</version>
|
||||
<version>6.9.0</version>
|
||||
</parent>
|
||||
<artifactId>struts2-core</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
@@ -61,7 +61,7 @@ public class CollectionConverter extends DefaultTypeConverter {
|
||||
|
||||
for (Object anObjArray : objArray) {
|
||||
Object convertedValue = converter.convertValue(context, target, member, propertyName, anObjArray, memberType);
|
||||
if (convertedValue != TypeConverter.NO_CONVERSION_POSSIBLE) {
|
||||
if (!TypeConverter.NO_CONVERSION_POSSIBLE.equals(convertedValue)) {
|
||||
result.add(convertedValue);
|
||||
}
|
||||
}
|
||||
@@ -72,7 +72,7 @@ public class CollectionConverter extends DefaultTypeConverter {
|
||||
|
||||
for (Object aCol : col) {
|
||||
Object convertedValue = converter.convertValue(context, target, member, propertyName, aCol, memberType);
|
||||
if (convertedValue != TypeConverter.NO_CONVERSION_POSSIBLE) {
|
||||
if (!TypeConverter.NO_CONVERSION_POSSIBLE.equals(convertedValue)) {
|
||||
result.add(convertedValue);
|
||||
}
|
||||
}
|
||||
@@ -80,7 +80,7 @@ public class CollectionConverter extends DefaultTypeConverter {
|
||||
result = createCollection(toType, memberType, -1);
|
||||
TypeConverter converter = getTypeConverter(context);
|
||||
Object convertedValue = converter.convertValue(context, target, member, propertyName, value, memberType);
|
||||
if (convertedValue != TypeConverter.NO_CONVERSION_POSSIBLE) {
|
||||
if (!TypeConverter.NO_CONVERSION_POSSIBLE.equals(convertedValue)) {
|
||||
result.add(convertedValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,18 +36,6 @@ import java.util.Objects;
|
||||
|
||||
public class StringConverter extends DefaultTypeConverter {
|
||||
|
||||
/**
|
||||
* Upper bound on the number of fraction digits emitted when formatting a number.
|
||||
* <p>
|
||||
* Covers every {@code double} and {@code float} value in full - the widest is
|
||||
* {@link Double#MIN_VALUE} at 325 fraction digits - so the round-trip precision
|
||||
* introduced by WW-4871 is preserved. Beyond that bound the length of the output
|
||||
* would follow the scale of the value rather than its precision, so a
|
||||
* {@link BigDecimal} scaled past this limit is rounded to it.
|
||||
*/
|
||||
private static final int MAX_FRACTION_DIGITS = 340;
|
||||
|
||||
|
||||
@Override
|
||||
public Object convertValue(Map<String, Object> context, Object target, Member member, String propertyName, Object value, Class toType) {
|
||||
String result;
|
||||
@@ -98,7 +86,7 @@ public class StringConverter extends DefaultTypeConverter {
|
||||
// TODO: delete this variable and corresponding if statement when jdk fixed java.text.NumberFormat.format's behavior with Float
|
||||
Object fixedValue = value;
|
||||
if (BigDecimal.class.isInstance(value) || Double.class.isInstance(value) || Float.class.isInstance(value)) {
|
||||
format.setMaximumFractionDigits(MAX_FRACTION_DIGITS);
|
||||
format.setMaximumFractionDigits(Integer.MAX_VALUE);
|
||||
if (Float.class.isInstance(value)) {
|
||||
fixedValue = Double.valueOf(value.toString());
|
||||
}
|
||||
|
||||
@@ -31,15 +31,6 @@ public interface OgnlCache<Key, Value> {
|
||||
|
||||
void putIfAbsent(Key key, Value value);
|
||||
|
||||
/**
|
||||
* Removes the mapping for the given key, if present.
|
||||
*
|
||||
* @param key the key to remove
|
||||
* @return the previous value associated with the key, or {@code null} if none
|
||||
* @since 6.11.0
|
||||
*/
|
||||
Value remove(Key key);
|
||||
|
||||
int size();
|
||||
|
||||
void clear();
|
||||
|
||||
@@ -56,11 +56,6 @@ public class OgnlCaffeineCache<K, V> implements OgnlCache<K, V> {
|
||||
cache.asMap().putIfAbsent(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V remove(K key) {
|
||||
return cache.asMap().remove(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return cache.asMap().size();
|
||||
|
||||
@@ -57,11 +57,6 @@ public class OgnlDefaultCache<K, V> implements OgnlCache<K, V> {
|
||||
this.clearIfEvictionLimitExceeded();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V remove(K key) {
|
||||
return ognlCache.remove(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return ognlCache.size();
|
||||
|
||||
@@ -64,11 +64,6 @@ public class OgnlLRUCache<K, V> implements OgnlCache<K, V> {
|
||||
ognlLRUCache.putIfAbsent(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V remove(K key) {
|
||||
return ognlLRUCache.remove(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return ognlLRUCache.size();
|
||||
|
||||
-9
@@ -20,7 +20,6 @@ package com.opensymphony.xwork2.ognl.accessor;
|
||||
|
||||
import com.opensymphony.xwork2.ObjectFactory;
|
||||
import com.opensymphony.xwork2.conversion.ObjectTypeDeterminer;
|
||||
import com.opensymphony.xwork2.conversion.TypeConverter;
|
||||
import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.ognl.OgnlUtil;
|
||||
@@ -30,8 +29,6 @@ import ognl.OgnlException;
|
||||
import ognl.PropertyAccessor;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
import org.apache.struts2.StrutsException;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@@ -46,8 +43,6 @@ import java.util.Map;
|
||||
*/
|
||||
public class XWorkListPropertyAccessor extends ListPropertyAccessor {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(XWorkListPropertyAccessor.class);
|
||||
|
||||
private XWorkCollectionPropertyAccessor _sAcc = new XWorkCollectionPropertyAccessor();
|
||||
|
||||
private XWorkConverter xworkConverter;
|
||||
@@ -172,10 +167,6 @@ public class XWorkListPropertyAccessor extends ListPropertyAccessor {
|
||||
}
|
||||
|
||||
Object realValue = getRealValue(context, value, convertToClass);
|
||||
if (realValue == TypeConverter.NO_CONVERSION_POSSIBLE) {
|
||||
LOG.debug("Unable to convert value for index [{}] to the declared element type, skipping assignment", name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (target instanceof List && name instanceof Number) {
|
||||
//make sure there are enough spaces in the List to set
|
||||
|
||||
+1
-11
@@ -20,7 +20,6 @@ package com.opensymphony.xwork2.ognl.accessor;
|
||||
|
||||
import com.opensymphony.xwork2.ObjectFactory;
|
||||
import com.opensymphony.xwork2.conversion.ObjectTypeDeterminer;
|
||||
import com.opensymphony.xwork2.conversion.TypeConverter;
|
||||
import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
|
||||
@@ -127,17 +126,8 @@ public class XWorkMapPropertyAccessor extends MapPropertyAccessor {
|
||||
LOG.trace("Entering setProperty({},{},{},{})", context, target, name, value);
|
||||
|
||||
Object key = getKey(context, name);
|
||||
if (key == TypeConverter.NO_CONVERSION_POSSIBLE) {
|
||||
LOG.debug("Unable to convert key [{}] to the declared key type, skipping assignment", name);
|
||||
return;
|
||||
}
|
||||
Object convertedValue = getValue(context, value);
|
||||
if (convertedValue == TypeConverter.NO_CONVERSION_POSSIBLE) {
|
||||
LOG.debug("Unable to convert value for key [{}] to the declared element type, skipping assignment", key);
|
||||
return;
|
||||
}
|
||||
Map map = (Map) target;
|
||||
map.put(key, convertedValue);
|
||||
map.put(key, getValue(context, value));
|
||||
}
|
||||
|
||||
private Object getValue(Map context, Object value) {
|
||||
|
||||
+13
-102
@@ -21,10 +21,6 @@ package com.opensymphony.xwork2.util;
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.LocalizedTextProvider;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.ognl.DefaultOgnlCacheFactory;
|
||||
import com.opensymphony.xwork2.ognl.OgnlCache;
|
||||
import com.opensymphony.xwork2.ognl.OgnlCacheFactory.CacheType;
|
||||
import org.apache.commons.lang3.EnumUtils;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
@@ -49,11 +45,6 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
abstract class AbstractLocalizedTextProvider implements LocalizedTextProvider {
|
||||
|
||||
// Pinned to the value implicitly computed for the pre-6.11.0 class shape, so sessions serialized by
|
||||
// an older node still deserialize here during a rolling upgrade. The caches this change made transient
|
||||
// are simply discarded from such a stream and rebuilt by readObject.
|
||||
private static final long serialVersionUID = -4563130226985473584L;
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(AbstractLocalizedTextProvider.class);
|
||||
|
||||
public static final String XWORK_MESSAGES_BUNDLE = "com/opensymphony/xwork2/xwork-messages";
|
||||
@@ -65,37 +56,16 @@ abstract class AbstractLocalizedTextProvider implements LocalizedTextProvider {
|
||||
private static final String TOMCAT_WEBAPP_CLASSLOADER_BASE = "org.apache.catalina.loader.WebappClassLoaderBase";
|
||||
private static final String RELOADED = "com.opensymphony.xwork2.util.LocalizedTextProvider.reloaded";
|
||||
|
||||
protected final ConcurrentMap<String, ResourceBundle> bundlesMap = new ConcurrentHashMap<>();
|
||||
protected boolean devMode = false;
|
||||
protected boolean reloadBundles = false;
|
||||
protected boolean searchDefaultBundlesFirst = false; // Search default resource bundles first. Note: This flag may not be meaningful to all implementations.
|
||||
|
||||
private final ConcurrentMap<MessageFormatKey, MessageFormat> messageFormats = new ConcurrentHashMap<>();
|
||||
private final ConcurrentMap<Integer, List<String>> classLoaderMap = new ConcurrentHashMap<>();
|
||||
private final Set<String> missingBundles = ConcurrentHashMap.newKeySet();
|
||||
private final ConcurrentMap<Integer, ClassLoader> delegatedClassLoaderMap = new ConcurrentHashMap<>();
|
||||
|
||||
// Dedicated monitor for bundlesMap-related synchronization: bundlesMap is reassigned by
|
||||
// rebuildI18nCaches(), so locking on it directly would lock on a monitor that can change identity.
|
||||
// transient + reinitialised in readObject: a bare Object is not Serializable.
|
||||
private transient Object bundlesMapLock = new Object();
|
||||
|
||||
private static final int DEFAULT_I18N_CACHE_MAX_SIZE = 10000;
|
||||
|
||||
private volatile CacheType i18nCacheType = CacheType.WTLFU;
|
||||
private volatile int i18nCacheMaxSize = DEFAULT_I18N_CACHE_MAX_SIZE;
|
||||
|
||||
private <K, V> OgnlCache<K, V> buildI18nCache() {
|
||||
return new DefaultOgnlCacheFactory<K, V>(i18nCacheMaxSize, i18nCacheType).buildOgnlCache();
|
||||
}
|
||||
|
||||
// The OgnlCache implementations are themselves thread-safe; volatile only safely publishes the
|
||||
// reference when rebuildI18nCaches() replaces a cache (during injection / readObject), so S3077
|
||||
// ("volatile is not enough") does not apply here.
|
||||
@SuppressWarnings("java:S3077")
|
||||
protected transient volatile OgnlCache<String, ResourceBundle> bundlesMap = buildI18nCache();
|
||||
@SuppressWarnings("java:S3077")
|
||||
private transient volatile OgnlCache<MessageFormatKey, MessageFormat> messageFormats = buildI18nCache();
|
||||
@SuppressWarnings("java:S3077")
|
||||
private transient volatile OgnlCache<String, Boolean> missingBundles = buildI18nCache();
|
||||
|
||||
/**
|
||||
* Adds the bundle to the internal list of default bundles.
|
||||
* If the bundle already exists in the list it will be re-added.
|
||||
@@ -129,21 +99,6 @@ abstract class AbstractLocalizedTextProvider implements LocalizedTextProvider {
|
||||
return Thread.currentThread().getContextClassLoader();
|
||||
}
|
||||
|
||||
/** Test-support accessor: current number of cached resource bundles. */
|
||||
protected int bundlesMapSize() {
|
||||
return bundlesMap.size();
|
||||
}
|
||||
|
||||
/** Test-support accessor: current number of cached missing-bundle markers. */
|
||||
protected int missingBundlesSize() {
|
||||
return missingBundles.size();
|
||||
}
|
||||
|
||||
/** Test-support accessor: current number of cached message formats. */
|
||||
protected int messageFormatsSize() {
|
||||
return messageFormats.size();
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_CUSTOM_I18N_RESOURCES, required = false)
|
||||
public void setCustomI18NResources(String bundles) {
|
||||
if (bundles != null && bundles.length() > 0) {
|
||||
@@ -266,7 +221,7 @@ abstract class AbstractLocalizedTextProvider implements LocalizedTextProvider {
|
||||
* @param classLoader a {@link ClassLoader} to look up the bundle from if none can be found on the current thread's classloader
|
||||
*/
|
||||
public void setDelegatedClassLoader(final ClassLoader classLoader) {
|
||||
synchronized (bundlesMapLock) {
|
||||
synchronized (bundlesMap) {
|
||||
delegatedClassLoaderMap.put(getCurrentThreadContextClassLoader().hashCode(), classLoader);
|
||||
}
|
||||
}
|
||||
@@ -488,52 +443,6 @@ abstract class AbstractLocalizedTextProvider implements LocalizedTextProvider {
|
||||
this.searchDefaultBundlesFirst = Boolean.parseBoolean(searchDefaultBundlesFirst);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param cacheType the type of cache to use for the localized-text caches
|
||||
*
|
||||
* @since 6.11.0
|
||||
*/
|
||||
@Inject(value = StrutsConstants.STRUTS_I18N_CACHE_TYPE, required = false)
|
||||
public void setI18nCacheType(String cacheType) {
|
||||
this.i18nCacheType = EnumUtils.getEnumIgnoreCase(CacheType.class, cacheType, CacheType.WTLFU);
|
||||
rebuildI18nCaches();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param cacheMaxSize the maximum size of each localized-text cache
|
||||
*
|
||||
* @since 6.11.0
|
||||
*/
|
||||
@Inject(value = StrutsConstants.STRUTS_I18N_CACHE_MAXSIZE, required = false)
|
||||
public void setI18nCacheMaxSize(String cacheMaxSize) {
|
||||
this.i18nCacheMaxSize = Integer.parseInt(cacheMaxSize);
|
||||
rebuildI18nCaches();
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuilds the localized-text caches from the current type/size. Called during dependency injection
|
||||
* (single-threaded startup, before the provider serves lookups); discards any warm-up entries.
|
||||
*/
|
||||
private void rebuildI18nCaches() {
|
||||
bundlesMap = buildI18nCache();
|
||||
messageFormats = buildI18nCache();
|
||||
missingBundles = buildI18nCache();
|
||||
}
|
||||
|
||||
private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException {
|
||||
in.defaultReadObject();
|
||||
bundlesMapLock = new Object();
|
||||
// Field initialisers do not run during deserialization, so a stream written before these settings
|
||||
// existed (an older node in a rolling upgrade) leaves them at null/0. Restore the defaults.
|
||||
if (i18nCacheType == null) {
|
||||
i18nCacheType = CacheType.WTLFU;
|
||||
}
|
||||
if (i18nCacheMaxSize <= 0) {
|
||||
i18nCacheMaxSize = DEFAULT_I18N_CACHE_MAX_SIZE;
|
||||
}
|
||||
rebuildI18nCaches();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the given resource bundle by it's name.
|
||||
* <p>
|
||||
@@ -549,32 +458,34 @@ abstract class AbstractLocalizedTextProvider implements LocalizedTextProvider {
|
||||
ClassLoader classLoader = getCurrentThreadContextClassLoader();
|
||||
String key = createMissesKey(String.valueOf(classLoader.hashCode()), aBundleName, locale);
|
||||
|
||||
if (missingBundles.get(key) != null) {
|
||||
if (missingBundles.contains(key)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ResourceBundle bundle = null;
|
||||
try {
|
||||
bundle = bundlesMap.get(key);
|
||||
if (bundle == null) {
|
||||
if (bundlesMap.containsKey(key)) {
|
||||
bundle = bundlesMap.get(key);
|
||||
} else {
|
||||
bundle = ResourceBundle.getBundle(aBundleName, locale, classLoader);
|
||||
bundlesMap.putIfAbsent(key, bundle);
|
||||
}
|
||||
} catch (MissingResourceException ex) {
|
||||
if (delegatedClassLoaderMap.containsKey(classLoader.hashCode())) {
|
||||
try {
|
||||
bundle = bundlesMap.get(key);
|
||||
if (bundle == null) {
|
||||
if (bundlesMap.containsKey(key)) {
|
||||
bundle = bundlesMap.get(key);
|
||||
} else {
|
||||
bundle = ResourceBundle.getBundle(aBundleName, locale, delegatedClassLoaderMap.get(classLoader.hashCode()));
|
||||
bundlesMap.putIfAbsent(key, bundle);
|
||||
}
|
||||
} catch (MissingResourceException e) {
|
||||
LOG.debug("Missing resource bundle [{}]!", aBundleName, e);
|
||||
missingBundles.put(key, Boolean.TRUE);
|
||||
missingBundles.add(key);
|
||||
}
|
||||
} else {
|
||||
LOG.debug("Missing resource bundle [{}]!", aBundleName);
|
||||
missingBundles.put(key, Boolean.TRUE);
|
||||
missingBundles.add(key);
|
||||
}
|
||||
}
|
||||
return bundle;
|
||||
|
||||
@@ -18,8 +18,6 @@
|
||||
*/
|
||||
package com.opensymphony.xwork2.util;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import com.opensymphony.xwork2.config.ConfigurationException;
|
||||
import com.opensymphony.xwork2.ognl.OgnlUtil;
|
||||
|
||||
@@ -35,17 +33,6 @@ import static java.util.stream.Collectors.toSet;
|
||||
import static org.apache.commons.lang3.StringUtils.strip;
|
||||
|
||||
public class ConfigParseUtil {
|
||||
// Size the cache to prevent excessive memory usage in environments with many classloaders and/or large numbers of classes being validated.
|
||||
// While still providing a reasonable caching benefit for common cases (e.g. multiple Struts instances in the same container, or multiple calls to validate the same class across different containers).
|
||||
// The cache is sized to allow for some level of caching across multiple classloaders, while still allowing for a reasonable number of classes to be cached per classloader.
|
||||
private static final int MAX_CLASSLOADER_CACHE_SIZE = 25;
|
||||
// The cache for validated classes is a two-level cache, with the first level keyed by ClassLoader and the second level keyed by class name.
|
||||
private static final int MAX_CLASS_CACHE_PER_LOADER_SIZE = 50;
|
||||
|
||||
private static final Cache<ClassLoader, Cache<String, Class<?>>> VALIDATED_CLASS_CACHE = Caffeine.newBuilder()
|
||||
.weakKeys()
|
||||
.maximumSize(MAX_CLASSLOADER_CACHE_SIZE)
|
||||
.build();
|
||||
|
||||
private ConfigParseUtil() {
|
||||
}
|
||||
@@ -86,7 +73,7 @@ public class ConfigParseUtil {
|
||||
Set<Class<?>> classes = new HashSet<>();
|
||||
for (String className : classNames) {
|
||||
try {
|
||||
classes.add(loadAndCacheClass(validatingClassLoader, className));
|
||||
classes.add(validatingClassLoader.loadClass(className));
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new ConfigurationException("Cannot load class for exclusion/exemption configuration: " + className, e);
|
||||
}
|
||||
@@ -94,35 +81,6 @@ public class ConfigParseUtil {
|
||||
return classes;
|
||||
}
|
||||
|
||||
private static Class<?> loadAndCacheClass(ClassLoader validatingClassLoader, String className) throws ClassNotFoundException {
|
||||
Cache<String, Class<?>> classLoaderCache = VALIDATED_CLASS_CACHE.get(validatingClassLoader,
|
||||
key -> Caffeine.newBuilder().weakValues().maximumSize(MAX_CLASS_CACHE_PER_LOADER_SIZE).build());
|
||||
|
||||
try {
|
||||
return classLoaderCache.get(className, key -> {
|
||||
try {
|
||||
return validatingClassLoader.loadClass(key);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new ClassLookupException(e);
|
||||
}
|
||||
});
|
||||
} catch (ClassLookupException e) {
|
||||
// The ClassLookupException only serves to wrap the checked ClassNotFoundException thrown by ClassLoader.loadClass.
|
||||
throw (ClassNotFoundException) e.getCause();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a wrapper class to allow the checked ClassNotFoundException thrown by ClassLoader.loadClass to be propagated
|
||||
* We should always be able to unwrap this exception without risk of ClassCastException since the only code that can throw it is the mapping function passed to the cache
|
||||
* and it only ever throws this wrapper with a ClassNotFoundException cause.
|
||||
*/
|
||||
private static final class ClassLookupException extends RuntimeException {
|
||||
private ClassLookupException(ClassNotFoundException cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
|
||||
public static Set<String> toPackageNamesSet(String newDelimitedPackageNames) throws ConfigurationException {
|
||||
Set<String> packageNames = commaDelimitedStringToSet(newDelimitedPackageNames)
|
||||
.stream().map(s -> strip(s, ".")).collect(toSet());
|
||||
|
||||
@@ -33,9 +33,6 @@ import java.util.ResourceBundle;
|
||||
*/
|
||||
public class GlobalLocalizedTextProvider extends AbstractLocalizedTextProvider {
|
||||
|
||||
// Pinned to the value implicitly computed for the pre-6.11.0 class shape, see AbstractLocalizedTextProvider.
|
||||
private static final long serialVersionUID = 7569216885652454296L;
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(GlobalLocalizedTextProvider.class);
|
||||
|
||||
public GlobalLocalizedTextProvider() {
|
||||
|
||||
@@ -36,9 +36,6 @@ import java.util.ResourceBundle;
|
||||
*/
|
||||
public class StrutsLocalizedTextProvider extends AbstractLocalizedTextProvider {
|
||||
|
||||
// Pinned to the value implicitly computed for the pre-6.11.0 class shape, see AbstractLocalizedTextProvider.
|
||||
private static final long serialVersionUID = -4377984952850818176L;
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(StrutsLocalizedTextProvider.class);
|
||||
|
||||
/**
|
||||
|
||||
@@ -100,14 +100,6 @@ public final class StrutsConstants {
|
||||
/** The default locale for the Struts application */
|
||||
public static final String STRUTS_LOCALE = "struts.locale";
|
||||
|
||||
/**
|
||||
* When enabled, request-derived locales (from {@code Accept-Language}, used when {@code struts.locale} is
|
||||
* unset) are restricted to the JVM's available-locale set; unavailable values fall back to the default.
|
||||
*
|
||||
* @since 6.11.0
|
||||
*/
|
||||
public static final String STRUTS_LOCALE_VALIDATE_REQUEST = "struts.locale.validateRequestLocale";
|
||||
|
||||
/** Whether to use a Servlet request parameter workaround necessary for some versions of WebLogic */
|
||||
public static final String STRUTS_DISPATCHER_PARAMETERSWORKAROUND = "struts.dispatcher.parametersWorkaround";
|
||||
|
||||
@@ -296,22 +288,6 @@ public final class StrutsConstants {
|
||||
*/
|
||||
public static final String STRUTS_OGNL_BEANINFO_CACHE_FACTORY = "struts.ognl.beanInfoCacheFactory";
|
||||
|
||||
/**
|
||||
* Specifies the type of cache to use for the localized-text provider caches. Valid values defined in
|
||||
* {@link com.opensymphony.xwork2.ognl.OgnlCacheFactory.CacheType}.
|
||||
*
|
||||
* @since 6.11.0
|
||||
*/
|
||||
public static final String STRUTS_I18N_CACHE_TYPE = "struts.i18n.cacheType";
|
||||
|
||||
/**
|
||||
* Specifies the maximum size of each localized-text provider cache. Configure based on the cache type
|
||||
* chosen and application-specific needs.
|
||||
*
|
||||
* @since 6.11.0
|
||||
*/
|
||||
public static final String STRUTS_I18N_CACHE_MAXSIZE = "struts.i18n.cacheMaxSize";
|
||||
|
||||
/**
|
||||
* Specifies the type of cache to use for BeanInfo objects.
|
||||
* @since 6.4.0
|
||||
@@ -542,13 +518,6 @@ public final class StrutsConstants {
|
||||
public static final String STRUTS_CSP_NONCE_READER = "struts.csp.nonce.reader";
|
||||
public static final String STRUTS_CSP_NONCE_SOURCE = "struts.csp.nonce.source";
|
||||
|
||||
/**
|
||||
* See {@link org.apache.struts2.action.CspReportAction}
|
||||
*
|
||||
* @since 6.11.0
|
||||
*/
|
||||
public static final String STRUTS_CSP_REPORT_MAX_SIZE = "struts.csp.report.maxSize";
|
||||
|
||||
/**
|
||||
* Specifies the type of cache to use for proxy detection in ProxyUtil.
|
||||
* Valid values defined in {@link com.opensymphony.xwork2.ognl.OgnlCacheFactory.CacheType}.
|
||||
|
||||
@@ -19,16 +19,11 @@
|
||||
package org.apache.struts2.action;
|
||||
|
||||
import com.opensymphony.xwork2.ActionSupport;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
|
||||
import static org.apache.struts2.interceptor.csp.CspSettings.CSP_REPORT_TYPE;
|
||||
|
||||
@@ -56,58 +51,7 @@ import static org.apache.struts2.interceptor.csp.CspSettings.CSP_REPORT_TYPE;
|
||||
* @see DefaultCspReportAction
|
||||
*/
|
||||
public abstract class CspReportAction extends ActionSupport implements ServletRequestAware, ServletResponseAware {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(CspReportAction.class);
|
||||
|
||||
/**
|
||||
* Default upper bound, in characters, on the report body accepted by {@link #withServletRequest}.
|
||||
* CSP violation reports are small JSON documents; anything larger is not treated as a report.
|
||||
*/
|
||||
public static final int DEFAULT_MAX_REPORT_SIZE = 8192;
|
||||
|
||||
/**
|
||||
* Largest value accepted for {@code struts.csp.report.maxSize}. A configured value above this is
|
||||
* ignored, so that a mistyped setting cannot size a per-request buffer large enough to exhaust
|
||||
* memory.
|
||||
*/
|
||||
private static final int MAX_REPORT_SIZE_LIMIT = 1024 * 1024;
|
||||
|
||||
private HttpServletRequest request;
|
||||
private int maxReportSize = DEFAULT_MAX_REPORT_SIZE;
|
||||
|
||||
/**
|
||||
* Sets the upper bound, in characters, on an accepted report body. A body exceeding this size is
|
||||
* discarded and not passed to {@link #processReport(String)}.
|
||||
* <p>
|
||||
* The value is injected from {@code struts.csp.report.maxSize} when the action is built, which is
|
||||
* before the interceptor stack runs. It is deliberately not an action property: the report body is
|
||||
* read by {@link #withServletRequest(HttpServletRequest)}, which the {@code servletConfig}
|
||||
* interceptor invokes ahead of {@code staticParams} and {@code params}, so a value applied by
|
||||
* either of those would arrive too late to have any effect.
|
||||
*
|
||||
* @param maxReportSize maximum accepted report size in characters
|
||||
* @since 6.11.0
|
||||
*/
|
||||
@Inject(value = StrutsConstants.STRUTS_CSP_REPORT_MAX_SIZE, required = false)
|
||||
public void setMaxReportSize(String maxReportSize) {
|
||||
if (StringUtils.isBlank(maxReportSize)) {
|
||||
return;
|
||||
}
|
||||
int size;
|
||||
try {
|
||||
size = Integer.parseInt(maxReportSize.trim());
|
||||
} catch (NumberFormatException e) {
|
||||
LOG.warn("Ignoring non-numeric {} value: {}, keeping {}",
|
||||
StrutsConstants.STRUTS_CSP_REPORT_MAX_SIZE, maxReportSize, this.maxReportSize);
|
||||
return;
|
||||
}
|
||||
if (size < 1 || size > MAX_REPORT_SIZE_LIMIT) {
|
||||
LOG.warn("Ignoring out-of-range {} value: {}, expected 1..{}, keeping {}",
|
||||
StrutsConstants.STRUTS_CSP_REPORT_MAX_SIZE, size, MAX_REPORT_SIZE_LIMIT, this.maxReportSize);
|
||||
return;
|
||||
}
|
||||
this.maxReportSize = size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void withServletRequest(HttpServletRequest request) {
|
||||
@@ -116,36 +60,13 @@ public abstract class CspReportAction extends ActionSupport implements ServletRe
|
||||
}
|
||||
|
||||
try {
|
||||
String cspReport = readReport(request.getReader());
|
||||
if (cspReport == null) {
|
||||
LOG.warn("Discarding CSP report larger than the configured limit of {} characters", maxReportSize);
|
||||
return;
|
||||
}
|
||||
BufferedReader reader = request.getReader();
|
||||
String cspReport = reader.readLine();
|
||||
processReport(cspReport);
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads at most {@link #maxReportSize} characters from the report body.
|
||||
*
|
||||
* @param reader reader over the report body
|
||||
* @return the report body, or {@code null} if it exceeds the limit
|
||||
* @throws IOException if the body cannot be read
|
||||
*/
|
||||
private String readReport(Reader reader) throws IOException {
|
||||
char[] buffer = new char[maxReportSize];
|
||||
int total = 0;
|
||||
int read;
|
||||
while (total < buffer.length && (read = reader.read(buffer, total, buffer.length - total)) != -1) {
|
||||
total += read;
|
||||
}
|
||||
if (total == buffer.length && reader.read() != -1) {
|
||||
return null;
|
||||
}
|
||||
return new String(buffer, 0, total);
|
||||
}
|
||||
|
||||
private boolean isCspReportRequest(HttpServletRequest request) {
|
||||
if (!"POST".equals(request.getMethod()) || request.getContentLength() <= 0){
|
||||
return false;
|
||||
|
||||
@@ -152,11 +152,6 @@ public class Dispatcher {
|
||||
*/
|
||||
private String defaultLocale;
|
||||
|
||||
/**
|
||||
* Store state of {@link StrutsConstants#STRUTS_LOCALE_VALIDATE_REQUEST} setting.
|
||||
*/
|
||||
private boolean validateRequestLocale = false;
|
||||
|
||||
/**
|
||||
* Store state of StrutsConstants.STRUTS_MULTIPART_SAVEDIR setting.
|
||||
*/
|
||||
@@ -316,18 +311,6 @@ public class Dispatcher {
|
||||
defaultLocale = val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify state of {@link StrutsConstants#STRUTS_LOCALE_VALIDATE_REQUEST} setting.
|
||||
*
|
||||
* @param val New setting
|
||||
*
|
||||
* @since 6.11.0
|
||||
*/
|
||||
@Inject(value = StrutsConstants.STRUTS_LOCALE_VALIDATE_REQUEST, required = false)
|
||||
public void setValidateRequestLocale(String val) {
|
||||
validateRequestLocale = Boolean.parseBoolean(val);
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify state of StrutsConstants.STRUTS_I18N_ENCODING setting.
|
||||
*
|
||||
@@ -967,7 +950,7 @@ public class Dispatcher {
|
||||
locale = LocaleUtils.toLocale(defaultLocale);
|
||||
} catch (IllegalArgumentException e) {
|
||||
try {
|
||||
locale = resolveRequestLocale(request);
|
||||
locale = request.getLocale();
|
||||
LOG.warn(new ParameterizedMessage("Cannot convert 'struts.locale' = [{}] to proper locale, defaulting to request locale [{}]",
|
||||
defaultLocale, locale), e);
|
||||
} catch (RuntimeException rex) {
|
||||
@@ -978,7 +961,7 @@ public class Dispatcher {
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
locale = resolveRequestLocale(request);
|
||||
locale = request.getLocale();
|
||||
} catch (RuntimeException rex) {
|
||||
LOG.warn("Cannot get locale from HTTP Request, falling back to system default locale", rex);
|
||||
locale = Locale.getDefault();
|
||||
@@ -987,33 +970,6 @@ public class Dispatcher {
|
||||
return locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the request locale. When {@code struts.locale.validateRequestLocale} is enabled and the
|
||||
* request locale is not part of the JVM's available-locale set, falls back to the configured
|
||||
* {@code struts.locale} when set and parseable, otherwise the JVM default. When disabled (default),
|
||||
* returns the request locale unchanged.
|
||||
*
|
||||
* @param request the current request
|
||||
* @return the locale to use for this request
|
||||
*
|
||||
* @since 6.11.0
|
||||
*/
|
||||
protected Locale resolveRequestLocale(HttpServletRequest request) {
|
||||
Locale locale = request.getLocale();
|
||||
if (!validateRequestLocale || LocaleUtils.isAvailableLocale(locale)) {
|
||||
return locale;
|
||||
}
|
||||
if (defaultLocale != null) {
|
||||
try {
|
||||
return LocaleUtils.toLocale(defaultLocale);
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOG.debug("Configured 'struts.locale' = [{}] is not parseable; falling back to system default", defaultLocale);
|
||||
}
|
||||
}
|
||||
LOG.debug("Request locale [{}] is not available; falling back to system default locale", locale);
|
||||
return Locale.getDefault();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the path to save uploaded files to (this is configurable).
|
||||
*
|
||||
|
||||
@@ -33,11 +33,7 @@ import java.util.StringTokenizer;
|
||||
/**
|
||||
* Extended version of {@link RestfulActionMapper}, see documentation for more details
|
||||
* <a href="https://struts.apache.org/core-developers/restful-action-mapper.html">Restful2ActionMapper</a>
|
||||
*
|
||||
* @deprecated since 6.12.0, this legacy mapper predates the Struts REST plugin, which is the maintained
|
||||
* way to build REST-style applications with Struts. Scheduled for removal in the next major release.
|
||||
*/
|
||||
@Deprecated
|
||||
public class Restful2ActionMapper extends DefaultActionMapper {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(Restful2ActionMapper.class);
|
||||
|
||||
@@ -23,55 +23,29 @@ import com.opensymphony.xwork2.inject.Inject;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.struts2.RequestUtils;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
import org.apache.struts2.url.UrlDecoder;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Simple Restfull Action Mapper to support REST application
|
||||
* See docs for more information
|
||||
* <a href="https://struts.apache.org/core-developers/restful-action-mapper.html">RestfulActionMapper</a>
|
||||
*
|
||||
* @deprecated since 6.12.0, this legacy mapper predates the Struts REST plugin, which is the maintained
|
||||
* way to build REST-style applications with Struts. Scheduled for removal in the next major release.
|
||||
*/
|
||||
@Deprecated
|
||||
public class RestfulActionMapper implements ActionMapper {
|
||||
|
||||
protected static final Logger LOG = LogManager.getLogger(RestfulActionMapper.class);
|
||||
|
||||
private UrlDecoder decoder;
|
||||
|
||||
/**
|
||||
* Matches action names allowed in the request URI, aligned with {@link DefaultActionMapper}.
|
||||
*/
|
||||
private Pattern allowedActionNames = Pattern.compile("[a-zA-Z0-9._!/\\-]*");
|
||||
|
||||
/**
|
||||
* Action name used when the name extracted from the URI is not allowed, aligned with {@link DefaultActionMapper}.
|
||||
*/
|
||||
private String defaultActionName = "index";
|
||||
|
||||
@Inject
|
||||
public void setDecoder(UrlDecoder decoder) {
|
||||
this.decoder = decoder;
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_ALLOWED_ACTION_NAMES, required = false)
|
||||
public void setAllowedActionNames(String allowedActionNames) {
|
||||
this.allowedActionNames = Pattern.compile(allowedActionNames);
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_DEFAULT_ACTION_NAME, required = false)
|
||||
public void setDefaultActionName(String defaultActionName) {
|
||||
this.defaultActionName = defaultActionName;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.apache.struts2.dispatcher.mapper.ActionMapper#getMapping(javax.servlet.http.HttpServletRequest)
|
||||
*/
|
||||
@@ -83,7 +57,7 @@ public class RestfulActionMapper implements ActionMapper {
|
||||
return null;
|
||||
}
|
||||
|
||||
String actionName = cleanupActionName(uri.substring(1, nextSlash));
|
||||
String actionName = uri.substring(1, nextSlash);
|
||||
Map<String, Object> parameters = new HashMap<>();
|
||||
try {
|
||||
StringTokenizer st = new StringTokenizer(uri.substring(nextSlash), "/");
|
||||
@@ -122,22 +96,6 @@ public class RestfulActionMapper implements ActionMapper {
|
||||
return new ActionMapping(actionName, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks action name against the allowed pattern; if it does not match, returns the default action name.
|
||||
* Mirrors {@link DefaultActionMapper#cleanupActionName(String)}.
|
||||
*
|
||||
* @param rawActionName action name extracted from the URI
|
||||
* @return safe action name
|
||||
*/
|
||||
protected String cleanupActionName(final String rawActionName) {
|
||||
if (allowedActionNames.matcher(rawActionName).matches()) {
|
||||
return rawActionName;
|
||||
} else {
|
||||
LOG.warn("{} did not match allowed action names {} - default action {} will be used!", rawActionName, allowedActionNames, defaultActionName);
|
||||
return defaultActionName;
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.apache.struts2.dispatcher.mapper.ActionMapper#getUriFromActionMapping(org.apache.struts2.dispatcher.mapper.ActionMapping)
|
||||
*/
|
||||
|
||||
+1
-2
@@ -90,8 +90,7 @@ public class HttpMethodInterceptor extends AbstractInterceptor {
|
||||
invocation.getProxy().getMethod(), AllowedHttpMethod.class.getSimpleName(), request.getMethod());
|
||||
return doIntercept(invocation, method);
|
||||
}
|
||||
}
|
||||
if (AnnotationUtils.isAnnotatedBy(action.getClass(), HTTP_METHOD_ANNOTATIONS)) {
|
||||
} else if (AnnotationUtils.isAnnotatedBy(action.getClass(), HTTP_METHOD_ANNOTATIONS)) {
|
||||
LOG.debug("Action: {} annotated with: {}, checking if request: {} meets allowed methods!",
|
||||
action, AllowedHttpMethod.class.getSimpleName(), request.getMethod());
|
||||
return doIntercept(invocation, action.getClass());
|
||||
|
||||
@@ -21,7 +21,6 @@ package org.apache.struts2.result;
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.ActionInvocation;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import org.apache.commons.text.StringEscapeUtils;
|
||||
import org.apache.struts2.dispatcher.mapper.ActionMapper;
|
||||
import org.apache.struts2.dispatcher.mapper.ActionMapping;
|
||||
|
||||
@@ -102,7 +101,7 @@ public class PostbackResult extends StrutsResultSupport {
|
||||
|
||||
// Render
|
||||
PrintWriter pw = new PrintWriter(response.getOutputStream());
|
||||
pw.write("<!DOCTYPE html><html><body><form action=\"" + StringEscapeUtils.escapeHtml4(finalLocation) + "\" method=\"POST\">");
|
||||
pw.write("<!DOCTYPE html><html><body><form action=\"" + finalLocation + "\" method=\"POST\">");
|
||||
writeFormElements(request, pw);
|
||||
writePrologueScript(pw);
|
||||
pw.write("</html>");
|
||||
|
||||
@@ -24,9 +24,6 @@
|
||||
|
||||
### This can be used to set your default locale and encoding scheme
|
||||
# struts.locale=en_US
|
||||
### When true, restrict request-derived locales (Accept-Language, used when struts.locale is unset) to the
|
||||
### JVM's available-locale set; unavailable values fall back to the default locale. Defaults to false.
|
||||
struts.locale.validateRequestLocale=false
|
||||
struts.i18n.encoding=UTF-8
|
||||
|
||||
### if specified, the default object factory can be overridden here
|
||||
@@ -243,13 +240,6 @@ struts.ognl.expressionCacheType=wtlfu
|
||||
### chosen and application-specific needs.
|
||||
struts.ognl.expressionCacheMaxSize=10000
|
||||
|
||||
### Specifies the type of cache to use for the localized-text provider caches. See StrutsConstants for details.
|
||||
struts.i18n.cacheType=wtlfu
|
||||
|
||||
### Specifies the maximum size of each localized-text provider cache. This should be configured based on the
|
||||
### cache type chosen and application-specific needs.
|
||||
struts.i18n.cacheMaxSize=10000
|
||||
|
||||
### Specifies the type of cache to use for BeanInfo objects. See StrutsConstants class for further information.
|
||||
struts.ognl.beanInfoCacheType=wtlfu
|
||||
|
||||
@@ -300,8 +290,4 @@ struts.url.decoder=strutsUrlDecoder
|
||||
### Defines source to read nonce value from, possible values are: request, session
|
||||
struts.csp.nonceSource=session
|
||||
|
||||
### Maximum size, in characters, of a CSP violation report accepted by CspReportAction
|
||||
### Reports larger than this are discarded. Values outside 1..1048576 are ignored.
|
||||
struts.csp.report.maxSize=8192
|
||||
|
||||
### END SNIPPET: complete_file
|
||||
|
||||
@@ -79,8 +79,6 @@
|
||||
class="org.apache.struts2.dispatcher.mapper.CompositeActionMapper"/>
|
||||
<bean type="org.apache.struts2.dispatcher.mapper.ActionMapper" name="prefix"
|
||||
class="org.apache.struts2.dispatcher.mapper.PrefixBasedActionMapper"/>
|
||||
<!-- Deprecated for removal: the legacy "restful" and "restful2" mappers predate the Struts REST
|
||||
plugin, which is the maintained way to build REST-style applications. -->
|
||||
<bean type="org.apache.struts2.dispatcher.mapper.ActionMapper" name="restful"
|
||||
class="org.apache.struts2.dispatcher.mapper.RestfulActionMapper"/>
|
||||
<bean type="org.apache.struts2.dispatcher.mapper.ActionMapper" name="restful2"
|
||||
|
||||
-128
@@ -1,128 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.conversion.impl;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.XWorkTestCase;
|
||||
import com.opensymphony.xwork2.conversion.TypeConverter;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class CollectionConverterTest extends XWorkTestCase {
|
||||
|
||||
/**
|
||||
* WW-5701: the marker constant's value is ordinary text, so an element that genuinely holds
|
||||
* that text converts successfully and must be kept.
|
||||
* <p>
|
||||
* The value is built at runtime rather than written as a literal on purpose: a literal would be
|
||||
* interned to the very same instance as the constant's value, which no request-derived
|
||||
* parameter ever is. A servlet container builds parameter values from the request bytes.
|
||||
*/
|
||||
public void testElementWhoseTextEqualsTheMarkerIsKept() {
|
||||
String asSubmittedByAUser = new String("ognl.NoConversionPossible".toCharArray());
|
||||
assertNotSame("fixture must not be interned", TypeConverter.NO_CONVERSION_POSSIBLE, asSubmittedByAUser);
|
||||
|
||||
Holder holder = new Holder();
|
||||
ValueStack vs = ActionContext.getContext().getValueStack();
|
||||
vs.push(holder);
|
||||
|
||||
vs.setValue("names", new String[]{"alpha", asSubmittedByAUser, "omega"});
|
||||
|
||||
assertEquals(Arrays.asList("alpha", "ognl.NoConversionPossible", "omega"), holder.getNames());
|
||||
}
|
||||
|
||||
/**
|
||||
* The guard must still do its job: a genuinely unconvertible element is dropped.
|
||||
*/
|
||||
public void testUnconvertibleElementIsStillDropped() {
|
||||
Holder holder = new Holder();
|
||||
ValueStack vs = ActionContext.getContext().getValueStack();
|
||||
vs.push(holder);
|
||||
|
||||
vs.setValue("numbers", new String[]{"1", "not-a-number", "3"});
|
||||
|
||||
assertEquals(Arrays.asList(1L, 3L), holder.getNumbers());
|
||||
}
|
||||
|
||||
/**
|
||||
* The same guard on the path taken when the submitted value is itself a collection rather than
|
||||
* an array - here a List feeding a Set-typed property.
|
||||
*/
|
||||
public void testUnconvertibleElementIsDroppedFromACollectionSource() {
|
||||
Holder holder = new Holder();
|
||||
ValueStack vs = ActionContext.getContext().getValueStack();
|
||||
vs.push(holder);
|
||||
|
||||
vs.setValue("numberSet", Arrays.asList("1", "not-a-number", "3"));
|
||||
|
||||
assertEquals(new HashSet<>(Arrays.asList(1L, 3L)), holder.getNumberSet());
|
||||
}
|
||||
|
||||
/**
|
||||
* The same guard on the path taken when a single value is assigned to a collection property.
|
||||
* The property is seeded first so that a setter which is never called cannot pass vacuously.
|
||||
*/
|
||||
public void testUnconvertibleSingleValueIsDropped() {
|
||||
Holder holder = new Holder();
|
||||
holder.setNumbers(new ArrayList<>(Arrays.asList(99L)));
|
||||
ValueStack vs = ActionContext.getContext().getValueStack();
|
||||
vs.push(holder);
|
||||
|
||||
vs.setValue("numbers", "not-a-number");
|
||||
|
||||
assertEquals(Collections.emptyList(), holder.getNumbers());
|
||||
}
|
||||
|
||||
public static class Holder {
|
||||
private List<String> names = new ArrayList<>();
|
||||
private List<Long> numbers = new ArrayList<>();
|
||||
private Set<Long> numberSet = new LinkedHashSet<>();
|
||||
|
||||
public List<String> getNames() {
|
||||
return names;
|
||||
}
|
||||
|
||||
public void setNames(List<String> names) {
|
||||
this.names = names;
|
||||
}
|
||||
|
||||
public List<Long> getNumbers() {
|
||||
return numbers;
|
||||
}
|
||||
|
||||
public void setNumbers(List<Long> numbers) {
|
||||
this.numbers = numbers;
|
||||
}
|
||||
|
||||
public Set<Long> getNumberSet() {
|
||||
return numberSet;
|
||||
}
|
||||
|
||||
public void setNumberSet(Set<Long> numberSet) {
|
||||
this.numberSet = numberSet;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,6 @@ import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.struts2.StrutsInternalTestCase;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -103,18 +102,6 @@ public class StringConverterTest extends StrutsInternalTestCase {
|
||||
assertEquals(aBitBiggerThanDouble.substring(0, 309) + "," + aBitBiggerThanDouble.substring(310), value);
|
||||
}
|
||||
|
||||
public void testBigDecimalFractionDigitsAreBounded() throws Exception {
|
||||
// given
|
||||
StringConverter converter = new StringConverter();
|
||||
Map<String, Object> context = createContextWithLocale(new Locale("pl", "PL"));
|
||||
|
||||
// when the scale of the value exceeds the supported number of fraction digits
|
||||
Object value = converter.convertValue(context, null, null, null, new BigDecimal(BigInteger.ONE, 100_000), null);
|
||||
|
||||
// then the length of the output is bounded by the converter, not by the scale of the value
|
||||
assertEquals("0", value);
|
||||
}
|
||||
|
||||
public void testStringArrayToStringConversion() {
|
||||
// given
|
||||
StringConverter converter = new StringConverter();
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.ognl;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
public class OgnlCacheRemoveTest {
|
||||
|
||||
private void assertRemoveContract(OgnlCache<String, String> cache) {
|
||||
cache.put("k", "v");
|
||||
assertEquals("v", cache.get("k"));
|
||||
assertEquals("remove returns previous value", "v", cache.remove("k"));
|
||||
assertNull("entry gone after remove", cache.get("k"));
|
||||
assertNull("remove of absent key returns null", cache.remove("absent"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void caffeineCacheRemove() {
|
||||
assertRemoveContract(new OgnlCaffeineCache<>(10, 16));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultCacheRemove() {
|
||||
assertRemoveContract(new OgnlDefaultCache<>(10, 16, 0.75f));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lruCacheRemove() {
|
||||
assertRemoveContract(new OgnlLRUCache<>(10, 16, 0.75f));
|
||||
}
|
||||
}
|
||||
-16
@@ -64,22 +64,6 @@ public class XWorkListPropertyAccessorTest extends XWorkTestCase {
|
||||
assertEquals(myList.size(), vs.findValue("strings.size"));
|
||||
}
|
||||
|
||||
public void testUnconvertibleElementIsNotStored() {
|
||||
ValueStack vs = ActionContext.getContext().getValueStack();
|
||||
ListHolder listHolder = new ListHolder();
|
||||
listHolder.setLongs(new ArrayList<>());
|
||||
vs.push(listHolder);
|
||||
|
||||
vs.setValue("longs[0]", "1");
|
||||
vs.setValue("longs[1]", "not-a-number");
|
||||
|
||||
assertEquals(Long.valueOf(1), listHolder.getLongs().get(0));
|
||||
for (Object element : (List) listHolder.getLongs()) {
|
||||
assertTrue("list must not hold a non-Long element: " + element,
|
||||
element == null || element instanceof Long);
|
||||
}
|
||||
}
|
||||
|
||||
public void testAutoGrowthCollectionLimit() {
|
||||
PropertyAccessor accessor = container.getInstance(PropertyAccessor.class, ArrayList.class.getName());
|
||||
((XWorkListPropertyAccessor) accessor).setAutoGrowCollectionLimit("2");
|
||||
|
||||
-45
@@ -25,7 +25,6 @@ import com.opensymphony.xwork2.util.ValueStack;
|
||||
import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class XWorkMapPropertyAccessorTest extends XWorkTestCase {
|
||||
@@ -58,50 +57,6 @@ public class XWorkMapPropertyAccessorTest extends XWorkTestCase {
|
||||
assertNull(vs.findValue("map['key']"));
|
||||
}
|
||||
|
||||
public void testUnconvertibleValueIsNotStored() {
|
||||
TypedMapHolder holder = new TypedMapHolder();
|
||||
ValueStack vs = ActionContext.getContext().getValueStack();
|
||||
vs.push(holder);
|
||||
|
||||
vs.setValue("counts[1]", "5");
|
||||
vs.setValue("counts[2]", "not-a-number");
|
||||
|
||||
assertEquals(Integer.valueOf(5), holder.getCounts().get(1L));
|
||||
assertOnlyDeclaredTypes(holder.getCounts());
|
||||
}
|
||||
|
||||
public void testUnconvertibleKeyIsNotStored() {
|
||||
TypedMapHolder holder = new TypedMapHolder();
|
||||
ValueStack vs = ActionContext.getContext().getValueStack();
|
||||
vs.push(holder);
|
||||
|
||||
vs.setValue("counts[1]", "5");
|
||||
vs.setValue("counts['abc']", "6");
|
||||
|
||||
assertEquals(Integer.valueOf(5), holder.getCounts().get(1L));
|
||||
assertOnlyDeclaredTypes(holder.getCounts());
|
||||
}
|
||||
|
||||
/**
|
||||
* A Map declared to hold Long keys and Integer values must never be left holding anything else.
|
||||
*/
|
||||
private static void assertOnlyDeclaredTypes(Map<Long, Integer> map) {
|
||||
for (Object o : ((Map) map).entrySet()) {
|
||||
Map.Entry entry = (Map.Entry) o;
|
||||
assertTrue("key is not a Long: " + entry.getKey(), entry.getKey() instanceof Long);
|
||||
assertTrue("value is not an Integer: " + entry.getValue(), entry.getValue() instanceof Integer);
|
||||
}
|
||||
}
|
||||
|
||||
public static class TypedMapHolder {
|
||||
@Element(value = Integer.class)
|
||||
private final Map<Long, Integer> counts = new HashMap<>();
|
||||
|
||||
public Map<Long, Integer> getCounts() {
|
||||
return counts;
|
||||
}
|
||||
}
|
||||
|
||||
private static class MapHolder {
|
||||
private final Map map;
|
||||
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.util;
|
||||
|
||||
/**
|
||||
* Simple fixture whose class-associated bundle ({@code CacheFixture.properties}) backs the
|
||||
* localized-text caching tests.
|
||||
*
|
||||
* @since 6.11.0
|
||||
*/
|
||||
public class CacheFixture {
|
||||
}
|
||||
@@ -1,255 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.util;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.opensymphony.xwork2.config.ConfigurationException;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public class ConfigParseUtilTest {
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
validatedClassCache().invalidateAll();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
validatedClassCache().invalidateAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* (a) Single-loader caching: one loader validates several distinct classes; repeating the call
|
||||
* loads each class exactly once. Covers both "repeated calls hit the cache" and "the inner cache
|
||||
* is keyed per class name".
|
||||
*/
|
||||
@Test
|
||||
public void testSameLoaderCachesEachDistinctClassOnce() {
|
||||
CountingClassLoader loader = new CountingClassLoader(getClass().getClassLoader(), "single-loader");
|
||||
Set<String> classNames = new HashSet<>();
|
||||
classNames.add(String.class.getName());
|
||||
classNames.add(Integer.class.getName());
|
||||
classNames.add(Boolean.class.getName());
|
||||
|
||||
ConfigParseUtil.validateClasses(classNames, loader);
|
||||
ConfigParseUtil.validateClasses(classNames, loader);
|
||||
|
||||
assertEquals(1, loader.getLoadCount(String.class.getName()));
|
||||
assertEquals(1, loader.getLoadCount(Integer.class.getName()));
|
||||
assertEquals(1, loader.getLoadCount(Boolean.class.getName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* (b) Per-loader isolation: the outer cache is keyed by classloader identity, not by toString().
|
||||
* Two loaders that share the same toString() each load the class once, and re-validating one
|
||||
* loader still hits its own cache.
|
||||
*/
|
||||
@Test
|
||||
public void testDifferentLoadersWithSameNameCacheIndependently() {
|
||||
CountingClassLoader firstLoader = new CountingClassLoader(getClass().getClassLoader(), "same-name");
|
||||
CountingClassLoader secondLoader = new CountingClassLoader(getClass().getClassLoader(), "same-name");
|
||||
Set<String> classNames = Collections.singleton(String.class.getName());
|
||||
|
||||
ConfigParseUtil.validateClasses(classNames, firstLoader);
|
||||
ConfigParseUtil.validateClasses(classNames, secondLoader);
|
||||
|
||||
assertEquals(1, firstLoader.getStringClassLoads());
|
||||
assertEquals(1, secondLoader.getStringClassLoads());
|
||||
|
||||
// Re-validating the first loader still hits its own cache.
|
||||
ConfigParseUtil.validateClasses(classNames, firstLoader);
|
||||
assertEquals(1, firstLoader.getStringClassLoads());
|
||||
}
|
||||
|
||||
/**
|
||||
* Negative case: a missing class throws ConfigurationException (cause ClassNotFoundException) on
|
||||
* every call, and the failure is not cached (each call re-attempts the load).
|
||||
*/
|
||||
@Test
|
||||
public void testMissingClassThrowsAndIsNotCached() {
|
||||
String missingClassName = "com.opensymphony.xwork2.util.NonExistingClassForValidationTest";
|
||||
Set<String> classNames = Collections.singleton(missingClassName);
|
||||
int[] missingClassLoads = new int[1];
|
||||
ClassLoader loader = new ClassLoader(getClass().getClassLoader()) {
|
||||
@Override
|
||||
public Class<?> loadClass(String name) throws ClassNotFoundException {
|
||||
if (missingClassName.equals(name)) {
|
||||
missingClassLoads[0]++;
|
||||
throw new ClassNotFoundException(name);
|
||||
}
|
||||
return super.loadClass(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "missing-class-loader";
|
||||
}
|
||||
};
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
try {
|
||||
ConfigParseUtil.validateClasses(classNames, loader);
|
||||
fail("Expected ConfigurationException for class: " + missingClassName);
|
||||
} catch (ConfigurationException e) {
|
||||
assertTrue(e.getMessage().contains(missingClassName));
|
||||
assertNotNull(e.getCause());
|
||||
assertEquals(ClassNotFoundException.class, e.getCause().getClass());
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals(2, missingClassLoads[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* (c) Outer cache bound: registering more classloaders than the maximum keeps the outer cache at
|
||||
* or below its configured size.
|
||||
*/
|
||||
@Test
|
||||
public void testOuterCacheBoundedByMaxClassloaders() {
|
||||
Set<String> classNames = Collections.singleton(String.class.getName());
|
||||
|
||||
for (int i = 0; i < outerCacheLimit() + 10; i++) {
|
||||
CountingClassLoader loader = new CountingClassLoader(getClass().getClassLoader(), "loader-" + i);
|
||||
ConfigParseUtil.validateClasses(classNames, loader);
|
||||
}
|
||||
|
||||
Cache<Object, Object> cache = validatedClassCache();
|
||||
cache.cleanUp();
|
||||
|
||||
assertTrue("Outer cache size should not exceed configured maximum",
|
||||
cache.estimatedSize() <= outerCacheLimit());
|
||||
}
|
||||
|
||||
/**
|
||||
* (c) Inner cache bound: validating more class names than the per-loader maximum keeps that
|
||||
* loader's inner cache at or below its configured size. Synthetic names are resolved to a real
|
||||
* class so the count is driven by distinct keys, not by which JDK classes happen to exist.
|
||||
*/
|
||||
@Test
|
||||
public void testInnerCacheBoundedByMaxClassesPerLoader() {
|
||||
int limit = innerCacheLimit();
|
||||
ClassLoader loader = new ClassLoader(getClass().getClassLoader()) {
|
||||
@Override
|
||||
public Class<?> loadClass(String name) {
|
||||
// Resolve any synthetic name to a strongly-reachable class so weakValues never evicts it.
|
||||
return Object.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "inner-bound-loader";
|
||||
}
|
||||
};
|
||||
|
||||
Set<String> classNames = new LinkedHashSet<>();
|
||||
for (int i = 0; i <= limit + 10; i++) {
|
||||
classNames.add("synthetic.Class" + i);
|
||||
}
|
||||
assertTrue("Test must request more class names than the inner cache capacity",
|
||||
classNames.size() > limit);
|
||||
|
||||
ConfigParseUtil.validateClasses(classNames, loader);
|
||||
|
||||
Cache<Object, Object> innerCache = innerCacheFor(loader);
|
||||
innerCache.cleanUp();
|
||||
assertTrue("Inner cache size should not exceed configured maximum per loader",
|
||||
innerCache.estimatedSize() <= limit);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Cache<Object, Object> validatedClassCache() {
|
||||
try {
|
||||
Field cacheField = ConfigParseUtil.class.getDeclaredField("VALIDATED_CLASS_CACHE");
|
||||
cacheField.setAccessible(true);
|
||||
return (Cache<Object, Object>) cacheField.get(null);
|
||||
} catch (NoSuchFieldException | IllegalAccessException e) {
|
||||
throw new AssertionError("Cannot access ConfigParseUtil cache field", e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Cache<Object, Object> innerCacheFor(ClassLoader loader) {
|
||||
Cache<Object, Object> outer = validatedClassCache();
|
||||
Object inner = outer.getIfPresent(loader);
|
||||
assertNotNull("Expected an inner cache entry for loader", inner);
|
||||
return (Cache<Object, Object>) inner;
|
||||
}
|
||||
|
||||
private static int outerCacheLimit() {
|
||||
return intConstant("MAX_CLASSLOADER_CACHE_SIZE");
|
||||
}
|
||||
|
||||
private static int innerCacheLimit() {
|
||||
return intConstant("MAX_CLASS_CACHE_PER_LOADER_SIZE");
|
||||
}
|
||||
|
||||
private static int intConstant(String fieldName) {
|
||||
try {
|
||||
Field field = ConfigParseUtil.class.getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
return field.getInt(null);
|
||||
} catch (NoSuchFieldException | IllegalAccessException e) {
|
||||
throw new AssertionError("Cannot access ConfigParseUtil constant: " + fieldName, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class CountingClassLoader extends ClassLoader {
|
||||
private final String loaderName;
|
||||
private final Map<String, Integer> loadCounts = new HashMap<>();
|
||||
|
||||
private CountingClassLoader(ClassLoader parent, String loaderName) {
|
||||
super(parent);
|
||||
this.loaderName = loaderName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> loadClass(String name) throws ClassNotFoundException {
|
||||
loadCounts.merge(name, 1, Integer::sum);
|
||||
return super.loadClass(name);
|
||||
}
|
||||
|
||||
private int getStringClassLoads() {
|
||||
return loadCounts.getOrDefault(String.class.getName(), 0);
|
||||
}
|
||||
|
||||
private int getLoadCount(String className) {
|
||||
return loadCounts.getOrDefault(className, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return loaderName;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,11 +34,6 @@ import com.opensymphony.xwork2.test.TestBean2;
|
||||
import org.apache.struts2.config.StrutsXmlConfigurationProvider;
|
||||
import org.apache.struts2.interceptor.parameter.StrutsParameter;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.util.Date;
|
||||
@@ -568,109 +563,6 @@ public class StrutsLocalizedTextProviderTest extends XWorkTestCase {
|
||||
assertEquals("Result of bean2.name lookup not as expected ?", "Okay! You found Me!", messageResult);
|
||||
}
|
||||
|
||||
public void testCachesAreBoundedByConfiguredMaxSize() {
|
||||
TestStrutsLocalizedTextProvider provider = new TestStrutsLocalizedTextProvider();
|
||||
provider.setI18nCacheMaxSize("100");
|
||||
ValueStack valueStack = ActionContext.getContext().getValueStack();
|
||||
|
||||
for (int i = 0; i < 20000; i++) {
|
||||
Locale locale = Locale.forLanguageTag("en-US-x" + String.format("%05d", i));
|
||||
provider.findText(CacheFixture.class, "cache.missing", locale, "Fallback", null, valueStack);
|
||||
}
|
||||
|
||||
assertTrue("bundlesMap not bounded ?", provider.bundlesMapSize() <= 2000);
|
||||
assertTrue("missingBundles not bounded ?", provider.missingBundlesSize() <= 2000);
|
||||
assertTrue("messageFormats not bounded ?", provider.messageFormatsSize() <= 2000);
|
||||
}
|
||||
|
||||
public void testCorrectTextStillReturnedUnderEviction() {
|
||||
TestStrutsLocalizedTextProvider provider = new TestStrutsLocalizedTextProvider();
|
||||
provider.setI18nCacheMaxSize("50");
|
||||
ValueStack valueStack = ActionContext.getContext().getValueStack();
|
||||
|
||||
// Force heavy eviction with many distinct locales.
|
||||
for (int i = 0; i < 5000; i++) {
|
||||
Locale locale = Locale.forLanguageTag("en-US-x" + String.format("%05d", i));
|
||||
provider.findText(CacheFixture.class, "cache.missing", locale, "Fallback", null, valueStack);
|
||||
}
|
||||
|
||||
// A real key in a real locale still resolves correctly after eviction pressure.
|
||||
String result = provider.findText(CacheFixture.class, "cache.static", Locale.ENGLISH, null, null, valueStack);
|
||||
assertEquals("Static cached value", result);
|
||||
}
|
||||
|
||||
public void testReloadClearsBoundedCaches() {
|
||||
TestStrutsLocalizedTextProvider provider = new TestStrutsLocalizedTextProvider();
|
||||
ValueStack valueStack = ActionContext.getContext().getValueStack();
|
||||
|
||||
provider.findText(CacheFixture.class, "cache.missing", Locale.ENGLISH, "Fallback", null, valueStack);
|
||||
assertTrue("missingBundles not populated ?", provider.missingBundlesSize() > 0);
|
||||
|
||||
provider.callReloadBundlesForceReload();
|
||||
assertEquals("reload did not clear bundlesMap ?", 0, provider.bundlesMapSize());
|
||||
}
|
||||
|
||||
public void testProviderIsUsableAfterDeserialization() throws Exception {
|
||||
StrutsLocalizedTextProvider provider = new StrutsLocalizedTextProvider();
|
||||
ValueStack valueStack = ActionContext.getContext().getValueStack();
|
||||
provider.findText(CacheFixture.class, "cache.static", Locale.ENGLISH, null, null, valueStack);
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
|
||||
oos.writeObject(provider);
|
||||
}
|
||||
Object restored;
|
||||
try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()))) {
|
||||
restored = ois.readObject();
|
||||
}
|
||||
StrutsLocalizedTextProvider deserialized = (StrutsLocalizedTextProvider) restored;
|
||||
// Caches were transient (null right after defaultReadObject) but readObject rebuilds them:
|
||||
assertEquals("Deserialized caches not rebuilt empty", 0, deserialized.bundlesMapSize());
|
||||
String result = deserialized.findText(CacheFixture.class, "cache.static", Locale.ENGLISH, null, null, valueStack);
|
||||
assertEquals("Static cached value", result);
|
||||
}
|
||||
|
||||
/**
|
||||
* A stream written before the i18n cache settings existed carries no value for them, and field
|
||||
* initialisers do not run during deserialization, so they arrive as null/0. The provider must still
|
||||
* come back usable rather than failing while rebuilding its caches.
|
||||
*/
|
||||
public void testProviderIsUsableAfterDeserializingLegacyStream() throws Exception {
|
||||
StrutsLocalizedTextProvider provider = new StrutsLocalizedTextProvider();
|
||||
ValueStack valueStack = ActionContext.getContext().getValueStack();
|
||||
provider.findText(CacheFixture.class, "cache.static", Locale.ENGLISH, null, null, valueStack);
|
||||
|
||||
// Simulate the absent-field state an older stream produces.
|
||||
Field cacheType = AbstractLocalizedTextProvider.class.getDeclaredField("i18nCacheType");
|
||||
cacheType.setAccessible(true);
|
||||
cacheType.set(provider, null);
|
||||
Field maxSize = AbstractLocalizedTextProvider.class.getDeclaredField("i18nCacheMaxSize");
|
||||
maxSize.setAccessible(true);
|
||||
maxSize.setInt(provider, 0);
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
|
||||
oos.writeObject(provider);
|
||||
}
|
||||
Object restored;
|
||||
try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()))) {
|
||||
restored = ois.readObject();
|
||||
}
|
||||
|
||||
StrutsLocalizedTextProvider deserialized = (StrutsLocalizedTextProvider) restored;
|
||||
String result = deserialized.findText(CacheFixture.class, "cache.static", Locale.ENGLISH, null, null, valueStack);
|
||||
assertEquals("Static cached value", result);
|
||||
}
|
||||
|
||||
public void testCacheTypeSelectionKeepsProviderWorking() {
|
||||
TestStrutsLocalizedTextProvider provider = new TestStrutsLocalizedTextProvider();
|
||||
provider.setI18nCacheType("basic");
|
||||
ValueStack valueStack = ActionContext.getContext().getValueStack();
|
||||
String result = provider.findText(CacheFixture.class, "cache.static", Locale.ENGLISH, null, null, valueStack);
|
||||
assertEquals("Static cached value", result);
|
||||
assertTrue("bundlesMap should populate", provider.bundlesMapSize() >= 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
@@ -1,185 +0,0 @@
|
||||
/*
|
||||
* 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.action;
|
||||
|
||||
import com.opensymphony.xwork2.XWorkTestCase;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
import org.apache.struts2.interceptor.csp.CspSettings;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* Verifies that {@link CspReportAction} applies an upper bound to the report body it accepts, and
|
||||
* that the bound is configurable.
|
||||
*/
|
||||
public class CspReportActionReportSizeTest extends XWorkTestCase {
|
||||
|
||||
/**
|
||||
* The reader supplied by the container buffers ahead, so consumption is bounded by the limit
|
||||
* plus one buffer rather than by the limit exactly. That overshoot is fixed, not proportional
|
||||
* to the size of the body.
|
||||
*/
|
||||
private static final long READ_AHEAD_ALLOWANCE = 8192L;
|
||||
|
||||
/**
|
||||
* Produces {@code total} characters without buffering them, and records how many the caller
|
||||
* actually consumed.
|
||||
*/
|
||||
private static final class CountingReader extends Reader {
|
||||
private final long total;
|
||||
private final AtomicLong consumed;
|
||||
private long produced = 0;
|
||||
|
||||
CountingReader(long total, AtomicLong consumed) {
|
||||
this.total = total;
|
||||
this.consumed = consumed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(char[] cbuf, int off, int len) {
|
||||
if (produced >= total) {
|
||||
return -1;
|
||||
}
|
||||
int count = (int) Math.min(len, total - produced);
|
||||
for (int i = 0; i < count; i++) {
|
||||
cbuf[off + i] = 'a';
|
||||
}
|
||||
produced += count;
|
||||
consumed.addAndGet(count);
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
// characters are generated on demand, so there is nothing to release
|
||||
}
|
||||
}
|
||||
|
||||
private static final class CapturingCspReportAction extends CspReportAction {
|
||||
String captured;
|
||||
int reports;
|
||||
|
||||
@Override
|
||||
void processReport(String jsonCspReport) {
|
||||
captured = jsonCspReport;
|
||||
reports++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A request that both declares and delivers {@code size} characters, matching what a client can
|
||||
* actually send: the declared length and the delivered body agree.
|
||||
*/
|
||||
private MockHttpServletRequest requestOfSize(final long size, final AtomicLong consumed) {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("POST", "/csp-reports") {
|
||||
@Override
|
||||
public int getContentLength() {
|
||||
return (int) Math.min(size, Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedReader getReader() {
|
||||
return new BufferedReader(new CountingReader(size, consumed));
|
||||
}
|
||||
};
|
||||
request.setContentType(CspSettings.CSP_REPORT_TYPE);
|
||||
return request;
|
||||
}
|
||||
|
||||
public void testReportAboveLimitIsNotProcessed() {
|
||||
AtomicLong consumed = new AtomicLong();
|
||||
MockHttpServletRequest request = requestOfSize(64L * 1024 * 1024, consumed);
|
||||
|
||||
CapturingCspReportAction action = new CapturingCspReportAction();
|
||||
action.withServletRequest(request);
|
||||
|
||||
assertEquals("A report above the limit should not be processed", 0, action.reports);
|
||||
assertTrue("Consumed " + consumed.get() + " characters for a limit of "
|
||||
+ CspReportAction.DEFAULT_MAX_REPORT_SIZE,
|
||||
consumed.get() <= CspReportAction.DEFAULT_MAX_REPORT_SIZE + READ_AHEAD_ALLOWANCE);
|
||||
}
|
||||
|
||||
public void testReportWithinLimitIsProcessed() {
|
||||
String sampleReport = "{\"csp-report\":{\"document-uri\":\"https://example.test/\"}}";
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("POST", "/csp-reports");
|
||||
request.setContent(sampleReport.getBytes());
|
||||
request.setContentType(CspSettings.CSP_REPORT_TYPE);
|
||||
|
||||
CapturingCspReportAction action = new CapturingCspReportAction();
|
||||
action.withServletRequest(request);
|
||||
|
||||
assertEquals("A report within the limit should be processed", 1, action.reports);
|
||||
assertEquals("The report should be passed through unchanged", sampleReport, action.captured);
|
||||
}
|
||||
|
||||
public void testConfiguredLimitIsApplied() {
|
||||
AtomicLong consumed = new AtomicLong();
|
||||
MockHttpServletRequest request = requestOfSize(4096, consumed);
|
||||
|
||||
CapturingCspReportAction action = new CapturingCspReportAction();
|
||||
action.setMaxReportSize("1024");
|
||||
action.withServletRequest(request);
|
||||
|
||||
assertEquals("A report above the configured limit should not be processed", 0, action.reports);
|
||||
assertTrue("Consumed " + consumed.get() + " characters for a configured limit of 1024",
|
||||
consumed.get() <= 1024L + READ_AHEAD_ALLOWANCE);
|
||||
}
|
||||
|
||||
/**
|
||||
* The key named by {@link StrutsConstants#STRUTS_CSP_REPORT_MAX_SIZE} must exist in
|
||||
* default.properties under exactly that name. If the two drift apart the value is silently never
|
||||
* injected, leaving the limit hard-coded and the documented setting inert.
|
||||
*/
|
||||
public void testLimitKeyIsDefinedInDefaultProperties() throws IOException {
|
||||
Properties defaults = new Properties();
|
||||
try (InputStream in = getClass().getClassLoader()
|
||||
.getResourceAsStream("org/apache/struts2/default.properties")) {
|
||||
assertNotNull("default.properties should be on the classpath", in);
|
||||
defaults.load(in);
|
||||
}
|
||||
|
||||
assertEquals(StrutsConstants.STRUTS_CSP_REPORT_MAX_SIZE + " should be defined in default.properties",
|
||||
String.valueOf(CspReportAction.DEFAULT_MAX_REPORT_SIZE),
|
||||
defaults.getProperty(StrutsConstants.STRUTS_CSP_REPORT_MAX_SIZE));
|
||||
}
|
||||
|
||||
public void testUnusableConfiguredValuesAreIgnored() {
|
||||
String[] unusable = {"", " ", "not-a-number", "0", "-1", "2147483647"};
|
||||
|
||||
for (String value : unusable) {
|
||||
AtomicLong consumed = new AtomicLong();
|
||||
MockHttpServletRequest request = requestOfSize(64L * 1024 * 1024, consumed);
|
||||
|
||||
CapturingCspReportAction action = new CapturingCspReportAction();
|
||||
action.setMaxReportSize(value);
|
||||
action.withServletRequest(request);
|
||||
|
||||
assertEquals("A report above the default limit should not be processed for value '"
|
||||
+ value + "'", 0, action.reports);
|
||||
assertTrue("Consumed " + consumed.get() + " characters for value '" + value + "'",
|
||||
consumed.get() <= CspReportAction.DEFAULT_MAX_REPORT_SIZE + READ_AHEAD_ALLOWANCE);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -570,46 +570,6 @@ public class DispatcherTest extends StrutsJUnit4InternalTestCase {
|
||||
assertEquals(Locale.getDefault(), context.getLocale()); // Expect the system default value when Mock request access fails.
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateRequestLocaleOffPassesThrough() {
|
||||
initDispatcher(new HashMap<>());
|
||||
dispatcher.setDefaultLocale(null); // Force struts.locale unset; the test-config default would otherwise mask the request locale.
|
||||
HttpServletRequest request = mock(HttpServletRequest.class);
|
||||
// A syntactically valid but not JVM-available locale.
|
||||
Locale exotic = new Locale("en", "US", "xzz99");
|
||||
when(request.getLocale()).thenReturn(exotic);
|
||||
|
||||
assertEquals("Default off must pass the request locale through unchanged",
|
||||
exotic, dispatcher.getLocale(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateRequestLocaleOnKeepsAvailableLocale() {
|
||||
Map<String, String> params = new HashMap<>();
|
||||
params.put(StrutsConstants.STRUTS_LOCALE_VALIDATE_REQUEST, "true");
|
||||
initDispatcher(params);
|
||||
dispatcher.setDefaultLocale(null); // Force struts.locale unset; the test-config default would otherwise mask the request locale.
|
||||
HttpServletRequest request = mock(HttpServletRequest.class);
|
||||
when(request.getLocale()).thenReturn(Locale.UK);
|
||||
|
||||
assertEquals("Available request locale must be kept", Locale.UK, dispatcher.getLocale(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateRequestLocaleOnFallsBackForUnavailableLocale() {
|
||||
Map<String, String> params = new HashMap<>();
|
||||
params.put(StrutsConstants.STRUTS_LOCALE_VALIDATE_REQUEST, "true");
|
||||
initDispatcher(params);
|
||||
dispatcher.setDefaultLocale(null); // Force struts.locale unset; the test-config default would otherwise mask the request locale.
|
||||
HttpServletRequest request = mock(HttpServletRequest.class);
|
||||
Locale exotic = new Locale("en", "US", "xzz99");
|
||||
when(request.getLocale()).thenReturn(exotic);
|
||||
|
||||
// struts.locale unset in this dispatcher -> fall back to the JVM default.
|
||||
assertEquals("Unavailable request locale must fall back to system default",
|
||||
Locale.getDefault(), dispatcher.getLocale(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dispatcherReinjectedAfterReload() {
|
||||
HttpServletRequest request = mock(HttpServletRequest.class);
|
||||
|
||||
@@ -105,22 +105,6 @@ public class RestfulActionMapperTest extends StrutsInternalTestCase {
|
||||
assertEquals("europe", am.getParams().get("region"));
|
||||
}
|
||||
|
||||
public void testGetMappingRejectsActionNameWithDisallowedCharacters() {
|
||||
StrutsMockHttpServletRequest request = new StrutsMockHttpServletRequest();
|
||||
request.setupGetServletPath("/%{1+1}/x");
|
||||
|
||||
ActionMapping am = mapper.getMapping(request, null);
|
||||
assertEquals("index", am.getName());
|
||||
}
|
||||
|
||||
public void testGetMappingAcceptsRegularActionName() {
|
||||
StrutsMockHttpServletRequest request = new StrutsMockHttpServletRequest();
|
||||
request.setupGetServletPath("/my-app.action/x");
|
||||
|
||||
ActionMapping am = mapper.getMapping(request, null);
|
||||
assertEquals("my-app.action", am.getName());
|
||||
}
|
||||
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
mapper = new RestfulActionMapper();
|
||||
|
||||
-71
@@ -19,17 +19,13 @@
|
||||
package org.apache.struts2.interceptor.httpmethod;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.ActionProxy;
|
||||
import com.opensymphony.xwork2.mock.MockActionInvocation;
|
||||
import com.opensymphony.xwork2.mock.MockActionProxy;
|
||||
import org.apache.struts2.HttpMethodsTestAction;
|
||||
import org.apache.struts2.StrutsInternalTestCase;
|
||||
import org.apache.struts2.TestAction;
|
||||
import org.apache.struts2.config.StrutsXmlConfigurationProvider;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class HttpMethodInterceptorTest extends StrutsInternalTestCase {
|
||||
|
||||
private HttpMethodInterceptor interceptor;
|
||||
@@ -258,73 +254,6 @@ public class HttpMethodInterceptorTest extends StrutsInternalTestCase {
|
||||
assertEquals(HttpMethod.POST, action.getHttpMethod());
|
||||
}
|
||||
|
||||
/**
|
||||
* Regression for wildcard-resolved methods with no method-level HTTP annotation:
|
||||
* a class-level {@code @AllowedHttpMethod(POST)} must still cause GET to be rejected.
|
||||
* Previously the interceptor's {@code if/else-if} structure made the class-level
|
||||
* branch unreachable when {@code isMethodSpecified()=true} and the resolved method
|
||||
* carried no annotation of its own.
|
||||
*/
|
||||
public void testWildcardResolvedUnannotatedMethodRespectsClassLevelAnnotation() throws Exception {
|
||||
HttpMethodsTestAction action = new HttpMethodsTestAction();
|
||||
prepareActionInvocation(action);
|
||||
actionProxy.setMethod("execute");
|
||||
actionProxy.setMethodSpecified(true);
|
||||
|
||||
prepareRequest("get");
|
||||
|
||||
String resultName = interceptor.intercept(invocation);
|
||||
|
||||
assertEquals("bad-request", resultName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Counterpart to the above: POST against a wildcard-resolved unannotated method must succeed
|
||||
* when the class allows POST via {@code @AllowedHttpMethod(POST)}.
|
||||
*/
|
||||
public void testWildcardResolvedUnannotatedMethodAllowsPostWithClassLevelAnnotation() throws Exception {
|
||||
HttpMethodsTestAction action = new HttpMethodsTestAction();
|
||||
prepareActionInvocation(action);
|
||||
actionProxy.setMethod("execute");
|
||||
actionProxy.setMethodSpecified(true);
|
||||
invocation.setResultCode("success");
|
||||
|
||||
prepareRequest("post");
|
||||
|
||||
String resultName = interceptor.intercept(invocation);
|
||||
|
||||
assertEquals("success", resultName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exercises the full wildcard resolution path through a real {@link com.opensymphony.xwork2.DefaultActionProxy}.
|
||||
* <p>
|
||||
* Config (from xwork-test-allowed-methods.xml):
|
||||
* {@code <action name="Wild-*" class="HttpMethodsTestAction" method="{1}">}.
|
||||
* URL {@code Wild-execute} resolves to {@code ActionSupport.execute()} — no method-level
|
||||
* HTTP annotation. {@code HttpMethodsTestAction} carries class-level
|
||||
* {@code @AllowedHttpMethod(POST)}, so GET must be rejected end-to-end.
|
||||
*/
|
||||
public void testWildcardResolvedExecuteRejectsGetThroughRealProxy() throws Exception {
|
||||
loadConfigurationProviders(new StrutsXmlConfigurationProvider(
|
||||
"com/opensymphony/xwork2/config/providers/xwork-test-allowed-methods.xml"));
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/Wild-execute");
|
||||
Map<String, Object> extraContext = ActionContext.of()
|
||||
.withServletRequest(request)
|
||||
.getContextMap();
|
||||
|
||||
ActionProxy proxy = actionProxyFactory.createActionProxy("", "Wild-execute", null, extraContext);
|
||||
|
||||
assertEquals("execute", proxy.getMethod());
|
||||
assertTrue("Wildcard-resolved method must report isMethodSpecified()=true", proxy.isMethodSpecified());
|
||||
|
||||
HttpMethodInterceptor realInterceptor = new HttpMethodInterceptor();
|
||||
String result = realInterceptor.intercept(proxy.getInvocation());
|
||||
|
||||
assertEquals("bad-request", result);
|
||||
}
|
||||
|
||||
private void prepareActionInvocation(Object action) {
|
||||
interceptor = new HttpMethodInterceptor();
|
||||
invocation = new MockActionInvocation();
|
||||
|
||||
-39
@@ -38,7 +38,6 @@ import com.opensymphony.xwork2.ognl.OgnlValueStack;
|
||||
import com.opensymphony.xwork2.ognl.OgnlValueStackFactory;
|
||||
import com.opensymphony.xwork2.ognl.accessor.CompoundRootAccessor;
|
||||
import com.opensymphony.xwork2.ognl.accessor.RootAccessor;
|
||||
import com.opensymphony.xwork2.util.Element;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
import com.opensymphony.xwork2.util.ValueStackFactory;
|
||||
import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
|
||||
@@ -1013,44 +1012,6 @@ public class ParametersInterceptorTest extends XWorkTestCase {
|
||||
container.inject(config.getInterceptors().get(0).getInterceptor());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* WW-5700: a value that cannot be converted to the map's element type must not be stored.
|
||||
* An unchecked s:checkbox with submitUnchecked="true" submits the CheckboxInterceptor's
|
||||
* uncheckedValue, "false", which cannot become an Integer.
|
||||
*/
|
||||
public void testUnconvertibleValueIsNotBoundIntoTypedMap() {
|
||||
CheckboxAction action = new CheckboxAction();
|
||||
ValueStack vs = ActionContext.getContext().getValueStack();
|
||||
vs.push(action);
|
||||
|
||||
ParametersInterceptor pi = new ParametersInterceptor();
|
||||
container.inject(pi);
|
||||
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("capDeferral[100]", "1");
|
||||
params.put("capDeferral[200]", "false");
|
||||
|
||||
pi.applyParameters(action, vs, HttpParameters.create(params).build());
|
||||
|
||||
Map<Long, Integer> capDeferral = action.getCapDeferral();
|
||||
assertEquals("sanity: the convertible value must still bind", Integer.valueOf(1), capDeferral.get(100L));
|
||||
for (Object entry : ((Map) capDeferral).entrySet()) {
|
||||
Map.Entry e = (Map.Entry) entry;
|
||||
assertTrue("key is not a Long: " + e.getKey(), e.getKey() instanceof Long);
|
||||
assertTrue("value is not an Integer: " + e.getValue(), e.getValue() instanceof Integer);
|
||||
}
|
||||
}
|
||||
|
||||
public static class CheckboxAction {
|
||||
@Element(value = Integer.class)
|
||||
private final Map<Long, Integer> capDeferral = new HashMap<>();
|
||||
|
||||
@StrutsParameter(depth = 1)
|
||||
public Map<Long, Integer> getCapDeferral() {
|
||||
return capDeferral;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ValidateAction implements ValidationAware {
|
||||
|
||||
@@ -146,108 +146,5 @@ public class PostbackResultTest extends StrutsInternalTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* WW-5623: Verify that HTML special characters in finalLocation are properly
|
||||
* escaped in the rendered form action attribute.
|
||||
*/
|
||||
public void testFormActionHtmlEscaping() throws Exception {
|
||||
ActionContext context = ActionContext.getContext();
|
||||
ValueStack stack = context.getValueStack();
|
||||
MockHttpServletRequest req = new MockHttpServletRequest();
|
||||
MockHttpServletResponse res = new MockHttpServletResponse();
|
||||
context.put(ServletActionContext.HTTP_REQUEST, req);
|
||||
context.put(ServletActionContext.HTTP_RESPONSE, res);
|
||||
|
||||
// Push an object with a malicious property onto the value stack
|
||||
stack.push(new Object() {
|
||||
public String getTargetUrl() {
|
||||
return "/test\"onmouseover=\"alert(1)";
|
||||
}
|
||||
});
|
||||
|
||||
PostbackResult result = new PostbackResult();
|
||||
result.setLocation("/redirect?url=${targetUrl}");
|
||||
result.setPrependServletContext(false);
|
||||
|
||||
IMocksControl control = createControl();
|
||||
ActionInvocation mockInvocation = control.createMock(ActionInvocation.class);
|
||||
expect(mockInvocation.getInvocationContext()).andReturn(context).anyTimes();
|
||||
expect(mockInvocation.getStack()).andReturn(stack).anyTimes();
|
||||
|
||||
control.replay();
|
||||
result.setActionMapper(container.getInstance(ActionMapper.class));
|
||||
|
||||
// Call doExecute directly with a malicious location containing all critical chars
|
||||
result.doExecute("/test\"onmouseover=\"alert(1)\"¶m=<script>", mockInvocation);
|
||||
|
||||
String output = res.getContentAsString();
|
||||
|
||||
// The action attribute must contain escaped HTML entities
|
||||
assertTrue("Double quote should be escaped to "",
|
||||
output.contains("action=\"/test"onmouseover="alert(1)"&param=<script>\""));
|
||||
// Must not contain unescaped double-quote that breaks out of the attribute
|
||||
assertFalse("Raw double-quote must not appear in action value",
|
||||
output.contains("action=\"/test\""));
|
||||
|
||||
control.verify();
|
||||
}
|
||||
|
||||
/**
|
||||
* WW-5623: Verify that each individual HTML special character is properly escaped.
|
||||
*/
|
||||
public void testFormActionEscapesAllHtmlSpecialChars() throws Exception {
|
||||
ActionContext context = ActionContext.getContext();
|
||||
MockHttpServletRequest req = new MockHttpServletRequest();
|
||||
MockHttpServletResponse res = new MockHttpServletResponse();
|
||||
context.put(ServletActionContext.HTTP_REQUEST, req);
|
||||
context.put(ServletActionContext.HTTP_RESPONSE, res);
|
||||
|
||||
IMocksControl control = createControl();
|
||||
ActionInvocation mockInvocation = control.createMock(ActionInvocation.class);
|
||||
expect(mockInvocation.getInvocationContext()).andReturn(context).anyTimes();
|
||||
|
||||
control.replay();
|
||||
|
||||
PostbackResult result = new PostbackResult();
|
||||
result.setActionMapper(container.getInstance(ActionMapper.class));
|
||||
result.doExecute("/path?a=1&b=2\"<>", mockInvocation);
|
||||
|
||||
String output = res.getContentAsString();
|
||||
|
||||
assertTrue("Ampersand should be escaped", output.contains("&"));
|
||||
assertTrue("Double-quote should be escaped", output.contains("""));
|
||||
assertTrue("Less-than should be escaped", output.contains("<"));
|
||||
assertTrue("Greater-than should be escaped", output.contains(">"));
|
||||
|
||||
control.verify();
|
||||
}
|
||||
|
||||
/**
|
||||
* WW-5623: Verify that a clean location (no special chars) renders unchanged.
|
||||
*/
|
||||
public void testFormActionCleanLocationUnchanged() throws Exception {
|
||||
ActionContext context = ActionContext.getContext();
|
||||
MockHttpServletRequest req = new MockHttpServletRequest();
|
||||
MockHttpServletResponse res = new MockHttpServletResponse();
|
||||
context.put(ServletActionContext.HTTP_REQUEST, req);
|
||||
context.put(ServletActionContext.HTTP_RESPONSE, res);
|
||||
|
||||
IMocksControl control = createControl();
|
||||
ActionInvocation mockInvocation = control.createMock(ActionInvocation.class);
|
||||
expect(mockInvocation.getInvocationContext()).andReturn(context).anyTimes();
|
||||
|
||||
control.replay();
|
||||
|
||||
PostbackResult result = new PostbackResult();
|
||||
result.setActionMapper(container.getInstance(ActionMapper.class));
|
||||
result.doExecute("/clean/path/action.do", mockInvocation);
|
||||
|
||||
String output = res.getContentAsString();
|
||||
|
||||
assertTrue("Clean location should render as-is in action attribute",
|
||||
output.contains("action=\"/clean/path/action.do\""));
|
||||
|
||||
control.verify();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
cache.static=Static cached value
|
||||
@@ -19,7 +19,7 @@
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Apache Maven Wrapper startup batch script, version 3.3.4
|
||||
# Apache Maven Wrapper startup batch script, version 3.2.0
|
||||
#
|
||||
# Required ENV vars:
|
||||
# ------------------
|
||||
@@ -33,84 +33,75 @@
|
||||
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
if [ -z "$MAVEN_SKIP_RC" ]; then
|
||||
if [ -z "$MAVEN_SKIP_RC" ] ; then
|
||||
|
||||
if [ -f /usr/local/etc/mavenrc ]; then
|
||||
if [ -f /usr/local/etc/mavenrc ] ; then
|
||||
. /usr/local/etc/mavenrc
|
||||
fi
|
||||
|
||||
if [ -f /etc/mavenrc ]; then
|
||||
if [ -f /etc/mavenrc ] ; then
|
||||
. /etc/mavenrc
|
||||
fi
|
||||
|
||||
if [ -f "$HOME/.mavenrc" ]; then
|
||||
if [ -f "$HOME/.mavenrc" ] ; then
|
||||
. "$HOME/.mavenrc"
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
# OS specific support. $var _must_ be set to either true or false.
|
||||
cygwin=false
|
||||
darwin=false
|
||||
cygwin=false;
|
||||
darwin=false;
|
||||
mingw=false
|
||||
case "$(uname)" in
|
||||
CYGWIN*) cygwin=true ;;
|
||||
MINGW*) mingw=true ;;
|
||||
Darwin*)
|
||||
darwin=true
|
||||
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
|
||||
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
|
||||
if [ -z "$JAVA_HOME" ]; then
|
||||
if [ -x "/usr/libexec/java_home" ]; then
|
||||
JAVA_HOME="$(/usr/libexec/java_home)"
|
||||
export JAVA_HOME
|
||||
else
|
||||
JAVA_HOME="/Library/Java/Home"
|
||||
export JAVA_HOME
|
||||
CYGWIN*) cygwin=true ;;
|
||||
MINGW*) mingw=true;;
|
||||
Darwin*) darwin=true
|
||||
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
|
||||
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
|
||||
if [ -z "$JAVA_HOME" ]; then
|
||||
if [ -x "/usr/libexec/java_home" ]; then
|
||||
JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME
|
||||
else
|
||||
JAVA_HOME="/Library/Java/Home"; export JAVA_HOME
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -z "$JAVA_HOME" ]; then
|
||||
if [ -r /etc/gentoo-release ]; then
|
||||
if [ -z "$JAVA_HOME" ] ; then
|
||||
if [ -r /etc/gentoo-release ] ; then
|
||||
JAVA_HOME=$(java-config --jre-home)
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Cygwin, ensure paths are in UNIX format before anything is touched
|
||||
if $cygwin; then
|
||||
[ -n "$JAVA_HOME" ] \
|
||||
&& JAVA_HOME=$(cygpath --unix "$JAVA_HOME")
|
||||
[ -n "$CLASSPATH" ] \
|
||||
&& CLASSPATH=$(cygpath --path --unix "$CLASSPATH")
|
||||
if $cygwin ; then
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME=$(cygpath --unix "$JAVA_HOME")
|
||||
[ -n "$CLASSPATH" ] &&
|
||||
CLASSPATH=$(cygpath --path --unix "$CLASSPATH")
|
||||
fi
|
||||
|
||||
# For Mingw, ensure paths are in UNIX format before anything is touched
|
||||
if $mingw; then
|
||||
[ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] \
|
||||
&& JAVA_HOME="$(
|
||||
cd "$JAVA_HOME" || (
|
||||
echo "cannot cd into $JAVA_HOME." >&2
|
||||
exit 1
|
||||
)
|
||||
pwd
|
||||
)"
|
||||
if $mingw ; then
|
||||
[ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] &&
|
||||
JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)"
|
||||
fi
|
||||
|
||||
if [ -z "$JAVA_HOME" ]; then
|
||||
javaExecutable="$(which javac)"
|
||||
if [ -n "$javaExecutable" ] && ! [ "$(expr "$javaExecutable" : '\([^ ]*\)')" = "no" ]; then
|
||||
if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then
|
||||
# readlink(1) is not available as standard on Solaris 10.
|
||||
readLink=$(which readlink)
|
||||
if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then
|
||||
if $darwin; then
|
||||
javaHome="$(dirname "$javaExecutable")"
|
||||
javaExecutable="$(cd "$javaHome" && pwd -P)/javac"
|
||||
if $darwin ; then
|
||||
javaHome="$(dirname "\"$javaExecutable\"")"
|
||||
javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac"
|
||||
else
|
||||
javaExecutable="$(readlink -f "$javaExecutable")"
|
||||
javaExecutable="$(readlink -f "\"$javaExecutable\"")"
|
||||
fi
|
||||
javaHome="$(dirname "$javaExecutable")"
|
||||
javaHome="$(dirname "\"$javaExecutable\"")"
|
||||
javaHome=$(expr "$javaHome" : '\(.*\)/bin')
|
||||
JAVA_HOME="$javaHome"
|
||||
export JAVA_HOME
|
||||
@@ -118,60 +109,52 @@ if [ -z "$JAVA_HOME" ]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$JAVACMD" ]; then
|
||||
if [ -n "$JAVA_HOME" ]; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ]; then
|
||||
if [ -z "$JAVACMD" ] ; then
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
else
|
||||
JAVACMD="$(
|
||||
\unset -f command 2>/dev/null
|
||||
\command -v java
|
||||
)"
|
||||
JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -x "$JAVACMD" ]; then
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
echo "Error: JAVA_HOME is not defined correctly." >&2
|
||||
echo " We cannot execute $JAVACMD" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$JAVA_HOME" ]; then
|
||||
echo "Warning: JAVA_HOME environment variable is not set." >&2
|
||||
if [ -z "$JAVA_HOME" ] ; then
|
||||
echo "Warning: JAVA_HOME environment variable is not set."
|
||||
fi
|
||||
|
||||
# traverses directory structure from process work directory to filesystem root
|
||||
# first directory with .mvn subdirectory is considered project base directory
|
||||
find_maven_basedir() {
|
||||
if [ -z "$1" ]; then
|
||||
echo "Path not specified to find_maven_basedir" >&2
|
||||
if [ -z "$1" ]
|
||||
then
|
||||
echo "Path not specified to find_maven_basedir"
|
||||
return 1
|
||||
fi
|
||||
|
||||
basedir="$1"
|
||||
wdir="$1"
|
||||
while [ "$wdir" != '/' ]; do
|
||||
if [ -d "$wdir"/.mvn ]; then
|
||||
while [ "$wdir" != '/' ] ; do
|
||||
if [ -d "$wdir"/.mvn ] ; then
|
||||
basedir=$wdir
|
||||
break
|
||||
fi
|
||||
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
|
||||
if [ -d "${wdir}" ]; then
|
||||
wdir=$(
|
||||
cd "$wdir/.." || exit 1
|
||||
pwd
|
||||
)
|
||||
wdir=$(cd "$wdir/.." || exit 1; pwd)
|
||||
fi
|
||||
# end of workaround
|
||||
done
|
||||
printf '%s' "$(
|
||||
cd "$basedir" || exit 1
|
||||
pwd
|
||||
)"
|
||||
printf '%s' "$(cd "$basedir" || exit 1; pwd)"
|
||||
}
|
||||
|
||||
# concatenates all lines of a file
|
||||
@@ -182,7 +165,7 @@ concat_lines() {
|
||||
# enabled. Otherwise, we may read lines that are delimited with
|
||||
# \r\n and produce $'-Xarg\r' rather than -Xarg due to word
|
||||
# splitting rules.
|
||||
tr -s '\r\n' ' ' <"$1"
|
||||
tr -s '\r\n' ' ' < "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -194,85 +177,75 @@ log() {
|
||||
|
||||
BASE_DIR=$(find_maven_basedir "$(dirname "$0")")
|
||||
if [ -z "$BASE_DIR" ]; then
|
||||
exit 1
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
|
||||
export MAVEN_PROJECTBASEDIR
|
||||
MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR
|
||||
log "$MAVEN_PROJECTBASEDIR"
|
||||
|
||||
trim() {
|
||||
# MWRAPPER-139:
|
||||
# Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds.
|
||||
# Needed for removing poorly interpreted newline sequences when running in more
|
||||
# exotic environments such as mingw bash on Windows.
|
||||
printf "%s" "${1}" | tr -d '[:space:]'
|
||||
}
|
||||
|
||||
##########################################################################################
|
||||
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
|
||||
# This allows using the maven wrapper in projects that prohibit checking in binary data.
|
||||
##########################################################################################
|
||||
wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar"
|
||||
if [ -r "$wrapperJarPath" ]; then
|
||||
log "Found $wrapperJarPath"
|
||||
log "Found $wrapperJarPath"
|
||||
else
|
||||
log "Couldn't find $wrapperJarPath, downloading it ..."
|
||||
log "Couldn't find $wrapperJarPath, downloading it ..."
|
||||
|
||||
if [ -n "$MVNW_REPOURL" ]; then
|
||||
wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.3.4/maven-wrapper-3.3.4.jar"
|
||||
else
|
||||
wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.4/maven-wrapper-3.3.4.jar"
|
||||
fi
|
||||
while IFS="=" read -r key value; do
|
||||
case "$key" in wrapperUrl)
|
||||
wrapperUrl=$(trim "${value-}")
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done <"$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
|
||||
log "Downloading from: $wrapperUrl"
|
||||
|
||||
if $cygwin; then
|
||||
wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath")
|
||||
fi
|
||||
|
||||
if command -v wget >/dev/null; then
|
||||
log "Found wget ... using wget"
|
||||
[ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet"
|
||||
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
|
||||
wget ${QUIET:+"$QUIET"} "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
|
||||
if [ -n "$MVNW_REPOURL" ]; then
|
||||
wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
|
||||
else
|
||||
wget ${QUIET:+"$QUIET"} --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
|
||||
wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
|
||||
fi
|
||||
elif command -v curl >/dev/null; then
|
||||
log "Found curl ... using curl"
|
||||
[ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent"
|
||||
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
|
||||
curl ${QUIET:+"$QUIET"} -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
|
||||
else
|
||||
curl ${QUIET:+"$QUIET"} --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
|
||||
fi
|
||||
else
|
||||
log "Falling back to using Java to download"
|
||||
javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java"
|
||||
javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class"
|
||||
# For Cygwin, switch paths to Windows format before running javac
|
||||
while IFS="=" read -r key value; do
|
||||
# Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' )
|
||||
safeValue=$(echo "$value" | tr -d '\r')
|
||||
case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;;
|
||||
esac
|
||||
done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
|
||||
log "Downloading from: $wrapperUrl"
|
||||
|
||||
if $cygwin; then
|
||||
javaSource=$(cygpath --path --windows "$javaSource")
|
||||
javaClass=$(cygpath --path --windows "$javaClass")
|
||||
wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath")
|
||||
fi
|
||||
if [ -e "$javaSource" ]; then
|
||||
if [ ! -e "$javaClass" ]; then
|
||||
log " - Compiling MavenWrapperDownloader.java ..."
|
||||
("$JAVA_HOME/bin/javac" "$javaSource")
|
||||
fi
|
||||
if [ -e "$javaClass" ]; then
|
||||
log " - Running MavenWrapperDownloader.java ..."
|
||||
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath"
|
||||
fi
|
||||
|
||||
if command -v wget > /dev/null; then
|
||||
log "Found wget ... using wget"
|
||||
[ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet"
|
||||
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
|
||||
wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
|
||||
else
|
||||
wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
|
||||
fi
|
||||
elif command -v curl > /dev/null; then
|
||||
log "Found curl ... using curl"
|
||||
[ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent"
|
||||
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
|
||||
curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
|
||||
else
|
||||
curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
|
||||
fi
|
||||
else
|
||||
log "Falling back to using Java to download"
|
||||
javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java"
|
||||
javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class"
|
||||
# For Cygwin, switch paths to Windows format before running javac
|
||||
if $cygwin; then
|
||||
javaSource=$(cygpath --path --windows "$javaSource")
|
||||
javaClass=$(cygpath --path --windows "$javaClass")
|
||||
fi
|
||||
if [ -e "$javaSource" ]; then
|
||||
if [ ! -e "$javaClass" ]; then
|
||||
log " - Compiling MavenWrapperDownloader.java ..."
|
||||
("$JAVA_HOME/bin/javac" "$javaSource")
|
||||
fi
|
||||
if [ -e "$javaClass" ]; then
|
||||
log " - Running MavenWrapperDownloader.java ..."
|
||||
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
##########################################################################################
|
||||
# End of extension
|
||||
@@ -281,25 +254,22 @@ fi
|
||||
# If specified, validate the SHA-256 sum of the Maven wrapper jar file
|
||||
wrapperSha256Sum=""
|
||||
while IFS="=" read -r key value; do
|
||||
case "$key" in wrapperSha256Sum)
|
||||
wrapperSha256Sum=$(trim "${value-}")
|
||||
break
|
||||
;;
|
||||
case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;;
|
||||
esac
|
||||
done <"$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
|
||||
done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
|
||||
if [ -n "$wrapperSha256Sum" ]; then
|
||||
wrapperSha256Result=false
|
||||
if command -v sha256sum >/dev/null; then
|
||||
if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c - >/dev/null 2>&1; then
|
||||
if command -v sha256sum > /dev/null; then
|
||||
if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then
|
||||
wrapperSha256Result=true
|
||||
fi
|
||||
elif command -v shasum >/dev/null; then
|
||||
if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c >/dev/null 2>&1; then
|
||||
elif command -v shasum > /dev/null; then
|
||||
if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then
|
||||
wrapperSha256Result=true
|
||||
fi
|
||||
else
|
||||
echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2
|
||||
echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties." >&2
|
||||
echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available."
|
||||
echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties."
|
||||
exit 1
|
||||
fi
|
||||
if [ $wrapperSha256Result = false ]; then
|
||||
@@ -314,12 +284,12 @@ MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin; then
|
||||
[ -n "$JAVA_HOME" ] \
|
||||
&& JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME")
|
||||
[ -n "$CLASSPATH" ] \
|
||||
&& CLASSPATH=$(cygpath --path --windows "$CLASSPATH")
|
||||
[ -n "$MAVEN_PROJECTBASEDIR" ] \
|
||||
&& MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR")
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME")
|
||||
[ -n "$CLASSPATH" ] &&
|
||||
CLASSPATH=$(cygpath --path --windows "$CLASSPATH")
|
||||
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
|
||||
MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR")
|
||||
fi
|
||||
|
||||
# Provide a "standardized" way to retrieve the CLI args that will
|
||||
|
||||
@@ -1,206 +1,205 @@
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Licensed to the Apache Software Foundation (ASF) under one
|
||||
@REM or more contributor license agreements. See the NOTICE file
|
||||
@REM distributed with this work for additional information
|
||||
@REM regarding copyright ownership. The ASF licenses this file
|
||||
@REM to you under the Apache License, Version 2.0 (the
|
||||
@REM "License"); you may not use this file except in compliance
|
||||
@REM with the License. You may obtain a copy of the License at
|
||||
@REM
|
||||
@REM http://www.apache.org/licenses/LICENSE-2.0
|
||||
@REM
|
||||
@REM Unless required by applicable law or agreed to in writing,
|
||||
@REM software distributed under the License is distributed on an
|
||||
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
@REM KIND, either express or implied. See the License for the
|
||||
@REM specific language governing permissions and limitations
|
||||
@REM under the License.
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Apache Maven Wrapper startup batch script, version 3.3.4
|
||||
@REM
|
||||
@REM Required ENV vars:
|
||||
@REM JAVA_HOME - location of a JDK home dir
|
||||
@REM
|
||||
@REM Optional ENV vars
|
||||
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
|
||||
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
|
||||
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||
@REM e.g. to debug Maven itself, use
|
||||
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
|
||||
@echo off
|
||||
@REM set title of command window
|
||||
title %0
|
||||
@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
|
||||
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
|
||||
|
||||
@REM set %HOME% to equivalent of $HOME
|
||||
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
|
||||
|
||||
@REM Execute a user defined script before this one
|
||||
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
|
||||
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
|
||||
if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
|
||||
if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
|
||||
:skipRcPre
|
||||
|
||||
@setlocal
|
||||
|
||||
set ERROR_CODE=0
|
||||
|
||||
@REM To isolate internal variables from possible post scripts, we use another setlocal
|
||||
@setlocal
|
||||
|
||||
@REM ==== START VALIDATION ====
|
||||
if not "%JAVA_HOME%" == "" goto OkJHome
|
||||
|
||||
echo. >&2
|
||||
echo Error: JAVA_HOME not found in your environment. >&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||
echo location of your Java installation. >&2
|
||||
echo. >&2
|
||||
goto error
|
||||
|
||||
:OkJHome
|
||||
if exist "%JAVA_HOME%\bin\java.exe" goto init
|
||||
|
||||
echo. >&2
|
||||
echo Error: JAVA_HOME is set to an invalid directory. >&2
|
||||
echo JAVA_HOME = "%JAVA_HOME%" >&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||
echo location of your Java installation. >&2
|
||||
echo. >&2
|
||||
goto error
|
||||
|
||||
@REM ==== END VALIDATION ====
|
||||
|
||||
:init
|
||||
|
||||
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
|
||||
@REM Fallback to current working directory if not found.
|
||||
|
||||
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
|
||||
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
|
||||
|
||||
set EXEC_DIR=%CD%
|
||||
set WDIR=%EXEC_DIR%
|
||||
:findBaseDir
|
||||
IF EXIST "%WDIR%"\.mvn goto baseDirFound
|
||||
cd ..
|
||||
IF "%WDIR%"=="%CD%" goto baseDirNotFound
|
||||
set WDIR=%CD%
|
||||
goto findBaseDir
|
||||
|
||||
:baseDirFound
|
||||
set MAVEN_PROJECTBASEDIR=%WDIR%
|
||||
cd "%EXEC_DIR%"
|
||||
goto endDetectBaseDir
|
||||
|
||||
:baseDirNotFound
|
||||
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
|
||||
cd "%EXEC_DIR%"
|
||||
|
||||
:endDetectBaseDir
|
||||
|
||||
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
|
||||
|
||||
@setlocal EnableExtensions EnableDelayedExpansion
|
||||
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
|
||||
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
|
||||
|
||||
:endReadAdditionalConfig
|
||||
|
||||
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
|
||||
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
|
||||
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||
|
||||
set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.4/maven-wrapper-3.3.4.jar"
|
||||
|
||||
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
|
||||
IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B
|
||||
)
|
||||
|
||||
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
|
||||
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
|
||||
if exist %WRAPPER_JAR% (
|
||||
if "%MVNW_VERBOSE%" == "true" (
|
||||
echo Found %WRAPPER_JAR%
|
||||
)
|
||||
) else (
|
||||
if not "%MVNW_REPOURL%" == "" (
|
||||
SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.3.4/maven-wrapper-3.3.4.jar"
|
||||
)
|
||||
if "%MVNW_VERBOSE%" == "true" (
|
||||
echo Couldn't find %WRAPPER_JAR%, downloading it ...
|
||||
echo Downloading from: %WRAPPER_URL%
|
||||
)
|
||||
|
||||
powershell -Command "&{"^
|
||||
"$webclient = new-object System.Net.WebClient;"^
|
||||
"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
|
||||
"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
|
||||
"}"^
|
||||
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^
|
||||
"}"
|
||||
if "%MVNW_VERBOSE%" == "true" (
|
||||
echo Finished downloading %WRAPPER_JAR%
|
||||
)
|
||||
)
|
||||
@REM End of extension
|
||||
|
||||
@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file
|
||||
SET WRAPPER_SHA_256_SUM=""
|
||||
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
|
||||
IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B
|
||||
)
|
||||
IF NOT %WRAPPER_SHA_256_SUM%=="" (
|
||||
powershell -Command "&{"^
|
||||
"Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash;"^
|
||||
"$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^
|
||||
"If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^
|
||||
" Write-Error 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^
|
||||
" Write-Error 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^
|
||||
" Write-Error 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^
|
||||
" exit 1;"^
|
||||
"}"^
|
||||
"}"
|
||||
if ERRORLEVEL 1 goto error
|
||||
)
|
||||
|
||||
@REM Provide a "standardized" way to retrieve the CLI args that will
|
||||
@REM work with both Windows and non-Windows executions.
|
||||
set MAVEN_CMD_LINE_ARGS=%*
|
||||
|
||||
%MAVEN_JAVA_EXE% ^
|
||||
%JVM_CONFIG_MAVEN_PROPS% ^
|
||||
%MAVEN_OPTS% ^
|
||||
%MAVEN_DEBUG_OPTS% ^
|
||||
-classpath %WRAPPER_JAR% ^
|
||||
"-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
|
||||
%WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
|
||||
if ERRORLEVEL 1 goto error
|
||||
goto end
|
||||
|
||||
:error
|
||||
set ERROR_CODE=1
|
||||
|
||||
:end
|
||||
@endlocal & set ERROR_CODE=%ERROR_CODE%
|
||||
|
||||
if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
|
||||
@REM check for post script, once with legacy .bat ending and once with .cmd ending
|
||||
if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
|
||||
if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
|
||||
:skipRcPost
|
||||
|
||||
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
|
||||
if "%MAVEN_BATCH_PAUSE%"=="on" pause
|
||||
|
||||
if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
|
||||
|
||||
cmd /C exit /B %ERROR_CODE%
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Licensed to the Apache Software Foundation (ASF) under one
|
||||
@REM or more contributor license agreements. See the NOTICE file
|
||||
@REM distributed with this work for additional information
|
||||
@REM regarding copyright ownership. The ASF licenses this file
|
||||
@REM to you under the Apache License, Version 2.0 (the
|
||||
@REM "License"); you may not use this file except in compliance
|
||||
@REM with the License. You may obtain a copy of the License at
|
||||
@REM
|
||||
@REM http://www.apache.org/licenses/LICENSE-2.0
|
||||
@REM
|
||||
@REM Unless required by applicable law or agreed to in writing,
|
||||
@REM software distributed under the License is distributed on an
|
||||
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
@REM KIND, either express or implied. See the License for the
|
||||
@REM specific language governing permissions and limitations
|
||||
@REM under the License.
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Apache Maven Wrapper startup batch script, version 3.2.0
|
||||
@REM
|
||||
@REM Required ENV vars:
|
||||
@REM JAVA_HOME - location of a JDK home dir
|
||||
@REM
|
||||
@REM Optional ENV vars
|
||||
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
|
||||
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
|
||||
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||
@REM e.g. to debug Maven itself, use
|
||||
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
|
||||
@echo off
|
||||
@REM set title of command window
|
||||
title %0
|
||||
@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
|
||||
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
|
||||
|
||||
@REM set %HOME% to equivalent of $HOME
|
||||
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
|
||||
|
||||
@REM Execute a user defined script before this one
|
||||
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
|
||||
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
|
||||
if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
|
||||
if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
|
||||
:skipRcPre
|
||||
|
||||
@setlocal
|
||||
|
||||
set ERROR_CODE=0
|
||||
|
||||
@REM To isolate internal variables from possible post scripts, we use another setlocal
|
||||
@setlocal
|
||||
|
||||
@REM ==== START VALIDATION ====
|
||||
if not "%JAVA_HOME%" == "" goto OkJHome
|
||||
|
||||
echo.
|
||||
echo Error: JAVA_HOME not found in your environment. >&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||
echo location of your Java installation. >&2
|
||||
echo.
|
||||
goto error
|
||||
|
||||
:OkJHome
|
||||
if exist "%JAVA_HOME%\bin\java.exe" goto init
|
||||
|
||||
echo.
|
||||
echo Error: JAVA_HOME is set to an invalid directory. >&2
|
||||
echo JAVA_HOME = "%JAVA_HOME%" >&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||
echo location of your Java installation. >&2
|
||||
echo.
|
||||
goto error
|
||||
|
||||
@REM ==== END VALIDATION ====
|
||||
|
||||
:init
|
||||
|
||||
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
|
||||
@REM Fallback to current working directory if not found.
|
||||
|
||||
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
|
||||
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
|
||||
|
||||
set EXEC_DIR=%CD%
|
||||
set WDIR=%EXEC_DIR%
|
||||
:findBaseDir
|
||||
IF EXIST "%WDIR%"\.mvn goto baseDirFound
|
||||
cd ..
|
||||
IF "%WDIR%"=="%CD%" goto baseDirNotFound
|
||||
set WDIR=%CD%
|
||||
goto findBaseDir
|
||||
|
||||
:baseDirFound
|
||||
set MAVEN_PROJECTBASEDIR=%WDIR%
|
||||
cd "%EXEC_DIR%"
|
||||
goto endDetectBaseDir
|
||||
|
||||
:baseDirNotFound
|
||||
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
|
||||
cd "%EXEC_DIR%"
|
||||
|
||||
:endDetectBaseDir
|
||||
|
||||
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
|
||||
|
||||
@setlocal EnableExtensions EnableDelayedExpansion
|
||||
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
|
||||
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
|
||||
|
||||
:endReadAdditionalConfig
|
||||
|
||||
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
|
||||
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
|
||||
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||
|
||||
set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
|
||||
|
||||
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
|
||||
IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B
|
||||
)
|
||||
|
||||
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
|
||||
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
|
||||
if exist %WRAPPER_JAR% (
|
||||
if "%MVNW_VERBOSE%" == "true" (
|
||||
echo Found %WRAPPER_JAR%
|
||||
)
|
||||
) else (
|
||||
if not "%MVNW_REPOURL%" == "" (
|
||||
SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
|
||||
)
|
||||
if "%MVNW_VERBOSE%" == "true" (
|
||||
echo Couldn't find %WRAPPER_JAR%, downloading it ...
|
||||
echo Downloading from: %WRAPPER_URL%
|
||||
)
|
||||
|
||||
powershell -Command "&{"^
|
||||
"$webclient = new-object System.Net.WebClient;"^
|
||||
"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
|
||||
"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
|
||||
"}"^
|
||||
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^
|
||||
"}"
|
||||
if "%MVNW_VERBOSE%" == "true" (
|
||||
echo Finished downloading %WRAPPER_JAR%
|
||||
)
|
||||
)
|
||||
@REM End of extension
|
||||
|
||||
@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file
|
||||
SET WRAPPER_SHA_256_SUM=""
|
||||
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
|
||||
IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B
|
||||
)
|
||||
IF NOT %WRAPPER_SHA_256_SUM%=="" (
|
||||
powershell -Command "&{"^
|
||||
"$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^
|
||||
"If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^
|
||||
" Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^
|
||||
" Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^
|
||||
" Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^
|
||||
" exit 1;"^
|
||||
"}"^
|
||||
"}"
|
||||
if ERRORLEVEL 1 goto error
|
||||
)
|
||||
|
||||
@REM Provide a "standardized" way to retrieve the CLI args that will
|
||||
@REM work with both Windows and non-Windows executions.
|
||||
set MAVEN_CMD_LINE_ARGS=%*
|
||||
|
||||
%MAVEN_JAVA_EXE% ^
|
||||
%JVM_CONFIG_MAVEN_PROPS% ^
|
||||
%MAVEN_OPTS% ^
|
||||
%MAVEN_DEBUG_OPTS% ^
|
||||
-classpath %WRAPPER_JAR% ^
|
||||
"-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
|
||||
%WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
|
||||
if ERRORLEVEL 1 goto error
|
||||
goto end
|
||||
|
||||
:error
|
||||
set ERROR_CODE=1
|
||||
|
||||
:end
|
||||
@endlocal & set ERROR_CODE=%ERROR_CODE%
|
||||
|
||||
if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
|
||||
@REM check for post script, once with legacy .bat ending and once with .cmd ending
|
||||
if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
|
||||
if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
|
||||
:skipRcPost
|
||||
|
||||
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
|
||||
if "%MAVEN_BATCH_PAUSE%"=="on" pause
|
||||
|
||||
if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
|
||||
|
||||
cmd /C exit /B %ERROR_CODE%
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.12.0-SNAPSHOT</version>
|
||||
<version>6.9.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-async-plugin</artifactId>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.12.0-SNAPSHOT</version>
|
||||
<version>6.9.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.12.0-SNAPSHOT</version>
|
||||
<version>6.9.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-cdi-plugin</artifactId>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.12.0-SNAPSHOT</version>
|
||||
<version>6.9.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-config-browser-plugin</artifactId>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.12.0-SNAPSHOT</version>
|
||||
<version>6.9.0</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.12.0-SNAPSHOT</version>
|
||||
<version>6.9.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-dwr-plugin</artifactId>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.12.0-SNAPSHOT</version>
|
||||
<version>6.9.0</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.12.0-SNAPSHOT</version>
|
||||
<version>6.9.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-gxp-plugin</artifactId>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.12.0-SNAPSHOT</version>
|
||||
<version>6.9.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-jasperreports-plugin</artifactId>
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.12.0-SNAPSHOT</version>
|
||||
<version>6.9.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-javatemplates-plugin</artifactId>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.12.0-SNAPSHOT</version>
|
||||
<version>6.9.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-jfreechart-plugin</artifactId>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.12.0-SNAPSHOT</version>
|
||||
<version>6.9.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-json-plugin</artifactId>
|
||||
|
||||
@@ -57,9 +57,6 @@ public class JSONUtil {
|
||||
|
||||
public final static String RFC3339_FORMAT = "yyyy-MM-dd'T'HH:mm:ss";
|
||||
public static final boolean CACHE_BEAN_INFO_DEFAULT = true;
|
||||
|
||||
/** Chunk size used to read input incrementally while applying the length limit. */
|
||||
private static final int READ_CHUNK_SIZE = 8192;
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(JSONUtil.class);
|
||||
|
||||
@@ -340,15 +337,13 @@ public class JSONUtil {
|
||||
*/
|
||||
public Object deserializeInput(Reader reader, int maxLength, int maxElements, int maxDepth,
|
||||
int maxStringLength, int maxKeyLength) throws JSONException {
|
||||
BufferedReader bufferReader = new BufferedReader(reader);
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
char[] chunk = new char[READ_CHUNK_SIZE];
|
||||
String line;
|
||||
|
||||
try {
|
||||
int read;
|
||||
// Apply the limit while reading rather than afterwards, so input that contains no
|
||||
// line terminator is not accumulated in full before the limit can be evaluated.
|
||||
while ((read = reader.read(chunk)) != -1) {
|
||||
buffer.append(chunk, 0, read);
|
||||
while ((line = bufferReader.readLine()) != null) {
|
||||
buffer.append(line);
|
||||
if (buffer.length() > maxLength) {
|
||||
throw new JSONException("JSON input length exceeds maximum allowed length of " + maxLength);
|
||||
}
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
/*
|
||||
* 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.json;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* Verifies that {@link JSONUtil#deserializeInput(Reader, int, int, int, int, int)} applies the
|
||||
* configured input length limit while reading, bounding how much input is consumed before the limit
|
||||
* takes effect, and that input within the limit still parses.
|
||||
*/
|
||||
public class JSONUtilInputLimitTest {
|
||||
|
||||
/**
|
||||
* Emits {@code total} characters with no line terminator anywhere, and records how many
|
||||
* characters the caller actually consumed.
|
||||
*/
|
||||
private static final class UnterminatedReader extends Reader {
|
||||
private final long total;
|
||||
private final AtomicLong consumed;
|
||||
private long produced = 0;
|
||||
|
||||
UnterminatedReader(long total, AtomicLong consumed) {
|
||||
this.total = total;
|
||||
this.consumed = consumed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(char[] cbuf, int off, int len) {
|
||||
if (produced >= total) {
|
||||
return -1;
|
||||
}
|
||||
int count = (int) Math.min(len, total - produced);
|
||||
for (int i = 0; i < count; i++) {
|
||||
cbuf[off + i] = 'a';
|
||||
}
|
||||
produced += count;
|
||||
consumed.addAndGet(count);
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
// characters are generated on demand, so there is nothing to release
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void inputWithoutLineTerminatorIsLimitedWhileReading() {
|
||||
int maxLength = 1024;
|
||||
long inputSize = 64L * 1024 * 1024;
|
||||
AtomicLong consumed = new AtomicLong();
|
||||
|
||||
JSONUtil util = new JSONUtil();
|
||||
Reader input = new UnterminatedReader(inputSize, consumed);
|
||||
|
||||
try {
|
||||
util.deserializeInput(input, maxLength, 100, 10, 1000, 100);
|
||||
fail("Expected JSONException for exceeding max length");
|
||||
} catch (JSONException expected) {
|
||||
// the limit is expected to be reported
|
||||
}
|
||||
|
||||
long read = consumed.get();
|
||||
// Reading proceeds in chunks, so a single chunk of overshoot beyond the limit is expected.
|
||||
assertTrue("Consumed " + read + " characters for a limit of " + maxLength,
|
||||
read < maxLength + 65_536L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void inputWithinLimitIsParsed() throws JSONException {
|
||||
JSONUtil util = new JSONUtil();
|
||||
|
||||
Object result = util.deserializeInput(
|
||||
new StringReader("{\"a\":1, \"b\":\"hello\"}"), 1024, 100, 10, 1000, 100);
|
||||
|
||||
assertTrue("Expected a parsed JSON object", result instanceof Map);
|
||||
assertEquals(1L, ((Map<?, ?>) result).get("a"));
|
||||
assertEquals("hello", ((Map<?, ?>) result).get("b"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void inputSpanningMultipleLinesIsParsed() throws JSONException {
|
||||
JSONUtil util = new JSONUtil();
|
||||
|
||||
// Line terminators between tokens are insignificant whitespace to the reader.
|
||||
Object result = util.deserializeInput(
|
||||
new StringReader("{\n\"a\":1,\n\"b\":2\n}"), 1024, 100, 10, 1000, 100);
|
||||
|
||||
assertTrue("Expected a parsed JSON object", result instanceof Map);
|
||||
assertEquals(1L, ((Map<?, ?>) result).get("a"));
|
||||
assertEquals(2L, ((Map<?, ?>) result).get("b"));
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.12.0-SNAPSHOT</version>
|
||||
<version>6.9.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-junit-plugin</artifactId>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.12.0-SNAPSHOT</version>
|
||||
<version>6.9.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-osgi-plugin</artifactId>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.12.0-SNAPSHOT</version>
|
||||
<version>6.9.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-oval-plugin</artifactId>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.12.0-SNAPSHOT</version>
|
||||
<version>6.9.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-pell-multipart-plugin</artifactId>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.12.0-SNAPSHOT</version>
|
||||
<version>6.9.0</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.12.0-SNAPSHOT</version>
|
||||
<version>6.9.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.12.0-SNAPSHOT</version>
|
||||
<version>6.9.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-portlet-junit-plugin</artifactId>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.12.0-SNAPSHOT</version>
|
||||
<version>6.9.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-portlet-mocks-plugin</artifactId>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.12.0-SNAPSHOT</version>
|
||||
<version>6.9.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-portlet-tiles-plugin</artifactId>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.12.0-SNAPSHOT</version>
|
||||
<version>6.9.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-portlet-plugin</artifactId>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.12.0-SNAPSHOT</version>
|
||||
<version>6.9.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-rest-plugin</artifactId>
|
||||
|
||||
@@ -351,7 +351,6 @@ public class RestActionMapper extends DefaultActionMapper {
|
||||
Configuration config = configManager.getConfiguration();
|
||||
String prefix = uri.substring(0, lastSlash);
|
||||
namespace = "";
|
||||
boolean rootAvailable = false;
|
||||
// Find the longest matching namespace, defaulting to the default
|
||||
for (Object o : config.getPackageConfigs().values()) {
|
||||
String ns = ((PackageConfig) o).getNamespace();
|
||||
@@ -360,19 +359,9 @@ public class RestActionMapper extends DefaultActionMapper {
|
||||
namespace = ns;
|
||||
}
|
||||
}
|
||||
if ("/".equals(ns)) {
|
||||
rootAvailable = true;
|
||||
}
|
||||
}
|
||||
|
||||
// must be read before the root namespace is selected below, as it is relative to ""
|
||||
name = uri.substring(namespace.length() + 1);
|
||||
|
||||
// WW-5688, WW-2461: still none found, use the root namespace if it is declared, so that
|
||||
// an id-bearing uri lands in the same namespace as the one without an id
|
||||
if (rootAvailable && namespace.isEmpty()) {
|
||||
namespace = "/";
|
||||
}
|
||||
}
|
||||
|
||||
mapping.setNamespace(cleanupNamespaceName(namespace));
|
||||
|
||||
-85
@@ -1,85 +0,0 @@
|
||||
/*
|
||||
* 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.rest;
|
||||
|
||||
import com.opensymphony.xwork2.XWorkTestCase;
|
||||
import org.apache.struts2.config.StrutsXmlConfigurationProvider;
|
||||
import org.apache.struts2.dispatcher.mapper.ActionMapping;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
|
||||
/**
|
||||
* WW-5688: an action declared in the root namespace has to stay reachable whether or not the
|
||||
* request URI carries an id, so that {@code index} and {@code show} resolve to the same action.
|
||||
*
|
||||
* <p>These assertions go all the way to the {@code ActionConfig} rather than stopping at the
|
||||
* mapping, because the reported symptom is a 404 - the mapper handing back a namespace that
|
||||
* the configuration cannot resolve.</p>
|
||||
*/
|
||||
public class RestActionMapperRootNamespaceTest extends XWorkTestCase {
|
||||
|
||||
private RestActionMapper mapper;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
loadConfigurationProviders(new StrutsXmlConfigurationProvider("ww-5688.xml"));
|
||||
mapper = new RestActionMapper();
|
||||
}
|
||||
|
||||
private ActionMapping map(String servletPath, String httpMethod) {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.setContextPath("/myapp");
|
||||
request.setMethod(httpMethod);
|
||||
request.setRequestURI("/myapp" + servletPath);
|
||||
request.setServletPath(servletPath);
|
||||
return mapper.getMapping(request, configurationManager);
|
||||
}
|
||||
|
||||
private void assertResolves(String servletPath, String httpMethod, String expectedMethod) {
|
||||
ActionMapping mapping = map(servletPath, httpMethod);
|
||||
assertNotNull(httpMethod + " " + servletPath + " produced no mapping", mapping);
|
||||
assertEquals("dog", mapping.getName());
|
||||
assertEquals(expectedMethod, mapping.getMethod());
|
||||
assertNotNull(httpMethod + " " + servletPath + " must resolve to the action declared in the root namespace,"
|
||||
+ " but namespace '" + mapping.getNamespace() + "' does not hold it",
|
||||
configurationManager.getConfiguration().getRuntimeConfiguration()
|
||||
.getActionConfig(mapping.getNamespace(), mapping.getName()));
|
||||
}
|
||||
|
||||
public void testIndexResolvesInRootNamespace() {
|
||||
assertResolves("/dog", "GET", "index");
|
||||
}
|
||||
|
||||
public void testShowResolvesInRootNamespace() {
|
||||
assertResolves("/dog/1", "GET", "show");
|
||||
}
|
||||
|
||||
public void testUpdateResolvesInRootNamespace() {
|
||||
assertResolves("/dog/1", "PUT", "update");
|
||||
}
|
||||
|
||||
public void testDestroyResolvesInRootNamespace() {
|
||||
assertResolves("/dog/1", "DELETE", "destroy");
|
||||
}
|
||||
|
||||
public void testIdIsStillExtracted() {
|
||||
ActionMapping mapping = map("/dog/1", "GET");
|
||||
assertEquals("1", ((String[]) mapping.getParams().get("id"))[0]);
|
||||
}
|
||||
}
|
||||
@@ -261,23 +261,6 @@ public class RestActionMapperTest extends TestCase {
|
||||
tryUri("/my/foo/23;edit", "/my", "foo/23;edit");
|
||||
}
|
||||
|
||||
/**
|
||||
* WW-5688: when a package declares the root namespace, a uri that matches no more specific
|
||||
* namespace belongs to that root rather than to the default namespace, so that "/foo" and
|
||||
* "/foo/23" agree on where the action lives. Without a root package the default namespace
|
||||
* still wins - see {@link #testParseNameAndNamespace()}.
|
||||
*/
|
||||
public void testParseNameAndNamespaceWithRootPackage() {
|
||||
config.addPackageConfig("root", new PackageConfig.Builder("root").namespace("/").build());
|
||||
|
||||
tryUri("/foo/23", "/", "foo/23");
|
||||
tryUri("/foo/", "/", "foo/");
|
||||
tryUri("/", "/", "");
|
||||
|
||||
// a longer declared namespace still outranks the root
|
||||
tryUri("/my/foo/23", "/my", "foo/23");
|
||||
}
|
||||
|
||||
public void testShouldAllowExclamation() throws Exception {
|
||||
req.setRequestURI("/myapp/animals/dog/fido!edit");
|
||||
req.setServletPath("/animals/dog/fido!edit");
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!--
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
-->
|
||||
<!DOCTYPE struts PUBLIC
|
||||
"-//Apache Software Foundation//DTD Struts Configuration 6.0//EN"
|
||||
"https://struts.apache.org/dtds/struts-6.0.dtd">
|
||||
|
||||
<!-- WW-5688: the only declared package sits at the root namespace -->
|
||||
<struts>
|
||||
<package name="ww-5688-root" namespace="/">
|
||||
<action name="dog" class="org.apache.struts2.ActionSupport"/>
|
||||
</package>
|
||||
</struts>
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.12.0-SNAPSHOT</version>
|
||||
<version>6.9.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-sitemesh-plugin</artifactId>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.12.0-SNAPSHOT</version>
|
||||
<version>6.9.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-spring-plugin</artifactId>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.12.0-SNAPSHOT</version>
|
||||
<version>6.9.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-testng-plugin</artifactId>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.12.0-SNAPSHOT</version>
|
||||
<version>6.9.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-tiles-plugin</artifactId>
|
||||
|
||||
+2
-36
@@ -26,6 +26,7 @@ import org.apache.tiles.request.ApplicationContext;
|
||||
import org.apache.tiles.request.ApplicationResource;
|
||||
import org.apache.tiles.request.locale.LocaleUtil;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
@@ -52,13 +53,6 @@ public class CachingLocaleUrlDefinitionDAO extends BaseLocaleUrlDefinitionDAO im
|
||||
*/
|
||||
public static final String CHECK_REFRESH_INIT_PARAMETER = "org.apache.tiles.definition.dao.LocaleUrlDefinitionDAO.CHECK_REFRESH";
|
||||
|
||||
/**
|
||||
* Default upper bound on the number of customization keys (locales) whose definitions are cached at once. Since the
|
||||
* customization key is derived from the request locale, this bounds the cache so it cannot grow without limit as
|
||||
* distinct locales are encountered.
|
||||
*/
|
||||
public static final int DEFAULT_MAX_CACHED_LOCALES = 1000;
|
||||
|
||||
/**
|
||||
* The locale-specific set of definitions objects.
|
||||
*
|
||||
@@ -66,12 +60,6 @@ public class CachingLocaleUrlDefinitionDAO extends BaseLocaleUrlDefinitionDAO im
|
||||
*/
|
||||
protected Map<Locale, Map<String, Definition>> locale2definitionMap;
|
||||
|
||||
/**
|
||||
* Maximum number of customization keys (locales) retained in {@link #locale2definitionMap}. When exceeded, the
|
||||
* eldest entry is evicted (and reloaded on demand if requested again).
|
||||
*/
|
||||
protected int maxCachedLocales = DEFAULT_MAX_CACHED_LOCALES;
|
||||
|
||||
/**
|
||||
* Flag that, when <code>true</code>, enables automatic checking of URLs
|
||||
* changing.
|
||||
@@ -94,29 +82,7 @@ public class CachingLocaleUrlDefinitionDAO extends BaseLocaleUrlDefinitionDAO im
|
||||
*/
|
||||
public CachingLocaleUrlDefinitionDAO(ApplicationContext applicationContext) {
|
||||
super(applicationContext);
|
||||
locale2definitionMap = new LinkedHashMap<Locale, Map<String, Definition>>(16, 0.75f, false) {
|
||||
@Override
|
||||
protected boolean removeEldestEntry(Map.Entry<Locale, Map<String, Definition>> eldest) {
|
||||
if (size() <= maxCachedLocales) {
|
||||
return false;
|
||||
}
|
||||
if (definitionResolver != null) {
|
||||
definitionResolver.removePatternPaths(eldest.getKey());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum number of customization keys (locales) whose definitions are cached. When more distinct keys are
|
||||
* requested, the eldest cached entry is evicted so the cache cannot grow without bound. Evicted entries are reloaded
|
||||
* on demand if requested again, so eviction never changes rendering, only re-incurs a load.
|
||||
*
|
||||
* @param maxCachedLocales the maximum number of cached customization keys; values below 1 are treated as 1
|
||||
*/
|
||||
public void setMaxCachedLocales(int maxCachedLocales) {
|
||||
this.maxCachedLocales = Math.max(1, maxCachedLocales);
|
||||
locale2definitionMap = new HashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+2
-8
@@ -21,9 +21,9 @@ package org.apache.tiles.core.definition.pattern;
|
||||
import org.apache.tiles.api.Definition;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* A pattern definition resolver that stores {@link DefinitionPatternMatcher}
|
||||
@@ -39,7 +39,7 @@ public abstract class AbstractPatternDefinitionResolver<T> implements PatternDef
|
||||
/**
|
||||
* Stores patterns depending on the locale they refer to.
|
||||
*/
|
||||
private final Map<T, List<DefinitionPatternMatcher>> localePatternPaths = new ConcurrentHashMap<>();
|
||||
private final Map<T, List<DefinitionPatternMatcher>> localePatternPaths = new HashMap<>();
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public Definition resolveDefinition(String name, T customizationKey) {
|
||||
@@ -103,10 +103,4 @@ public abstract class AbstractPatternDefinitionResolver<T> implements PatternDef
|
||||
if (localePatternPaths.get(customizationKey) != null)
|
||||
localePatternPaths.get(customizationKey).clear();
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void removePatternPaths(T customizationKey) {
|
||||
localePatternPaths.remove(customizationKey);
|
||||
}
|
||||
}
|
||||
|
||||
-8
@@ -60,12 +60,4 @@ public interface PatternDefinitionResolver<T> {
|
||||
* @param customizationKey customization key
|
||||
*/
|
||||
void clearPatternPaths(T customizationKey);
|
||||
|
||||
/**
|
||||
* Removes the stored patterns for a specific customization key entirely, including the key itself. Used when the
|
||||
* owning definitions cache evicts a customization key so that the pattern store cannot grow without bound.
|
||||
*
|
||||
* @param customizationKey customization key
|
||||
*/
|
||||
void removePatternPaths(T customizationKey);
|
||||
}
|
||||
|
||||
-37
@@ -34,7 +34,6 @@ import org.apache.tiles.request.ApplicationResource;
|
||||
import org.apache.tiles.request.locale.URLApplicationResource;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -369,40 +368,4 @@ public class CachingLocaleUrlDefinitionDAOTest extends TestCase {
|
||||
assertEquals(1, attributes.size());
|
||||
verify(applicationContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* The definitions cache is keyed by locale, so it must not grow beyond the configured bound as distinct
|
||||
* locales are requested; the eldest entry is evicted instead, and the eviction is propagated to the pattern
|
||||
* resolver so its per-locale store cannot grow without bound either.
|
||||
*/
|
||||
public void testLocaleCacheIsBounded() {
|
||||
List<ApplicationResource> sourceURLs = new ArrayList<>();
|
||||
sourceURLs.add(url1);
|
||||
sourceURLs.add(url2);
|
||||
sourceURLs.add(url3);
|
||||
definitionDao.setSources(sourceURLs);
|
||||
definitionDao.setReader(new DigesterDefinitionsReader());
|
||||
|
||||
List<Locale> evicted = new ArrayList<>();
|
||||
WildcardDefinitionPatternMatcherFactory factory = new WildcardDefinitionPatternMatcherFactory();
|
||||
PatternDefinitionResolver<Locale> recordingResolver = new BasicPatternDefinitionResolver<Locale>(factory, factory) {
|
||||
@Override
|
||||
public void removePatternPaths(Locale customizationKey) {
|
||||
evicted.add(customizationKey);
|
||||
super.removePatternPaths(customizationKey);
|
||||
}
|
||||
};
|
||||
definitionDao.setPatternDefinitionResolver(recordingResolver);
|
||||
definitionDao.setMaxCachedLocales(2);
|
||||
|
||||
for (Locale locale : new Locale[]{Locale.US, Locale.FRENCH, Locale.CANADA_FRENCH, Locale.CHINA}) {
|
||||
assertNotNull("Definitions for " + locale + " were not loaded.",
|
||||
definitionDao.getDefinitions(locale));
|
||||
}
|
||||
|
||||
assertEquals("Definitions cache must not grow beyond the configured bound",
|
||||
2, definitionDao.locale2definitionMap.size());
|
||||
assertEquals("Evicted locales must be removed from the pattern resolver in lockstep",
|
||||
Arrays.asList(Locale.US, Locale.FRENCH), evicted);
|
||||
}
|
||||
}
|
||||
|
||||
-29
@@ -79,35 +79,6 @@ public class AbstractPatternDefinitionResolverTest {
|
||||
testResolveDefinitionImpl();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for
|
||||
* {@link AbstractPatternDefinitionResolver#removePatternPaths(Object)}: the key and its patterns are dropped
|
||||
* entirely, so nothing resolves for that key afterwards.
|
||||
*/
|
||||
@Test
|
||||
public void testRemovePatternPaths() {
|
||||
firstMatcher = createMock(DefinitionPatternMatcher.class);
|
||||
thirdMatcher = createMock(DefinitionPatternMatcher.class);
|
||||
|
||||
Definition firstDefinition = new Definition("first", null, null);
|
||||
Definition firstTransformedDefinition = new Definition("firstTransformed", null, null);
|
||||
|
||||
expect(firstMatcher.createDefinition("firstTransformed")).andReturn(firstTransformedDefinition);
|
||||
replay(firstMatcher, thirdMatcher);
|
||||
|
||||
Map<String, Definition> localeDefsMap = new LinkedHashMap<>();
|
||||
localeDefsMap.put("first", firstDefinition);
|
||||
resolver.storeDefinitionPatterns(localeDefsMap, 1);
|
||||
|
||||
assertEquals(firstTransformedDefinition, resolver.resolveDefinition("firstTransformed", 1));
|
||||
|
||||
resolver.removePatternPaths(1);
|
||||
assertNull("removePatternPaths must drop the entry so nothing resolves for the key",
|
||||
resolver.resolveDefinition("firstTransformed", 1));
|
||||
|
||||
verify(firstMatcher, thirdMatcher);
|
||||
}
|
||||
|
||||
private void testResolveDefinitionImpl() {
|
||||
|
||||
firstMatcher = createMock(DefinitionPatternMatcher.class);
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.12.0-SNAPSHOT</version>
|
||||
<version>6.9.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-velocity-plugin</artifactId>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>6.12.0-SNAPSHOT</version>
|
||||
<version>6.9.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-xslt-plugin</artifactId>
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>struts2-parent</artifactId>
|
||||
<version>6.12.0-SNAPSHOT</version>
|
||||
<version>6.9.0</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>Struts 2</name>
|
||||
<url>https://struts.apache.org/</url>
|
||||
@@ -104,22 +104,22 @@
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.build.outputTimestamp>2026-08-01T10:28:23Z</project.build.outputTimestamp>
|
||||
<project.build.outputTimestamp>2026-04-10T09:52:32Z</project.build.outputTimestamp>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
|
||||
<!-- dependency versions in alphanumeric order -->
|
||||
<asm.version>9.10.1</asm.version>
|
||||
<jackson.version>2.22.2</jackson.version>
|
||||
<log4j2.version>2.26.1</log4j2.version>
|
||||
<asm.version>9.9.1</asm.version>
|
||||
<jackson.version>2.21.2</jackson.version>
|
||||
<log4j2.version>2.25.4</log4j2.version>
|
||||
<ognl.version>3.3.5</ognl.version>
|
||||
<slf4j.version>2.0.18</slf4j.version>
|
||||
<slf4j.version>2.0.17</slf4j.version>
|
||||
<spring.platformVersion>5.3.39</spring.platformVersion>
|
||||
<tiles.version>3.0.8</tiles.version>
|
||||
<tiles-request.version>1.0.7</tiles-request.version>
|
||||
<maven-surefire-plugin.version>3.5.6</maven-surefire-plugin.version>
|
||||
<maven-surefire-plugin.version>3.5.5</maven-surefire-plugin.version>
|
||||
<hibernate-validator.version>6.2.4.Final</hibernate-validator.version>
|
||||
<freemarker.version>2.3.35</freemarker.version>
|
||||
<freemarker.version>2.3.34</freemarker.version>
|
||||
|
||||
<!-- Site generation -->
|
||||
<fluido-skin.version>1.9</fluido-skin.version>
|
||||
@@ -224,7 +224,7 @@
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
<version>0.8.15</version>
|
||||
<version>0.8.14</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>prepare-agent</id>
|
||||
@@ -300,7 +300,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<version>3.11.0</version>
|
||||
<version>3.10.0</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
@@ -341,7 +341,7 @@
|
||||
<plugin>
|
||||
<groupId>org.owasp</groupId>
|
||||
<artifactId>dependency-check-maven</artifactId>
|
||||
<version>13.0.0</version>
|
||||
<version>12.2.0</version>
|
||||
<configuration>
|
||||
<suppressionFiles>
|
||||
<suppressionFile>src/etc/project-suppression.xml</suppressionFile>
|
||||
@@ -354,7 +354,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-enforcer-plugin</artifactId>
|
||||
<version>3.6.3</version>
|
||||
<version>3.6.2</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>enforce</id>
|
||||
@@ -407,7 +407,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-site-plugin</artifactId>
|
||||
<version>3.22.0</version>
|
||||
<version>3.21.0</version>
|
||||
<configuration>
|
||||
<relativizeDecorationLinks>false</relativizeDecorationLinks>
|
||||
</configuration>
|
||||
@@ -770,7 +770,7 @@
|
||||
<dependency>
|
||||
<groupId>org.easymock</groupId>
|
||||
<artifactId>easymock</artifactId>
|
||||
<version>5.7.0</version>
|
||||
<version>5.6.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -846,12 +846,12 @@
|
||||
<dependency>
|
||||
<groupId>commons-logging</groupId>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
<version>1.4.0</version>
|
||||
<version>1.3.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-collections4</artifactId>
|
||||
<version>4.6.0</version>
|
||||
<version>4.5.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-fileupload</groupId>
|
||||
@@ -892,7 +892,7 @@
|
||||
<dependency>
|
||||
<groupId>commons-validator</groupId>
|
||||
<artifactId>commons-validator</artifactId>
|
||||
<version>1.11.0</version>
|
||||
<version>1.10.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Mocks for unit testing (by Spring) -->
|
||||
|
||||
Reference in New Issue
Block a user