From c9c5f373876228aba3ac6bb4eed0beff8b936198 Mon Sep 17 00:00:00 2001 From: Jonathan Cobb Date: Mon, 6 Jul 2020 17:25:44 -0400 Subject: [PATCH] cache forName lookups --- .../util/collection/ExpirationMap.java | 22 +++++++-- .../util/reflect/ReflectionUtil.java | 45 +++++++++++-------- 2 files changed, 45 insertions(+), 22 deletions(-) diff --git a/src/main/java/org/cobbzilla/util/collection/ExpirationMap.java b/src/main/java/org/cobbzilla/util/collection/ExpirationMap.java index b5cdd82..6f4ebdd 100644 --- a/src/main/java/org/cobbzilla/util/collection/ExpirationMap.java +++ b/src/main/java/org/cobbzilla/util/collection/ExpirationMap.java @@ -10,11 +10,11 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; import java.util.function.BiFunction; import java.util.function.Function; import java.util.stream.Collectors; +import static java.util.concurrent.TimeUnit.HOURS; import static org.cobbzilla.util.daemon.ZillaRuntime.notSupported; import static org.cobbzilla.util.daemon.ZillaRuntime.now; import static org.cobbzilla.util.time.TimeUtil.isTimestampInFuture; @@ -25,9 +25,9 @@ public class ExpirationMap implements Map { private final Map> map; - @Getter @Setter private long expiration = TimeUnit.HOURS.toMillis(1); - @Getter @Setter private long maxExpiration = TimeUnit.HOURS.toMillis(2); - @Getter @Setter private long cleanInterval = TimeUnit.HOURS.toMillis(4); + @Getter @Setter private long expiration = HOURS.toMillis(1); + @Getter @Setter private long maxExpiration = HOURS.toMillis(2); + @Getter @Setter private long cleanInterval = HOURS.toMillis(4); public ExpirationMap setExpirations(long val) { final var isNewExpirationShorter = val < this.expiration; @@ -46,18 +46,32 @@ public class ExpirationMap implements Map { public ExpirationMap() { this.map = new ConcurrentHashMap<>(); } + public ExpirationMap(int initialCapacity) { this.map = new ConcurrentHashMap<>(initialCapacity); } + public ExpirationMap(long val) { this(); setExpirations(val); } + public ExpirationMap(int initialCapacity, long val) { this(initialCapacity); setExpirations(val); } + public ExpirationMap(long val, ExpirationEvictionPolicy evictionPolicy) { this(val); this.evictionPolicy = evictionPolicy; } + public ExpirationMap(int initialCapacity, long val, ExpirationEvictionPolicy evictionPolicy) { + this(initialCapacity, val); + this.evictionPolicy = evictionPolicy; + } + public ExpirationMap(ExpirationEvictionPolicy evictionPolicy) { this(); this.evictionPolicy = evictionPolicy; } + public ExpirationMap(int initialCapacity, ExpirationEvictionPolicy evictionPolicy) { + this(initialCapacity); + this.evictionPolicy = evictionPolicy; + } + @Accessors(chain=true) private class ExpirationMapEntry { public final VAL value; diff --git a/src/main/java/org/cobbzilla/util/reflect/ReflectionUtil.java b/src/main/java/org/cobbzilla/util/reflect/ReflectionUtil.java index 3bdf1bf..1ce9492 100644 --- a/src/main/java/org/cobbzilla/util/reflect/ReflectionUtil.java +++ b/src/main/java/org/cobbzilla/util/reflect/ReflectionUtil.java @@ -26,6 +26,7 @@ import java.util.stream.Collectors; import static com.google.common.base.Preconditions.checkNotNull; import static java.lang.reflect.Modifier.isFinal; import static java.lang.reflect.Modifier.isStatic; +import static java.util.concurrent.TimeUnit.MINUTES; import static org.apache.commons.lang3.reflect.FieldUtils.getAllFields; import static org.cobbzilla.util.collection.ArrayUtil.arrayToString; import static org.cobbzilla.util.daemon.ZillaRuntime.*; @@ -110,6 +111,8 @@ public class ReflectionUtil { return null; } + private static final Map forNameCache = new ExpirationMap<>(100, MINUTES.toMillis(20), ExpirationEvictionPolicy.atime); + /** * Do a Class.forName and only throw unchecked exceptions. * @param clazz full class name. May end in [] to indicate array class @@ -117,26 +120,32 @@ public class ReflectionUtil { * @return A Class<clazz> object */ public static Class forName(String clazz) { - if (empty(clazz)) return (Class) Object.class; - if (clazz.endsWith("[]")) return arrayClass(forName(clazz.substring(0, clazz.length()-2))); - try { - return (Class) Class.forName(clazz); - } catch (ClassNotFoundException e) { - switch (clazz) { - case "boolean": return (Class) boolean.class; - case "byte": return (Class) byte.class; - case "short": return (Class) short.class; - case "char": return (Class) char.class; - case "int": return (Class) int.class; - case "long": return (Class) long.class; - case "float": return (Class) float.class; - case "double": return (Class) double.class; - } - return die("Class.forName("+clazz+") error: "+shortError(e)); + Class cached = forNameCache.get(clazz); + if (cached == null) { + if (empty(clazz)) return (Class) Object.class; + if (clazz.endsWith("[]")) return arrayClass(forName(clazz.substring(0, clazz.length()-2))); + try { + cached = (Class) Class.forName(clazz); + forNameCache.put(clazz, cached); + + } catch (ClassNotFoundException e) { + switch (clazz) { + case "boolean": return (Class) boolean.class; + case "byte": return (Class) byte.class; + case "short": return (Class) short.class; + case "char": return (Class) char.class; + case "int": return (Class) int.class; + case "long": return (Class) long.class; + case "float": return (Class) float.class; + case "double": return (Class) double.class; + } + return die("Class.forName("+clazz+") error: "+shortError(e)); - } catch (Exception e) { - return die("Class.forName("+clazz+") error: "+shortError(e)); + } catch (Exception e) { + return die("Class.forName("+clazz+") error: "+shortError(e)); + } } + return cached; } public static Collection forNames(String[] classNames) {