You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@freemarker.apache.org by dd...@apache.org on 2016/02/03 22:48:53 UTC
incubator-freemarker git commit: 1. Added
MultiTemplateLoader.setSticky(boolean) and MultiTemplateLoader.isSticky(),
with which you can disable the default behavior,
where once a template was found in a child TemplateLoader,
it will be searched there fir
Repository: incubator-freemarker
Updated Branches:
refs/heads/2.3.24-gae-stabilization cbf945702 -> 8ee5bc47e
1. Added MultiTemplateLoader.setSticky(boolean) and MultiTemplateLoader.isSticky(), with which you can disable the default behavior, where once a template was found in a child TemplateLoader, it will be searched there first next time (typically, when the template update delay is expired). With the sticky property set to false, the child TemplateLoader-s will be always searched in the order as they were added to the MultiTemplateLoader.
2. Added StringTemplateLoader.removeTemplate(String) method.
Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/8ee5bc47
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/8ee5bc47
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/8ee5bc47
Branch: refs/heads/2.3.24-gae-stabilization
Commit: 8ee5bc47eeb07594bb26e1dae31aa27a82c393e0
Parents: cbf9457
Author: ddekany <dd...@apache.org>
Authored: Wed Feb 3 22:48:27 2016 +0100
Committer: ddekany <dd...@apache.org>
Committed: Wed Feb 3 22:48:27 2016 +0100
----------------------------------------------------------------------
.../freemarker/cache/MultiTemplateLoader.java | 61 ++++++++++++------
.../cache/StatefulTemplateLoader.java | 2 +-
.../freemarker/cache/StringTemplateLoader.java | 13 ++++
src/manual/en_US/book.xml | 20 ++++++
.../cache/MultiTemplateLoaderTest.java | 67 ++++++++++++++++++++
5 files changed, 143 insertions(+), 20 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8ee5bc47/src/main/java/freemarker/cache/MultiTemplateLoader.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/cache/MultiTemplateLoader.java b/src/main/java/freemarker/cache/MultiTemplateLoader.java
index 1500056..c86baa3 100644
--- a/src/main/java/freemarker/cache/MultiTemplateLoader.java
+++ b/src/main/java/freemarker/cache/MultiTemplateLoader.java
@@ -21,25 +21,24 @@ package freemarker.cache;
import java.io.IOException;
import java.io.Reader;
-import java.util.Collections;
-import java.util.HashMap;
import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
/**
* A {@link TemplateLoader} that uses a set of other loaders to load the templates. On every request, loaders are
- * queried in the order of their appearance in the array of loaders provided to the constructor. However, if a request
- * for some template name was already satisfied in the past by one of the loaders, that Loader is queried first (a soft
- * affinity).
+ * queried in the order of their appearance in the array of loaders provided to the constructor. However, by default, if
+ * a request for some template name was already satisfied in the past by one of the loaders, that loader is queried
+ * first (stickiness). This behavior can be disabled with {@link #setSticky(boolean)}, then the loaders are always
+ * queried in the order of their appearance in the array.
*
- * <p>
- * This class is <em>not</em> thread-safe. If it's accessed from multiple threads concurrently, proper synchronization
- * must be provided by the callers. Note that {@link TemplateCache}, the natural user of this class, provides the
- * necessary synchronizations when it uses this class, so then you don't have to worry this.
+ * <p>This class is thread-safe.
*/
public class MultiTemplateLoader implements StatefulTemplateLoader {
private final TemplateLoader[] loaders;
- private final Map lastLoaderForName = Collections.synchronizedMap(new HashMap());
+ private final Map<String, TemplateLoader> lastLoaderForName = new ConcurrentHashMap<String, TemplateLoader>();
+
+ private boolean sticky = true;
/**
* Creates a new multi template Loader that will use the specified loaders.
@@ -53,13 +52,15 @@ public class MultiTemplateLoader implements StatefulTemplateLoader {
public Object findTemplateSource(String name)
throws IOException {
- // Use soft affinity - give the loader that last found this
- // resource a chance to find it again first.
- TemplateLoader lastLoader = (TemplateLoader) lastLoaderForName.get(name);
- if (lastLoader != null) {
- Object source = lastLoader.findTemplateSource(name);
- if (source != null) {
- return new MultiSource(source, lastLoader);
+ if (sticky) {
+ // Use soft affinity - give the loader that last found this
+ // resource a chance to find it again first.
+ TemplateLoader lastLoader = lastLoaderForName.get(name);
+ if (lastLoader != null) {
+ Object source = lastLoader.findTemplateSource(name);
+ if (source != null) {
+ return new MultiSource(source, lastLoader);
+ }
}
}
@@ -71,12 +72,16 @@ public class MultiTemplateLoader implements StatefulTemplateLoader {
TemplateLoader loader = loaders[i];
Object source = loader.findTemplateSource(name);
if (source != null) {
- lastLoaderForName.put(name, loader);
+ if (sticky) {
+ lastLoaderForName.put(name, loader);
+ }
return new MultiSource(source, loader);
}
}
- lastLoaderForName.remove(name);
+ if (sticky) {
+ lastLoaderForName.remove(name);
+ }
// Resource not found
return null;
}
@@ -100,6 +105,9 @@ public class MultiTemplateLoader implements StatefulTemplateLoader {
((MultiSource) templateSource).close();
}
+ /**
+ * Clears the soft affinity memory, also resets all enclosed {@link StatefulTemplateLoader}-s.
+ */
public void resetState() {
lastLoaderForName.clear();
for (int i = 0; i < loaders.length; i++) {
@@ -200,4 +208,19 @@ public class MultiTemplateLoader implements StatefulTemplateLoader {
return loaders[index];
}
+ /**
+ * @since 2.3.24
+ */
+ public boolean isSticky() {
+ return sticky;
+ }
+
+ /**
+ * @since 2.3.24
+ */
+ public void setSticky(boolean sticky) {
+ this.sticky = sticky;
+ }
+
+
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8ee5bc47/src/main/java/freemarker/cache/StatefulTemplateLoader.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/cache/StatefulTemplateLoader.java b/src/main/java/freemarker/cache/StatefulTemplateLoader.java
index 0387793..0e0a21c 100644
--- a/src/main/java/freemarker/cache/StatefulTemplateLoader.java
+++ b/src/main/java/freemarker/cache/StatefulTemplateLoader.java
@@ -22,7 +22,7 @@ package freemarker.cache;
import freemarker.template.Configuration;
/**
- * Interface that can be implemented by template loaders that maintain some
+ * Interface that can be implemented by {@link TemplateLoader}-s that maintain some
* sort of internal state (i.e. caches of earlier lookups for performance
* optimization purposes etc.) and support resetting of their state.
*/
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8ee5bc47/src/main/java/freemarker/cache/StringTemplateLoader.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/cache/StringTemplateLoader.java b/src/main/java/freemarker/cache/StringTemplateLoader.java
index e3ccf1a..8b8bd4a 100644
--- a/src/main/java/freemarker/cache/StringTemplateLoader.java
+++ b/src/main/java/freemarker/cache/StringTemplateLoader.java
@@ -94,6 +94,19 @@ public class StringTemplateLoader implements TemplateLoader {
templates.put(name, new StringTemplateSource(name, templateSource, lastModified));
}
+ /**
+ * Removes the template with the specified name if it was added earlier.
+ *
+ * @param name Exactly the key with which the template was added.
+ *
+ * @return Whether a template was found with the given key (and hence was removed now)
+ *
+ * @since 2.3.24
+ */
+ public boolean removeTemplate(String name) {
+ return templates.remove(name) != null;
+ }
+
public void closeTemplateSource(Object templateSource) {
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8ee5bc47/src/manual/en_US/book.xml
----------------------------------------------------------------------
diff --git a/src/manual/en_US/book.xml b/src/manual/en_US/book.xml
index 0ea368d..e4fb10e 100644
--- a/src/manual/en_US/book.xml
+++ b/src/manual/en_US/book.xml
@@ -26987,6 +26987,26 @@ TemplateModel x = env.getVariable("x"); // get variable x</programlisting>
</listitem>
<listitem>
+ <para>Added
+ <literal>MultiTemplateLoader.setSticky(boolean)</literal> and
+ <literal>MultiTemplateLoader.isSticky()</literal>, with which
+ you can disable the default behavior, where once a template was
+ found in a child <literal>TemplateLoader</literal>, it will be
+ searched there first next time (typically, when the template
+ update delay is expired). With the <literal>sticky</literal>
+ property set to <literal>false</literal>, the child
+ <literal>TemplateLoader</literal>-s will be always searched in
+ the order as they were added to the
+ <literal>MultiTemplateLoader</literal>.</para>
+ </listitem>
+
+ <listitem>
+ <para>Added
+ <literal>StringTemplateLoader.removeTemplate(String)</literal>
+ method.</para>
+ </listitem>
+
+ <listitem>
<para>Bug fixed, only with
<literal>incompatible_improvements</literal> set to 2.3.24
(<link linkend="topic.defaultObjectWrapperIcI">see how
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8ee5bc47/src/test/java/freemarker/cache/MultiTemplateLoaderTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/cache/MultiTemplateLoaderTest.java b/src/test/java/freemarker/cache/MultiTemplateLoaderTest.java
new file mode 100644
index 0000000..c67ca76
--- /dev/null
+++ b/src/test/java/freemarker/cache/MultiTemplateLoaderTest.java
@@ -0,0 +1,67 @@
+package freemarker.cache;
+
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+
+import org.apache.commons.io.IOUtils;
+import org.junit.Test;
+
+public class MultiTemplateLoaderTest {
+
+ @Test
+ public void testBasics() throws IOException {
+ StringTemplateLoader stl1 = new StringTemplateLoader();
+ stl1.putTemplate("1.ftl", "1");
+ stl1.putTemplate("both.ftl", "both 1");
+
+ StringTemplateLoader stl2 = new StringTemplateLoader();
+ stl2.putTemplate("2.ftl", "2");
+ stl2.putTemplate("both.ftl", "both 2");
+
+ MultiTemplateLoader mtl = new MultiTemplateLoader(new TemplateLoader[] { stl1, stl2 });
+ assertEquals("1", getTemplate(mtl, "1.ftl"));
+ assertEquals("2", getTemplate(mtl, "2.ftl"));
+ assertEquals("both 1", getTemplate(mtl, "both.ftl"));
+ assertNull(getTemplate(mtl, "neither.ftl"));
+ }
+
+ @Test
+ public void testSticky() throws IOException {
+ testStickiness(true);
+ }
+
+ @Test
+ public void testNonSticky() throws IOException {
+ testStickiness(false);
+ }
+
+ private void testStickiness(boolean sticky) throws IOException {
+ StringTemplateLoader stl1 = new StringTemplateLoader();
+ stl1.putTemplate("both.ftl", "both 1");
+
+ StringTemplateLoader stl2 = new StringTemplateLoader();
+ stl2.putTemplate("both.ftl", "both 2");
+
+ MultiTemplateLoader mtl = new MultiTemplateLoader(new TemplateLoader[] { stl1, stl2 });
+ mtl.setSticky(sticky);
+
+ assertEquals("both 1", getTemplate(mtl, "both.ftl"));
+ assertTrue(stl1.removeTemplate("both.ftl"));
+ assertEquals("both 2", getTemplate(mtl, "both.ftl"));
+ stl1.putTemplate("both.ftl", "both 1");
+ assertEquals(sticky ? "both 2" : "both 1", getTemplate(mtl, "both.ftl"));
+ assertTrue(stl2.removeTemplate("both.ftl"));
+ assertEquals("both 1", getTemplate(mtl, "both.ftl"));
+ }
+
+ private String getTemplate(TemplateLoader tl, String name) throws IOException {
+ Object tSrc = tl.findTemplateSource(name);
+ if (tSrc == null) {
+ return null;
+ }
+
+ return IOUtils.toString(tl.getReader(tSrc, "UTF-8"));
+ }
+
+}