Skip to content

Commit

Permalink
JEXL-414: added cache interface, synchronized & concurrent implementa…
Browse files Browse the repository at this point in the history
…tions and factory handling;
  • Loading branch information
Henri Biestro committed Nov 18, 2023
1 parent b2431dd commit e4b6b39
Show file tree
Hide file tree
Showing 10 changed files with 264 additions and 64 deletions.
1 change: 1 addition & 0 deletions RELEASE-NOTES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ New Features in 3.3.1:

Bugs Fixed in 3.3.1:
===================
* JEXL-415: Incorrect template eval result
* JEXL-412: Ambiguous syntax between namespace function call and map object definition.
* JEXL-410: JexlFeatures: ctor does not enable all features
* JEXL-409: Disable LEXICAL should disable LEXICAL_SHADE
Expand Down
5 changes: 5 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,11 @@
<version>2.10.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.googlecode.concurrentlinkedhashmap</groupId>
<artifactId>concurrentlinkedhashmap-lru</artifactId>
<version>1.4.2</version>
</dependency>
</dependencies>

<build>
Expand Down
5 changes: 4 additions & 1 deletion src/changes/changes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,12 @@
Allow 'trailing commas' or ellipsis while defining array, map and set literals
</action>
<!-- FIX -->
<action dev="henrib" type="fix" issue="JEXL-415" due-to="Xu Pengcheng" >
Incorrect template eval result.
</action>
<action dev="henrib" type="fix" issue="JEXL-412" due-to="Xu Pengcheng" >
Ambiguous syntax between namespace function call and map object definition.
</action>action>
</action>
<action dev="henrib" type="fix" issue="JEXL-410" due-to="sebb">
JexlFeatures: ctor does not enable all features
</action>
Expand Down
24 changes: 23 additions & 1 deletion src/main/java/org/apache/commons/jexl3/JexlBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.function.IntFunction;

import org.apache.commons.jexl3.internal.Engine;
import org.apache.commons.jexl3.introspection.JexlPermissions;
Expand Down Expand Up @@ -132,6 +133,9 @@ public static void setDefaultPermissions(final JexlPermissions permissions) {
/** The cache size. */
private int cache = -1;

/** The cache class factory. */
private IntFunction<JexlCache<?,?>> cacheFactory = JexlCache.createConcurrent();

/** The stack overflow limit. */
private int stackOverflow = Integer.MAX_VALUE;

Expand Down Expand Up @@ -615,11 +619,29 @@ public JexlBuilder cache(final int size) {
return this;
}

/**
* Sets the expression cache size the engine will use.
*
* @param factory the function to produce a cache.
* @return this builder
*/
public JexlBuilder cacheFactory(final IntFunction<JexlCache<?, ?>> factory) {
this.cacheFactory = factory;
return this;
}

/**
* @return the cache size
*/
public int cache() {
return cache;
return cache;
}

/**
* @return the cache factory
*/
public IntFunction<JexlCache<?, ?>> cacheFactory() {
return this.cacheFactory;
}

/**
Expand Down
85 changes: 85 additions & 0 deletions src/main/java/org/apache/commons/jexl3/JexlCache.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.jexl3;

import org.apache.commons.jexl3.internal.ConcurrentCache;
import org.apache.commons.jexl3.internal.SoftCache;

import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.function.IntFunction;

/**
* Caching scripts or templates interface.
* @param <K> source
* @param <V> script or template
*/
public interface JexlCache<K, V> {
/**
* Returns the cache size.
*
* @return the cache size
*/
int size();

/**
* Clears the cache.
*/
void clear();

/**
* Gets a value from cache.
*
* @param key the cache entry key
* @return the cache entry value
*/
V get(K key);

/**
* Puts a value in cache.
*
* @param key the cache entry key
* @param script the cache entry value
*/
V put(K key, V script);

/**
* Produces the cache entry set.
* <p>
* For implementations testing only
* </p>
* @return the cache entry list
*/
default Collection<Map.Entry<K, V>> entries() {
return Collections.emptyList();
}

/**
* @return a synchronized cache factory amenable to low concurrency usage
*/
static IntFunction<JexlCache<?,?>> createSynchronized() {
return SoftCache::new;
}

/**
* @return a concurrent cache factory amenable to high concurrency usage
*/
static IntFunction<JexlCache<?,?>> createConcurrent() {
return ConcurrentCache::new;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.jexl3.internal;

import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap;

import java.lang.ref.SoftReference;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;

/**
* A cache whose underlying map is a ConcurrentLinkedHashMap.
*
* @param <K> the cache key entry type
* @param <V> the cache key value type
*/
public class ConcurrentCache<K, V> extends SoftCache<K, V> {
/**
* Creates a new instance of a concurrent cache.
*
* @param theSize the cache size
*/
public ConcurrentCache(final int theSize) {
super(theSize);
}

@Override
public Collection<Map.Entry<K, V>> entries() {
final SoftReference<Map<K, V>> ref = reference;
final Map<K, V> map = ref != null ? ref.get() : null;
return map == null? Collections.emptyList() : map.entrySet();
}

@Override
public <K, V> Map<K, V> createMap(final int cacheSize) {
return new ConcurrentLinkedHashMap.Builder<K, V>()
.concurrencyLevel(Runtime.getRuntime().availableProcessors())
.maximumWeightedCapacity(cacheSize)
.build();
}
}


12 changes: 10 additions & 2 deletions src/main/java/org/apache/commons/jexl3/internal/Engine.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import java.util.function.IntFunction;
import java.util.function.Predicate;

import org.apache.commons.jexl3.JexlCache;
import org.apache.commons.jexl3.JexlArithmetic;
import org.apache.commons.jexl3.JexlBuilder;
import org.apache.commons.jexl3.JexlContext;
Expand Down Expand Up @@ -160,7 +162,7 @@ private UberspectHolder() {}
/**
* The expression cache.
*/
protected final SoftCache<Source, ASTJexlScript> cache;
protected final JexlCache<Source, ASTJexlScript> cache;
/**
* The default jxlt engine.
*/
Expand All @@ -173,6 +175,10 @@ private UberspectHolder() {}
* A cached version of the options.
*/
protected final JexlOptions options;
/**
* The cache factory method.
*/
protected final IntFunction<JexlCache<?, ?>> cacheFactory;

/**
* Creates an engine with default arguments.
Expand Down Expand Up @@ -229,7 +235,9 @@ public Engine(final JexlBuilder conf) {
this.scriptFeatures = new JexlFeatures(features).script(true).namespaceTest(nsTest);
this.charset = conf.charset();
// caching:
this.cache = conf.cache() <= 0 ? null : new SoftCache<>(conf.cache());
IntFunction<JexlCache<?, ?>> factory = conf.cacheFactory();
this.cacheFactory = factory == null ? SoftCache::new : factory;
this.cache = (JexlCache<Source, ASTJexlScript>) (conf.cache() > 0 ? factory.apply(conf.cache()) : null);
this.cacheThreshold = conf.cacheThreshold();
if (uberspect == null) {
throw new IllegalArgumentException("uberspect can not be null");
Expand Down
Loading

0 comments on commit e4b6b39

Please sign in to comment.