mirror of
https://github.com/apache/struts.git
synced 2026-08-05 14:47:09 +00:00
Update:
- Refactored the cache design to utilize a factory pattern. - Updated unit tests to match refactoring.
This commit is contained in:
@@ -28,6 +28,9 @@ import com.opensymphony.xwork2.conversion.*;
|
||||
import com.opensymphony.xwork2.conversion.impl.*;
|
||||
import com.opensymphony.xwork2.factory.*;
|
||||
import com.opensymphony.xwork2.inject.*;
|
||||
import com.opensymphony.xwork2.ognl.DefaultOgnlBeanInfoCacheFactory;
|
||||
import com.opensymphony.xwork2.ognl.DefaultOgnlExpressionCacheFactory;
|
||||
import com.opensymphony.xwork2.ognl.OgnlCacheFactory;
|
||||
import com.opensymphony.xwork2.ognl.OgnlReflectionProvider;
|
||||
import com.opensymphony.xwork2.ognl.OgnlUtil;
|
||||
import com.opensymphony.xwork2.ognl.OgnlValueStackFactory;
|
||||
@@ -283,6 +286,9 @@ public class DefaultConfiguration implements Configuration {
|
||||
|
||||
builder.factory(ValueSubstitutor.class, EnvsValueSubstitutor.class, Scope.SINGLETON);
|
||||
|
||||
builder.factory(OgnlCacheFactory.class, StrutsConstants.STRUTS_OGNL_EXPRESSIONCACHE_FACTORY, DefaultOgnlExpressionCacheFactory.class, Scope.SINGLETON);
|
||||
builder.factory(OgnlCacheFactory.class, StrutsConstants.STRUTS_OGNL_BEANINFOCACHE_FACTORY, DefaultOgnlBeanInfoCacheFactory.class, Scope.SINGLETON);
|
||||
|
||||
builder.constant(StrutsConstants.STRUTS_DEVMODE, "false");
|
||||
builder.constant(StrutsConstants.STRUTS_OGNL_LOG_MISSING_PROPERTIES, "false");
|
||||
builder.constant(StrutsConstants.STRUTS_OGNL_ENABLE_EVAL_EXPRESSION, "false");
|
||||
|
||||
+11
@@ -91,6 +91,9 @@ import com.opensymphony.xwork2.ognl.accessor.XWorkMapPropertyAccessor;
|
||||
import com.opensymphony.xwork2.ognl.accessor.XWorkMethodAccessor;
|
||||
import com.opensymphony.xwork2.util.CompoundRoot;
|
||||
import com.opensymphony.xwork2.LocalizedTextProvider;
|
||||
import com.opensymphony.xwork2.ognl.DefaultOgnlBeanInfoCacheFactory;
|
||||
import com.opensymphony.xwork2.ognl.DefaultOgnlExpressionCacheFactory;
|
||||
import com.opensymphony.xwork2.ognl.OgnlCacheFactory;
|
||||
import com.opensymphony.xwork2.util.StrutsLocalizedTextProvider;
|
||||
import com.opensymphony.xwork2.util.OgnlTextParser;
|
||||
import com.opensymphony.xwork2.util.PatternMatcher;
|
||||
@@ -127,19 +130,24 @@ import java.util.Set;
|
||||
|
||||
public class StrutsDefaultConfigurationProvider implements ConfigurationProvider {
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Configuration configuration) throws ConfigurationException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadPackages() throws ConfigurationException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsReload() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(ContainerBuilder builder, LocatableProperties props)
|
||||
throws ConfigurationException {
|
||||
|
||||
@@ -218,6 +226,9 @@ public class StrutsDefaultConfigurationProvider implements ConfigurationProvider
|
||||
, Scope.SINGLETON)
|
||||
|
||||
.factory(ValueSubstitutor.class, EnvsValueSubstitutor.class, Scope.SINGLETON)
|
||||
|
||||
.factory(OgnlCacheFactory.class, StrutsConstants.STRUTS_OGNL_EXPRESSIONCACHE_FACTORY, DefaultOgnlExpressionCacheFactory.class, Scope.SINGLETON)
|
||||
.factory(OgnlCacheFactory.class, StrutsConstants.STRUTS_OGNL_BEANINFOCACHE_FACTORY, DefaultOgnlBeanInfoCacheFactory.class, Scope.SINGLETON)
|
||||
;
|
||||
|
||||
props.setProperty(StrutsConstants.STRUTS_ENABLE_DYNAMIC_METHOD_INVOCATION, Boolean.FALSE.toString());
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2022 Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.ognl;
|
||||
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
|
||||
/**
|
||||
* Default OGNL Cache factory implementation.
|
||||
*
|
||||
* Currently used for BeanInfo cache creation.
|
||||
*
|
||||
* @param <Key> The type for the cache key entries
|
||||
* @param <Value> The type for the cache value entries
|
||||
*/
|
||||
public class DefaultOgnlBeanInfoCacheFactory<Key, Value> extends DefaultOgnlCacheFactory {
|
||||
|
||||
@Override
|
||||
@Inject(value = StrutsConstants.STRUTS_OGNL_BEANINFO_CACHE_MAXSIZE, required = false)
|
||||
protected void setCacheMaxSize(String maxSize) {
|
||||
super.setCacheMaxSize(maxSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Inject(value = StrutsConstants.STRUTS_OGNL_BEANINFO_CACHE_LRU_MODE, required = false)
|
||||
protected void setUseLRUCache(String useLRUMode) {
|
||||
super.setUseLRUCache(useLRUMode);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2022 Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.ognl;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
|
||||
/**
|
||||
* Default OGNL Cache factory implementation.
|
||||
*
|
||||
* Currently used for Expression cache and BeanInfo cache creation.
|
||||
*
|
||||
* @param <Key> The type for the cache key entries
|
||||
* @param <Value> The type for the cache value entries
|
||||
*/
|
||||
public class DefaultOgnlCacheFactory<Key, Value> implements OgnlCacheFactory {
|
||||
|
||||
private final AtomicBoolean useLRUCache = new AtomicBoolean(false);
|
||||
private final AtomicInteger cacheMaxSize = new AtomicInteger(25000);
|
||||
|
||||
@Override
|
||||
public OgnlCache<Key, Value> buildOgnlCache(int evictionLimit, int initialCapacity, float loadFactor, boolean lruCache) {
|
||||
if (lruCache) {
|
||||
return new OgnlLRUCache<>(evictionLimit, initialCapacity, loadFactor);
|
||||
} else {
|
||||
return new OgnlDefaultCache<>(evictionLimit, initialCapacity, loadFactor);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCacheMaxSize() {
|
||||
return cacheMaxSize.get();
|
||||
}
|
||||
|
||||
protected void setCacheMaxSize(String maxSize) {
|
||||
cacheMaxSize.set(Integer.parseInt(maxSize));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getUseLRUCache() {
|
||||
return useLRUCache.get();
|
||||
}
|
||||
|
||||
protected void setUseLRUCache(String useLRUMode) {
|
||||
useLRUCache.set(BooleanUtils.toBoolean(useLRUMode));
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2022 Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.ognl;
|
||||
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
|
||||
/**
|
||||
* Default OGNL Expression Cache factory implementation.
|
||||
*
|
||||
* Currently used for Expression cache creation.
|
||||
*
|
||||
* @param <Key> The type for the cache key entries
|
||||
* @param <Value> The type for the cache value entries
|
||||
*/
|
||||
public class DefaultOgnlExpressionCacheFactory<Key, Value> extends DefaultOgnlCacheFactory {
|
||||
|
||||
@Override
|
||||
@Inject(value = StrutsConstants.STRUTS_OGNL_EXPRESSION_CACHE_MAXSIZE, required = false)
|
||||
protected void setCacheMaxSize(String maxSize) {
|
||||
super.setCacheMaxSize(maxSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Inject(value = StrutsConstants.STRUTS_OGNL_EXPRESSION_CACHE_LRU_MODE, required = false)
|
||||
protected void setUseLRUCache(String useLRUMode) {
|
||||
super.setUseLRUCache(useLRUMode);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2022 Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.ognl;
|
||||
|
||||
/**
|
||||
* A basic cache interface for use with OGNL processing (such as Expression, BeanInfo).
|
||||
* All OGNL caches will have an eviction limit, but setting an extremely high value can
|
||||
* simulate an "effectively unlimited" cache.
|
||||
*
|
||||
* @param <Key> The type for the cache key entries
|
||||
* @param <Value> The type for the cache value entries
|
||||
*/
|
||||
public interface OgnlCache<Key, Value> {
|
||||
|
||||
public Value get(Key key);
|
||||
|
||||
public void put(Key key, Value value);
|
||||
|
||||
public void putIfAbsent(Key key, Value value);
|
||||
|
||||
public int size();
|
||||
|
||||
public void clear();
|
||||
|
||||
public int getEvictionLimit();
|
||||
|
||||
public void setEvictionLimit(int cacheEvictionLimit);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2022 Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.ognl;
|
||||
|
||||
/**
|
||||
* Used by {@link com.opensymphony.xwork2.ognl.OgnlUtil} to create appropriate OGNL
|
||||
* caches based on configuration.
|
||||
*
|
||||
* @param <Key> The type for the cache key entries
|
||||
* @param <Value> The type for the cache value entries
|
||||
*/
|
||||
public interface OgnlCacheFactory<Key, Value> {
|
||||
OgnlCache<Key, Value> buildOgnlCache(int evictionLimit, int initialCapacity, float loadFactor, boolean lruCache);
|
||||
int getCacheMaxSize();
|
||||
boolean getUseLRUCache();
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright 2022 Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.ognl;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* Default OGNL cache implementation.
|
||||
*
|
||||
* Setting a very high eviction limit simulates an unlimited cache.
|
||||
* Setting too low an eviction limit will make the cache ineffective.
|
||||
*
|
||||
* @param <Key> The type for the cache key entries
|
||||
* @param <Value> The type for the cache value entries
|
||||
*/
|
||||
public class OgnlDefaultCache<Key, Value> implements OgnlCache<Key, Value> {
|
||||
|
||||
private final ConcurrentHashMap<Key, Value> ognlCache;
|
||||
private final AtomicInteger cacheEvictionLimit = new AtomicInteger(25000);
|
||||
|
||||
public OgnlDefaultCache(int evictionLimit, int initialCapacity, float loadFactor) {
|
||||
this.cacheEvictionLimit.set(evictionLimit);
|
||||
ognlCache = new ConcurrentHashMap<>(initialCapacity, loadFactor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value get(Key key) {
|
||||
return ognlCache.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(Key key, Value value) {
|
||||
ognlCache.put(key, value);
|
||||
this.clearIfEvictionLimitExceeded();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putIfAbsent(Key key, Value value) {
|
||||
ognlCache.putIfAbsent(key, value);
|
||||
this.clearIfEvictionLimitExceeded();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return ognlCache.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
ognlCache.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEvictionLimit() {
|
||||
return this.cacheEvictionLimit.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEvictionLimit(int cacheEvictionLimit) {
|
||||
this.cacheEvictionLimit.set(cacheEvictionLimit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the cache if the eviction limit has been exceeded.
|
||||
*/
|
||||
private void clearIfEvictionLimitExceeded() {
|
||||
if (ognlCache.size() > cacheEvictionLimit.get()) {
|
||||
ognlCache.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2022 Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.ognl;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* A basic OGNL LRU cache implementation.
|
||||
*
|
||||
* The implementation utilizes a {@link Collections#synchronizedMap(java.util.Map)}
|
||||
* backed by a {@link LinkedHashMap}. May be replaced by a more efficient implementation in the future.
|
||||
*
|
||||
* Setting too low an eviction limit will produce more overhead than value.
|
||||
* Setting too high an eviction limit may also produce more overhead than value.
|
||||
* An appropriate eviction limit will need to be determined on an individual application basis.
|
||||
*
|
||||
* @param <Key> The type for the cache key entries
|
||||
* @param <Value> The type for the cache value entries
|
||||
*/
|
||||
public class OgnlLRUCache<Key, Value> implements OgnlCache<Key, Value> {
|
||||
|
||||
private final Map<Key, Value> ognlLRUCache;
|
||||
private final AtomicInteger cacheEvictionLimit = new AtomicInteger(2500);
|
||||
|
||||
public OgnlLRUCache(int evictionLimit, int initialCapacity, float loadFactor) {
|
||||
this.cacheEvictionLimit.set(evictionLimit);
|
||||
// Access-order mode selected (order mode true in LinkedHashMap constructor).
|
||||
ognlLRUCache = Collections.synchronizedMap (new LinkedHashMap<Key, Value>(initialCapacity, loadFactor, true) {
|
||||
@Override
|
||||
protected boolean removeEldestEntry(Map.Entry<Key,Value> eldest) {
|
||||
return (this.size() > cacheEvictionLimit.get());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value get(Key key) {
|
||||
return ognlLRUCache.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(Key key, Value value) {
|
||||
ognlLRUCache.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putIfAbsent(Key key, Value value) {
|
||||
ognlLRUCache.putIfAbsent(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return ognlLRUCache.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
ognlLRUCache.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEvictionLimit() {
|
||||
return this.cacheEvictionLimit.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEvictionLimit(int cacheEvictionLimit) {
|
||||
this.cacheEvictionLimit.set(cacheEvictionLimit);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -38,10 +38,7 @@ import java.beans.Introspector;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
||||
@@ -58,14 +55,10 @@ public class OgnlUtil {
|
||||
// Flag used to reduce flooding logs with WARNs about using DevMode excluded packages
|
||||
private final AtomicBoolean warnReported = new AtomicBoolean(false);
|
||||
|
||||
private final AtomicBoolean useLRUExpressionCache = new AtomicBoolean(false);
|
||||
private final AtomicBoolean useLRUBeanInfoCache = new AtomicBoolean(false);
|
||||
private final AtomicInteger expressionsCacheMaxSize = new AtomicInteger(25000);
|
||||
private final AtomicInteger beanInfoCacheMaxSize = new AtomicInteger(25000);
|
||||
private final ConcurrentMap<String, Object> expressionsCache = new ConcurrentHashMap<>();
|
||||
private final LRUCache<String, Object> expressionsCacheLRU = new LRUCache<>(expressionsCacheMaxSize.get(), 16, 0.75f);
|
||||
private final ConcurrentMap<Class<?>, BeanInfo> beanInfoCache = new ConcurrentHashMap<>();
|
||||
private final LRUCache<Class<?>, BeanInfo> beanInfoCacheLRU = new LRUCache<>(beanInfoCacheMaxSize.get(), 16, 0.75f);
|
||||
private final OgnlCacheFactory<String, Object> ognlExpressionCacheFactory;
|
||||
private final OgnlCacheFactory<Class<?>, BeanInfo> ognlBeanInfoCacheFactory;
|
||||
private final OgnlCache<String, Object> expressionCache;
|
||||
private final OgnlCache<Class<?>, BeanInfo> beanInfoCache;
|
||||
private TypeConverter defaultConverter;
|
||||
|
||||
private boolean devMode;
|
||||
@@ -85,7 +78,25 @@ public class OgnlUtil {
|
||||
private boolean allowStaticMethodAccess;
|
||||
private boolean disallowProxyMemberAccess;
|
||||
|
||||
/**
|
||||
* Construct a new OgnlUtil instance for use with the framework
|
||||
*/
|
||||
public OgnlUtil() {
|
||||
this(null, null); // Instantiate default Expression and BeanInfo caches (null factories)
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new OgnlUtil instance for use with the framework, with optional
|
||||
* cache factories for Ognl Expression and BeanInfo caches.
|
||||
*
|
||||
* @param ognlExpressionCacheFactory factory for Expression cache instance. If null, use default
|
||||
* @param ognlBeanInfoCacheFactory for BeanInfo cache instance. If null, use default
|
||||
*/
|
||||
@Inject
|
||||
public OgnlUtil(
|
||||
@Inject(value = StrutsConstants.STRUTS_OGNL_EXPRESSIONCACHE_FACTORY, required = false) OgnlCacheFactory<String, Object> ognlExpressionCacheFactory,
|
||||
@Inject(value = StrutsConstants.STRUTS_OGNL_BEANINFOCACHE_FACTORY, required = false) OgnlCacheFactory<Class<?>, BeanInfo> ognlBeanInfoCacheFactory
|
||||
) {
|
||||
excludedClasses = Collections.unmodifiableSet(new HashSet<>());
|
||||
excludedPackageNamePatterns = Collections.unmodifiableSet(new HashSet<>());
|
||||
excludedPackageNames = Collections.unmodifiableSet(new HashSet<>());
|
||||
@@ -93,6 +104,19 @@ public class OgnlUtil {
|
||||
devModeExcludedClasses = Collections.unmodifiableSet(new HashSet<>());
|
||||
devModeExcludedPackageNamePatterns = Collections.unmodifiableSet(new HashSet<>());
|
||||
devModeExcludedPackageNames = Collections.unmodifiableSet(new HashSet<>());
|
||||
this.ognlExpressionCacheFactory = ognlExpressionCacheFactory;
|
||||
this.ognlBeanInfoCacheFactory = ognlBeanInfoCacheFactory;
|
||||
|
||||
if (ognlExpressionCacheFactory != null) {
|
||||
this.expressionCache = ognlExpressionCacheFactory.buildOgnlCache(ognlExpressionCacheFactory.getCacheMaxSize(), 16, 0.75f, ognlExpressionCacheFactory.getUseLRUCache());
|
||||
} else {
|
||||
this.expressionCache = new OgnlDefaultCache<>(25000, 16, 0.75f);
|
||||
}
|
||||
if (ognlBeanInfoCacheFactory != null) {
|
||||
this.beanInfoCache = ognlBeanInfoCacheFactory.buildOgnlCache(ognlBeanInfoCacheFactory.getCacheMaxSize(), 16, 0.75f, ognlBeanInfoCacheFactory.getUseLRUCache());
|
||||
} else {
|
||||
this.beanInfoCache = new OgnlDefaultCache<>(25000, 16, 0.75f);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject
|
||||
@@ -111,25 +135,13 @@ public class OgnlUtil {
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_OGNL_EXPRESSION_CACHE_MAXSIZE, required = false)
|
||||
protected void setExpressionsCacheMaxSize(String maxSize) {
|
||||
expressionsCacheMaxSize.set(Integer.parseInt(maxSize));
|
||||
expressionsCacheLRU.setEvictionLimit(expressionsCacheMaxSize.get());
|
||||
protected void setExpressionCacheMaxSize(String maxSize) {
|
||||
expressionCache.setEvictionLimit(Integer.parseInt(maxSize));
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_OGNL_BEANINFO_CACHE_MAXSIZE, required = false)
|
||||
protected void setBeanInfoCacheMaxSize(String maxSize) {
|
||||
beanInfoCacheMaxSize.set(Integer.parseInt(maxSize));
|
||||
beanInfoCacheLRU.setEvictionLimit(beanInfoCacheMaxSize.get());
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_OGNL_EXPRESSION_CACHE_LRU_MODE, required = false)
|
||||
protected void setUseLRUExpressionCache(String useLRUMode) {
|
||||
useLRUExpressionCache.set(BooleanUtils.toBoolean(useLRUMode));
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_OGNL_BEANINFO_CACHE_LRU_MODE, required = false)
|
||||
protected void setUseLRUBeanInfoCache(String useLRUMode) {
|
||||
useLRUBeanInfoCache.set(BooleanUtils.toBoolean(useLRUMode));
|
||||
beanInfoCache.setEvictionLimit(Integer.parseInt(maxSize));
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_OGNL_ENABLE_EVAL_EXPRESSION, required = false)
|
||||
@@ -302,8 +314,7 @@ public class OgnlUtil {
|
||||
* @since 2.5.21
|
||||
*/
|
||||
public void clearExpressionCache() {
|
||||
expressionsCache.clear();
|
||||
expressionsCacheLRU.clear();
|
||||
expressionCache.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -314,11 +325,7 @@ public class OgnlUtil {
|
||||
* @since 2.5.21
|
||||
*/
|
||||
public int expressionCacheSize() {
|
||||
if (useLRUExpressionCache.get()) {
|
||||
return expressionsCacheLRU.size();
|
||||
} else {
|
||||
return expressionsCache.size();
|
||||
}
|
||||
return expressionCache.size();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -336,7 +343,6 @@ public class OgnlUtil {
|
||||
*/
|
||||
public void clearBeanInfoCache() {
|
||||
beanInfoCache.clear();
|
||||
beanInfoCacheLRU.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -347,11 +353,7 @@ public class OgnlUtil {
|
||||
* @since 2.5.21
|
||||
*/
|
||||
public int beanInfoCacheSize() {
|
||||
if (useLRUBeanInfoCache.get()) {
|
||||
return beanInfoCacheLRU.size();
|
||||
} else {
|
||||
return beanInfoCache.size();
|
||||
}
|
||||
return beanInfoCache.size();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -571,18 +573,11 @@ public class OgnlUtil {
|
||||
private <T> Object compileAndExecute(String expression, Map<String, Object> context, OgnlTask<T> task) throws OgnlException {
|
||||
Object tree;
|
||||
if (enableExpressionCache) {
|
||||
final Map<String, Object> expressionsCacheRef = (useLRUExpressionCache.get() ? expressionsCacheLRU.backingMapReference() : expressionsCache);
|
||||
tree = expressionsCacheRef.get(expression);
|
||||
tree = expressionCache.get(expression);
|
||||
if (tree == null) {
|
||||
tree = Ognl.parseExpression(expression);
|
||||
checkEnableEvalExpression(tree, context);
|
||||
expressionsCacheRef.putIfAbsent(expression, tree);
|
||||
if (! useLRUExpressionCache.get()) {
|
||||
// If using a standard cache, after attempting to add an expression check cache size against the limit.
|
||||
if (expressionsCacheRef.size() > expressionsCacheMaxSize.get()) {
|
||||
expressionsCacheRef.clear(); // Non-LRU cache has exceeded maximum configured size, so flush.
|
||||
}
|
||||
}
|
||||
expressionCache.putIfAbsent(expression, tree);
|
||||
}
|
||||
} else {
|
||||
tree = Ognl.parseExpression(expression);
|
||||
@@ -595,18 +590,11 @@ public class OgnlUtil {
|
||||
private <T> Object compileAndExecuteMethod(String expression, Map<String, Object> context, OgnlTask<T> task) throws OgnlException {
|
||||
Object tree;
|
||||
if (enableExpressionCache) {
|
||||
final Map<String, Object> expressionsCacheRef = (useLRUExpressionCache.get() ? expressionsCacheLRU.backingMapReference() : expressionsCache);
|
||||
tree = expressionsCacheRef.get(expression);
|
||||
tree = expressionCache.get(expression);
|
||||
if (tree == null) {
|
||||
tree = Ognl.parseExpression(expression);
|
||||
checkSimpleMethod(tree, context);
|
||||
expressionsCacheRef.putIfAbsent(expression, tree);
|
||||
if (! useLRUExpressionCache.get()) {
|
||||
// If using a standard cache, after attempting to add an expression check cache size against the limit.
|
||||
if (expressionsCacheRef.size() > expressionsCacheMaxSize.get()) {
|
||||
expressionsCacheRef.clear(); // Non-LRU cache has exceeded maximum configured size, so flush.
|
||||
}
|
||||
}
|
||||
expressionCache.putIfAbsent(expression, tree);
|
||||
}
|
||||
} else {
|
||||
tree = Ognl.parseExpression(expression);
|
||||
@@ -812,18 +800,11 @@ public class OgnlUtil {
|
||||
* @throws IntrospectionException is thrown if an exception occurs during introspection.
|
||||
*/
|
||||
public BeanInfo getBeanInfo(Class<?> clazz) throws IntrospectionException {
|
||||
final Map<Class<?>, BeanInfo> beanInfoCacheRef = (useLRUBeanInfoCache.get() ? beanInfoCacheLRU.backingMapReference() : beanInfoCache);
|
||||
synchronized (beanInfoCacheRef) {
|
||||
BeanInfo beanInfo = beanInfoCacheRef.get(clazz);
|
||||
synchronized (beanInfoCache) {
|
||||
BeanInfo beanInfo = beanInfoCache.get(clazz);
|
||||
if (beanInfo == null) {
|
||||
beanInfo = Introspector.getBeanInfo(clazz, Object.class);
|
||||
beanInfoCacheRef.putIfAbsent(clazz, beanInfo);
|
||||
if (! useLRUBeanInfoCache.get()) {
|
||||
// If using a standard cache, after attempting to add beanInfo check cache size against the limit.
|
||||
if (beanInfoCacheRef.size() > beanInfoCacheMaxSize.get()) {
|
||||
beanInfoCacheRef.clear(); // Non-LRU cache has exceeded maximum configured size, so flush.
|
||||
}
|
||||
}
|
||||
beanInfoCache.putIfAbsent(clazz, beanInfo);
|
||||
}
|
||||
return beanInfo;
|
||||
}
|
||||
@@ -882,66 +863,4 @@ public class OgnlUtil {
|
||||
T execute(Object tree) throws OgnlException;
|
||||
}
|
||||
|
||||
/**
|
||||
* A basic LRUCache implementation that utilizes a {@link Collections#synchronizedMap(java.util.Map)}
|
||||
* backed by a {@link LinkedHashMap}. May be replaced by a more efficient implementation in the future.
|
||||
*
|
||||
* @param <Key> Key type for the LRUCache
|
||||
* @param <Value> Value type for the LRUCache
|
||||
*/
|
||||
protected class LRUCache<Key, Value> {
|
||||
private final Map<Key, Value> lruCache;
|
||||
private final AtomicInteger cacheEvictionLimit = new AtomicInteger(25000);
|
||||
|
||||
public LRUCache(int evictionLimit, int initialCapacity, float loadFactor) {
|
||||
this.cacheEvictionLimit.set(evictionLimit);
|
||||
// Access-order mode selected (order mode true in LinkedHashMap constructor).
|
||||
lruCache = Collections.synchronizedMap (new LinkedHashMap<Key, Value>(initialCapacity, loadFactor, true) {
|
||||
@Override
|
||||
protected boolean removeEldestEntry(Map.Entry<Key,Value> eldest) {
|
||||
return (this.size() > cacheEvictionLimit.get());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Value get(Key key) {
|
||||
return lruCache.get(key);
|
||||
}
|
||||
|
||||
public void put(Key key, Value value) {
|
||||
lruCache.put(key, value);
|
||||
}
|
||||
|
||||
public void putIfAbsent(Key key, Value value) {
|
||||
lruCache.putIfAbsent(key, value);
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return lruCache.size();
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
lruCache.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Since the {@link LRUCache} is only intended to be used internally, we can cheat a bit
|
||||
* and allow access to the backing map directly via its reference. Allows it to be used
|
||||
* like the other {@link Map} caches within the module.
|
||||
*
|
||||
* @return The Map that backs the LRU cache.
|
||||
*/
|
||||
public Map<Key, Value> backingMapReference() {
|
||||
return lruCache;
|
||||
}
|
||||
|
||||
public int getEvictionLimit() {
|
||||
return this.cacheEvictionLimit.get();
|
||||
}
|
||||
|
||||
public void setEvictionLimit(int cacheEvictionLimit) {
|
||||
this.cacheEvictionLimit.set(cacheEvictionLimit);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,6 +249,18 @@ public final class StrutsConstants {
|
||||
/** Throw RuntimeException when a property is not found, or the evaluation of the expression fails */
|
||||
public static final String STRUTS_EL_THROW_EXCEPTION = "struts.el.throwExceptionOnFailure";
|
||||
|
||||
/**
|
||||
* Specifies an OGNL expression cache factory implementation. A default implementation is provided, but
|
||||
* could be replaced by a custom one if desired.
|
||||
*/
|
||||
public static final String STRUTS_OGNL_EXPRESSIONCACHE_FACTORY = "struts.ognl.expressionCacheFactory";
|
||||
|
||||
/**
|
||||
* Specifies an OGNL BeanInfo cache factory implementation. A default implementation is provided, but
|
||||
* could be replaced by a custom one if desired.
|
||||
*/
|
||||
public static final String STRUTS_OGNL_BEANINFOCACHE_FACTORY = "struts.ognl.beanInfoCacheFactory";
|
||||
|
||||
/**
|
||||
* Specifies a maximum number of cached BeanInfo used by OgnlUtility. Not specified/set by default. If
|
||||
* a positive integer is specified, it will set a limit whose behaviour depends on whether the
|
||||
|
||||
@@ -230,12 +230,12 @@ struts.ognl.enableExpressionCache=true
|
||||
### For expressionCacheLRUMode true, the limit will ensure the cache does not exceed
|
||||
### that size, dropping the oldest (least-recently-used) expressions to add new ones.
|
||||
### NOTE: If not set, the default is 25000, which may be excessive.
|
||||
#struts.ognl.expressionCacheMaxSize=1000
|
||||
# struts.ognl.expressionCacheMaxSize=1000
|
||||
|
||||
### Indicates if the OGNL expressionCache should use LRU mode.
|
||||
### NOTE: When true, make sure to set the expressionCacheMaxSize to a reasonable value
|
||||
### for your application. Otherwise the default limit will never (practically) be reached.
|
||||
#struts.ognl.expressionCacheLRUMode=false
|
||||
# struts.ognl.expressionCacheLRUMode=false
|
||||
|
||||
### Specify a limit to the number of entries in the OGNL beanInfoCache.
|
||||
### For the standard beanInfoCache mode, when the limit is exceeded the entire cache's
|
||||
@@ -243,12 +243,12 @@ struts.ognl.enableExpressionCache=true
|
||||
### For beanInfoCacheLRUMode true, the limit will ensure the cache does not exceed
|
||||
### that size, dropping the oldest (least-recently-used) expressions to add new ones.
|
||||
### NOTE: If not set, the default is 25000, which may be excessive.
|
||||
#struts.ognl.beanInfoCacheMaxSize=1000
|
||||
# struts.ognl.beanInfoCacheMaxSize=1000
|
||||
|
||||
### Indicates if the OGNL beanInfoCache should use LRU mode.
|
||||
### NOTE: When true, make sure to set the beanInfoCacheMaxSize to a reasonable value
|
||||
### for your application. Otherwise the default limit will never (practically) be reached.
|
||||
#struts.ognl.beanInfoCacheLRUMode=false
|
||||
### for your application. Otherwise the default limit will never (practically) be reached.
|
||||
# struts.ognl.beanInfoCacheLRUMode=false
|
||||
|
||||
### Indicates if Dispatcher should handle unexpected exceptions by calling sendError()
|
||||
### or simply rethrow it as a ServletException to allow future processing by other frameworks like Spring Security
|
||||
|
||||
@@ -228,6 +228,9 @@
|
||||
|
||||
<bean type="com.opensymphony.xwork2.config.providers.ValueSubstitutor" class="com.opensymphony.xwork2.config.providers.EnvsValueSubstitutor" scope="singleton"/>
|
||||
|
||||
<bean type="com.opensymphony.xwork2.ognl.OgnlCacheFactory" name="struts.ognl.expressionCacheFactory" class="com.opensymphony.xwork2.ognl.DefaultOgnlExpressionCacheFactory" scope="singleton"/>
|
||||
<bean type="com.opensymphony.xwork2.ognl.OgnlCacheFactory" name="struts.ognl.beanInfoCacheFactory" class="com.opensymphony.xwork2.ognl.DefaultOgnlBeanInfoCacheFactory" scope="singleton"/>
|
||||
|
||||
<package name="struts-default" abstract="true">
|
||||
<result-types>
|
||||
<result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/>
|
||||
|
||||
@@ -35,6 +35,7 @@ import com.opensymphony.xwork2.util.Owner;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
import com.opensymphony.xwork2.util.location.LocatableProperties;
|
||||
import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
|
||||
import java.beans.BeanInfo;
|
||||
import ognl.InappropriateExpressionException;
|
||||
import ognl.MethodFailedException;
|
||||
import ognl.NoSuchPropertyException;
|
||||
@@ -131,7 +132,7 @@ public class OgnlUtilTest extends XWorkTestCase {
|
||||
|
||||
public void testCacheEnabledMaxSize() throws OgnlException {
|
||||
ognlUtil.setEnableExpressionCache("true");
|
||||
ognlUtil.setExpressionsCacheMaxSize("1");
|
||||
ognlUtil.setExpressionCacheMaxSize("1");
|
||||
Object expr0 = ognlUtil.compile("test");
|
||||
Object expr2 = ognlUtil.compile("test");
|
||||
assertSame(expr0, expr2);
|
||||
@@ -153,22 +154,22 @@ public class OgnlUtilTest extends XWorkTestCase {
|
||||
assertSame(expr7, expr8);
|
||||
assertEquals("Expression LRU cache size should still be at its limit", 1, ognlUtil.expressionCacheSize());
|
||||
assertNotSame("1st test expression identical after ejection from LRU cache ?", expr5, expr0);
|
||||
ognlUtil.setUseLRUExpressionCache("false");
|
||||
}
|
||||
|
||||
public void testLRUCacheEnabled() throws OgnlException {
|
||||
// Force usage of LRU cache factories for the OgnlUtil instance
|
||||
this.ognlUtil = generateOgnlUtilInstanceWithDefaultLRUCacheFactories();
|
||||
ognlUtil.setEnableExpressionCache("true");
|
||||
ognlUtil.setUseLRUExpressionCache("true");
|
||||
Object expr0 = ognlUtil.compile("test");
|
||||
Object expr2 = ognlUtil.compile("test");
|
||||
assertSame(expr0, expr2);
|
||||
ognlUtil.setUseLRUExpressionCache("false");
|
||||
}
|
||||
|
||||
public void testLRUCacheEnabledMaxSize() throws OgnlException {
|
||||
// Force usage of LRU cache factories for the OgnlUtil instance
|
||||
this.ognlUtil = generateOgnlUtilInstanceWithDefaultLRUCacheFactories();
|
||||
ognlUtil.setEnableExpressionCache("true");
|
||||
ognlUtil.setUseLRUExpressionCache("true");
|
||||
ognlUtil.setExpressionsCacheMaxSize("1");
|
||||
ognlUtil.setExpressionCacheMaxSize("1");
|
||||
Object expr0 = ognlUtil.compile("test");
|
||||
Object expr2 = ognlUtil.compile("test");
|
||||
assertSame(expr0, expr2);
|
||||
@@ -182,7 +183,6 @@ public class OgnlUtilTest extends XWorkTestCase {
|
||||
assertSame(expr5, expr6);
|
||||
assertEquals("Expression LRU cache size should still be at its limit", 1, ognlUtil.expressionCacheSize());
|
||||
assertNotSame("1st test expression identical after ejection from LRU cache ?", expr5, expr0);
|
||||
ognlUtil.setUseLRUExpressionCache("false");
|
||||
}
|
||||
|
||||
public void testExpressionIsCachedIrrespectiveOfItsExecutionStatus() throws OgnlException {
|
||||
@@ -206,8 +206,10 @@ public class OgnlUtilTest extends XWorkTestCase {
|
||||
}
|
||||
|
||||
public void testExpressionIsLRUCachedIrrespectiveOfItsExecutionStatus() throws OgnlException {
|
||||
// Force usage of LRU cache factories for the OgnlUtil instance
|
||||
this.ognlUtil = generateOgnlUtilInstanceWithDefaultLRUCacheFactories();
|
||||
ognlUtil.setContainer(container); // Must be explicitly set as the generated OgnlUtil instance has no container
|
||||
ognlUtil.setEnableExpressionCache("true");
|
||||
ognlUtil.setUseLRUExpressionCache("true");
|
||||
Foo foo = new Foo();
|
||||
OgnlContext context = (OgnlContext) ognlUtil.createDefaultContext(foo);
|
||||
|
||||
@@ -225,7 +227,6 @@ public class OgnlUtilTest extends XWorkTestCase {
|
||||
} catch (Exception ex) {
|
||||
assertEquals("Expression with failed execution must have been cached nevertheless", ognlUtil.expressionCacheSize(), 2);
|
||||
}
|
||||
ognlUtil.setUseLRUExpressionCache("false");
|
||||
}
|
||||
|
||||
public void testMethodExpressionIsCachedIrrespectiveOfItsExecutionStatus() throws Exception {
|
||||
@@ -273,8 +274,9 @@ public class OgnlUtilTest extends XWorkTestCase {
|
||||
}
|
||||
|
||||
public void testClearExpressionLRUCache() throws OgnlException {
|
||||
// Force usage of LRU cache factories for the OgnlUtil instance
|
||||
this.ognlUtil = generateOgnlUtilInstanceWithDefaultLRUCacheFactories();
|
||||
ognlUtil.setEnableExpressionCache("true");
|
||||
ognlUtil.setUseLRUExpressionCache("true");
|
||||
// Test that the expression cache is functioning as expected.
|
||||
Object expr0 = ognlUtil.compile("test");
|
||||
Object expr1 = ognlUtil.compile("test");
|
||||
@@ -295,7 +297,6 @@ public class OgnlUtilTest extends XWorkTestCase {
|
||||
assertSame(expr3, expr4);
|
||||
assertSame(expr3, expr5);
|
||||
assertTrue("Expression cache empty after usage ?", ognlUtil.expressionCacheSize() > 0);
|
||||
ognlUtil.setUseLRUExpressionCache("false");
|
||||
}
|
||||
|
||||
public void testClearBeanInfoCache() throws IntrospectionException {
|
||||
@@ -369,7 +370,8 @@ public class OgnlUtilTest extends XWorkTestCase {
|
||||
}
|
||||
|
||||
public void testBeanInfoLRUCache() throws IntrospectionException {
|
||||
ognlUtil.setUseLRUBeanInfoCache("true");
|
||||
// Force usage of LRU cache factories for the OgnlUtil instance
|
||||
this.ognlUtil = generateOgnlUtilInstanceWithDefaultLRUCacheFactories();
|
||||
final TestBean1 testBean1 = new TestBean1();
|
||||
final TestBean2 testBean2 = new TestBean2();
|
||||
// Test that the BeanInfo cache is functioning as expected.
|
||||
@@ -388,11 +390,11 @@ public class OgnlUtilTest extends XWorkTestCase {
|
||||
// BeanInfo for TestBean1 and TestBean2 should always be different.
|
||||
assertNotSame(beanInfo1_1, beanInfo2_1);
|
||||
assertTrue("BeanInfo cache empty after usage ?", ognlUtil.beanInfoCacheSize() > 0);
|
||||
ognlUtil.setUseLRUBeanInfoCache("true");
|
||||
}
|
||||
|
||||
public void testBeanInfoLRUCacheLimits() throws IntrospectionException {
|
||||
ognlUtil.setUseLRUBeanInfoCache("true");
|
||||
// Force usage of LRU cache factories for the OgnlUtil instance
|
||||
this.ognlUtil = generateOgnlUtilInstanceWithDefaultLRUCacheFactories();
|
||||
ognlUtil.setBeanInfoCacheMaxSize("1");
|
||||
final TestBean1 testBean1 = new TestBean1();
|
||||
final TestBean2 testBean2 = new TestBean2();
|
||||
@@ -416,7 +418,6 @@ public class OgnlUtilTest extends XWorkTestCase {
|
||||
// LRU cache should not contain TestBean1 beaninfo anymore. A new entry should exist in the cache.
|
||||
Object beanInfo1_4 = ognlUtil.getBeanInfo(testBean1);
|
||||
assertNotSame("BeanInfo dropped from LRU cache is the same as newly added ?", beanInfo1_1, beanInfo1_4);
|
||||
ognlUtil.setUseLRUBeanInfoCache("false");
|
||||
ognlUtil.setBeanInfoCacheMaxSize(String.valueOf(Integer.MAX_VALUE));
|
||||
}
|
||||
|
||||
@@ -1312,6 +1313,20 @@ public class OgnlUtilTest extends XWorkTestCase {
|
||||
internalTestOgnlUtilExclusionsImmutable(basicOgnlUtil);
|
||||
}
|
||||
|
||||
public void testDefaultOgnlUtilExclusionsAlternateConstructor() {
|
||||
OgnlUtil basicOgnlUtil = new OgnlUtil(null, null);
|
||||
|
||||
internalTestInitialEmptyOgnlUtilExclusions(basicOgnlUtil);
|
||||
internalTestOgnlUtilExclusionsImmutable(basicOgnlUtil);
|
||||
}
|
||||
|
||||
public void testDefaultOgnlUtilExclusionsAlternateConstructorPopulated() {
|
||||
OgnlUtil basicOgnlUtil = new OgnlUtil(new DefaultOgnlExpressionCacheFactory<String, Object>(), new DefaultOgnlBeanInfoCacheFactory<Class<?>, BeanInfo>());
|
||||
|
||||
internalTestInitialEmptyOgnlUtilExclusions(basicOgnlUtil);
|
||||
internalTestOgnlUtilExclusionsImmutable(basicOgnlUtil);
|
||||
}
|
||||
|
||||
public void testOgnlUtilExcludedAdditivity() {
|
||||
Set<Class<?>> excludedClasses;
|
||||
Set<Pattern> excludedPackageNamePatterns;
|
||||
@@ -1675,6 +1690,34 @@ public class OgnlUtilTest extends XWorkTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
public void testGetExcludedPackageNamesAlternateConstructor() {
|
||||
// Getter should return an immutable collection
|
||||
OgnlUtil util = new OgnlUtil(null, null);
|
||||
util.setExcludedPackageNames("java.lang,java.awt");
|
||||
assertEquals(util.getExcludedPackageNames().size(), 2);
|
||||
try {
|
||||
util.getExcludedPackageNames().clear();
|
||||
} catch (Exception ex) {
|
||||
assertTrue(ex instanceof UnsupportedOperationException);
|
||||
} finally {
|
||||
assertEquals(util.getExcludedPackageNames().size(), 2);
|
||||
}
|
||||
}
|
||||
|
||||
public void testGetExcludedPackageNamesAlternateConstructorPopulated() {
|
||||
// Getter should return an immutable collection
|
||||
OgnlUtil util = new OgnlUtil(new DefaultOgnlExpressionCacheFactory<String, Object>(), new DefaultOgnlBeanInfoCacheFactory<Class<?>, BeanInfo>());
|
||||
util.setExcludedPackageNames("java.lang,java.awt");
|
||||
assertEquals(util.getExcludedPackageNames().size(), 2);
|
||||
try {
|
||||
util.getExcludedPackageNames().clear();
|
||||
} catch (Exception ex) {
|
||||
assertTrue(ex instanceof UnsupportedOperationException);
|
||||
} finally {
|
||||
assertEquals(util.getExcludedPackageNames().size(), 2);
|
||||
}
|
||||
}
|
||||
|
||||
public void testGetExcludedClasses() {
|
||||
// Getter should return an immutable collection
|
||||
OgnlUtil util = new OgnlUtil();
|
||||
@@ -1689,6 +1732,34 @@ public class OgnlUtilTest extends XWorkTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
public void testGetExcludedClassesAlternateConstructor() {
|
||||
// Getter should return an immutable collection
|
||||
OgnlUtil util = new OgnlUtil(null, null);
|
||||
util.setExcludedClasses("java.lang.Runtime,java.lang.ProcessBuilder,java.net.URL");
|
||||
assertEquals(util.getExcludedClasses().size(), 3);
|
||||
try {
|
||||
util.getExcludedClasses().clear();
|
||||
} catch (Exception ex) {
|
||||
assertTrue(ex instanceof UnsupportedOperationException);
|
||||
} finally {
|
||||
assertEquals(util.getExcludedClasses().size(), 3);
|
||||
}
|
||||
}
|
||||
|
||||
public void testGetExcludedClassesAlternateConstructorPopulated() {
|
||||
// Getter should return an immutable collection
|
||||
OgnlUtil util = new OgnlUtil(new DefaultOgnlExpressionCacheFactory<String, Object>(), new DefaultOgnlBeanInfoCacheFactory<Class<?>, BeanInfo>());
|
||||
util.setExcludedClasses("java.lang.Runtime,java.lang.ProcessBuilder,java.net.URL");
|
||||
assertEquals(util.getExcludedClasses().size(), 3);
|
||||
try {
|
||||
util.getExcludedClasses().clear();
|
||||
} catch (Exception ex) {
|
||||
assertTrue(ex instanceof UnsupportedOperationException);
|
||||
} finally {
|
||||
assertEquals(util.getExcludedClasses().size(), 3);
|
||||
}
|
||||
}
|
||||
|
||||
public void testGetExcludedPackageNamePatterns() {
|
||||
// Getter should return an immutable collection
|
||||
OgnlUtil util = new OgnlUtil();
|
||||
@@ -1703,10 +1774,64 @@ public class OgnlUtilTest extends XWorkTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
public void testGetExcludedPackageNamePatternsAlternateConstructor() {
|
||||
// Getter should return an immutable collection
|
||||
OgnlUtil util = new OgnlUtil(null, null);
|
||||
util.setExcludedPackageNamePatterns("java.lang.");
|
||||
assertEquals(util.getExcludedPackageNamePatterns().size(), 1);
|
||||
try {
|
||||
util.getExcludedPackageNamePatterns().clear();
|
||||
} catch (Exception ex) {
|
||||
assertTrue(ex instanceof UnsupportedOperationException);
|
||||
} finally {
|
||||
assertEquals(util.getExcludedPackageNamePatterns().size(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
public void testGetExcludedPackageNamePatternsAlternateConstructorPopulated() {
|
||||
// Getter should return an immutable collection
|
||||
OgnlUtil util = new OgnlUtil(new DefaultOgnlExpressionCacheFactory<String, Object>(), new DefaultOgnlBeanInfoCacheFactory<Class<?>, BeanInfo>());
|
||||
util.setExcludedPackageNamePatterns("java.lang.");
|
||||
assertEquals(util.getExcludedPackageNamePatterns().size(), 1);
|
||||
try {
|
||||
util.getExcludedPackageNamePatterns().clear();
|
||||
} catch (Exception ex) {
|
||||
assertTrue(ex instanceof UnsupportedOperationException);
|
||||
} finally {
|
||||
assertEquals(util.getExcludedPackageNamePatterns().size(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
public void testOgnlUtilDefaultCacheClass() throws OgnlException {
|
||||
OgnlDefaultCache<Integer, String> defaultCache = new OgnlDefaultCache<>(2, 16, 0.75f);
|
||||
assertEquals("Initial evictionLimit did not match initial value", 2, defaultCache.getEvictionLimit());
|
||||
defaultCache.setEvictionLimit(3);
|
||||
assertEquals("Updated evictionLimit did not match updated value", 3, defaultCache.getEvictionLimit());
|
||||
String lookupResult = defaultCache.get(Integer.valueOf(0));
|
||||
assertNull("Lookup of empty cache returned non-null value ?", lookupResult);
|
||||
defaultCache.put(Integer.valueOf(0), "Zero");
|
||||
lookupResult = defaultCache.get(Integer.valueOf(0));
|
||||
assertEquals("Retrieved value does not match put value ?", "Zero", lookupResult);
|
||||
defaultCache.put(Integer.valueOf(1), "One");
|
||||
defaultCache.put(Integer.valueOf(2), "Two");
|
||||
assertEquals("Default cache not size evictionlimit after adding three values ?", defaultCache.getEvictionLimit(), defaultCache.size());
|
||||
lookupResult = defaultCache.get(Integer.valueOf(2));
|
||||
assertEquals("Retrieved value does not match put value ?", "Two", lookupResult);
|
||||
defaultCache.put(Integer.valueOf(3), "Three");
|
||||
assertEquals("Default cache not size zero after an add that exceeded the evection limit ?", 0, defaultCache.size());
|
||||
lookupResult = defaultCache.get(Integer.valueOf(0));
|
||||
assertNull("Lookup of value 0 (should have been evicted with everything) returned non-null value ?", lookupResult);
|
||||
lookupResult = defaultCache.get(Integer.valueOf(3));
|
||||
assertNull("Lookup of value 3 (should have been evicted with everything) returned non-null value ?", lookupResult);
|
||||
defaultCache.putIfAbsent(Integer.valueOf(2), "Two");
|
||||
lookupResult = defaultCache.get(Integer.valueOf(2));
|
||||
assertEquals("Retrieved value does not match put value ?", "Two", lookupResult);
|
||||
defaultCache.clear();
|
||||
assertEquals("Default cache not empty after clear ?", 0, defaultCache.size());
|
||||
}
|
||||
|
||||
public void testOgnlUtilLRUCacheClass() throws OgnlException {
|
||||
OgnlUtil.LRUCache<Integer, String> lruCache = ognlUtil.new LRUCache<>(2, 16, 0.75f);
|
||||
Map<Integer, String> backingMap = lruCache.backingMapReference();
|
||||
assertNotNull("Backing Map somehow null ?", backingMap);
|
||||
OgnlLRUCache<Integer, String> lruCache = new OgnlLRUCache<>(2, 16, 0.75f);
|
||||
assertEquals("Initial evictionLimit did not match initial value", 2, lruCache.getEvictionLimit());
|
||||
lruCache.setEvictionLimit(3);
|
||||
assertEquals("Updated evictionLimit did not match updated value", 3, lruCache.getEvictionLimit());
|
||||
@@ -1727,10 +1852,24 @@ public class OgnlUtilTest extends XWorkTestCase {
|
||||
lruCache.putIfAbsent(Integer.valueOf(2), "Two");
|
||||
lookupResult = lruCache.get(Integer.valueOf(2));
|
||||
assertEquals("Retrieved value does not match put value ?", "Two", lookupResult);
|
||||
assertEquals("LRUCache and backing map size different after puts ?", lruCache.size(), backingMap.size());
|
||||
lruCache.clear();
|
||||
assertEquals("LRU cache not empty after clear ?", 0, lruCache.size());
|
||||
assertEquals("LRUCache and backing map size different after clear ?", lruCache.size(), backingMap.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a new OgnlUtil instance (not configured by the {@link ContainerBuilder}) that can be used for
|
||||
* basic tests, with its Expression and BeanInfo factories set to LRU mode.
|
||||
*
|
||||
* @return OgnlUtil instance with LRU enabled Expression and BeanInfo factories
|
||||
*/
|
||||
private OgnlUtil generateOgnlUtilInstanceWithDefaultLRUCacheFactories() {
|
||||
final OgnlUtil result;
|
||||
final DefaultOgnlCacheFactory expressionFactory = new DefaultOgnlExpressionCacheFactory<String, Object>();
|
||||
final DefaultOgnlCacheFactory beanInfoFactory = new DefaultOgnlBeanInfoCacheFactory<Class<?>, BeanInfo>();
|
||||
expressionFactory.setUseLRUCache("true");
|
||||
beanInfoFactory.setUseLRUCache("true");
|
||||
result = new OgnlUtil(expressionFactory, beanInfoFactory);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void reloadTestContainerConfiguration(boolean devMode, boolean allowStaticMethod) {
|
||||
|
||||
Reference in New Issue
Block a user