This is an automated email from the ASF dual-hosted git repository.
ddekany pushed a commit to branch FREEMARKER-35
in repository https://gitbox.apache.org/repos/asf/freemarker.git
The following commit(s) were added to refs/heads/FREEMARKER-35 by this push:
new b42a533c FREEMARKER-35: Added caching to
JavaTemplateTemporalFormatFactory
b42a533c is described below
commit b42a533c7d10b47bf77439adc5c0055924557f80
Author: ddekany <[email protected]>
AuthorDate: Sat Aug 27 19:19:20 2022 +0200
FREEMARKER-35: Added caching to JavaTemplateTemporalFormatFactory
---
.../java/freemarker/core/FastLRUKeyValueStore.java | 5 +++
.../core/JavaTemplateTemporalFormatFactory.java | 45 +++++++++++++++++++++-
...rmatByFormatStringCachingInEnvironmentTest.java | 4 ++
...poralFormatCurrentCachingInEnvironmentTest.java | 38 ++++++++++--------
4 files changed, 74 insertions(+), 18 deletions(-)
diff --git a/src/main/java/freemarker/core/FastLRUKeyValueStore.java
b/src/main/java/freemarker/core/FastLRUKeyValueStore.java
index db774162..9cbf465a 100644
--- a/src/main/java/freemarker/core/FastLRUKeyValueStore.java
+++ b/src/main/java/freemarker/core/FastLRUKeyValueStore.java
@@ -82,4 +82,9 @@ class FastLRUKeyValueStore<K, V> {
return putIfAbsentThenReturnStored(cacheKey, value);
}
+
+ void clear() {
+ olderEntries.clear();
+ recentEntries.clear();
+ }
}
diff --git
a/src/main/java/freemarker/core/JavaTemplateTemporalFormatFactory.java
b/src/main/java/freemarker/core/JavaTemplateTemporalFormatFactory.java
index ac4228bf..2ad345c3 100644
--- a/src/main/java/freemarker/core/JavaTemplateTemporalFormatFactory.java
+++ b/src/main/java/freemarker/core/JavaTemplateTemporalFormatFactory.java
@@ -21,11 +21,15 @@ package freemarker.core;
import java.time.temporal.Temporal;
import java.util.Locale;
+import java.util.Objects;
import java.util.TimeZone;
class JavaTemplateTemporalFormatFactory extends TemplateTemporalFormatFactory {
public static final JavaTemplateTemporalFormatFactory INSTANCE = new
JavaTemplateTemporalFormatFactory();
+ private final FastLRUKeyValueStore<CacheKey, JavaTemplateTemporalFormat>
formatCache =
+ new FastLRUKeyValueStore<>(512);
+
private JavaTemplateTemporalFormatFactory() {
// Not instantiated from outside
}
@@ -34,7 +38,46 @@ class JavaTemplateTemporalFormatFactory extends
TemplateTemporalFormatFactory {
public TemplateTemporalFormat get(
String params, Class<? extends Temporal> temporalClass, Locale
locale, TimeZone timeZone, Environment env)
throws TemplateValueFormatException {
- return new JavaTemplateTemporalFormat(params, temporalClass, locale,
timeZone);
+ CacheKey cacheKey = new CacheKey(params, temporalClass, locale,
timeZone);
+ JavaTemplateTemporalFormat format = formatCache.get(cacheKey);
+ if (format == null) {
+ format = new JavaTemplateTemporalFormat(params, temporalClass,
locale, timeZone);
+ format = formatCache.putIfAbsentThenReturnStored(cacheKey, format);
+ }
+ // JavaTemplateTemporalFormat-s use a
java.time.format.DateTimeFormatter internally, and so are thread safe,
+ // and immutable, so we don't have to clone() anything (unlike in
JavaTemplateDateFormatFactory):
+ return format;
+ }
+
+ void clear() {
+ formatCache.clear();
}
+ private static class CacheKey {
+ private final String params;
+ private final Class<? extends Temporal> temporalClass;
+ private final Locale locale;
+ private final TimeZone timeZone;
+
+ public CacheKey(String params, Class<? extends Temporal>
temporalClass, Locale locale, TimeZone timeZone) {
+ this.params = params;
+ this.temporalClass = temporalClass;
+ this.locale = locale;
+ this.timeZone = timeZone;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ CacheKey cacheKey = (CacheKey) o;
+ return params.equals(cacheKey.params) &&
temporalClass.equals(cacheKey.temporalClass) &&
+ locale.equals(cacheKey.locale) &&
timeZone.equals(cacheKey.timeZone);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(params, temporalClass, locale, timeZone);
+ }
+ }
}
diff --git
a/src/test/java/freemarker/core/TemplateTemporalFormatByFormatStringCachingInEnvironmentTest.java
b/src/test/java/freemarker/core/TemplateTemporalFormatByFormatStringCachingInEnvironmentTest.java
index cda9eb74..262fd4c3 100644
---
a/src/test/java/freemarker/core/TemplateTemporalFormatByFormatStringCachingInEnvironmentTest.java
+++
b/src/test/java/freemarker/core/TemplateTemporalFormatByFormatStringCachingInEnvironmentTest.java
@@ -170,12 +170,14 @@ public class
TemplateTemporalFormatByFormatStringCachingInEnvironmentTest
for (int i = 1; i <= 3; i++) {
env.setLocale(locales.get(locales.size() - i));
+ JavaTemplateTemporalFormatFactory.INSTANCE.clear();
assertSame(
env.getTemplateTemporalFormat(formatString,
LocalDate.class),
onceCachedFormats.get(onceCachedFormats.size() - i));
}
env.setLocale(locales.get(0));
+ JavaTemplateTemporalFormatFactory.INSTANCE.clear();
assertNotSame(
env.getTemplateTemporalFormat(formatString, LocalDate.class),
onceCachedFormats.get(0));
@@ -202,6 +204,7 @@ public class
TemplateTemporalFormatByFormatStringCachingInEnvironmentTest
TemplateValueFormatException {
for (int valueIndex = 0; valueIndex <
settingAssignments.numberOfValues(); valueIndex++) {
settingAssignments.execute(env, valueIndex);
+ JavaTemplateTemporalFormatFactory.INSTANCE.clear();
assertSame(env.getTemplateTemporalFormat(temporalClass),
cachedFormats.get(valueIndex));
}
}
@@ -212,6 +215,7 @@ public class
TemplateTemporalFormatByFormatStringCachingInEnvironmentTest
TemplateValueFormatException {
for (int valueIndex = 0; valueIndex <
settingAssignments.numberOfValues(); valueIndex++) {
String formatString = settingAssignments.getValue(valueIndex);
+ JavaTemplateTemporalFormatFactory.INSTANCE.clear();
assertSame(env.getTemplateTemporalFormat(formatString,
temporalClass), cachedFormats.get(valueIndex));
}
}
diff --git
a/src/test/java/freemarker/core/TemplateTemporalFormatCurrentCachingInEnvironmentTest.java
b/src/test/java/freemarker/core/TemplateTemporalFormatCurrentCachingInEnvironmentTest.java
index 86db4b05..dc02becf 100644
---
a/src/test/java/freemarker/core/TemplateTemporalFormatCurrentCachingInEnvironmentTest.java
+++
b/src/test/java/freemarker/core/TemplateTemporalFormatCurrentCachingInEnvironmentTest.java
@@ -161,50 +161,46 @@ public class
TemplateTemporalFormatCurrentCachingInEnvironmentTest
TemplateTemporalFormat lastFormat;
TemplateTemporalFormat newFormat;
- // Note: We call
env.clearCachedTemplateTemporalFormatsByFormatString() directly before all
- // env.getTemplateTemporalFormat calls, just to avoid that 2nd level
of cache hiding any bugs in the level
- // that we want to test here. But in almost all of these tests
scenarios it shouldn't have any effect anyway.
-
- env.clearCachedTemplateTemporalFormatsByFormatString();
+ clearHigherLevelCaches(env);
lastFormat = env.getTemplateTemporalFormat(temporalClass);
// Assert that it keeps returning the same instance from cache:
- env.clearCachedTemplateTemporalFormatsByFormatString();
+ clearHigherLevelCaches(env);
assertSame(lastFormat, env.getTemplateTemporalFormat(temporalClass));
- env.clearCachedTemplateTemporalFormatsByFormatString();
+ clearHigherLevelCaches(env);
assertSame(lastFormat, env.getTemplateTemporalFormat(temporalClass));
settingAssignments.execute(env, 0);
// Assert that the cache wasn't cleared when the setting was set to
the same value again:
- env.clearCachedTemplateTemporalFormatsByFormatString();
+ clearHigherLevelCaches(env);
assertSame(lastFormat, env.getTemplateTemporalFormat(temporalClass));
env.setLocale(Locale.JAPAN); // Possibly clears non-reusable
TemplateTemporalFormatCache field
- env.clearCachedTemplateTemporalFormatsByFormatString();
+ clearHigherLevelCaches(env);
newFormat = env.getTemplateTemporalFormat(temporalClass);
if (localeDependent) {
assertNotSame(lastFormat, newFormat);
} else {
- env.clearCachedTemplateTemporalFormatsByFormatString();
+ clearHigherLevelCaches(env);
assertSame(lastFormat,
env.getTemplateTemporalFormat(temporalClass));
}
lastFormat = newFormat;
env.setLocale(Locale.JAPAN);
- env.clearCachedTemplateTemporalFormatsByFormatString();
+ clearHigherLevelCaches(env);
assertSame(lastFormat, env.getTemplateTemporalFormat(temporalClass));
env.setLocale(Locale.GERMANY); // Possibly clears non-reusable
TemplateTemporalFormatCache field
env.setLocale(Locale.JAPAN);
// Assert that it restores the same instance from
TemplateTemporalFormatCache.reusableXxx field:
- env.clearCachedTemplateTemporalFormatsByFormatString();
+ clearHigherLevelCaches(env);
assertSame(lastFormat, env.getTemplateTemporalFormat(temporalClass));
env.setTimeZone(OTHER_TIME_ZONE); // Possibly clears non-reusable
TemplateTemporalFormatCache field
- env.clearCachedTemplateTemporalFormatsByFormatString();
+ clearHigherLevelCaches(env);
newFormat = env.getTemplateTemporalFormat(temporalClass);
if (timeZoneDependent) {
assertNotSame(newFormat, lastFormat);
- env.clearCachedTemplateTemporalFormatsByFormatString();
+ clearHigherLevelCaches(env);
assertSame(newFormat,
env.getTemplateTemporalFormat(temporalClass));
} else {
assertSame(newFormat, lastFormat);
@@ -213,19 +209,27 @@ public class
TemplateTemporalFormatCurrentCachingInEnvironmentTest
env.setTimeZone(DateUtil.UTC); // Possibly clears non-reusable
TemplateTemporalFormatCache field
env.setTimeZone(OTHER_TIME_ZONE);
- env.clearCachedTemplateTemporalFormatsByFormatString();
+ clearHigherLevelCaches(env);
// Assert that it restores the same instance from
TemplateTemporalFormatCache.reusableXxx field:
assertSame(lastFormat, env.getTemplateTemporalFormat(temporalClass));
settingAssignments.execute(env, 1); // Clears even
TemplateTemporalFormatCache.reusableXxx
- env.clearCachedTemplateTemporalFormatsByFormatString();
+ clearHigherLevelCaches(env);
newFormat = env.getTemplateTemporalFormat(temporalClass);
assertNotSame(lastFormat, newFormat);
settingAssignments.execute(env, 0); // Clears even
TemplateTemporalFormatCache.reusableXxx
- env.clearCachedTemplateTemporalFormatsByFormatString();
+ clearHigherLevelCaches(env);
newFormat = env.getTemplateTemporalFormat(temporalClass);
assertNotSame(lastFormat, newFormat);
}
+ /**
+ * Clears caches that are higher level than the "current format", so that
we can test the last reliably.
+ */
+ private void clearHigherLevelCaches(Environment env) {
+ JavaTemplateTemporalFormatFactory.INSTANCE.clear();
+ env.clearCachedTemplateTemporalFormatsByFormatString();
+ }
+
}