From e4b6b3956ed4f7e1404e4523c6629216b2384476 Mon Sep 17 00:00:00 2001
From: Henri Biestro
Date: Fri, 17 Nov 2023 20:22:17 -0800
Subject: [PATCH] JEXL-414: added cache interface, synchronized & concurrent
implementations and factory handling;
---
RELEASE-NOTES.txt | 1 +
pom.xml | 5 +
src/changes/changes.xml | 5 +-
.../org/apache/commons/jexl3/JexlBuilder.java | 24 +++-
.../org/apache/commons/jexl3/JexlCache.java | 85 +++++++++++++
.../jexl3/internal/ConcurrentCache.java | 58 +++++++++
.../apache/commons/jexl3/internal/Engine.java | 12 +-
.../commons/jexl3/internal/SoftCache.java | 118 ++++++++++--------
.../jexl3/internal/TemplateEngine.java | 5 +-
.../org/apache/commons/jexl3/CacheTest.java | 15 ++-
10 files changed, 264 insertions(+), 64 deletions(-)
create mode 100644 src/main/java/org/apache/commons/jexl3/JexlCache.java
create mode 100644 src/main/java/org/apache/commons/jexl3/internal/ConcurrentCache.java
diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index 1dfb7d879..1d173c6f3 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -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
diff --git a/pom.xml b/pom.xml
index c5a534dde..04f3cf3b8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -122,6 +122,11 @@
2.10.1test
+
+ com.googlecode.concurrentlinkedhashmap
+ concurrentlinkedhashmap-lru
+ 1.4.2
+
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 9d3c4c38e..80fc9107b 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -42,9 +42,12 @@
Allow 'trailing commas' or ellipsis while defining array, map and set literals
+
+ Incorrect template eval result.
+
Ambiguous syntax between namespace function call and map object definition.
- action>
+
JexlFeatures: ctor does not enable all features
diff --git a/src/main/java/org/apache/commons/jexl3/JexlBuilder.java b/src/main/java/org/apache/commons/jexl3/JexlBuilder.java
index 01656fefa..b67640d75 100644
--- a/src/main/java/org/apache/commons/jexl3/JexlBuilder.java
+++ b/src/main/java/org/apache/commons/jexl3/JexlBuilder.java
@@ -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;
@@ -132,6 +133,9 @@ public static void setDefaultPermissions(final JexlPermissions permissions) {
/** The cache size. */
private int cache = -1;
+ /** The cache class factory. */
+ private IntFunction> cacheFactory = JexlCache.createConcurrent();
+
/** The stack overflow limit. */
private int stackOverflow = Integer.MAX_VALUE;
@@ -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> factory) {
+ this.cacheFactory = factory;
+ return this;
+ }
+
/**
* @return the cache size
*/
public int cache() {
- return cache;
+ return cache;
+ }
+
+ /**
+ * @return the cache factory
+ */
+ public IntFunction> cacheFactory() {
+ return this.cacheFactory;
}
/**
diff --git a/src/main/java/org/apache/commons/jexl3/JexlCache.java b/src/main/java/org/apache/commons/jexl3/JexlCache.java
new file mode 100644
index 000000000..9bd9a7343
--- /dev/null
+++ b/src/main/java/org/apache/commons/jexl3/JexlCache.java
@@ -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 source
+ * @param script or template
+ */
+public interface JexlCache {
+ /**
+ * 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.
+ *
+ * For implementations testing only
+ *
+ * @return the cache entry list
+ */
+ default Collection> entries() {
+ return Collections.emptyList();
+ }
+
+ /**
+ * @return a synchronized cache factory amenable to low concurrency usage
+ */
+ static IntFunction> createSynchronized() {
+ return SoftCache::new;
+ }
+
+ /**
+ * @return a concurrent cache factory amenable to high concurrency usage
+ */
+ static IntFunction> createConcurrent() {
+ return ConcurrentCache::new;
+ }
+}
diff --git a/src/main/java/org/apache/commons/jexl3/internal/ConcurrentCache.java b/src/main/java/org/apache/commons/jexl3/internal/ConcurrentCache.java
new file mode 100644
index 000000000..03a391663
--- /dev/null
+++ b/src/main/java/org/apache/commons/jexl3/internal/ConcurrentCache.java
@@ -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 the cache key entry type
+ * @param the cache key value type
+ */
+public class ConcurrentCache extends SoftCache {
+ /**
+ * Creates a new instance of a concurrent cache.
+ *
+ * @param theSize the cache size
+ */
+ public ConcurrentCache(final int theSize) {
+ super(theSize);
+ }
+
+ @Override
+ public Collection> entries() {
+ final SoftReference
+ *
+ * Note that the underlying map is a synchronized LinkedHashMap.
+ * The reason is that a get() will reorder elements (the LRU queue) and thus
+ * needs to be guarded to be thread-safe.
+ *
*
* @param the cache key entry type
* @param the cache key value type
*/
-public class SoftCache {
+public class SoftCache implements JexlCache {
/**
* The default cache load factor.
*/
@@ -42,24 +49,19 @@ public class SoftCache {
/**
* The cache size.
*/
- private final int size;
+ protected final int size;
/**
* The soft reference to the cache map.
*/
- private SoftReference