Skip to content

Commit

Permalink
Added cache for functions
Browse files Browse the repository at this point in the history
  • Loading branch information
MrEasy committed May 17, 2022
1 parent b4b4d2c commit 5f5803d
Showing 1 changed file with 48 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;

import org.apache.commons.jxpath.CompiledExpression;
import org.apache.commons.jxpath.ExceptionHandler;
Expand Down Expand Up @@ -89,6 +90,8 @@ public class JXPathContextReferenceImpl extends JXPathContext {
private static final Compiler COMPILER = new TreeCompiler();
private static final Map<String, Expression> compiled;

private static final Map<Integer, Function> functionCache;

private static NodePointerFactory[] nodeFactoryArray;
private static final Collection<NodePointerFactory> nodeFactories = new CopyOnWriteArrayList<>();

Expand All @@ -98,14 +101,22 @@ public class JXPathContextReferenceImpl extends JXPathContext {
CACHE_STATISTICS = Boolean.getBoolean("jxpath.cache.statistics");
USE_SOFT_CACHE = "true".equalsIgnoreCase(System.getProperty("jxpath.cache.soft", "true"));
if (CACHE_ENABLED) {
final int compileCacheSize = Integer.getInteger("jxpath.cache.size", 50_000);
final int cacheSize = Integer.getInteger("jxpath.cache.size", 50_000);
if (USE_SOFT_CACHE) {
compiled = new SoftConcurrentHashMap<>(compileCacheSize);
compiled = new SoftConcurrentHashMap<>(cacheSize);
functionCache = new SoftConcurrentHashMap<>(cacheSize);
} else {
compiled = new LinkedHashMap<String, Expression>() {
@Override
protected boolean removeEldestEntry(java.util.Map.Entry eldest) {
return this.size() > compileCacheSize;
return this.size() > cacheSize;
}
};

functionCache = new LinkedHashMap<Integer, Function>() {
@Override
protected boolean removeEldestEntry(java.util.Map.Entry eldest) {
return this.size() > cacheSize;
}
};
}
Expand All @@ -123,6 +134,7 @@ protected boolean removeEldestEntry(java.util.Map.Entry eldest) {
}
else {
compiled = null;
functionCache = null;
}

nodeFactories.add(new CollectionPointerFactory());
Expand Down Expand Up @@ -765,16 +777,26 @@ public NodePointer getVariablePointer(QName name) {
* @return Function
*/
public Function getFunction(QName functionName, Object[] parameters) {
Integer functionHash = getFunctionHash(functionName, parameters);
if (CACHE_ENABLED) {
Function func = functionCache.get(functionHash);
if (func != null) {
return func;
}
}

String namespace = functionName.getPrefix();
String name = functionName.getName();
JXPathContext funcCtx = this;
Function func = null;
Functions funcs;
while (funcCtx != null) {
funcs = funcCtx.getFunctions();
if (funcs != null) {
func = funcs.getFunction(namespace, name, parameters);
Function func = funcs.getFunction(namespace, name, parameters);
if (func != null) {
if (CACHE_ENABLED) {
functionCache.put(functionHash, func);
}
return func;
}
}
Expand All @@ -784,6 +806,27 @@ public Function getFunction(QName functionName, Object[] parameters) {
"Undefined function: " + functionName.toString());
}


private static Integer getFunctionHash(QName functionName, Object[] parameters)
{
if (!CACHE_ENABLED) {
return Integer.valueOf(-1);
}

int hashParameters;
if (parameters == null) {
hashParameters = 0;
}
else
{
List<Object> paramList = Arrays.asList(parameters);

This comment has been minimized.

Copy link
@ecki

ecki Jun 4, 2022

Member

Arrays.stream(parameters) avoids the List step

List<Class> classList = paramList.stream().map(p -> p.getClass()).collect(Collectors.toList());

This comment has been minimized.

Copy link
@ecki

ecki Jun 4, 2022

Member

Wonder if we could write a ClassNameHasher which directly returns Integer, since that’s called quite often.

This comment has been minimized.

Copy link
@ecki

ecki Jun 5, 2022

Member

Actually prefer the naive code over a complicated stream.collect() (see mail)

      int a = Objects.hashCode(functionName);
      if (parameters != null)
          for(Object e : parameters)
          {
              a = 31 * a + e.getClass().hashCode();
          }
      }
      return a;
hashParameters = Arrays.hashCode(classList.toArray());
}
return Integer.valueOf(Objects.hash(functionName, hashParameters));
}


public void registerNamespace(String prefix, String namespaceURI) {
if (namespaceResolver.isSealed()) {
namespaceResolver = (NamespaceResolver) namespaceResolver.clone();
Expand Down

0 comments on commit 5f5803d

Please sign in to comment.