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/03/26 16:41:28 UTC

[14/32] 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 t

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
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"));
+    }
+    
+}