mirror of
https://github.com/apache/struts.git
synced 2026-08-06 23:27:07 +00:00
Merge pull request #528 from JCgH4164838Gh792C124B5/localS2_26_OgnlUtilOptionalCache1
Potential expression cache enhancement for 2.6 series
This commit is contained in:
@@ -28,6 +28,10 @@ import com.opensymphony.xwork2.conversion.*;
|
||||
import com.opensymphony.xwork2.conversion.impl.*;
|
||||
import com.opensymphony.xwork2.factory.*;
|
||||
import com.opensymphony.xwork2.inject.*;
|
||||
import com.opensymphony.xwork2.ognl.BeanInfoCacheFactory;
|
||||
import com.opensymphony.xwork2.ognl.DefaultOgnlBeanInfoCacheFactory;
|
||||
import com.opensymphony.xwork2.ognl.DefaultOgnlExpressionCacheFactory;
|
||||
import com.opensymphony.xwork2.ognl.ExpressionCacheFactory;
|
||||
import com.opensymphony.xwork2.ognl.OgnlReflectionProvider;
|
||||
import com.opensymphony.xwork2.ognl.OgnlUtil;
|
||||
import com.opensymphony.xwork2.ognl.OgnlValueStackFactory;
|
||||
@@ -79,30 +83,37 @@ public class DefaultConfiguration implements Configuration {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public PackageConfig getPackageConfig(String name) {
|
||||
return packageContexts.get(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UnknownHandlerConfig> getUnknownHandlerStack() {
|
||||
return unknownHandlerStack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUnknownHandlerStack(List<UnknownHandlerConfig> unknownHandlerStack) {
|
||||
this.unknownHandlerStack = unknownHandlerStack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getPackageConfigNames() {
|
||||
return packageContexts.keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, PackageConfig> getPackageConfigs() {
|
||||
return packageContexts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getLoadedFileNames() {
|
||||
return loadedFileNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RuntimeConfiguration getRuntimeConfiguration() {
|
||||
return runtimeConfiguration;
|
||||
}
|
||||
@@ -110,10 +121,12 @@ public class DefaultConfiguration implements Configuration {
|
||||
/**
|
||||
* @return the container
|
||||
*/
|
||||
@Override
|
||||
public Container getContainer() {
|
||||
return container;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPackageConfig(String name, PackageConfig packageContext) {
|
||||
PackageConfig check = packageContexts.get(name);
|
||||
if (check != null) {
|
||||
@@ -131,6 +144,7 @@ public class DefaultConfiguration implements Configuration {
|
||||
packageContexts.put(name, packageContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PackageConfig removePackageConfig(String packageName) {
|
||||
return packageContexts.remove(packageName);
|
||||
}
|
||||
@@ -138,11 +152,13 @@ public class DefaultConfiguration implements Configuration {
|
||||
/**
|
||||
* Allows the configuration to clean up any resources used
|
||||
*/
|
||||
@Override
|
||||
public void destroy() {
|
||||
packageContexts.clear();
|
||||
loadedFileNames.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rebuildRuntimeConfiguration() {
|
||||
runtimeConfiguration = buildRuntimeConfiguration();
|
||||
}
|
||||
@@ -151,10 +167,12 @@ public class DefaultConfiguration implements Configuration {
|
||||
* Calls the ConfigurationProviderFactory.getConfig() to tell it to reload the configuration and then calls
|
||||
* buildRuntimeConfiguration().
|
||||
*
|
||||
* @param providers list of ContainerProvider
|
||||
* @return list of package providers
|
||||
*
|
||||
* @throws ConfigurationException in case of any configuration errors
|
||||
*/
|
||||
@Override
|
||||
public synchronized List<PackageProvider> reloadContainer(List<ContainerProvider> providers) throws ConfigurationException {
|
||||
packageContexts.clear();
|
||||
loadedFileNames.clear();
|
||||
@@ -172,6 +190,7 @@ public class DefaultConfiguration implements Configuration {
|
||||
props.setConstants(builder);
|
||||
|
||||
builder.factory(Configuration.class, new Factory<Configuration>() {
|
||||
@Override
|
||||
public Configuration create(Context context) throws Exception {
|
||||
return DefaultConfiguration.this;
|
||||
}
|
||||
@@ -279,6 +298,8 @@ public class DefaultConfiguration implements Configuration {
|
||||
|
||||
builder.factory(ObjectTypeDeterminer.class, DefaultObjectTypeDeterminer.class, Scope.SINGLETON);
|
||||
builder.factory(PropertyAccessor.class, CompoundRoot.class.getName(), CompoundRootAccessor.class, Scope.SINGLETON);
|
||||
builder.factory(ExpressionCacheFactory.class, "defaultOgnlExpressionCacheFactory", DefaultOgnlExpressionCacheFactory.class, Scope.SINGLETON);
|
||||
builder.factory(BeanInfoCacheFactory.class, "defaultOgnlBeanInfoCacheFactory", DefaultOgnlBeanInfoCacheFactory.class, Scope.SINGLETON);
|
||||
builder.factory(OgnlUtil.class, Scope.SINGLETON);
|
||||
|
||||
builder.factory(ValueSubstitutor.class, EnvsValueSubstitutor.class, Scope.SINGLETON);
|
||||
@@ -417,10 +438,10 @@ public class DefaultConfiguration implements Configuration {
|
||||
|
||||
private static class RuntimeConfigurationImpl implements RuntimeConfiguration {
|
||||
|
||||
private Map<String, Map<String, ActionConfig>> namespaceActionConfigs;
|
||||
private Map<String, ActionConfigMatcher> namespaceActionConfigMatchers;
|
||||
private NamespaceMatcher namespaceMatcher;
|
||||
private Map<String, String> namespaceConfigs;
|
||||
private final Map<String, Map<String, ActionConfig>> namespaceActionConfigs;
|
||||
private final Map<String, ActionConfigMatcher> namespaceActionConfigMatchers;
|
||||
private final NamespaceMatcher namespaceMatcher;
|
||||
private final Map<String, String> namespaceConfigs;
|
||||
|
||||
public RuntimeConfigurationImpl(Map<String, Map<String, ActionConfig>> namespaceActionConfigs,
|
||||
Map<String, String> namespaceConfigs,
|
||||
@@ -448,6 +469,7 @@ public class DefaultConfiguration implements Configuration {
|
||||
* @param namespace the namespace for the action or null for the empty namespace, ""
|
||||
* @return the configuration information for action requested
|
||||
*/
|
||||
@Override
|
||||
public ActionConfig getActionConfig(String namespace, String name) {
|
||||
ActionConfig config = findActionConfigInNamespace(namespace, name);
|
||||
|
||||
@@ -503,6 +525,7 @@ public class DefaultConfiguration implements Configuration {
|
||||
*
|
||||
* @return a Map of namespace - > Map of ActionConfig objects, with the key being the action name
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Map<String, ActionConfig>> getActionConfigs() {
|
||||
return namespaceActionConfigs;
|
||||
}
|
||||
|
||||
+11
@@ -26,6 +26,8 @@ import com.opensymphony.xwork2.StrutsTextProviderFactory;
|
||||
import com.opensymphony.xwork2.TextProviderFactory;
|
||||
import com.opensymphony.xwork2.factory.DefaultUnknownHandlerFactory;
|
||||
import com.opensymphony.xwork2.factory.UnknownHandlerFactory;
|
||||
import com.opensymphony.xwork2.ognl.BeanInfoCacheFactory;
|
||||
import com.opensymphony.xwork2.ognl.ExpressionCacheFactory;
|
||||
import com.opensymphony.xwork2.ognl.accessor.HttpParametersPropertyAccessor;
|
||||
import com.opensymphony.xwork2.ognl.accessor.ParameterPropertyAccessor;
|
||||
import com.opensymphony.xwork2.security.AcceptedPatternsChecker;
|
||||
@@ -94,6 +96,8 @@ import com.opensymphony.xwork2.ognl.accessor.XWorkMapPropertyAccessor;
|
||||
import com.opensymphony.xwork2.ognl.accessor.XWorkMethodAccessor;
|
||||
import com.opensymphony.xwork2.util.CompoundRoot;
|
||||
import com.opensymphony.xwork2.LocalizedTextProvider;
|
||||
import com.opensymphony.xwork2.ognl.DefaultOgnlBeanInfoCacheFactory;
|
||||
import com.opensymphony.xwork2.ognl.DefaultOgnlExpressionCacheFactory;
|
||||
import com.opensymphony.xwork2.util.StrutsLocalizedTextProvider;
|
||||
import com.opensymphony.xwork2.util.OgnlTextParser;
|
||||
import com.opensymphony.xwork2.util.PatternMatcher;
|
||||
@@ -130,19 +134,24 @@ import java.util.Set;
|
||||
|
||||
public class StrutsDefaultConfigurationProvider implements ConfigurationProvider {
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Configuration configuration) throws ConfigurationException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadPackages() throws ConfigurationException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsReload() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(ContainerBuilder builder, LocatableProperties props)
|
||||
throws ConfigurationException {
|
||||
|
||||
@@ -208,6 +217,8 @@ public class StrutsDefaultConfigurationProvider implements ConfigurationProvider
|
||||
.factory(TextProviderFactory.class, StrutsTextProviderFactory.class, Scope.SINGLETON)
|
||||
.factory(LocaleProviderFactory.class, DefaultLocaleProviderFactory.class, Scope.SINGLETON)
|
||||
|
||||
.factory(ExpressionCacheFactory.class, "defaultOgnlExpressionCacheFactory", DefaultOgnlExpressionCacheFactory.class, Scope.SINGLETON)
|
||||
.factory(BeanInfoCacheFactory.class, "defaultOgnlBeanInfoCacheFactory", DefaultOgnlBeanInfoCacheFactory.class, Scope.SINGLETON)
|
||||
.factory(OgnlUtil.class, Scope.SINGLETON)
|
||||
.factory(CollectionConverter.class, Scope.SINGLETON)
|
||||
.factory(ArrayConverter.class, Scope.SINGLETON)
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2022 Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.ognl;
|
||||
|
||||
/**
|
||||
* A proxy interface to be used with Struts DI mechanism
|
||||
*/
|
||||
public interface BeanInfoCacheFactory<Key, Value> extends OgnlCacheFactory <Key, Value> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2022 Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.ognl;
|
||||
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
|
||||
/**
|
||||
* Default OGNL Cache factory implementation.
|
||||
*
|
||||
* Currently used for BeanInfo cache creation.
|
||||
*
|
||||
* @param <Key> The type for the cache key entries
|
||||
* @param <Value> The type for the cache value entries
|
||||
*/
|
||||
public class DefaultOgnlBeanInfoCacheFactory<Key, Value> extends DefaultOgnlCacheFactory<Key, Value>
|
||||
implements BeanInfoCacheFactory<Key, Value> {
|
||||
|
||||
@Override
|
||||
@Inject(value = StrutsConstants.STRUTS_OGNL_BEANINFO_CACHE_MAXSIZE, required = false)
|
||||
protected void setCacheMaxSize(String maxSize) {
|
||||
super.setCacheMaxSize(maxSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Inject(value = StrutsConstants.STRUTS_OGNL_BEANINFO_CACHE_LRU_MODE, required = false)
|
||||
protected void setUseLRUCache(String useLRUMode) {
|
||||
super.setUseLRUCache(useLRUMode);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2022 Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.ognl;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
|
||||
/**
|
||||
* Default OGNL Cache factory implementation.
|
||||
*
|
||||
* Currently used for Expression cache and BeanInfo cache creation.
|
||||
*
|
||||
* @param <Key> The type for the cache key entries
|
||||
* @param <Value> The type for the cache value entries
|
||||
*/
|
||||
public class DefaultOgnlCacheFactory<Key, Value> implements OgnlCacheFactory<Key, Value> {
|
||||
|
||||
private final AtomicBoolean useLRUCache = new AtomicBoolean(false);
|
||||
private final AtomicInteger cacheMaxSize = new AtomicInteger(25000);
|
||||
|
||||
@Override
|
||||
public OgnlCache<Key, Value> buildOgnlCache() {
|
||||
return buildOgnlCache(getCacheMaxSize(), 16, 0.75f, getUseLRUCache());
|
||||
}
|
||||
|
||||
@Override
|
||||
public OgnlCache<Key, Value> buildOgnlCache(int evictionLimit, int initialCapacity, float loadFactor, boolean lruCache) {
|
||||
if (lruCache) {
|
||||
return new OgnlLRUCache<>(evictionLimit, initialCapacity, loadFactor);
|
||||
} else {
|
||||
return new OgnlDefaultCache<>(evictionLimit, initialCapacity, loadFactor);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCacheMaxSize() {
|
||||
return cacheMaxSize.get();
|
||||
}
|
||||
|
||||
protected void setCacheMaxSize(String maxSize) {
|
||||
cacheMaxSize.set(Integer.parseInt(maxSize));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getUseLRUCache() {
|
||||
return useLRUCache.get();
|
||||
}
|
||||
|
||||
protected void setUseLRUCache(String useLRUMode) {
|
||||
useLRUCache.set(BooleanUtils.toBoolean(useLRUMode));
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2022 Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.ognl;
|
||||
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
|
||||
/**
|
||||
* Default OGNL Expression Cache factory implementation.
|
||||
*
|
||||
* Currently used for Expression cache creation.
|
||||
*
|
||||
* @param <Key> The type for the cache key entries
|
||||
* @param <Value> The type for the cache value entries
|
||||
*/
|
||||
public class DefaultOgnlExpressionCacheFactory<Key, Value> extends DefaultOgnlCacheFactory<Key, Value>
|
||||
implements ExpressionCacheFactory<Key, Value> {
|
||||
|
||||
@Override
|
||||
@Inject(value = StrutsConstants.STRUTS_OGNL_EXPRESSION_CACHE_MAXSIZE, required = false)
|
||||
protected void setCacheMaxSize(String maxSize) {
|
||||
super.setCacheMaxSize(maxSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Inject(value = StrutsConstants.STRUTS_OGNL_EXPRESSION_CACHE_LRU_MODE, required = false)
|
||||
protected void setUseLRUCache(String useLRUMode) {
|
||||
super.setUseLRUCache(useLRUMode);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2022 Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.ognl;
|
||||
|
||||
/**
|
||||
* A proxy interface to be used with Struts DI mechanism
|
||||
*/
|
||||
public interface ExpressionCacheFactory<Key, Value> extends OgnlCacheFactory <Key, Value> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2022 Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.ognl;
|
||||
|
||||
/**
|
||||
* A basic cache interface for use with OGNL processing (such as Expression, BeanInfo).
|
||||
* All OGNL caches will have an eviction limit, but setting an extremely high value can
|
||||
* simulate an "effectively unlimited" cache.
|
||||
*
|
||||
* @param <Key> The type for the cache key entries
|
||||
* @param <Value> The type for the cache value entries
|
||||
*/
|
||||
public interface OgnlCache<Key, Value> {
|
||||
|
||||
public Value get(Key key);
|
||||
|
||||
public void put(Key key, Value value);
|
||||
|
||||
public void putIfAbsent(Key key, Value value);
|
||||
|
||||
public int size();
|
||||
|
||||
public void clear();
|
||||
|
||||
public int getEvictionLimit();
|
||||
|
||||
public void setEvictionLimit(int cacheEvictionLimit);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2022 Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.ognl;
|
||||
|
||||
/**
|
||||
* Used by {@link com.opensymphony.xwork2.ognl.OgnlUtil} to create appropriate OGNL
|
||||
* caches based on configuration.
|
||||
*
|
||||
* @param <Key> The type for the cache key entries
|
||||
* @param <Value> The type for the cache value entries
|
||||
*/
|
||||
interface OgnlCacheFactory<Key, Value> {
|
||||
OgnlCache<Key, Value> buildOgnlCache();
|
||||
OgnlCache<Key, Value> buildOgnlCache(int evictionLimit, int initialCapacity, float loadFactor, boolean lruCache);
|
||||
int getCacheMaxSize();
|
||||
boolean getUseLRUCache();
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright 2022 Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.ognl;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* Default OGNL cache implementation.
|
||||
*
|
||||
* Setting a very high eviction limit simulates an unlimited cache.
|
||||
* Setting too low an eviction limit will make the cache ineffective.
|
||||
*
|
||||
* @param <Key> The type for the cache key entries
|
||||
* @param <Value> The type for the cache value entries
|
||||
*/
|
||||
public class OgnlDefaultCache<Key, Value> implements OgnlCache<Key, Value> {
|
||||
|
||||
private final ConcurrentHashMap<Key, Value> ognlCache;
|
||||
private final AtomicInteger cacheEvictionLimit = new AtomicInteger(25000);
|
||||
|
||||
public OgnlDefaultCache(int evictionLimit, int initialCapacity, float loadFactor) {
|
||||
this.cacheEvictionLimit.set(evictionLimit);
|
||||
ognlCache = new ConcurrentHashMap<>(initialCapacity, loadFactor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value get(Key key) {
|
||||
return ognlCache.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(Key key, Value value) {
|
||||
ognlCache.put(key, value);
|
||||
this.clearIfEvictionLimitExceeded();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putIfAbsent(Key key, Value value) {
|
||||
ognlCache.putIfAbsent(key, value);
|
||||
this.clearIfEvictionLimitExceeded();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return ognlCache.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
ognlCache.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEvictionLimit() {
|
||||
return this.cacheEvictionLimit.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEvictionLimit(int cacheEvictionLimit) {
|
||||
this.cacheEvictionLimit.set(cacheEvictionLimit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the cache if the eviction limit has been exceeded.
|
||||
*/
|
||||
private void clearIfEvictionLimitExceeded() {
|
||||
if (ognlCache.size() > cacheEvictionLimit.get()) {
|
||||
ognlCache.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2022 Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.ognl;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* A basic OGNL LRU cache implementation.
|
||||
*
|
||||
* The implementation utilizes a {@link Collections#synchronizedMap(java.util.Map)}
|
||||
* backed by a {@link LinkedHashMap}. May be replaced by a more efficient implementation in the future.
|
||||
*
|
||||
* Setting too low an eviction limit will produce more overhead than value.
|
||||
* Setting too high an eviction limit may also produce more overhead than value.
|
||||
* An appropriate eviction limit will need to be determined on an individual application basis.
|
||||
*
|
||||
* @param <Key> The type for the cache key entries
|
||||
* @param <Value> The type for the cache value entries
|
||||
*/
|
||||
public class OgnlLRUCache<Key, Value> implements OgnlCache<Key, Value> {
|
||||
|
||||
private final Map<Key, Value> ognlLRUCache;
|
||||
private final AtomicInteger cacheEvictionLimit = new AtomicInteger(2500);
|
||||
|
||||
public OgnlLRUCache(int evictionLimit, int initialCapacity, float loadFactor) {
|
||||
this.cacheEvictionLimit.set(evictionLimit);
|
||||
// Access-order mode selected (order mode true in LinkedHashMap constructor).
|
||||
ognlLRUCache = Collections.synchronizedMap (new LinkedHashMap<Key, Value>(initialCapacity, loadFactor, true) {
|
||||
@Override
|
||||
protected boolean removeEldestEntry(Map.Entry<Key,Value> eldest) {
|
||||
return (this.size() > cacheEvictionLimit.get());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value get(Key key) {
|
||||
return ognlLRUCache.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(Key key, Value value) {
|
||||
ognlLRUCache.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putIfAbsent(Key key, Value value) {
|
||||
ognlLRUCache.putIfAbsent(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return ognlLRUCache.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
ognlLRUCache.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEvictionLimit() {
|
||||
return this.cacheEvictionLimit.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEvictionLimit(int cacheEvictionLimit) {
|
||||
this.cacheEvictionLimit.set(cacheEvictionLimit);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -38,8 +38,6 @@ import java.beans.Introspector;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -57,8 +55,8 @@ public class OgnlUtil {
|
||||
// Flag used to reduce flooding logs with WARNs about using DevMode excluded packages
|
||||
private final AtomicBoolean warnReported = new AtomicBoolean(false);
|
||||
|
||||
private final ConcurrentMap<String, Object> expressions = new ConcurrentHashMap<>();
|
||||
private final ConcurrentMap<Class<?>, BeanInfo> beanInfoCache = new ConcurrentHashMap<>();
|
||||
private final OgnlCache<String, Object> expressionCache;
|
||||
private final OgnlCache<Class<?>, BeanInfo> beanInfoCache;
|
||||
private TypeConverter defaultConverter;
|
||||
|
||||
private boolean devMode;
|
||||
@@ -77,7 +75,31 @@ public class OgnlUtil {
|
||||
private boolean allowStaticFieldAccess = true;
|
||||
private boolean disallowProxyMemberAccess;
|
||||
|
||||
/**
|
||||
* Construct a new OgnlUtil instance for use with the framework
|
||||
*
|
||||
* @deprecated It is recommended to utilize the {@link OgnlUtil#OgnlUtil(com.opensymphony.xwork2.ognl.ExpressionCacheFactory, com.opensymphony.xwork2.ognl.BeanInfoCacheFactory) method instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public OgnlUtil() {
|
||||
this(null, null); // Instantiate default Expression and BeanInfo caches (null factories)
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new OgnlUtil instance for use with the framework, with optional
|
||||
* cache factories for OGNL Expression and BeanInfo caches.
|
||||
*
|
||||
* NOTE: Although the extension points are defined for the optional cache factories, developer-defined overrides do
|
||||
* do not appear to function at this time (it always appears to instantiate the default factories).
|
||||
* Construction injectors do not allow the optional flag, so the definitions must be defined.
|
||||
*
|
||||
* @param ognlExpressionCacheFactory factory for Expression cache instance. If null, it uses a default
|
||||
* @param ognlBeanInfoCacheFactory factory for BeanInfo cache instance. If null, it uses a default
|
||||
*/
|
||||
public OgnlUtil(
|
||||
@Inject(value = StrutsConstants.STRUTS_OGNL_EXPRESSION_CACHE_FACTORY, required = false) ExpressionCacheFactory<String, Object> ognlExpressionCacheFactory,
|
||||
@Inject(value = StrutsConstants.STRUTS_OGNL_BEANINFO_CACHE_FACTORY, required = false) BeanInfoCacheFactory<Class<?>, BeanInfo> ognlBeanInfoCacheFactory
|
||||
) {
|
||||
excludedClasses = Collections.unmodifiableSet(new HashSet<>());
|
||||
excludedPackageNamePatterns = Collections.unmodifiableSet(new HashSet<>());
|
||||
excludedPackageNames = Collections.unmodifiableSet(new HashSet<>());
|
||||
@@ -85,6 +107,12 @@ public class OgnlUtil {
|
||||
devModeExcludedClasses = Collections.unmodifiableSet(new HashSet<>());
|
||||
devModeExcludedPackageNamePatterns = Collections.unmodifiableSet(new HashSet<>());
|
||||
devModeExcludedPackageNames = Collections.unmodifiableSet(new HashSet<>());
|
||||
|
||||
OgnlCacheFactory<String, Object> ognlExpressionCacheFactory1 = (ognlExpressionCacheFactory != null ? ognlExpressionCacheFactory : new DefaultOgnlExpressionCacheFactory<>());
|
||||
OgnlCacheFactory<Class<?>, BeanInfo> ognlBeanInfoCacheFactory1 = (ognlBeanInfoCacheFactory != null ? ognlBeanInfoCacheFactory : new DefaultOgnlBeanInfoCacheFactory<>());
|
||||
|
||||
this.expressionCache = ognlExpressionCacheFactory1.buildOgnlCache();
|
||||
this.beanInfoCache = ognlBeanInfoCacheFactory1.buildOgnlCache();
|
||||
}
|
||||
|
||||
@Inject
|
||||
@@ -102,6 +130,16 @@ public class OgnlUtil {
|
||||
enableExpressionCache = BooleanUtils.toBoolean(cache);
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_OGNL_EXPRESSION_CACHE_MAXSIZE, required = false)
|
||||
protected void setExpressionCacheMaxSize(String maxSize) {
|
||||
expressionCache.setEvictionLimit(Integer.parseInt(maxSize));
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_OGNL_BEANINFO_CACHE_MAXSIZE, required = false)
|
||||
protected void setBeanInfoCacheMaxSize(String maxSize) {
|
||||
beanInfoCache.setEvictionLimit(Integer.parseInt(maxSize));
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_OGNL_ENABLE_EVAL_EXPRESSION, required = false)
|
||||
protected void setEnableEvalExpression(String evalExpression) {
|
||||
this.enableEvalExpression = BooleanUtils.toBoolean(evalExpression);
|
||||
@@ -267,7 +305,7 @@ public class OgnlUtil {
|
||||
* @since 2.5.21
|
||||
*/
|
||||
public void clearExpressionCache() {
|
||||
expressions.clear();
|
||||
expressionCache.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -278,7 +316,7 @@ public class OgnlUtil {
|
||||
* @since 2.5.21
|
||||
*/
|
||||
public int expressionCacheSize() {
|
||||
return expressions.size();
|
||||
return expressionCache.size();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -526,11 +564,11 @@ public class OgnlUtil {
|
||||
private <T> Object compileAndExecute(String expression, Map<String, Object> context, OgnlTask<T> task) throws OgnlException {
|
||||
Object tree;
|
||||
if (enableExpressionCache) {
|
||||
tree = expressions.get(expression);
|
||||
tree = expressionCache.get(expression);
|
||||
if (tree == null) {
|
||||
tree = Ognl.parseExpression(expression);
|
||||
checkEnableEvalExpression(tree, context);
|
||||
expressions.putIfAbsent(expression, tree);
|
||||
expressionCache.putIfAbsent(expression, tree);
|
||||
}
|
||||
} else {
|
||||
tree = Ognl.parseExpression(expression);
|
||||
@@ -543,11 +581,11 @@ public class OgnlUtil {
|
||||
private <T> Object compileAndExecuteMethod(String expression, Map<String, Object> context, OgnlTask<T> task) throws OgnlException {
|
||||
Object tree;
|
||||
if (enableExpressionCache) {
|
||||
tree = expressions.get(expression);
|
||||
tree = expressionCache.get(expression);
|
||||
if (tree == null) {
|
||||
tree = Ognl.parseExpression(expression);
|
||||
checkSimpleMethod(tree, context);
|
||||
expressions.putIfAbsent(expression, tree);
|
||||
expressionCache.putIfAbsent(expression, tree);
|
||||
}
|
||||
} else {
|
||||
tree = Ognl.parseExpression(expression);
|
||||
|
||||
@@ -250,6 +250,47 @@ public final class StrutsConstants {
|
||||
/** Throw RuntimeException when a property is not found, or the evaluation of the expression fails */
|
||||
public static final String STRUTS_EL_THROW_EXCEPTION = "struts.el.throwExceptionOnFailure";
|
||||
|
||||
/**
|
||||
* Specifies an OGNL expression cache factory implementation. A default implementation is provided, but
|
||||
* could be replaced by a custom one if desired.
|
||||
*
|
||||
* @since 2.6
|
||||
*/
|
||||
public static final String STRUTS_OGNL_EXPRESSION_CACHE_FACTORY = "struts.ognl.expressionCacheFactory";
|
||||
|
||||
/**
|
||||
* Specifies an OGNL BeanInfo cache factory implementation. A default implementation is provided, but
|
||||
* could be replaced by a custom one if desired.
|
||||
*
|
||||
* @since 2.6
|
||||
*/
|
||||
public static final String STRUTS_OGNL_BEANINFO_CACHE_FACTORY = "struts.ognl.beanInfoCacheFactory";
|
||||
|
||||
/**
|
||||
* Specifies a maximum number of cached BeanInfo used by OgnlUtility. Not specified/set by default. If
|
||||
* a positive integer is specified, it will set a limit whose behaviour depends on whether the
|
||||
* normal (default) cache or optional LRU cache is in place.
|
||||
*
|
||||
* For the normal (default) cache, exceeding the maximum will cause the entire cache to flush (clear).
|
||||
* For the optional LRU cache, once the maximum is reached, the least-recently-used (LRU) entry will be
|
||||
* removed when a new entry needs to be added (cache is fully-utilized).
|
||||
*
|
||||
* @since 2.6
|
||||
*/
|
||||
public static final String STRUTS_OGNL_BEANINFO_CACHE_MAXSIZE = "struts.ognl.beanInfoCacheMaxSize";
|
||||
|
||||
/**
|
||||
* Set the cache mode of the BeanInfo cache used by OgnlUtility. A value of true means enable
|
||||
* least-recently-used (LRU) mode, a value of false (or any non-true value) means to use the
|
||||
* default cache.
|
||||
*
|
||||
* Note: When enabling LRU cache mode you must also set a maximum size (via {@link #STRUTS_OGNL_BEANINFO_CACHE_MAXSIZE})
|
||||
* for it to be effective. Otherwise, there is no condition to evict a LRU entry (cache has no limit).
|
||||
*
|
||||
* @since 2.6
|
||||
*/
|
||||
public static final String STRUTS_OGNL_BEANINFO_CACHE_LRU_MODE = "struts.ognl.beanInfoCacheLRUMode";
|
||||
|
||||
/**
|
||||
* Logs properties that are not found (very verbose)
|
||||
* @since 2.6
|
||||
@@ -275,6 +316,31 @@ public final class StrutsConstants {
|
||||
*/
|
||||
public static final String STRUTS_ENABLE_OGNL_EXPRESSION_CACHE = STRUTS_OGNL_ENABLE_EXPRESSION_CACHE;
|
||||
|
||||
/**
|
||||
* Specifies a maximum number of cached parsed OGNL expressions. Not specified/set by default. If
|
||||
* a positive integer is specified, it will set a limit whose behaviour depends on whether the
|
||||
* normal (default) cache or optional LRU cache is in place.
|
||||
*
|
||||
* For the normal (default) cache, exceeding the maximum will cause the entire cache to flush (clear).
|
||||
* For the optional LRU cache, once the maximum is reached, the least-recently-used (LRU) entry will be
|
||||
* removed when a new entry needs to be added (cache is fully-utilized).
|
||||
*
|
||||
* @since 2.6
|
||||
*/
|
||||
public static final String STRUTS_OGNL_EXPRESSION_CACHE_MAXSIZE = "struts.ognl.expressionCacheMaxSize";
|
||||
|
||||
/**
|
||||
* Set the cache mode of the parsed OGNL expression cache. A value of true means enable
|
||||
* least-recently-used (LRU) mode, a value of false (or any non-true value) means to use the
|
||||
* default cache.
|
||||
*
|
||||
* Note: When enabling LRU cache mode you must also set a maximum size (via {@link #STRUTS_OGNL_EXPRESSION_CACHE_MAXSIZE})
|
||||
* for it to be effective. Otherwise, there is no condition to evict a LRU entry (cache has no limit).
|
||||
*
|
||||
* @since 2.6
|
||||
*/
|
||||
public static final String STRUTS_OGNL_EXPRESSION_CACHE_LRU_MODE = "struts.ognl.expressionCacheLRUMode";
|
||||
|
||||
/**
|
||||
* Enables evaluation of OGNL expressions
|
||||
* @since 2.6
|
||||
|
||||
@@ -23,6 +23,8 @@ import com.opensymphony.xwork2.LocaleProviderFactory;
|
||||
import com.opensymphony.xwork2.LocalizedTextProvider;
|
||||
import com.opensymphony.xwork2.TextProviderFactory;
|
||||
import com.opensymphony.xwork2.factory.UnknownHandlerFactory;
|
||||
import com.opensymphony.xwork2.ognl.BeanInfoCacheFactory;
|
||||
import com.opensymphony.xwork2.ognl.ExpressionCacheFactory;
|
||||
import com.opensymphony.xwork2.security.AcceptedPatternsChecker;
|
||||
import com.opensymphony.xwork2.security.ExcludedPatternsChecker;
|
||||
import com.opensymphony.xwork2.FileManager;
|
||||
@@ -366,6 +368,7 @@ import org.apache.struts2.views.util.UrlHelper;
|
||||
*/
|
||||
public class StrutsBeanSelectionProvider extends AbstractBeanSelectionProvider {
|
||||
|
||||
@Override
|
||||
public void register(ContainerBuilder builder, LocatableProperties props) {
|
||||
alias(ObjectFactory.class, StrutsConstants.STRUTS_OBJECTFACTORY, builder, props);
|
||||
alias(ActionFactory.class, StrutsConstants.STRUTS_OBJECTFACTORY_ACTIONFACTORY, builder, props);
|
||||
@@ -423,6 +426,9 @@ public class StrutsBeanSelectionProvider extends AbstractBeanSelectionProvider {
|
||||
|
||||
alias(DateFormatter.class, StrutsConstants.STRUTS_DATE_FORMATTER, builder, props, Scope.SINGLETON);
|
||||
|
||||
alias(ExpressionCacheFactory.class, StrutsConstants.STRUTS_OGNL_EXPRESSION_CACHE_FACTORY, builder, props, Scope.SINGLETON);
|
||||
alias(BeanInfoCacheFactory.class, StrutsConstants.STRUTS_OGNL_BEANINFO_CACHE_FACTORY, builder, props, Scope.SINGLETON);
|
||||
|
||||
switchDevMode(props);
|
||||
}
|
||||
|
||||
|
||||
@@ -143,6 +143,8 @@ public class ConstantConfig {
|
||||
private Boolean disallowProxyMemberAccess;
|
||||
private Integer ognlAutoGrowthCollectionLimit;
|
||||
private String staticContentPath;
|
||||
private BeanConfig expressionCacheFactory;
|
||||
private BeanConfig beaninfoCacheFactory;
|
||||
|
||||
protected String beanConfToString(BeanConfig beanConf) {
|
||||
return beanConf == null ? null : beanConf.getName();
|
||||
@@ -272,6 +274,8 @@ public class ConstantConfig {
|
||||
map.put(StrutsConstants.STRUTS_DISALLOW_PROXY_MEMBER_ACCESS, Objects.toString(disallowProxyMemberAccess, null));
|
||||
map.put(StrutsConstants.STRUTS_OGNL_AUTO_GROWTH_COLLECTION_LIMIT, Objects.toString(ognlAutoGrowthCollectionLimit, null));
|
||||
map.put(StrutsConstants.STRUTS_UI_STATIC_CONTENT_PATH, Objects.toString(staticContentPath, StaticContentLoader.DEFAULT_STATIC_CONTENT_PATH));
|
||||
map.put(StrutsConstants.STRUTS_OGNL_EXPRESSION_CACHE_FACTORY, beanConfToString(expressionCacheFactory));
|
||||
map.put(StrutsConstants.STRUTS_OGNL_BEANINFO_CACHE_FACTORY, beanConfToString(beaninfoCacheFactory));
|
||||
|
||||
return map;
|
||||
}
|
||||
@@ -1331,4 +1335,28 @@ public class ConstantConfig {
|
||||
public void setStaticContentPath(String staticContentPath) {
|
||||
this.staticContentPath = StaticContentLoader.Validator.validateStaticContentPath(staticContentPath);
|
||||
}
|
||||
|
||||
public BeanConfig getExpressionCacheFactory() {
|
||||
return expressionCacheFactory;
|
||||
}
|
||||
|
||||
public void setExpressionCacheFactory(BeanConfig expressionCacheFactory) {
|
||||
this.expressionCacheFactory = expressionCacheFactory;
|
||||
}
|
||||
|
||||
public void setExpressionCacheFactory(Class<?> clazz) {
|
||||
this.expressionCacheFactory = new BeanConfig(clazz, clazz.getName());
|
||||
}
|
||||
|
||||
public BeanConfig getBeaninfoCacheFactory() {
|
||||
return beaninfoCacheFactory;
|
||||
}
|
||||
|
||||
public void setBeaninfoCacheFactory(BeanConfig beaninfoCacheFactory) {
|
||||
this.beaninfoCacheFactory = beaninfoCacheFactory;
|
||||
}
|
||||
|
||||
public void setBeaninfoCacheFactory(Class<?> clazz) {
|
||||
this.beaninfoCacheFactory = new BeanConfig(clazz, clazz.getName());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,6 +229,37 @@ struts.ognl.logMissingProperties=false
|
||||
### if the application generates a lot of different expressions
|
||||
struts.ognl.enableExpressionCache=true
|
||||
|
||||
### Specify the OGNL expression cache factory and BeanInfo cache factory to use.
|
||||
### Currently, the default implementations are used, but can be replaced with custom ones if desired.
|
||||
struts.ognl.expressionCacheFactory=defaultOgnlExpressionCacheFactory
|
||||
struts.ognl.beanInfoCacheFactory=defaultOgnlBeanInfoCacheFactory
|
||||
|
||||
### Specify a limit to the number of entries in the OGNL expressionCache.
|
||||
### For the standard expressionCache mode, when the limit is exceeded the entire cache's
|
||||
### content will be cleared (can help prevent memory leaks).
|
||||
### For expressionCacheLRUMode true, the limit will ensure the cache does not exceed
|
||||
### that size, dropping the oldest (least-recently-used) expressions to add new ones.
|
||||
### NOTE: If not set, the default is 25000, which may be excessive.
|
||||
# struts.ognl.expressionCacheMaxSize=1000
|
||||
|
||||
### Indicates if the OGNL expressionCache should use LRU mode.
|
||||
### NOTE: When true, make sure to set the expressionCacheMaxSize to a reasonable value
|
||||
### for your application. Otherwise the default limit will never (practically) be reached.
|
||||
# struts.ognl.expressionCacheLRUMode=false
|
||||
|
||||
### Specify a limit to the number of entries in the OGNL beanInfoCache.
|
||||
### For the standard beanInfoCache mode, when the limit is exceeded the entire cache's
|
||||
### content will be cleared (can help prevent memory leaks).
|
||||
### For beanInfoCacheLRUMode true, the limit will ensure the cache does not exceed
|
||||
### that size, dropping the oldest (least-recently-used) expressions to add new ones.
|
||||
### NOTE: If not set, the default is 25000, which may be excessive.
|
||||
# struts.ognl.beanInfoCacheMaxSize=1000
|
||||
|
||||
### Indicates if the OGNL beanInfoCache should use LRU mode.
|
||||
### NOTE: When true, make sure to set the beanInfoCacheMaxSize to a reasonable value
|
||||
### for your application. Otherwise the default limit will never (practically) be reached.
|
||||
# struts.ognl.beanInfoCacheLRUMode=false
|
||||
|
||||
### Indicates if Dispatcher should handle unexpected exceptions by calling sendError()
|
||||
### or simply rethrow it as a ServletException to allow future processing by other frameworks like Spring Security
|
||||
struts.handle.exception=true
|
||||
|
||||
@@ -231,6 +231,9 @@
|
||||
<bean type="org.apache.struts2.components.date.DateFormatter" name="simpleDateFormatter" class="org.apache.struts2.components.date.SimpleDateFormatAdapter" scope="singleton"/>
|
||||
<bean type="org.apache.struts2.components.date.DateFormatter" name="dateTimeFormatter" class="org.apache.struts2.components.date.DateTimeFormatterAdapter" scope="singleton"/>
|
||||
|
||||
<bean type="com.opensymphony.xwork2.ognl.OgnlCacheFactory" name="ognlExpressionCacheFactory" class="com.opensymphony.xwork2.ognl.DefaultOgnlExpressionCacheFactory" scope="singleton"/>
|
||||
<bean type="com.opensymphony.xwork2.ognl.OgnlCacheFactory" name="ognlBeanInfoCacheFactory" class="com.opensymphony.xwork2.ognl.DefaultOgnlBeanInfoCacheFactory" scope="singleton"/>
|
||||
|
||||
<package name="struts-default" abstract="true">
|
||||
<result-types>
|
||||
<result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/>
|
||||
|
||||
@@ -35,6 +35,7 @@ import com.opensymphony.xwork2.util.Owner;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
import com.opensymphony.xwork2.util.location.LocatableProperties;
|
||||
import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
|
||||
import java.beans.BeanInfo;
|
||||
import ognl.InappropriateExpressionException;
|
||||
import ognl.MethodFailedException;
|
||||
import ognl.NoSuchPropertyException;
|
||||
@@ -129,6 +130,61 @@ public class OgnlUtilTest extends XWorkTestCase {
|
||||
assertSame(expr0, expr2);
|
||||
}
|
||||
|
||||
public void testCacheEnabledMaxSize() throws OgnlException {
|
||||
ognlUtil.setEnableExpressionCache("true");
|
||||
ognlUtil.setExpressionCacheMaxSize("1");
|
||||
Object expr0 = ognlUtil.compile("test");
|
||||
Object expr2 = ognlUtil.compile("test");
|
||||
assertSame(expr0, expr2);
|
||||
assertEquals("Expression cache size should be at its limit", 1, ognlUtil.expressionCacheSize());
|
||||
// Next epxression cached should cause the cache to clear (exceeding maximum sized).
|
||||
Object expr3 = ognlUtil.compile("test1");
|
||||
assertEquals("Expression cache should be empty", 0, ognlUtil.expressionCacheSize());
|
||||
Object expr4 = ognlUtil.compile("test1");
|
||||
Object expr5 = ognlUtil.compile("test1");
|
||||
assertEquals("Expression cache size should still be at its limit", 1, ognlUtil.expressionCacheSize());
|
||||
assertNotSame("2nd test expression cache attempt will exceed size and force clear, but somehow they match ?", expr3, expr4);
|
||||
assertSame(expr4, expr5);
|
||||
// Next epxression cached should cause the cache to clear (exceeding maximum sized).
|
||||
Object expr6 = ognlUtil.compile("test");
|
||||
assertEquals("Expression cache should be empty", 0, ognlUtil.expressionCacheSize());
|
||||
Object expr7 = ognlUtil.compile("test");
|
||||
Object expr8 = ognlUtil.compile("test");
|
||||
assertNotSame("2nd test expression cache attempt will exceed size and force clear, but somehow they match ?", expr6, expr7);
|
||||
assertSame(expr7, expr8);
|
||||
assertEquals("Expression LRU cache size should still be at its limit", 1, ognlUtil.expressionCacheSize());
|
||||
assertNotSame("1st test expression identical after ejection from LRU cache ?", expr5, expr0);
|
||||
}
|
||||
|
||||
public void testLRUCacheEnabled() throws OgnlException {
|
||||
// Force usage of LRU cache factories for the OgnlUtil instance
|
||||
this.ognlUtil = generateOgnlUtilInstanceWithDefaultLRUCacheFactories();
|
||||
ognlUtil.setEnableExpressionCache("true");
|
||||
Object expr0 = ognlUtil.compile("test");
|
||||
Object expr2 = ognlUtil.compile("test");
|
||||
assertSame(expr0, expr2);
|
||||
}
|
||||
|
||||
public void testLRUCacheEnabledMaxSize() throws OgnlException {
|
||||
// Force usage of LRU cache factories for the OgnlUtil instance
|
||||
this.ognlUtil = generateOgnlUtilInstanceWithDefaultLRUCacheFactories();
|
||||
ognlUtil.setEnableExpressionCache("true");
|
||||
ognlUtil.setExpressionCacheMaxSize("1");
|
||||
Object expr0 = ognlUtil.compile("test");
|
||||
Object expr2 = ognlUtil.compile("test");
|
||||
assertSame(expr0, expr2);
|
||||
assertEquals("Expression LRU cache size should be at its limit", 1, ognlUtil.expressionCacheSize());
|
||||
Object expr3 = ognlUtil.compile("test1");
|
||||
Object expr4 = ognlUtil.compile("test1");
|
||||
assertSame(expr3, expr4);
|
||||
assertEquals("Expression LRU cache size should still be at its limit", 1, ognlUtil.expressionCacheSize());
|
||||
Object expr5 = ognlUtil.compile("test");
|
||||
Object expr6 = ognlUtil.compile("test");
|
||||
assertSame(expr5, expr6);
|
||||
assertEquals("Expression LRU cache size should still be at its limit", 1, ognlUtil.expressionCacheSize());
|
||||
assertNotSame("1st test expression identical after ejection from LRU cache ?", expr5, expr0);
|
||||
}
|
||||
|
||||
public void testExpressionIsCachedIrrespectiveOfItsExecutionStatus() throws OgnlException {
|
||||
Foo foo = new Foo();
|
||||
OgnlContext context = (OgnlContext) ognlUtil.createDefaultContext(foo);
|
||||
@@ -149,6 +205,30 @@ public class OgnlUtilTest extends XWorkTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
public void testExpressionIsLRUCachedIrrespectiveOfItsExecutionStatus() throws OgnlException {
|
||||
// Force usage of LRU cache factories for the OgnlUtil instance
|
||||
this.ognlUtil = generateOgnlUtilInstanceWithDefaultLRUCacheFactories();
|
||||
ognlUtil.setContainer(container); // Must be explicitly set as the generated OgnlUtil instance has no container
|
||||
ognlUtil.setEnableExpressionCache("true");
|
||||
Foo foo = new Foo();
|
||||
OgnlContext context = (OgnlContext) ognlUtil.createDefaultContext(foo);
|
||||
|
||||
// Expression which executes with success
|
||||
try {
|
||||
ognlUtil.getValue("@com.opensymphony.xwork2.ognl.OgnlUtilTest@STATIC_FINAL_PUBLIC_ATTRIBUTE", context, foo);
|
||||
assertEquals("Successfully executed expression must have been cached", ognlUtil.expressionCacheSize(), 1);
|
||||
} catch (Exception ex) {
|
||||
fail("Expression execution should have succeeded here. Exception: " + ex);
|
||||
}
|
||||
// Expression which executes with failure
|
||||
try {
|
||||
ognlUtil.getValue("@com.opensymphony.xwork2.ognl.OgnlUtilTest@STATIC_PRIVATE_ATTRIBUTE", context, foo);
|
||||
fail("Expression execution should have failed here");
|
||||
} catch (Exception ex) {
|
||||
assertEquals("Expression with failed execution must have been cached nevertheless", ognlUtil.expressionCacheSize(), 2);
|
||||
}
|
||||
}
|
||||
|
||||
public void testMethodExpressionIsCachedIrrespectiveOfItsExecutionStatus() throws Exception {
|
||||
Foo foo = new Foo();
|
||||
OgnlContext context = (OgnlContext) ognlUtil.createDefaultContext(foo);
|
||||
@@ -193,6 +273,32 @@ public class OgnlUtilTest extends XWorkTestCase {
|
||||
assertTrue("Expression cache empty after usage ?", ognlUtil.expressionCacheSize() > 0);
|
||||
}
|
||||
|
||||
public void testClearExpressionLRUCache() throws OgnlException {
|
||||
// Force usage of LRU cache factories for the OgnlUtil instance
|
||||
this.ognlUtil = generateOgnlUtilInstanceWithDefaultLRUCacheFactories();
|
||||
ognlUtil.setEnableExpressionCache("true");
|
||||
// Test that the expression cache is functioning as expected.
|
||||
Object expr0 = ognlUtil.compile("test");
|
||||
Object expr1 = ognlUtil.compile("test");
|
||||
Object expr2 = ognlUtil.compile("test");
|
||||
// Cache in effect, so expr0, expr1, expr2 should be the same.
|
||||
assertSame(expr0, expr1);
|
||||
assertSame(expr0, expr2);
|
||||
assertTrue("Expression cache empty before clear ?", ognlUtil.expressionCacheSize() > 0);
|
||||
// Clear the Epxression cache and confirm subsequent requests are new.
|
||||
ognlUtil.clearExpressionCache();
|
||||
assertEquals("Expression cache not empty after clear ?", 0, ognlUtil.expressionCacheSize());
|
||||
Object expr3 = ognlUtil.compile("test");
|
||||
Object expr4 = ognlUtil.compile("test");
|
||||
Object expr5 = ognlUtil.compile("test");
|
||||
// Cache cleared, expr3 should be a new instance.
|
||||
assertNotSame(expr0, expr3);
|
||||
// Cache still in effect, so expr3, expr4, expr5 should be the same.
|
||||
assertSame(expr3, expr4);
|
||||
assertSame(expr3, expr5);
|
||||
assertTrue("Expression cache empty after usage ?", ognlUtil.expressionCacheSize() > 0);
|
||||
}
|
||||
|
||||
public void testClearBeanInfoCache() throws IntrospectionException {
|
||||
final TestBean1 testBean1 = new TestBean1();
|
||||
final TestBean2 testBean2 = new TestBean2();
|
||||
@@ -242,6 +348,79 @@ public class OgnlUtilTest extends XWorkTestCase {
|
||||
assertTrue("BeanInfo cache empty after usage ?", ognlUtil.beanInfoCacheSize() > 0);
|
||||
}
|
||||
|
||||
public void testBeanInfoCache() throws IntrospectionException {
|
||||
final TestBean1 testBean1 = new TestBean1();
|
||||
final TestBean2 testBean2 = new TestBean2();
|
||||
// Test that the BeanInfo cache is functioning as expected.
|
||||
Object beanInfo1_1 = ognlUtil.getBeanInfo(testBean1);
|
||||
Object beanInfo1_2 = ognlUtil.getBeanInfo(testBean1);
|
||||
Object beanInfo1_3 = ognlUtil.getBeanInfo(testBean1);
|
||||
// Cache in effect, so beanInfo1_1, beanInfo1_2, beanInfo1_3 should be the same.
|
||||
assertSame(beanInfo1_1, beanInfo1_2);
|
||||
assertSame(beanInfo1_1, beanInfo1_3);
|
||||
Object beanInfo2_1 = ognlUtil.getBeanInfo(testBean2);
|
||||
Object beanInfo2_2 = ognlUtil.getBeanInfo(testBean2);
|
||||
Object beanInfo2_3 = ognlUtil.getBeanInfo(testBean2);
|
||||
// Cache in effect, so beanInfo2_1, beanInfo2_2, beanInfo2_3 should be the same.
|
||||
assertSame(beanInfo2_1, beanInfo2_2);
|
||||
assertSame(beanInfo2_1, beanInfo2_3);
|
||||
// BeanInfo for TestBean1 and TestBean2 should always be different.
|
||||
assertNotSame(beanInfo1_1, beanInfo2_1);
|
||||
assertTrue("BeanInfo cache empty after usage ?", ognlUtil.beanInfoCacheSize() > 0);
|
||||
}
|
||||
|
||||
public void testBeanInfoLRUCache() throws IntrospectionException {
|
||||
// Force usage of LRU cache factories for the OgnlUtil instance
|
||||
this.ognlUtil = generateOgnlUtilInstanceWithDefaultLRUCacheFactories();
|
||||
final TestBean1 testBean1 = new TestBean1();
|
||||
final TestBean2 testBean2 = new TestBean2();
|
||||
// Test that the BeanInfo cache is functioning as expected.
|
||||
Object beanInfo1_1 = ognlUtil.getBeanInfo(testBean1);
|
||||
Object beanInfo1_2 = ognlUtil.getBeanInfo(testBean1);
|
||||
Object beanInfo1_3 = ognlUtil.getBeanInfo(testBean1);
|
||||
// Cache in effect, so beanInfo1_1, beanInfo1_2, beanInfo1_3 should be the same.
|
||||
assertSame(beanInfo1_1, beanInfo1_2);
|
||||
assertSame(beanInfo1_1, beanInfo1_3);
|
||||
Object beanInfo2_1 = ognlUtil.getBeanInfo(testBean2);
|
||||
Object beanInfo2_2 = ognlUtil.getBeanInfo(testBean2);
|
||||
Object beanInfo2_3 = ognlUtil.getBeanInfo(testBean2);
|
||||
// Cache in effect, so beanInfo2_1, beanInfo2_2, beanInfo2_3 should be the same.
|
||||
assertSame(beanInfo2_1, beanInfo2_2);
|
||||
assertSame(beanInfo2_1, beanInfo2_3);
|
||||
// BeanInfo for TestBean1 and TestBean2 should always be different.
|
||||
assertNotSame(beanInfo1_1, beanInfo2_1);
|
||||
assertTrue("BeanInfo cache empty after usage ?", ognlUtil.beanInfoCacheSize() > 0);
|
||||
}
|
||||
|
||||
public void testBeanInfoLRUCacheLimits() throws IntrospectionException {
|
||||
// Force usage of LRU cache factories for the OgnlUtil instance
|
||||
this.ognlUtil = generateOgnlUtilInstanceWithDefaultLRUCacheFactories();
|
||||
ognlUtil.setBeanInfoCacheMaxSize("1");
|
||||
final TestBean1 testBean1 = new TestBean1();
|
||||
final TestBean2 testBean2 = new TestBean2();
|
||||
// Test that the BeanInfo cache is functioning as expected.
|
||||
Object beanInfo1_1 = ognlUtil.getBeanInfo(testBean1);
|
||||
Object beanInfo1_2 = ognlUtil.getBeanInfo(testBean1);
|
||||
Object beanInfo1_3 = ognlUtil.getBeanInfo(testBean1);
|
||||
// Cache in effect, so beanInfo1_1, beanInfo1_2, beanInfo1_3 should be the same.
|
||||
assertSame(beanInfo1_1, beanInfo1_2);
|
||||
assertSame(beanInfo1_1, beanInfo1_3);
|
||||
Object beanInfo2_1 = ognlUtil.getBeanInfo(testBean2);
|
||||
Object beanInfo2_2 = ognlUtil.getBeanInfo(testBean2);
|
||||
Object beanInfo2_3 = ognlUtil.getBeanInfo(testBean2);
|
||||
// Cache in effect, so beanInfo2_1, beanInfo2_2, beanInfo2_3 should be the same.
|
||||
assertSame(beanInfo2_1, beanInfo2_2);
|
||||
assertSame(beanInfo2_1, beanInfo2_3);
|
||||
// BeanInfo for TestBean1 and TestBean2 should always be different.
|
||||
assertNotSame(beanInfo1_1, beanInfo2_1);
|
||||
assertTrue("BeanInfo cache empty after usage ?", ognlUtil.beanInfoCacheSize() > 0);
|
||||
assertEquals("BeanInfo LRU cache size should be at its limit", 1, ognlUtil.beanInfoCacheSize());
|
||||
// LRU cache should not contain TestBean1 beaninfo anymore. A new entry should exist in the cache.
|
||||
Object beanInfo1_4 = ognlUtil.getBeanInfo(testBean1);
|
||||
assertNotSame("BeanInfo dropped from LRU cache is the same as newly added ?", beanInfo1_1, beanInfo1_4);
|
||||
ognlUtil.setBeanInfoCacheMaxSize(String.valueOf(Integer.MAX_VALUE));
|
||||
}
|
||||
|
||||
public void testClearRuntimeCache() {
|
||||
// Confirm that no exceptions or failures arise when calling the convenience global clear method.
|
||||
OgnlUtil.clearRuntimeCache();
|
||||
@@ -1134,6 +1313,20 @@ public class OgnlUtilTest extends XWorkTestCase {
|
||||
internalTestOgnlUtilExclusionsImmutable(basicOgnlUtil);
|
||||
}
|
||||
|
||||
public void testDefaultOgnlUtilExclusionsAlternateConstructor() {
|
||||
OgnlUtil basicOgnlUtil = new OgnlUtil(null, null);
|
||||
|
||||
internalTestInitialEmptyOgnlUtilExclusions(basicOgnlUtil);
|
||||
internalTestOgnlUtilExclusionsImmutable(basicOgnlUtil);
|
||||
}
|
||||
|
||||
public void testDefaultOgnlUtilExclusionsAlternateConstructorPopulated() {
|
||||
OgnlUtil basicOgnlUtil = new OgnlUtil(new DefaultOgnlExpressionCacheFactory<String, Object>(), new DefaultOgnlBeanInfoCacheFactory<Class<?>, BeanInfo>());
|
||||
|
||||
internalTestInitialEmptyOgnlUtilExclusions(basicOgnlUtil);
|
||||
internalTestOgnlUtilExclusionsImmutable(basicOgnlUtil);
|
||||
}
|
||||
|
||||
public void testOgnlUtilExcludedAdditivity() {
|
||||
Set<Class<?>> excludedClasses;
|
||||
Set<Pattern> excludedPackageNamePatterns;
|
||||
@@ -1215,7 +1408,7 @@ public class OgnlUtilTest extends XWorkTestCase {
|
||||
Object accessedValue;
|
||||
|
||||
try {
|
||||
reloadTestContainerConfiguration(true, true); // Test with allowStaticFieldAccess true
|
||||
reloadTestContainerConfiguration(true); // Test with allowStaticFieldAccess true
|
||||
context = ognlUtil.createDefaultContext(null);
|
||||
} catch (Exception ex) {
|
||||
fail("unable to reload test configuration? Exception: " + ex);
|
||||
@@ -1270,7 +1463,7 @@ public class OgnlUtilTest extends XWorkTestCase {
|
||||
}
|
||||
|
||||
try {
|
||||
reloadTestContainerConfiguration(true, false); // Re-test with allowStaticFieldAccess false
|
||||
reloadTestContainerConfiguration(false); // Re-test with allowStaticFieldAccess false
|
||||
context = ognlUtil.createDefaultContext(null);
|
||||
} catch (Exception ex) {
|
||||
fail("unable to reload test configuration? Exception: " + ex);
|
||||
@@ -1497,6 +1690,34 @@ public class OgnlUtilTest extends XWorkTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
public void testGetExcludedPackageNamesAlternateConstructor() {
|
||||
// Getter should return an immutable collection
|
||||
OgnlUtil util = new OgnlUtil(null, null);
|
||||
util.setExcludedPackageNames("java.lang,java.awt");
|
||||
assertEquals(util.getExcludedPackageNames().size(), 2);
|
||||
try {
|
||||
util.getExcludedPackageNames().clear();
|
||||
} catch (Exception ex) {
|
||||
assertTrue(ex instanceof UnsupportedOperationException);
|
||||
} finally {
|
||||
assertEquals(util.getExcludedPackageNames().size(), 2);
|
||||
}
|
||||
}
|
||||
|
||||
public void testGetExcludedPackageNamesAlternateConstructorPopulated() {
|
||||
// Getter should return an immutable collection
|
||||
OgnlUtil util = new OgnlUtil(new DefaultOgnlExpressionCacheFactory<String, Object>(), new DefaultOgnlBeanInfoCacheFactory<Class<?>, BeanInfo>());
|
||||
util.setExcludedPackageNames("java.lang,java.awt");
|
||||
assertEquals(util.getExcludedPackageNames().size(), 2);
|
||||
try {
|
||||
util.getExcludedPackageNames().clear();
|
||||
} catch (Exception ex) {
|
||||
assertTrue(ex instanceof UnsupportedOperationException);
|
||||
} finally {
|
||||
assertEquals(util.getExcludedPackageNames().size(), 2);
|
||||
}
|
||||
}
|
||||
|
||||
public void testGetExcludedClasses() {
|
||||
// Getter should return an immutable collection
|
||||
OgnlUtil util = new OgnlUtil();
|
||||
@@ -1511,6 +1732,34 @@ public class OgnlUtilTest extends XWorkTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
public void testGetExcludedClassesAlternateConstructor() {
|
||||
// Getter should return an immutable collection
|
||||
OgnlUtil util = new OgnlUtil(null, null);
|
||||
util.setExcludedClasses("java.lang.Runtime,java.lang.ProcessBuilder,java.net.URL");
|
||||
assertEquals(util.getExcludedClasses().size(), 3);
|
||||
try {
|
||||
util.getExcludedClasses().clear();
|
||||
} catch (Exception ex) {
|
||||
assertTrue(ex instanceof UnsupportedOperationException);
|
||||
} finally {
|
||||
assertEquals(util.getExcludedClasses().size(), 3);
|
||||
}
|
||||
}
|
||||
|
||||
public void testGetExcludedClassesAlternateConstructorPopulated() {
|
||||
// Getter should return an immutable collection
|
||||
OgnlUtil util = new OgnlUtil(new DefaultOgnlExpressionCacheFactory<String, Object>(), new DefaultOgnlBeanInfoCacheFactory<Class<?>, BeanInfo>());
|
||||
util.setExcludedClasses("java.lang.Runtime,java.lang.ProcessBuilder,java.net.URL");
|
||||
assertEquals(util.getExcludedClasses().size(), 3);
|
||||
try {
|
||||
util.getExcludedClasses().clear();
|
||||
} catch (Exception ex) {
|
||||
assertTrue(ex instanceof UnsupportedOperationException);
|
||||
} finally {
|
||||
assertEquals(util.getExcludedClasses().size(), 3);
|
||||
}
|
||||
}
|
||||
|
||||
public void testGetExcludedPackageNamePatterns() {
|
||||
// Getter should return an immutable collection
|
||||
OgnlUtil util = new OgnlUtil();
|
||||
@@ -1525,6 +1774,132 @@ public class OgnlUtilTest extends XWorkTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
public void testGetExcludedPackageNamePatternsAlternateConstructor() {
|
||||
// Getter should return an immutable collection
|
||||
OgnlUtil util = new OgnlUtil(null, null);
|
||||
util.setExcludedPackageNamePatterns("java.lang.");
|
||||
assertEquals(util.getExcludedPackageNamePatterns().size(), 1);
|
||||
try {
|
||||
util.getExcludedPackageNamePatterns().clear();
|
||||
} catch (Exception ex) {
|
||||
assertTrue(ex instanceof UnsupportedOperationException);
|
||||
} finally {
|
||||
assertEquals(util.getExcludedPackageNamePatterns().size(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
public void testGetExcludedPackageNamePatternsAlternateConstructorPopulated() {
|
||||
// Getter should return an immutable collection
|
||||
OgnlUtil util = new OgnlUtil(new DefaultOgnlExpressionCacheFactory<String, Object>(), new DefaultOgnlBeanInfoCacheFactory<Class<?>, BeanInfo>());
|
||||
util.setExcludedPackageNamePatterns("java.lang.");
|
||||
assertEquals(util.getExcludedPackageNamePatterns().size(), 1);
|
||||
try {
|
||||
util.getExcludedPackageNamePatterns().clear();
|
||||
} catch (Exception ex) {
|
||||
assertTrue(ex instanceof UnsupportedOperationException);
|
||||
} finally {
|
||||
assertEquals(util.getExcludedPackageNamePatterns().size(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
public void testOgnlUtilDefaultCacheClass() throws OgnlException {
|
||||
OgnlDefaultCache<Integer, String> defaultCache = new OgnlDefaultCache<>(2, 16, 0.75f);
|
||||
assertEquals("Initial evictionLimit did not match initial value", 2, defaultCache.getEvictionLimit());
|
||||
defaultCache.setEvictionLimit(3);
|
||||
assertEquals("Updated evictionLimit did not match updated value", 3, defaultCache.getEvictionLimit());
|
||||
String lookupResult = defaultCache.get(Integer.valueOf(0));
|
||||
assertNull("Lookup of empty cache returned non-null value ?", lookupResult);
|
||||
defaultCache.put(Integer.valueOf(0), "Zero");
|
||||
lookupResult = defaultCache.get(Integer.valueOf(0));
|
||||
assertEquals("Retrieved value does not match put value ?", "Zero", lookupResult);
|
||||
defaultCache.put(Integer.valueOf(1), "One");
|
||||
defaultCache.put(Integer.valueOf(2), "Two");
|
||||
assertEquals("Default cache not size evictionlimit after adding three values ?", defaultCache.getEvictionLimit(), defaultCache.size());
|
||||
lookupResult = defaultCache.get(Integer.valueOf(2));
|
||||
assertEquals("Retrieved value does not match put value ?", "Two", lookupResult);
|
||||
defaultCache.put(Integer.valueOf(3), "Three");
|
||||
assertEquals("Default cache not size zero after an add that exceeded the evection limit ?", 0, defaultCache.size());
|
||||
lookupResult = defaultCache.get(Integer.valueOf(0));
|
||||
assertNull("Lookup of value 0 (should have been evicted with everything) returned non-null value ?", lookupResult);
|
||||
lookupResult = defaultCache.get(Integer.valueOf(3));
|
||||
assertNull("Lookup of value 3 (should have been evicted with everything) returned non-null value ?", lookupResult);
|
||||
defaultCache.putIfAbsent(Integer.valueOf(2), "Two");
|
||||
lookupResult = defaultCache.get(Integer.valueOf(2));
|
||||
assertEquals("Retrieved value does not match put value ?", "Two", lookupResult);
|
||||
defaultCache.clear();
|
||||
assertEquals("Default cache not empty after clear ?", 0, defaultCache.size());
|
||||
}
|
||||
|
||||
public void testOgnlUtilLRUCacheClass() throws OgnlException {
|
||||
OgnlLRUCache<Integer, String> lruCache = new OgnlLRUCache<>(2, 16, 0.75f);
|
||||
assertEquals("Initial evictionLimit did not match initial value", 2, lruCache.getEvictionLimit());
|
||||
lruCache.setEvictionLimit(3);
|
||||
assertEquals("Updated evictionLimit did not match updated value", 3, lruCache.getEvictionLimit());
|
||||
String lookupResult = lruCache.get(Integer.valueOf(0));
|
||||
assertNull("Lookup of empty cache returned non-null value ?", lookupResult);
|
||||
lruCache.put(Integer.valueOf(0), "Zero");
|
||||
lookupResult = lruCache.get(Integer.valueOf(0));
|
||||
assertEquals("Retrieved value does not match put value ?", "Zero", lookupResult);
|
||||
lruCache.put(Integer.valueOf(1), "One");
|
||||
lruCache.put(Integer.valueOf(2), "Two");
|
||||
assertEquals("LRU cache not size evictionlimit after adding three values ?", lruCache.getEvictionLimit(), lruCache.size());
|
||||
lookupResult = lruCache.get(Integer.valueOf(2));
|
||||
assertEquals("Retrieved value does not match put value ?", "Two", lookupResult);
|
||||
lruCache.put(Integer.valueOf(3), "Three");
|
||||
assertEquals("LRU cache not size evictionlimit after adding values ?", lruCache.getEvictionLimit(), lruCache.size());
|
||||
lookupResult = lruCache.get(Integer.valueOf(0));
|
||||
assertNull("Lookup of value 0 (should have dropped off LRU cache) returned non-null value ?", lookupResult);
|
||||
lruCache.putIfAbsent(Integer.valueOf(2), "Two");
|
||||
lookupResult = lruCache.get(Integer.valueOf(2));
|
||||
assertEquals("Retrieved value does not match put value ?", "Two", lookupResult);
|
||||
lruCache.clear();
|
||||
assertEquals("LRU cache not empty after clear ?", 0, lruCache.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Unit test primarily for code coverage
|
||||
*/
|
||||
public void testOgnlDefaultCacheFactoryCoverage() {
|
||||
OgnlCache<String, Object> ognlCache;
|
||||
DefaultOgnlCacheFactory defaultOgnlCacheFactory = new DefaultOgnlCacheFactory<String, Object>();
|
||||
// Normal cache
|
||||
defaultOgnlCacheFactory.setCacheMaxSize("12");
|
||||
defaultOgnlCacheFactory.setUseLRUCache("false");
|
||||
ognlCache = defaultOgnlCacheFactory.buildOgnlCache();
|
||||
assertNotNull("No param build method result null ?", ognlCache);
|
||||
assertEquals("Eviction limit for cache mismatches limit for factory ?", 12, ognlCache.getEvictionLimit() );
|
||||
ognlCache = defaultOgnlCacheFactory.buildOgnlCache(6, 6, 0.75f, false);
|
||||
assertNotNull("No param build method result null ?", ognlCache);
|
||||
assertEquals("Eviction limit for cache mismatches limit for factory ?", 6, ognlCache.getEvictionLimit() );
|
||||
// LRU cache
|
||||
defaultOgnlCacheFactory.setCacheMaxSize("30");
|
||||
defaultOgnlCacheFactory.setUseLRUCache("true");
|
||||
ognlCache = defaultOgnlCacheFactory.buildOgnlCache();
|
||||
assertNotNull("No param build method result null ?", ognlCache);
|
||||
assertEquals("Eviction limit for cache mismatches limit for factory ?", 30, ognlCache.getEvictionLimit() );
|
||||
ognlCache = defaultOgnlCacheFactory.buildOgnlCache(15, 15, 0.75f, false);
|
||||
assertNotNull("No param build method result null ?", ognlCache);
|
||||
assertEquals("Eviction limit for cache mismatches limit for factory ?", 15, ognlCache.getEvictionLimit() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a new OgnlUtil instance (not configured by the {@link ContainerBuilder}) that can be used for
|
||||
* basic tests, with its Expression and BeanInfo factories set to LRU mode.
|
||||
*
|
||||
* @return OgnlUtil instance with LRU enabled Expression and BeanInfo factories
|
||||
*/
|
||||
private OgnlUtil generateOgnlUtilInstanceWithDefaultLRUCacheFactories() {
|
||||
final OgnlUtil result;
|
||||
final DefaultOgnlExpressionCacheFactory<String, Object> expressionFactory = new DefaultOgnlExpressionCacheFactory<>();
|
||||
final DefaultOgnlBeanInfoCacheFactory<Class<?>, BeanInfo> beanInfoFactory = new DefaultOgnlBeanInfoCacheFactory<>();
|
||||
expressionFactory.setUseLRUCache("true");
|
||||
expressionFactory.setCacheMaxSize("25");
|
||||
beanInfoFactory.setUseLRUCache("true");
|
||||
beanInfoFactory.setCacheMaxSize("25");
|
||||
result = new OgnlUtil(expressionFactory, beanInfoFactory);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void reloadTestContainerConfiguration(boolean devMode, boolean allowStaticFieldAccess) {
|
||||
loadConfigurationProviders(new StubConfigurationProvider() {
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user