| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| package freemarker.cache; |
| |
| import static org.junit.Assert.*; |
| |
| import java.io.IOException; |
| import java.io.Reader; |
| import java.net.URL; |
| import java.util.Locale; |
| |
| import org.hamcrest.Matchers; |
| import org.junit.Test; |
| |
| import freemarker.core.ParseException; |
| import freemarker.template.Configuration; |
| import freemarker.template.MalformedTemplateNameException; |
| import freemarker.template.Template; |
| import freemarker.template.TemplateNotFoundException; |
| import freemarker.template.Version; |
| |
| public class TemplateCacheTest { |
| |
| @Test |
| public void testCachedException() throws Exception { |
| MockTemplateLoader loader = new MockTemplateLoader(); |
| TemplateCache cache = new TemplateCache(loader, new StrongCacheStorage()); |
| cache.setDelay(1000L); |
| loader.setThrowException(true); |
| try { |
| cache.getTemplate("t", Locale.getDefault(), "", true); |
| fail(); |
| } catch (IOException e) { |
| assertEquals("mock IO exception", e.getMessage()); |
| assertEquals(1, loader.getFindCount()); |
| try { |
| cache.getTemplate("t", Locale.getDefault(), "", true); |
| fail(); |
| } catch (IOException e2) { |
| // Still 1 - returned cached exception |
| assertThat(e2.getMessage(), |
| Matchers.allOf(Matchers.containsString("There was an error loading the template on an " + |
| "earlier attempt"))); |
| assertSame(e, e2.getCause()); |
| assertEquals(1, loader.getFindCount()); |
| try { |
| Thread.sleep(1100L); |
| cache.getTemplate("t", Locale.getDefault(), "", true); |
| fail(); |
| } catch (IOException e3) { |
| // Cache had to retest |
| assertEquals("mock IO exception", e.getMessage()); |
| assertEquals(2, loader.getFindCount()); |
| } |
| } |
| } |
| } |
| |
| @Test |
| public void testCachedNotFound() throws Exception { |
| MockTemplateLoader loader = new MockTemplateLoader(); |
| TemplateCache cache = new TemplateCache(loader, new StrongCacheStorage(), new Configuration()); |
| cache.setDelay(1000L); |
| cache.setLocalizedLookup(false); |
| assertNull(cache.getTemplate("t", Locale.getDefault(), "", true)); |
| assertEquals(1, loader.getFindCount()); |
| assertNull(cache.getTemplate("t", Locale.getDefault(), "", true)); |
| // Still 1 - returned cached exception |
| assertEquals(1, loader.getFindCount()); |
| Thread.sleep(1100L); |
| assertNull(cache.getTemplate("t", Locale.getDefault(), "", true)); |
| // Cache had to retest |
| assertEquals(2, loader.getFindCount()); |
| } |
| |
| private static class MockTemplateLoader implements TemplateLoader { |
| private boolean throwException; |
| private int findCount; |
| |
| public void setThrowException(boolean throwException) { |
| this.throwException = throwException; |
| } |
| |
| public int getFindCount() { |
| return findCount; |
| } |
| |
| public void closeTemplateSource(Object templateSource) |
| throws IOException { |
| } |
| |
| public Object findTemplateSource(String name) throws IOException { |
| ++findCount; |
| if (throwException) { |
| throw new IOException("mock IO exception"); |
| } |
| return null; |
| } |
| |
| public long getLastModified(Object templateSource) { |
| return 0; |
| } |
| |
| public Reader getReader(Object templateSource, String encoding) |
| throws IOException { |
| return null; |
| } |
| |
| } |
| |
| @Test |
| public void testManualRemovalPlain() throws IOException { |
| Configuration cfg = new Configuration(); |
| cfg.setCacheStorage(new StrongCacheStorage()); |
| StringTemplateLoader loader = new StringTemplateLoader(); |
| cfg.setTemplateLoader(loader); |
| cfg.setTemplateUpdateDelay(Integer.MAX_VALUE); |
| |
| loader.putTemplate("1.ftl", "1 v1"); |
| loader.putTemplate("2.ftl", "2 v1"); |
| assertEquals("1 v1", cfg.getTemplate("1.ftl").toString()); |
| assertEquals("2 v1", cfg.getTemplate("2.ftl").toString()); |
| |
| loader.putTemplate("1.ftl", "1 v2"); |
| loader.putTemplate("2.ftl", "2 v2"); |
| assertEquals("1 v1", cfg.getTemplate("1.ftl").toString()); // no change |
| assertEquals("2 v1", cfg.getTemplate("2.ftl").toString()); // no change |
| |
| cfg.removeTemplateFromCache("1.ftl"); |
| assertEquals("1 v2", cfg.getTemplate("1.ftl").toString()); // changed |
| assertEquals("2 v1", cfg.getTemplate("2.ftl").toString()); |
| |
| cfg.removeTemplateFromCache("2.ftl"); |
| assertEquals("1 v2", cfg.getTemplate("1.ftl").toString()); |
| assertEquals("2 v2", cfg.getTemplate("2.ftl").toString()); // changed |
| } |
| |
| @Test |
| public void testManualRemovalI18ed() throws IOException { |
| Configuration cfg = new Configuration(); |
| cfg.setCacheStorage(new StrongCacheStorage()); |
| cfg.setLocale(Locale.US); |
| StringTemplateLoader loader = new StringTemplateLoader(); |
| cfg.setTemplateLoader(loader); |
| cfg.setTemplateUpdateDelay(Integer.MAX_VALUE); |
| |
| loader.putTemplate("1_en_US.ftl", "1_en_US v1"); |
| loader.putTemplate("1_en.ftl", "1_en v1"); |
| loader.putTemplate("1.ftl", "1 v1"); |
| |
| assertEquals("1_en_US v1", cfg.getTemplate("1.ftl").toString()); |
| assertEquals("1_en v1", cfg.getTemplate("1.ftl", Locale.UK).toString()); |
| assertEquals("1 v1", cfg.getTemplate("1.ftl", Locale.GERMANY).toString()); |
| |
| loader.putTemplate("1_en_US.ftl", "1_en_US v2"); |
| loader.putTemplate("1_en.ftl", "1_en v2"); |
| loader.putTemplate("1.ftl", "1 v2"); |
| assertEquals("1_en_US v1", cfg.getTemplate("1.ftl").toString()); |
| assertEquals("1_en v1", cfg.getTemplate("1.ftl", Locale.UK).toString()); |
| assertEquals("1 v1", cfg.getTemplate("1.ftl", Locale.GERMANY).toString()); |
| |
| cfg.removeTemplateFromCache("1.ftl"); |
| assertEquals("1_en_US v2", cfg.getTemplate("1.ftl").toString()); |
| assertEquals("1_en v1", cfg.getTemplate("1.ftl", Locale.UK).toString()); |
| assertEquals("1 v1", cfg.getTemplate("1.ftl", Locale.GERMANY).toString()); |
| assertEquals("1 v2", cfg.getTemplate("1.ftl", Locale.ITALY).toString()); |
| |
| cfg.removeTemplateFromCache("1.ftl", Locale.GERMANY); |
| assertEquals("1_en v1", cfg.getTemplate("1.ftl", Locale.UK).toString()); |
| assertEquals("1 v2", cfg.getTemplate("1.ftl", Locale.GERMANY).toString()); |
| |
| cfg.removeTemplateFromCache("1.ftl", Locale.CANADA); |
| assertEquals("1_en v1", cfg.getTemplate("1.ftl", Locale.UK).toString()); |
| |
| cfg.removeTemplateFromCache("1.ftl", Locale.UK); |
| assertEquals("1_en v2", cfg.getTemplate("1.ftl", Locale.UK).toString()); |
| } |
| |
| @Test |
| public void testZeroUpdateDelay() throws IOException { |
| Configuration cfg = new Configuration(); |
| cfg.setLocale(Locale.US); |
| cfg.setCacheStorage(new StrongCacheStorage()); |
| StringTemplateLoader loader = new StringTemplateLoader(); |
| cfg.setTemplateLoader(loader); |
| cfg.setTemplateUpdateDelay(0); |
| for (int i = 1; i <= 3; i++) { |
| loader.putTemplate("t.ftl", "v" + i, i); |
| assertEquals("v" + i, cfg.getTemplate("t.ftl").toString()); |
| } |
| |
| loader.putTemplate("t.ftl", "v10", 10); |
| assertEquals("v10", cfg.getTemplate("t.ftl").toString()); |
| loader.putTemplate("t.ftl", "v11", 10); // same time stamp, different content |
| assertEquals("v10", cfg.getTemplate("t.ftl").toString()); // still v10 |
| assertEquals("v10", cfg.getTemplate("t.ftl").toString()); // still v10 |
| } |
| |
| @Test |
| public void testIncompatibleImprovementsChangesURLConCaching() throws IOException { |
| Version newVersion = Configuration.VERSION_2_3_21; |
| Version oldVersion = Configuration.VERSION_2_3_20; |
| |
| { |
| Configuration cfg = new Configuration(oldVersion); |
| cfg.setTemplateUpdateDelay(0); |
| final MonitoredClassTemplateLoader templateLoader = new MonitoredClassTemplateLoader(); |
| assertNull(templateLoader.getURLConnectionUsesCaches()); |
| cfg.setTemplateLoader(templateLoader); |
| |
| assertNull(templateLoader.getLastTemplateSourceModification()); |
| cfg.getTemplate("test.ftl"); |
| assertNull(templateLoader.getLastTemplateSourceModification()); |
| |
| cfg.setIncompatibleImprovements(newVersion); |
| assertNull(templateLoader.getLastTemplateSourceModification()); |
| cfg.getTemplate("test.ftl"); |
| assertEquals(Boolean.FALSE, templateLoader.getLastTemplateSourceModification()); |
| |
| templateLoader.setURLConnectionUsesCaches(Boolean.valueOf(true)); |
| templateLoader.setLastTemplateSourceModification(null); |
| cfg.getTemplate("test.ftl"); |
| assertNull(templateLoader.getLastTemplateSourceModification()); |
| |
| templateLoader.setURLConnectionUsesCaches(Boolean.valueOf(false)); |
| templateLoader.setLastTemplateSourceModification(null); |
| cfg.getTemplate("test.ftl"); |
| assertNull(templateLoader.getLastTemplateSourceModification()); |
| |
| templateLoader.setURLConnectionUsesCaches(null); |
| templateLoader.setLastTemplateSourceModification(null); |
| cfg.getTemplate("test.ftl"); |
| assertEquals(Boolean.FALSE, templateLoader.getLastTemplateSourceModification()); |
| |
| templateLoader.setURLConnectionUsesCaches(null); |
| cfg.setIncompatibleImprovements(oldVersion); |
| templateLoader.setLastTemplateSourceModification(null); |
| cfg.getTemplate("test.ftl"); |
| assertNull(templateLoader.getLastTemplateSourceModification()); |
| |
| cfg.setTemplateLoader(new MultiTemplateLoader( |
| new TemplateLoader[] { new MultiTemplateLoader( |
| new TemplateLoader[] { templateLoader }) })); |
| cfg.setIncompatibleImprovements(newVersion); |
| cfg.getTemplate("test.ftl"); |
| assertEquals(Boolean.FALSE, templateLoader.getLastTemplateSourceModification()); |
| } |
| } |
| |
| @Test |
| public void testWrongEncodingReload() throws IOException { |
| Configuration cfg = new Configuration(Configuration.VERSION_2_3_22); |
| cfg.setLocale(Locale.US); |
| |
| StringTemplateLoader tl = new StringTemplateLoader(); |
| tl.putTemplate("utf-8_en.ftl", "<#ftl encoding='utf-8'>Foo"); |
| tl.putTemplate("utf-8.ftl", "Bar"); |
| cfg.setTemplateLoader(tl); |
| |
| { |
| Template t = cfg.getTemplate("utf-8.ftl", "Utf-8"); |
| assertEquals("utf-8.ftl", t.getName()); |
| assertEquals("utf-8_en.ftl", t.getSourceName()); |
| assertEquals("Utf-8", t.getEncoding()); |
| assertEquals("Foo", t.toString()); |
| } |
| |
| { |
| Template t = cfg.getTemplate("utf-8.ftl", "Utf-16"); |
| assertEquals("utf-8.ftl", t.getName()); |
| assertEquals("utf-8_en.ftl", t.getSourceName()); |
| assertEquals("utf-8", t.getEncoding()); |
| assertEquals("Foo", t.toString()); |
| } |
| } |
| |
| @Test |
| public void testEncodingSelection() throws IOException { |
| Locale hungary = new Locale("hu", "HU"); |
| |
| Configuration cfg = new Configuration(Configuration.VERSION_2_3_22); |
| cfg.setDefaultEncoding("utf-8"); |
| |
| StringTemplateLoader tl = new StringTemplateLoader(); |
| tl.putTemplate("t.ftl", "Foo"); |
| tl.putTemplate("t_de.ftl", "Vuu"); |
| tl.putTemplate("t2.ftl", "<#ftl encoding='UTF-16LE'>Foo"); |
| tl.putTemplate("t2_de.ftl", "<#ftl encoding='UTF-16BE'>Vuu"); |
| cfg.setTemplateLoader(tl); |
| |
| // No locale-to-encoding mapping exists yet: |
| { |
| Template t = cfg.getTemplate("t.ftl", Locale.GERMANY); |
| assertEquals("t.ftl", t.getName()); |
| assertEquals("t_de.ftl", t.getSourceName()); |
| assertEquals("utf-8", t.getEncoding()); |
| assertEquals("Vuu", t.toString()); |
| } |
| |
| cfg.setEncoding(Locale.GERMANY, "ISO-8859-1"); |
| cfg.setEncoding(hungary, "ISO-8859-2"); |
| { |
| Template t = cfg.getTemplate("t.ftl", Locale.CHINESE); |
| assertEquals("t.ftl", t.getName()); |
| assertEquals("t.ftl", t.getSourceName()); |
| assertEquals("utf-8", t.getEncoding()); |
| assertEquals("Foo", t.toString()); |
| } |
| { |
| Template t = cfg.getTemplate("t.ftl", Locale.GERMANY); |
| assertEquals("t.ftl", t.getName()); |
| assertEquals("t_de.ftl", t.getSourceName()); |
| assertEquals("ISO-8859-1", t.getEncoding()); |
| assertEquals("Vuu", t.toString()); |
| } |
| { |
| Template t = cfg.getTemplate("t.ftl", hungary); |
| assertEquals("t.ftl", t.getName()); |
| assertEquals("t.ftl", t.getSourceName()); |
| assertEquals("ISO-8859-2", t.getEncoding()); |
| assertEquals("Foo", t.toString()); |
| } |
| |
| // #ftl header overrides: |
| { |
| Template t = cfg.getTemplate("t2.ftl", Locale.CHINESE); |
| assertEquals("t2.ftl", t.getName()); |
| assertEquals("t2.ftl", t.getSourceName()); |
| assertEquals("UTF-16LE", t.getEncoding()); |
| assertEquals("Foo", t.toString()); |
| } |
| { |
| Template t = cfg.getTemplate("t2.ftl", Locale.GERMANY); |
| assertEquals("t2.ftl", t.getName()); |
| assertEquals("t2_de.ftl", t.getSourceName()); |
| assertEquals("UTF-16BE", t.getEncoding()); |
| assertEquals("Vuu", t.toString()); |
| } |
| { |
| Template t = cfg.getTemplate("t2.ftl", hungary); |
| assertEquals("t2.ftl", t.getName()); |
| assertEquals("t2.ftl", t.getSourceName()); |
| assertEquals("UTF-16LE", t.getEncoding()); |
| assertEquals("Foo", t.toString()); |
| } |
| } |
| |
| @Test |
| public void testTemplateNameFormatExceptionAndBackwardCompatibility() throws TemplateNotFoundException, MalformedTemplateNameException, ParseException, IOException { |
| Configuration cfg = new Configuration(Configuration.VERSION_2_3_22); |
| |
| assertNull(cfg.getTemplate("../x", null, null, null, true, true)); |
| try { |
| cfg.getTemplate("../x"); |
| fail(); |
| } catch (TemplateNotFoundException e) { |
| // expected |
| } |
| |
| // [2.4] Test it with IcI 2.4 |
| |
| cfg.setTemplateNameFormat(TemplateNameFormat.DEFAULT_2_4_0); |
| try { |
| cfg.getTemplate("../x", null, null, null, true, true); |
| fail(); |
| } catch (MalformedTemplateNameException e) { |
| // expected |
| } |
| try { |
| cfg.getTemplate("../x"); |
| fail(); |
| } catch (MalformedTemplateNameException e) { |
| // expected |
| } |
| } |
| |
| private static class MonitoredClassTemplateLoader extends ClassTemplateLoader { |
| |
| private Boolean lastTemplateSourceModification; |
| |
| public MonitoredClassTemplateLoader() { |
| super(TemplateCacheTest.class, ""); |
| } |
| |
| @Override |
| public Object findTemplateSource(String name) throws IOException { |
| final URL url = getURL(name); |
| if (url == null) return null; |
| return new SpyingURLTemplateSource(url, getURLConnectionUsesCaches()); |
| } |
| |
| Boolean getLastTemplateSourceModification() { |
| return lastTemplateSourceModification; |
| } |
| |
| void setLastTemplateSourceModification(Boolean lastTemplateSourceModification) { |
| this.lastTemplateSourceModification = lastTemplateSourceModification; |
| } |
| |
| private class SpyingURLTemplateSource extends URLTemplateSource { |
| |
| SpyingURLTemplateSource(URL url, Boolean useCaches) throws IOException { |
| super(url, useCaches); |
| } |
| |
| @Override |
| void setUseCaches(boolean useCaches) { |
| setLastTemplateSourceModification(Boolean.valueOf(useCaches)); |
| super.setUseCaches(useCaches); |
| } |
| |
| } |
| |
| } |
| |
| } |