http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b0e08ccb/src/main/java/org/apache/freemarker/core/templateresolver/impl/MruCacheStorage.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/templateresolver/impl/MruCacheStorage.java b/src/main/java/org/apache/freemarker/core/templateresolver/impl/MruCacheStorage.java new file mode 100644 index 0000000..62fe137 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/templateresolver/impl/MruCacheStorage.java @@ -0,0 +1,325 @@ +/* + * 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 org.apache.freemarker.core.templateresolver.impl; + +import java.lang.ref.ReferenceQueue; +import java.lang.ref.SoftReference; +import java.util.HashMap; +import java.util.Map; + +import org.apache.freemarker.core.templateresolver.CacheStorage; +import org.apache.freemarker.core.templateresolver.CacheStorageWithGetSize; + +/** + * A cache storage that implements a two-level Most Recently Used cache. In the + * first level, items are strongly referenced up to the specified maximum. When + * the maximum is exceeded, the least recently used item is moved into the + * second level cache, where they are softly referenced, up to another + * specified maximum. When the second level maximum is also exceeded, the least + * recently used item is discarded altogether. This cache storage is a + * generalization of both {@link StrongCacheStorage} and + * {@link SoftCacheStorage} - the effect of both of them can be achieved by + * setting one maximum to zero and the other to the largest positive integer. + * On the other hand, if you wish to use this storage in a strong-only mode, or + * in a soft-only mode, you might consider using {@link StrongCacheStorage} or + * {@link SoftCacheStorage} instead, as they can be used by + * {@link DefaultTemplateResolver} concurrently without any synchronization on a 5.0 or + * later JRE. + * + * <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 DefaultTemplateResolver}, the natural user of this class provides the + * necessary synchronizations when it uses the class. + * Also you might consider whether you need this sort of a mixed storage at all + * in your solution, as in most cases SoftCacheStorage can also be sufficient. + * SoftCacheStorage will use Java soft references, and they already use access + * timestamps internally to bias the garbage collector against clearing + * recently used references, so you can get reasonably good (and + * memory-sensitive) most-recently-used caching through + * {@link SoftCacheStorage} as well. + * + * @see org.apache.freemarker.core.Configuration#setCacheStorage(CacheStorage) + */ +public class MruCacheStorage implements CacheStorageWithGetSize { + private final MruEntry strongHead = new MruEntry(); + private final MruEntry softHead = new MruEntry(); + { + softHead.linkAfter(strongHead); + } + private final Map map = new HashMap(); + private final ReferenceQueue refQueue = new ReferenceQueue(); + private final int strongSizeLimit; + private final int softSizeLimit; + private int strongSize = 0; + private int softSize = 0; + + /** + * Creates a new MRU cache storage with specified maximum cache sizes. Each + * cache size can vary between 0 and {@link Integer#MAX_VALUE}. + * @param strongSizeLimit the maximum number of strongly referenced templates; when exceeded, the entry used + * the least recently will be moved into the soft cache. + * @param softSizeLimit the maximum number of softly referenced templates; when exceeded, the entry used + * the least recently will be discarded. + */ + public MruCacheStorage(int strongSizeLimit, int softSizeLimit) { + if (strongSizeLimit < 0) throw new IllegalArgumentException("strongSizeLimit < 0"); + if (softSizeLimit < 0) throw new IllegalArgumentException("softSizeLimit < 0"); + this.strongSizeLimit = strongSizeLimit; + this.softSizeLimit = softSizeLimit; + } + + public Object get(Object key) { + removeClearedReferences(); + MruEntry entry = (MruEntry) map.get(key); + if (entry == null) { + return null; + } + relinkEntryAfterStrongHead(entry, null); + Object value = entry.getValue(); + if (value instanceof MruReference) { + // This can only happen with strongSizeLimit == 0 + return ((MruReference) value).get(); + } + return value; + } + + public void put(Object key, Object value) { + removeClearedReferences(); + MruEntry entry = (MruEntry) map.get(key); + if (entry == null) { + entry = new MruEntry(key, value); + map.put(key, entry); + linkAfterStrongHead(entry); + } else { + relinkEntryAfterStrongHead(entry, value); + } + + } + + public void remove(Object key) { + removeClearedReferences(); + removeInternal(key); + } + + private void removeInternal(Object key) { + MruEntry entry = (MruEntry) map.remove(key); + if (entry != null) { + unlinkEntryAndInspectIfSoft(entry); + } + } + + public void clear() { + strongHead.makeHead(); + softHead.linkAfter(strongHead); + map.clear(); + strongSize = softSize = 0; + // Quick refQueue processing + while (refQueue.poll() != null); + } + + private void relinkEntryAfterStrongHead(MruEntry entry, Object newValue) { + if (unlinkEntryAndInspectIfSoft(entry) && newValue == null) { + // Turn soft reference into strong reference, unless is was cleared + MruReference mref = (MruReference) entry.getValue(); + Object strongValue = mref.get(); + if (strongValue != null) { + entry.setValue(strongValue); + linkAfterStrongHead(entry); + } else { + map.remove(mref.getKey()); + } + } else { + if (newValue != null) { + entry.setValue(newValue); + } + linkAfterStrongHead(entry); + } + } + + private void linkAfterStrongHead(MruEntry entry) { + entry.linkAfter(strongHead); + if (strongSize == strongSizeLimit) { + // softHead.previous is LRU strong entry + MruEntry lruStrong = softHead.getPrevious(); + // Attila: This is equaivalent to strongSizeLimit != 0 + // DD: But entry.linkAfter(strongHead) was just executed above, so + // lruStrong != strongHead is true even if strongSizeLimit == 0. + if (lruStrong != strongHead) { + lruStrong.unlink(); + if (softSizeLimit > 0) { + lruStrong.linkAfter(softHead); + lruStrong.setValue(new MruReference(lruStrong, refQueue)); + if (softSize == softSizeLimit) { + // List is circular, so strongHead.previous is LRU soft entry + MruEntry lruSoft = strongHead.getPrevious(); + lruSoft.unlink(); + map.remove(lruSoft.getKey()); + } else { + ++softSize; + } + } else { + map.remove(lruStrong.getKey()); + } + } + } else { + ++strongSize; + } + } + + private boolean unlinkEntryAndInspectIfSoft(MruEntry entry) { + entry.unlink(); + if (entry.getValue() instanceof MruReference) { + --softSize; + return true; + } else { + --strongSize; + return false; + } + } + + private void removeClearedReferences() { + for (; ; ) { + MruReference ref = (MruReference) refQueue.poll(); + if (ref == null) { + break; + } + removeInternal(ref.getKey()); + } + } + + /** + * Returns the configured upper limit of the number of strong cache entries. + * + * @since 2.3.21 + */ + public int getStrongSizeLimit() { + return strongSizeLimit; + } + + /** + * Returns the configured upper limit of the number of soft cache entries. + * + * @since 2.3.21 + */ + public int getSoftSizeLimit() { + return softSizeLimit; + } + + /** + * Returns the <em>current</em> number of strong cache entries. + * + * @see #getStrongSizeLimit() + * @since 2.3.21 + */ + public int getStrongSize() { + return strongSize; + } + + /** + * Returns a close approximation of the <em>current</em> number of soft cache entries. + * + * @see #getSoftSizeLimit() + * @since 2.3.21 + */ + public int getSoftSize() { + removeClearedReferences(); + return softSize; + } + + /** + * Returns a close approximation of the current number of cache entries. + * + * @see #getStrongSize() + * @see #getSoftSize() + * @since 2.3.21 + */ + public int getSize() { + return getSoftSize() + getStrongSize(); + } + + private static final class MruEntry { + private MruEntry prev; + private MruEntry next; + private final Object key; + private Object value; + + /** + * Used solely to construct the head element + */ + MruEntry() { + makeHead(); + key = value = null; + } + + MruEntry(Object key, Object value) { + this.key = key; + this.value = value; + } + + Object getKey() { + return key; + } + + Object getValue() { + return value; + } + + void setValue(Object value) { + this.value = value; + } + + MruEntry getPrevious() { + return prev; + } + + void linkAfter(MruEntry entry) { + next = entry.next; + entry.next = this; + prev = entry; + next.prev = this; + } + + void unlink() { + next.prev = prev; + prev.next = next; + prev = null; + next = null; + } + + void makeHead() { + prev = next = this; + } + } + + private static class MruReference extends SoftReference { + private final Object key; + + MruReference(MruEntry entry, ReferenceQueue queue) { + super(entry.getValue(), queue); + this.key = entry.getKey(); + } + + Object getKey() { + return key; + } + } + + +} \ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b0e08ccb/src/main/java/org/apache/freemarker/core/templateresolver/impl/MultiTemplateLoader.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/templateresolver/impl/MultiTemplateLoader.java b/src/main/java/org/apache/freemarker/core/templateresolver/impl/MultiTemplateLoader.java new file mode 100644 index 0000000..f7841fb --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/templateresolver/impl/MultiTemplateLoader.java @@ -0,0 +1,172 @@ +/* + * 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 org.apache.freemarker.core.templateresolver.impl; + +import java.io.IOException; +import java.io.Serializable; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.freemarker.core.templateresolver.TemplateLoader; +import org.apache.freemarker.core.templateresolver.TemplateLoaderSession; +import org.apache.freemarker.core.templateresolver.TemplateLoadingResult; +import org.apache.freemarker.core.templateresolver.TemplateLoadingResultStatus; +import org.apache.freemarker.core.templateresolver.TemplateLoadingSource; +import org.apache.freemarker.core.util.NullArgumentException; + +/** + * 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. Except, when the + * {@linkplain #setSticky(boolean)} sticky} setting is set to {@code true} (default is false {@code false}), if + * a request for some template name was already satisfied in the past by one of the loaders, that loader is queried + * first (stickiness). + * + * <p>This class is thread-safe. + */ +// TODO JUnit test +public class MultiTemplateLoader implements TemplateLoader { + + private final TemplateLoader[] templateLoaders; + private final Map<String, TemplateLoader> lastTemplateLoaderForName = new ConcurrentHashMap<String, TemplateLoader>(); + + private boolean sticky = false; + + /** + * Creates a new instance that will use the specified template loaders. + * + * @param templateLoaders + * the template loaders that are used to load templates, in the order as they will be searched + * (except where {@linkplain #setSticky(boolean) stickiness} says otherwise). + */ + public MultiTemplateLoader(TemplateLoader... templateLoaders) { + NullArgumentException.check("templateLoaders", templateLoaders); + this.templateLoaders = templateLoaders.clone(); + } + + /** + * Clears the sickiness memory, also resets the state of all enclosed {@link TemplateLoader}-s. + */ + @Override + public void resetState() { + lastTemplateLoaderForName.clear(); + for (TemplateLoader templateLoader : templateLoaders) { + templateLoader.resetState(); + } + } + + /** + * Show class name and some details that are useful in template-not-found errors. + * + * @since 2.3.21 + */ + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("MultiTemplateLoader("); + for (int i = 0; i < templateLoaders.length; i++) { + if (i != 0) { + sb.append(", "); + } + sb.append("loader").append(i + 1).append(" = ").append(templateLoaders[i]); + } + sb.append(")"); + return sb.toString(); + } + + /** + * Returns the number of {@link TemplateLoader}-s directly inside this {@link TemplateLoader}. + * + * @since 2.3.23 + */ + public int getTemplateLoaderCount() { + return templateLoaders.length; + } + + /** + * Returns the {@link TemplateLoader} at the given index. + * + * @param index + * Must be below {@link #getTemplateLoaderCount()}. + */ + public TemplateLoader getTemplateLoader(int index) { + return templateLoaders[index]; + } + + /** + * Getter pair of {@link #setSticky(boolean)}. + */ + public boolean isSticky() { + return sticky; + } + + /** + * Sets if for a name that was already loaded earlier the same {@link TemplateLoader} will be tried first, or + * we always try the {@link TemplateLoader}-s strictly in the order as it was specified in the constructor. + * The default is {@code false}. + */ + public void setSticky(boolean sticky) { + this.sticky = sticky; + } + + @Override + public TemplateLoaderSession createSession() { + return null; + } + + @Override + public TemplateLoadingResult load(String name, TemplateLoadingSource ifSourceDiffersFrom, + Serializable ifVersionDiffersFrom, TemplateLoaderSession session) throws IOException { + TemplateLoader lastLoader = null; + if (sticky) { + // Use soft affinity - give the loader that last found this + // resource a chance to find it again first. + lastLoader = lastTemplateLoaderForName.get(name); + if (lastLoader != null) { + TemplateLoadingResult result = lastLoader.load(name, ifSourceDiffersFrom, ifVersionDiffersFrom, session); + if (result.getStatus() != TemplateLoadingResultStatus.NOT_FOUND) { + return result; + } + } + } + + // If there is no affine loader, or it could not find the resource + // again, try all loaders in order of appearance. If any manages + // to find the resource, then associate it as the new affine loader + // for this resource. + for (TemplateLoader templateLoader : templateLoaders) { + if (lastLoader != templateLoader) { + TemplateLoadingResult result = templateLoader.load( + name, ifSourceDiffersFrom, ifVersionDiffersFrom, session); + if (result.getStatus() != TemplateLoadingResultStatus.NOT_FOUND) { + if (sticky) { + lastTemplateLoaderForName.put(name, templateLoader); + } + return result; + } + } + } + + if (sticky) { + lastTemplateLoaderForName.remove(name); + } + return TemplateLoadingResult.NOT_FOUND; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b0e08ccb/src/main/java/org/apache/freemarker/core/templateresolver/impl/NullCacheStorage.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/templateresolver/impl/NullCacheStorage.java b/src/main/java/org/apache/freemarker/core/templateresolver/impl/NullCacheStorage.java new file mode 100644 index 0000000..9bc24eb --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/templateresolver/impl/NullCacheStorage.java @@ -0,0 +1,70 @@ +/* + * 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 org.apache.freemarker.core.templateresolver.impl; + +import org.apache.freemarker.core.templateresolver.CacheStorage; +import org.apache.freemarker.core.templateresolver.CacheStorageWithGetSize; +import org.apache.freemarker.core.templateresolver.ConcurrentCacheStorage; + +/** + * A cache storage that doesn't store anything. Use this if you + * don't want caching. + * + * @see org.apache.freemarker.core.Configuration#setCacheStorage(CacheStorage) + * + * @since 2.3.17 + */ +public class NullCacheStorage implements ConcurrentCacheStorage, CacheStorageWithGetSize { + + /** + * @since 2.3.22 + */ + public static final NullCacheStorage INSTANCE = new NullCacheStorage(); + + public boolean isConcurrent() { + return true; + } + + public Object get(Object key) { + return null; + } + + public void put(Object key, Object value) { + // do nothing + } + + public void remove(Object key) { + // do nothing + } + + public void clear() { + // do nothing + } + + /** + * Always returns 0. + * + * @since 2.3.21 + */ + public int getSize() { + return 0; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b0e08ccb/src/main/java/org/apache/freemarker/core/templateresolver/impl/SoftCacheStorage.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/templateresolver/impl/SoftCacheStorage.java b/src/main/java/org/apache/freemarker/core/templateresolver/impl/SoftCacheStorage.java new file mode 100644 index 0000000..cb0d963 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/templateresolver/impl/SoftCacheStorage.java @@ -0,0 +1,144 @@ +/* + * 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 org.apache.freemarker.core.templateresolver.impl; + +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.SoftReference; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import org.apache.freemarker.core.templateresolver.CacheStorage; +import org.apache.freemarker.core.templateresolver.CacheStorageWithGetSize; +import org.apache.freemarker.core.templateresolver.ConcurrentCacheStorage; +import org.apache.freemarker.core.util.UndeclaredThrowableException; + +/** + * Soft cache storage is a cache storage that uses {@link SoftReference} objects to hold the objects it was passed, + * therefore allows the garbage collector to purge the cache when it determines that it wants to free up memory. This + * class is thread-safe to the extent that its underlying map is. The parameterless constructor uses a thread-safe map + * since 2.3.24 or Java 5. + * + * @see org.apache.freemarker.core.Configuration#setCacheStorage(CacheStorage) + */ +public class SoftCacheStorage implements ConcurrentCacheStorage, CacheStorageWithGetSize { + private static final Method atomicRemove = getAtomicRemoveMethod(); + + private final ReferenceQueue queue = new ReferenceQueue(); + private final Map map; + private final boolean concurrent; + + /** + * Creates an instance that uses a {@link ConcurrentMap} internally. + */ + public SoftCacheStorage() { + this(new ConcurrentHashMap()); + } + + /** + * Returns true if the underlying Map is a {@code ConcurrentMap}. + */ + public boolean isConcurrent() { + return concurrent; + } + + public SoftCacheStorage(Map backingMap) { + map = backingMap; + this.concurrent = map instanceof ConcurrentMap; + } + + public Object get(Object key) { + processQueue(); + Reference ref = (Reference) map.get(key); + return ref == null ? null : ref.get(); + } + + public void put(Object key, Object value) { + processQueue(); + map.put(key, new SoftValueReference(key, value, queue)); + } + + public void remove(Object key) { + processQueue(); + map.remove(key); + } + + public void clear() { + map.clear(); + processQueue(); + } + + /** + * Returns a close approximation of the number of cache entries. + * + * @since 2.3.21 + */ + public int getSize() { + processQueue(); + return map.size(); + } + + private void processQueue() { + for (; ; ) { + SoftValueReference ref = (SoftValueReference) queue.poll(); + if (ref == null) { + return; + } + Object key = ref.getKey(); + if (concurrent) { + try { + atomicRemove.invoke(map, new Object[] { key, ref }); + } catch (IllegalAccessException e) { + throw new UndeclaredThrowableException(e); + } catch (InvocationTargetException e) { + throw new UndeclaredThrowableException(e); + } + } else if (map.get(key) == ref) { + map.remove(key); + } + } + } + + private static final class SoftValueReference extends SoftReference { + private final Object key; + + SoftValueReference(Object key, Object value, ReferenceQueue queue) { + super(value, queue); + this.key = key; + } + + Object getKey() { + return key; + } + } + + private static Method getAtomicRemoveMethod() { + try { + return Class.forName("java.util.concurrent.ConcurrentMap").getMethod("remove", new Class[] { Object.class, Object.class }); + } catch (ClassNotFoundException e) { + return null; + } catch (NoSuchMethodException e) { + throw new UndeclaredThrowableException(e); + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b0e08ccb/src/main/java/org/apache/freemarker/core/templateresolver/impl/StringTemplateLoader.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/templateresolver/impl/StringTemplateLoader.java b/src/main/java/org/apache/freemarker/core/templateresolver/impl/StringTemplateLoader.java new file mode 100644 index 0000000..f56ac32 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/templateresolver/impl/StringTemplateLoader.java @@ -0,0 +1,199 @@ +/* + * 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 org.apache.freemarker.core.templateresolver.impl; + +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.io.StringReader; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicLong; + +import org.apache.freemarker.core.templateresolver.CacheStorage; +import org.apache.freemarker.core.templateresolver.TemplateLoader; +import org.apache.freemarker.core.templateresolver.TemplateLoaderSession; +import org.apache.freemarker.core.templateresolver.TemplateLoadingResult; +import org.apache.freemarker.core.templateresolver.TemplateLoadingSource; +import org.apache.freemarker.core.util.StringUtil; + +/** + * A {@link TemplateLoader} that uses a {@link Map} with {@code String} as its source of templates. This is similar to + * {@link StringTemplateLoader}, but uses {@code String} instead of {@link String}; see more details there. + * + * <p>Note that {@link StringTemplateLoader} can't be used with a distributed (cluster-wide) {@link CacheStorage}, + * as it produces {@link TemplateLoadingSource}-s that deliberately throw exception on serialization (because the + * content is only accessible within a single JVM, and is also volatile). + */ +// TODO JUnit tests +public class StringTemplateLoader implements TemplateLoader { + + private static final AtomicLong INSTANCE_COUNTER = new AtomicLong(); + + private final long instanceId = INSTANCE_COUNTER.get(); + private final AtomicLong templatesRevision = new AtomicLong(); + private final ConcurrentMap<String, ContentHolder> templates = new ConcurrentHashMap<>(); + + /** + * Puts a template into the template loader. The name can contain slashes to denote logical directory structure, but + * must not start with a slash. Each template will get an unique revision number, thus replacing a template will + * cause the template cache to reload it (when the update delay expires). + * + * <p>This method is thread-safe. + * + * @param name + * the name of the template. + * @param content + * the source code of the template. + */ + public void putTemplate(String name, String content) { + templates.put( + name, + new ContentHolder(content, new Source(instanceId, name), templatesRevision.incrementAndGet())); + } + + /** + * Removes the template with the specified name if it was added earlier. + * + * <p> + * This method is thread-safe. + * + * @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) + */ + public boolean removeTemplate(String name) { + return templates.remove(name) != null; + } + + @Override + public TemplateLoaderSession createSession() { + return null; + } + + @Override + public TemplateLoadingResult load(String name, TemplateLoadingSource ifSourceDiffersFrom, + Serializable ifVersionDiffersFrom, TemplateLoaderSession session) throws IOException { + ContentHolder contentHolder = templates.get(name); + if (contentHolder == null) { + return TemplateLoadingResult.NOT_FOUND; + } else if (ifSourceDiffersFrom != null && ifSourceDiffersFrom.equals(contentHolder.source) + && Objects.equals(ifVersionDiffersFrom, contentHolder.version)) { + return TemplateLoadingResult.NOT_MODIFIED; + } else { + return new TemplateLoadingResult( + contentHolder.source, contentHolder.version, + new StringReader(contentHolder.content), + null); + } + } + + @Override + public void resetState() { + // Do nothing + } + + /** + * Show class name and some details that are useful in template-not-found errors. + */ + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(_TemplateLoaderUtils.getClassNameForToString(this)); + sb.append("(Map { "); + int cnt = 0; + for (String name : templates.keySet()) { + cnt++; + if (cnt != 1) { + sb.append(", "); + } + if (cnt > 10) { + sb.append("..."); + break; + } + sb.append(StringUtil.jQuote(name)); + sb.append("=..."); + } + if (cnt != 0) { + sb.append(' '); + } + sb.append("})"); + return sb.toString(); + } + + private static class ContentHolder { + private final String content; + private final Source source; + private final long version; + + public ContentHolder(String content, Source source, long version) { + this.content = content; + this.source = source; + this.version = version; + } + + } + + @SuppressWarnings("serial") + private static class Source implements TemplateLoadingSource { + + private final long instanceId; + private final String name; + + public Source(long instanceId, String name) { + this.instanceId = instanceId; + this.name = name; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (int) (instanceId ^ (instanceId >>> 32)); + result = prime * result + ((name == null) ? 0 : name.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + Source other = (Source) obj; + if (instanceId != other.instanceId) return false; + if (name == null) { + if (other.name != null) return false; + } else if (!name.equals(other.name)) { + return false; + } + return true; + } + + private void writeObject(ObjectOutputStream out) throws IOException { + throw new IOException(StringTemplateLoader.class.getName() + + " sources can't be serialized, as they don't support clustering."); + } + + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b0e08ccb/src/main/java/org/apache/freemarker/core/templateresolver/impl/StrongCacheStorage.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/templateresolver/impl/StrongCacheStorage.java b/src/main/java/org/apache/freemarker/core/templateresolver/impl/StrongCacheStorage.java new file mode 100644 index 0000000..fa13778 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/templateresolver/impl/StrongCacheStorage.java @@ -0,0 +1,71 @@ +/* + * 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 org.apache.freemarker.core.templateresolver.impl; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.freemarker.core.templateresolver.CacheStorage; +import org.apache.freemarker.core.templateresolver.CacheStorageWithGetSize; +import org.apache.freemarker.core.templateresolver.ConcurrentCacheStorage; + +/** + * Strong cache storage is a cache storage that simply wraps a {@link Map}. It holds a strong reference to all objects + * it was passed, therefore prevents the cache from being purged during garbage collection. This class is always + * thread-safe since 2.3.24, before that if we are running on Java 5 or later. + * + * @see org.apache.freemarker.core.Configuration#setCacheStorage(CacheStorage) + */ +public class StrongCacheStorage implements ConcurrentCacheStorage, CacheStorageWithGetSize { + + private final Map map = new ConcurrentHashMap(); + + /** + * Always returns {@code true}. + */ + public boolean isConcurrent() { + return true; + } + + public Object get(Object key) { + return map.get(key); + } + + public void put(Object key, Object value) { + map.put(key, value); + } + + public void remove(Object key) { + map.remove(key); + } + + /** + * Returns a close approximation of the number of cache entries. + * + * @since 2.3.21 + */ + public int getSize() { + return map.size(); + } + + public void clear() { + map.clear(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b0e08ccb/src/main/java/org/apache/freemarker/core/templateresolver/impl/TemplateLoaderBasedTemplateLookupContext.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/templateresolver/impl/TemplateLoaderBasedTemplateLookupContext.java b/src/main/java/org/apache/freemarker/core/templateresolver/impl/TemplateLoaderBasedTemplateLookupContext.java new file mode 100644 index 0000000..0d5a95c --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/templateresolver/impl/TemplateLoaderBasedTemplateLookupContext.java @@ -0,0 +1,66 @@ +/* + * 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 org.apache.freemarker.core.templateresolver.impl; + +import java.io.Serializable; +import java.util.Locale; + +import org.apache.freemarker.core.templateresolver.TemplateLoader; +import org.apache.freemarker.core.templateresolver.TemplateLoadingResult; +import org.apache.freemarker.core.templateresolver.TemplateLoadingSource; +import org.apache.freemarker.core.templateresolver.TemplateLookupContext; + +/** + * Base class for implementing a {@link TemplateLookupContext} that works with {@link TemplateLoader}-s. + */ +public abstract class TemplateLoaderBasedTemplateLookupContext + extends TemplateLookupContext<TemplateLoaderBasedTemplateLookupResult> { + + private final TemplateLoadingSource cachedResultSource; + private final Serializable cachedResultVersion; + + protected TemplateLoaderBasedTemplateLookupContext(String templateName, Locale templateLocale, + Object customLookupCondition, TemplateLoadingSource cachedResultSource, Serializable cachedResultVersion) { + super(templateName, templateLocale, customLookupCondition); + this.cachedResultSource = cachedResultSource; + this.cachedResultVersion = cachedResultVersion; + } + + protected TemplateLoadingSource getCachedResultSource() { + return cachedResultSource; + } + + protected Serializable getCachedResultVersion() { + return cachedResultVersion; + } + + @Override + public final TemplateLoaderBasedTemplateLookupResult createNegativeLookupResult() { + return TemplateLoaderBasedTemplateLookupResult.getNegativeResult(); + } + + /** + * Creates a positive or negative lookup result depending on {@link TemplateLoadingResult#getStatus()}. + */ + protected final TemplateLoaderBasedTemplateLookupResult createLookupResult( + String templateSourceName, TemplateLoadingResult templateLoaderResult) { + return TemplateLoaderBasedTemplateLookupResult.from(templateSourceName, templateLoaderResult); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b0e08ccb/src/main/java/org/apache/freemarker/core/templateresolver/impl/TemplateLoaderBasedTemplateLookupResult.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/templateresolver/impl/TemplateLoaderBasedTemplateLookupResult.java b/src/main/java/org/apache/freemarker/core/templateresolver/impl/TemplateLoaderBasedTemplateLookupResult.java new file mode 100644 index 0000000..09a0324 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/templateresolver/impl/TemplateLoaderBasedTemplateLookupResult.java @@ -0,0 +1,124 @@ +/* + * 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 org.apache.freemarker.core.templateresolver.impl; + +import org.apache.freemarker.core.templateresolver.TemplateLoader; +import org.apache.freemarker.core.templateresolver.TemplateLoadingResult; +import org.apache.freemarker.core.templateresolver.TemplateLoadingResultStatus; +import org.apache.freemarker.core.templateresolver.TemplateLookupResult; +import org.apache.freemarker.core.util.NullArgumentException; + +/** + * Class of {@link TemplateLookupResult} instances created by {@link TemplateLoaderBasedTemplateLookupContext}. To + * create instances of this inside your own {@link TemplateLoaderBasedTemplateLookupContext} subclass, call + * {@link TemplateLoaderBasedTemplateLookupContext#createLookupResult(String, TemplateLoadingResult)} and + * {@link TemplateLoaderBasedTemplateLookupContext#createNegativeLookupResult()}. You should not try to create instances + * anywhere else. Also, this class deliberately can't be subclassed (except inside FreeMarker). + */ +public abstract class TemplateLoaderBasedTemplateLookupResult extends TemplateLookupResult { + + /** Used internally to get a not-found result (currently just a static singleton). */ + static TemplateLoaderBasedTemplateLookupResult getNegativeResult() { + return NegativeTemplateLookupResult.INSTANCE; + } + + /** Used internally to create the appropriate kind of result from the parameters. */ + static TemplateLoaderBasedTemplateLookupResult from(String templateSourceName, TemplateLoadingResult templateLoaderResult) { + return templateLoaderResult.getStatus() != TemplateLoadingResultStatus.NOT_FOUND + ? new PositiveTemplateLookupResult(templateSourceName, templateLoaderResult) + : getNegativeResult(); + } + + private TemplateLoaderBasedTemplateLookupResult() { + // + } + + /** + * Used internally to extract the {@link TemplateLoadingResult}; {@code null} if {@link #isPositive()} is + * {@code false}. + */ + public abstract TemplateLoadingResult getTemplateLoaderResult(); + + private static final class PositiveTemplateLookupResult extends TemplateLoaderBasedTemplateLookupResult { + + private final String templateSourceName; + private final TemplateLoadingResult templateLoaderResult; + + /** + * @param templateSourceName + * The name of the matching template found. This is not necessarily the same as the template name + * with which the template was originally requested. For example, one may gets a template for the + * {@code "foo.ftl"} name, but due to localized lookup the template is actually loaded from + * {@code "foo_de.ftl"}. Then this parameter must be {@code "foo_de.ftl"}, not {@code "foo.ftl"}. Not + * {@code null}. + * + * @param templateLoaderResult + * See {@link TemplateLoader#load} to understand what that means. Not + * {@code null}. + */ + private PositiveTemplateLookupResult(String templateSourceName, TemplateLoadingResult templateLoaderResult) { + NullArgumentException.check("templateName", templateSourceName); + NullArgumentException.check("templateLoaderResult", templateLoaderResult); + + this.templateSourceName = templateSourceName; + this.templateLoaderResult = templateLoaderResult; + } + + @Override + public String getTemplateSourceName() { + return templateSourceName; + } + + @Override + public TemplateLoadingResult getTemplateLoaderResult() { + return templateLoaderResult; + } + + @Override + public boolean isPositive() { + return true; + } + } + + private static final class NegativeTemplateLookupResult extends TemplateLoaderBasedTemplateLookupResult { + + private static final TemplateLoaderBasedTemplateLookupResult.NegativeTemplateLookupResult INSTANCE = new NegativeTemplateLookupResult(); + + private NegativeTemplateLookupResult() { + // nop + } + + @Override + public String getTemplateSourceName() { + return null; + } + + @Override + public TemplateLoadingResult getTemplateLoaderResult() { + return null; + } + + @Override + public boolean isPositive() { + return false; + } + + } + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b0e08ccb/src/main/java/org/apache/freemarker/core/templateresolver/impl/URLTemplateLoader.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/templateresolver/impl/URLTemplateLoader.java b/src/main/java/org/apache/freemarker/core/templateresolver/impl/URLTemplateLoader.java new file mode 100644 index 0000000..fdb56b1 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/templateresolver/impl/URLTemplateLoader.java @@ -0,0 +1,229 @@ +/* + * 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 org.apache.freemarker.core.templateresolver.impl; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.Serializable; +import java.net.JarURLConnection; +import java.net.URL; +import java.net.URLConnection; +import java.util.Objects; + +import org.apache.freemarker.core.Configuration; +import org.apache.freemarker.core._CoreLogs; +import org.apache.freemarker.core.templateresolver.TemplateLoader; +import org.apache.freemarker.core.templateresolver.TemplateLoaderSession; +import org.apache.freemarker.core.templateresolver.TemplateLoadingResult; +import org.apache.freemarker.core.templateresolver.TemplateLoadingSource; +import org.apache.freemarker.core.templateresolver.TemplateLookupStrategy; +import org.slf4j.Logger; + +/** + * This is an abstract template loader that can load templates whose location can be described by an URL. Subclasses + * only need to override the {@link #getURL(String)}, {@link #extractNegativeResult(URLConnection)}, and perhaps the + * {@link #prepareConnection(URLConnection)} method. + */ +// TODO JUnit test (including implementing a HTTP-based template loader to test the new protected methods) +public abstract class URLTemplateLoader implements TemplateLoader { + + private static final Logger LOG = _CoreLogs.TEMPLATE_RESOLVER; + + private Boolean urlConnectionUsesCaches = false; + + /** + * Getter pair of {@link #setURLConnectionUsesCaches(Boolean)}. + * + * @since 2.3.21 + */ + public Boolean getURLConnectionUsesCaches() { + return urlConnectionUsesCaches; + } + + /** + * Sets if {@link URLConnection#setUseCaches(boolean)} will be called, and with what value. By default this is + * {@code false}, because FreeMarker has its own template cache with its own update delay setting + * ({@link Configuration#setTemplateUpdateDelay(int)}). If this is set to {@code null}, + * {@link URLConnection#setUseCaches(boolean)} won't be called. + */ + public void setURLConnectionUsesCaches(Boolean urlConnectionUsesCaches) { + this.urlConnectionUsesCaches = urlConnectionUsesCaches; + } + + @Override + public TemplateLoaderSession createSession() { + return null; + } + + @Override + public TemplateLoadingResult load(String name, TemplateLoadingSource ifSourceDiffersFrom, + Serializable ifVersionDiffersFrom, TemplateLoaderSession session) throws IOException { + URL url = getURL(name); + if (url == null) { + return TemplateLoadingResult.NOT_FOUND; + } + + URLConnection conn = url.openConnection(); + Boolean urlConnectionUsesCaches = getURLConnectionUsesCaches(); + if (urlConnectionUsesCaches != null) { + conn.setUseCaches(urlConnectionUsesCaches); + } + + prepareConnection(conn); + conn.connect(); + + InputStream inputStream = null; + Long version; + URLTemplateLoadingSource source; + try { + TemplateLoadingResult negativeResult = extractNegativeResult(conn); + if (negativeResult != null) { + return negativeResult; + } + + // To prevent clustering issues, getLastModified(fallbackToJarLMD=false) + long lmd = getLastModified(conn, false); + version = lmd != -1 ? lmd : null; + + source = new URLTemplateLoadingSource(url); + + if (ifSourceDiffersFrom != null && ifSourceDiffersFrom.equals(source) + && Objects.equals(ifVersionDiffersFrom, version)) { + return TemplateLoadingResult.NOT_MODIFIED; + } + + inputStream = conn.getInputStream(); + } catch (Throwable e) { + try { + if (inputStream == null) { + // There's no URLConnection.close(), so we do this hack. In case of HttpURLConnection we could call + // disconnect(), but that's perhaps too aggressive. + conn.getInputStream().close(); + } + } catch (IOException e2) { + LOG.debug("Failed to close connection inputStream", e2); + } + throw e; + } + return new TemplateLoadingResult(source, version, inputStream, null); + } + + @Override + public void resetState() { + // Do nothing + } + + /** + * {@link URLConnection#getLastModified()} with JDK bug workarounds. Because of JDK-6956385, for files inside a jar, + * it returns the last modification time of the jar itself, rather than the last modification time of the file + * inside the jar. + * + * @param fallbackToJarLMD + * Tells if the file is in side jar, then we should return the last modification time of the jar itself, + * or -1 (to work around JDK-6956385). + */ + public static long getLastModified(URLConnection conn, boolean fallbackToJarLMD) throws IOException { + if (conn instanceof JarURLConnection) { + // There is a bug in sun's jar url connection that causes file handle leaks when calling getLastModified() + // (see https://bugs.openjdk.java.net/browse/JDK-6956385). + // Since the time stamps of jar file contents can't vary independent from the jar file timestamp, just use + // the jar file timestamp + if (fallbackToJarLMD) { + URL jarURL = ((JarURLConnection) conn).getJarFileURL(); + if (jarURL.getProtocol().equals("file")) { + // Return the last modified time of the underlying file - saves some opening and closing + return new File(jarURL.getFile()).lastModified(); + } else { + // Use the URL mechanism + URLConnection jarConn = null; + try { + jarConn = jarURL.openConnection(); + return jarConn.getLastModified(); + } finally { + try { + if (jarConn != null) { + jarConn.getInputStream().close(); + } + } catch (IOException e) { + LOG.warn("Failed to close URL connection for: {}", conn, e); + } + } + } + } else { + return -1; + } + } else { + return conn.getLastModified(); + } + } + + /** + * Given a template name (plus potential locale decorations) retrieves an URL that points the template source. + * + * @param name + * the name of the sought template (including the locale decorations, or other decorations the + * {@link TemplateLookupStrategy} uses). + * + * @return An URL that points to the template source, or null if it can be determined that the template source does + * not exist. For many implementations the existence of the template can't be decided at this point, and you + * rely on {@link #extractNegativeResult(URLConnection)} instead. + */ + protected abstract URL getURL(String name); + + /** + * Called before the resource if read, checks if we can immediately return a {@link TemplateLoadingResult#NOT_FOUND} + * or {@link TemplateLoadingResult#NOT_MODIFIED}, or throw an {@link IOException}. For example, for a HTTP-based + * storage, the HTTP response status 404 could result in {@link TemplateLoadingResult#NOT_FOUND}, 304 in + * {@link TemplateLoadingResult#NOT_MODIFIED}, and some others, like 500 in throwing an {@link IOException}. + * + * <p>Some + * implementations rely on {@link #getURL(String)} returning {@code null} when a template is missing, in which case + * this method is certainly not applicable. + */ + protected abstract TemplateLoadingResult extractNegativeResult(URLConnection conn) throws IOException; + + /** + * Called before anything that causes the connection to actually build up. This is where + * {@link URLConnection#setIfModifiedSince(long)} and such can be called if someone overrides this. + * The default implementation in {@link URLTemplateLoader} does nothing. + */ + protected void prepareConnection(URLConnection conn) { + // Does nothing + } + + /** + * Can be used by subclasses to canonicalize URL path prefixes. + * @param prefix the path prefix to canonicalize + * @return the canonicalized prefix. All backslashes are replaced with + * forward slashes, and a trailing slash is appended if the original + * prefix wasn't empty and didn't already end with a slash. + */ + protected static String canonicalizePrefix(String prefix) { + // make it foolproof + prefix = prefix.replace('\\', '/'); + // ensure there's a trailing slash + if (prefix.length() > 0 && !prefix.endsWith("/")) { + prefix += "/"; + } + return prefix; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b0e08ccb/src/main/java/org/apache/freemarker/core/templateresolver/impl/URLTemplateLoadingSource.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/templateresolver/impl/URLTemplateLoadingSource.java b/src/main/java/org/apache/freemarker/core/templateresolver/impl/URLTemplateLoadingSource.java new file mode 100644 index 0000000..13ebe4d --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/templateresolver/impl/URLTemplateLoadingSource.java @@ -0,0 +1,58 @@ +/* + * 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 org.apache.freemarker.core.templateresolver.impl; + +import java.net.URL; + +import org.apache.freemarker.core.templateresolver.TemplateLoadingSource; +import org.apache.freemarker.core.util.NullArgumentException; + +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; + +@SuppressWarnings("serial") +public class URLTemplateLoadingSource implements TemplateLoadingSource { + + private final URL url; + + public URLTemplateLoadingSource(URL url) { + NullArgumentException.check("url", url); + this.url = url; + } + + public URL getUrl() { + return url; + } + + @Override + public int hashCode() { + return url.hashCode(); + } + + @Override + @SuppressFBWarnings("EQ_CHECK_FOR_OPERAND_NOT_COMPATIBLE_WITH_THIS") + public boolean equals(Object obj) { + return url.equals(obj); + } + + @Override + public String toString() { + return url.toString(); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b0e08ccb/src/main/java/org/apache/freemarker/core/templateresolver/impl/_TemplateLoaderUtils.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/templateresolver/impl/_TemplateLoaderUtils.java b/src/main/java/org/apache/freemarker/core/templateresolver/impl/_TemplateLoaderUtils.java new file mode 100644 index 0000000..763ce4c --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/templateresolver/impl/_TemplateLoaderUtils.java @@ -0,0 +1,43 @@ +/* + * 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 org.apache.freemarker.core.templateresolver.impl; + +import org.apache.freemarker.core.Configuration; +import org.apache.freemarker.core.templateresolver.TemplateLoader; + +/** + * For internal use only; don't depend on this, there's no backward compatibility guarantee at all! + * This class is to work around the lack of module system in Java, i.e., so that other FreeMarker packages can + * access things inside this package that users shouldn't. + */ +public final class _TemplateLoaderUtils { + + private _TemplateLoaderUtils() { + // Not meant to be instantiated + } + + public static String getClassNameForToString(TemplateLoader templateLoader) { + final Class<? extends TemplateLoader> tlClass = templateLoader.getClass(); + final Package tlPackage = tlClass.getPackage(); + return tlPackage == Configuration.class.getPackage() || tlPackage == TemplateLoader.class.getPackage() + ? tlClass.getSimpleName() : tlClass.getName(); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b0e08ccb/src/main/java/org/apache/freemarker/core/templateresolver/package.html ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/templateresolver/package.html b/src/main/java/org/apache/freemarker/core/templateresolver/package.html index bf806df..4b1b6cb 100644 --- a/src/main/java/org/apache/freemarker/core/templateresolver/package.html +++ b/src/main/java/org/apache/freemarker/core/templateresolver/package.html @@ -19,12 +19,7 @@ <html> <head> </head> -<body bgcolor="white"> -<p>Template <em>loading</em> and caching. -Beside the actual template cache, it contains loaders that can load template -files from the file system, from the classpath, or from a web application -context. If you have specific needs, you can plug custom template loaders into -the system by implementing the template loader interface. -</p> +<body> +<p>Template lookup, loading, caching and template naming rules.</p> </body> </html> http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b0e08ccb/src/main/java/org/apache/freemarker/servlet/FreemarkerServlet.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/servlet/FreemarkerServlet.java b/src/main/java/org/apache/freemarker/servlet/FreemarkerServlet.java index 666ae48..2f11ad8 100644 --- a/src/main/java/org/apache/freemarker/servlet/FreemarkerServlet.java +++ b/src/main/java/org/apache/freemarker/servlet/FreemarkerServlet.java @@ -55,10 +55,10 @@ import org.apache.freemarker.core.model.ObjectWrapper; import org.apache.freemarker.core.model.TemplateModel; import org.apache.freemarker.core.model.TemplateModelException; import org.apache.freemarker.core.model.impl._StaticObjectWrappers; -import org.apache.freemarker.core.templateresolver.ClassTemplateLoader; -import org.apache.freemarker.core.templateresolver.FileTemplateLoader; -import org.apache.freemarker.core.templateresolver.MultiTemplateLoader; import org.apache.freemarker.core.templateresolver.TemplateLoader; +import org.apache.freemarker.core.templateresolver.impl.ClassTemplateLoader; +import org.apache.freemarker.core.templateresolver.impl.FileTemplateLoader; +import org.apache.freemarker.core.templateresolver.impl.MultiTemplateLoader; import org.apache.freemarker.core.util.SecurityUtilities; import org.apache.freemarker.core.util.StringUtil; import org.apache.freemarker.servlet.jsp.TaglibFactory; http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b0e08ccb/src/main/java/org/apache/freemarker/servlet/InitParamParser.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/servlet/InitParamParser.java b/src/main/java/org/apache/freemarker/servlet/InitParamParser.java index 17a3d12..35f4680 100644 --- a/src/main/java/org/apache/freemarker/servlet/InitParamParser.java +++ b/src/main/java/org/apache/freemarker/servlet/InitParamParser.java @@ -31,10 +31,10 @@ import org.apache.freemarker.core.Configuration; import org.apache.freemarker.core._TemplateAPI; import org.apache.freemarker.core.ast._ObjectBuilderSettingEvaluator; import org.apache.freemarker.core.ast._SettingEvaluationEnvironment; -import org.apache.freemarker.core.templateresolver.ClassTemplateLoader; -import org.apache.freemarker.core.templateresolver.FileTemplateLoader; -import org.apache.freemarker.core.templateresolver.MultiTemplateLoader; import org.apache.freemarker.core.templateresolver.TemplateLoader; +import org.apache.freemarker.core.templateresolver.impl.ClassTemplateLoader; +import org.apache.freemarker.core.templateresolver.impl.FileTemplateLoader; +import org.apache.freemarker.core.templateresolver.impl.MultiTemplateLoader; import org.apache.freemarker.core.util.StringUtil; import org.slf4j.Logger; http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b0e08ccb/src/main/java/org/apache/freemarker/servlet/WebAppTemplateLoader.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/servlet/WebAppTemplateLoader.java b/src/main/java/org/apache/freemarker/servlet/WebAppTemplateLoader.java index 1c2da86..8e74033 100644 --- a/src/main/java/org/apache/freemarker/servlet/WebAppTemplateLoader.java +++ b/src/main/java/org/apache/freemarker/servlet/WebAppTemplateLoader.java @@ -35,13 +35,13 @@ import org.apache.freemarker.core.Configuration; import org.apache.freemarker.core._CoreLogs; import org.apache.freemarker.core.templateresolver.TemplateLoader; import org.apache.freemarker.core.templateresolver.TemplateLoaderSession; -import org.apache.freemarker.core.templateresolver._TemplateLoaderUtils; import org.apache.freemarker.core.util.CollectionUtils; import org.apache.freemarker.core.util.NullArgumentException; import org.apache.freemarker.core.util.StringUtil; import org.apache.freemarker.core.templateresolver.TemplateLoadingResult; import org.apache.freemarker.core.templateresolver.TemplateLoadingSource; -import org.apache.freemarker.core.templateresolver.URLTemplateLoader; +import org.apache.freemarker.core.templateresolver.impl.URLTemplateLoader; +import org.apache.freemarker.core.templateresolver.impl._TemplateLoaderUtils; import org.slf4j.Logger; /** http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b0e08ccb/src/manual/en_US/book.xml ---------------------------------------------------------------------- diff --git a/src/manual/en_US/book.xml b/src/manual/en_US/book.xml index 2e9015b..f635e88 100644 --- a/src/manual/en_US/book.xml +++ b/src/manual/en_US/book.xml @@ -29115,7 +29115,7 @@ TemplateModel x = env.getVariable("x"); // get variable x</programlisting> This allows specifying the naming rules used by FreeMarker. For now, custom implementations aren't allowed, and you can only chose between - <literal>TemplateNameFormat.DEFAULT_2_3_0</literal> (the + <literal>DefaultTemplateNameFormatFM2.INSTANCE</literal> (the default) and <literal>DEFAULT_2_4_0</literal> (recommended, at least for new projects). <literal>DEFAULT_2_4_0</literal> has several advantages, but isn't fully backward compatible @@ -29134,7 +29134,7 @@ TemplateModel x = env.getVariable("x"); // get variable x</programlisting> list of differences in the <link xlink:href="http://freemarker.org/docs/api/freemarker/cache/TemplateNameFormat.html#DEFAULT_2_4_0">Java API documentation of - <literal>TemplateNameFormat.DEFAULT_2_4_0</literal></link>.</para> + <literal>DefaultTemplateNameFormat.INSTANCE</literal></link>.</para> </listitem> <listitem> http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b0e08ccb/src/manual/zh_CN/book.xml ---------------------------------------------------------------------- diff --git a/src/manual/zh_CN/book.xml b/src/manual/zh_CN/book.xml index 327b239..fea72e7 100644 --- a/src/manual/zh_CN/book.xml +++ b/src/manual/zh_CN/book.xml @@ -21893,7 +21893,7 @@ TemplateModel x = env.getVariable("x"); // get variable x</programlisting> (<literal>Configuration.setTemplateNameFormat(TemplateNameFormat)</literal>)ã å®å 许æå® FreeMarker 使ç¨çå½åè§åãç°å¨ï¼ä¸åå 许èªå®ä¹å®ç°äºï¼ ä» å¯ä»¥éæ©å¨ - <literal>TemplateNameFormat.DEFAULT_2_3_0</literal> (é»è®¤ç) å + <literal>DefaultTemplateNameFormatFM2.INSTANCE</literal> (é»è®¤ç) å <literal>DEFAULT_2_4_0</literal> (æ¨èçï¼è³å°å¯¹æ°é¡¹ç®éæ©)ä¹ä¸éæ©ã <literal>DEFAULT_2_4_0</literal> æä¸äºä¼ç¹ï¼ä½æ¯ä¸è½å®å ¨ååå ¼å®¹ (坿¯å¾å¤åºç¨ç¨åºä¸ä¼è¢«å½±å)ã å ¸åçé误æ¯ä½¿ç¨åææ æ¥ä»£æ¿ææ ï¼ @@ -21907,7 +21907,7 @@ TemplateModel x = env.getVariable("x"); // get variable x</programlisting> (bugs)ï¼å¤§å¤æ°é½æ¯åç¿»è¯ç¹æ®æ¥éª¤ä¹åç <literal>..</literal> ç¸å ³ï¼ æ¯å¦ <literal>.</literal> æ <literal>*</literal>ãè¯·å¨ <link xlink:href="http://freemarker.org/docs/api/freemarker/cache/TemplateNameFormat.html#DEFAULT_2_4_0"> - <literal>TemplateNameFormat.DEFAULT_2_4_0</literal>ç Java API ææ¡£</link> + <literal>DefaultTemplateNameFormat.INSTANCE</literal>ç Java API ææ¡£</link> ä¸åèåç§ç¸å¼ä¹å¤ã</para> </listitem> http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b0e08ccb/src/test/java/org/apache/freemarker/core/ConfigurationTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/freemarker/core/ConfigurationTest.java b/src/test/java/org/apache/freemarker/core/ConfigurationTest.java index 6e14cc3..9cec543 100644 --- a/src/test/java/org/apache/freemarker/core/ConfigurationTest.java +++ b/src/test/java/org/apache/freemarker/core/ConfigurationTest.java @@ -75,17 +75,19 @@ import org.apache.freemarker.core.model.impl.SimpleScalar; import org.apache.freemarker.core.model.impl._StaticObjectWrappers; import org.apache.freemarker.core.model.impl.beans.BeansWrapperBuilder; import org.apache.freemarker.core.model.impl.beans.StringModel; -import org.apache.freemarker.core.templateresolver.ByteArrayTemplateLoader; import org.apache.freemarker.core.templateresolver.CacheStorageWithGetSize; -import org.apache.freemarker.core.templateresolver.NullCacheStorage; -import org.apache.freemarker.core.templateresolver.SoftCacheStorage; -import org.apache.freemarker.core.templateresolver.StringTemplateLoader; -import org.apache.freemarker.core.templateresolver.StrongCacheStorage; -import org.apache.freemarker.core.templateresolver.DefaultTemplateResolver; import org.apache.freemarker.core.templateresolver.TemplateLookupContext; import org.apache.freemarker.core.templateresolver.TemplateLookupResult; import org.apache.freemarker.core.templateresolver.TemplateLookupStrategy; -import org.apache.freemarker.core.templateresolver.TemplateNameFormat; +import org.apache.freemarker.core.templateresolver.impl.ByteArrayTemplateLoader; +import org.apache.freemarker.core.templateresolver.impl.DefaultTemplateLookupStrategy; +import org.apache.freemarker.core.templateresolver.impl.DefaultTemplateNameFormat; +import org.apache.freemarker.core.templateresolver.impl.DefaultTemplateNameFormatFM2; +import org.apache.freemarker.core.templateresolver.impl.DefaultTemplateResolver; +import org.apache.freemarker.core.templateresolver.impl.NullCacheStorage; +import org.apache.freemarker.core.templateresolver.impl.SoftCacheStorage; +import org.apache.freemarker.core.templateresolver.impl.StringTemplateLoader; +import org.apache.freemarker.core.templateresolver.impl.StrongCacheStorage; import org.apache.freemarker.core.util.DateUtil; import org.apache.freemarker.core.util.NullArgumentException; import org.apache.freemarker.core.util.NullWriter; @@ -239,9 +241,9 @@ public class ConfigurationTest extends TestCase { } assertFalse(cfg.isTemplateLookupStrategyExplicitlySet()); - assertSame(TemplateLookupStrategy.DEFAULT_2_3_0, cfg.getTemplateLookupStrategy()); + assertSame(DefaultTemplateLookupStrategy.INSTANCE, cfg.getTemplateLookupStrategy()); // - cfg.setTemplateLookupStrategy(TemplateLookupStrategy.DEFAULT_2_3_0); + cfg.setTemplateLookupStrategy(DefaultTemplateLookupStrategy.INSTANCE); assertTrue(cfg.isTemplateLookupStrategyExplicitlySet()); // for (int i = 0; i < 2; i++) { @@ -250,16 +252,16 @@ public class ConfigurationTest extends TestCase { } assertFalse(cfg.isTemplateNameFormatExplicitlySet()); - assertSame(TemplateNameFormat.DEFAULT_2_3_0, cfg.getTemplateNameFormat()); + assertSame(DefaultTemplateNameFormatFM2.INSTANCE, cfg.getTemplateNameFormat()); // - cfg.setTemplateNameFormat(TemplateNameFormat.DEFAULT_2_4_0); + cfg.setTemplateNameFormat(DefaultTemplateNameFormat.INSTANCE); assertTrue(cfg.isTemplateNameFormatExplicitlySet()); - assertSame(TemplateNameFormat.DEFAULT_2_4_0, cfg.getTemplateNameFormat()); + assertSame(DefaultTemplateNameFormat.INSTANCE, cfg.getTemplateNameFormat()); // for (int i = 0; i < 2; i++) { cfg.unsetTemplateNameFormat(); assertFalse(cfg.isTemplateNameFormatExplicitlySet()); - assertSame(TemplateNameFormat.DEFAULT_2_3_0, cfg.getTemplateNameFormat()); + assertSame(DefaultTemplateNameFormatFM2.INSTANCE, cfg.getTemplateNameFormat()); } assertFalse(cfg.isCacheStorageExplicitlySet()); @@ -682,15 +684,15 @@ public class ConfigurationTest extends TestCase { assertEquals(0, cache.getSize()); cfg.getTemplate("toCache1.ftl"); assertEquals(1, cache.getSize()); - cfg.setTemplateNameFormat(TemplateNameFormat.DEFAULT_2_3_0); + cfg.setTemplateNameFormat(DefaultTemplateNameFormatFM2.INSTANCE); assertEquals(1, cache.getSize()); - cfg.setTemplateNameFormat(TemplateNameFormat.DEFAULT_2_4_0); + cfg.setTemplateNameFormat(DefaultTemplateNameFormat.INSTANCE); assertEquals(0, cache.getSize()); cfg.getTemplate("toCache1.ftl"); assertEquals(1, cache.getSize()); - cfg.setTemplateNameFormat(TemplateNameFormat.DEFAULT_2_4_0); + cfg.setTemplateNameFormat(DefaultTemplateNameFormat.INSTANCE); assertEquals(1, cache.getSize()); - cfg.setTemplateNameFormat(TemplateNameFormat.DEFAULT_2_3_0); + cfg.setTemplateNameFormat(DefaultTemplateNameFormatFM2.INSTANCE); assertEquals(0, cache.getSize()); } @@ -709,7 +711,7 @@ public class ConfigurationTest extends TestCase { assertEquals("In a/b.ftl", template.toString()); } - cfg.setTemplateNameFormat(TemplateNameFormat.DEFAULT_2_4_0); + cfg.setTemplateNameFormat(DefaultTemplateNameFormat.INSTANCE); { final Template template = cfg.getTemplate("a/./../b.ftl"); @@ -721,11 +723,11 @@ public class ConfigurationTest extends TestCase { public void testTemplateNameFormatSetSetting() throws Exception { Configuration cfg = new Configuration(Configuration.VERSION_2_3_22); - assertSame(TemplateNameFormat.DEFAULT_2_3_0, cfg.getTemplateNameFormat()); + assertSame(DefaultTemplateNameFormatFM2.INSTANCE, cfg.getTemplateNameFormat()); cfg.setSetting(Configuration.TEMPLATE_NAME_FORMAT_KEY, "defAult_2_4_0"); - assertSame(TemplateNameFormat.DEFAULT_2_4_0, cfg.getTemplateNameFormat()); + assertSame(DefaultTemplateNameFormat.INSTANCE, cfg.getTemplateNameFormat()); cfg.setSetting(Configuration.TEMPLATE_NAME_FORMAT_KEY, "defaUlt_2_3_0"); - assertSame(TemplateNameFormat.DEFAULT_2_3_0, cfg.getTemplateNameFormat()); + assertSame(DefaultTemplateNameFormatFM2.INSTANCE, cfg.getTemplateNameFormat()); assertTrue(cfg.isTemplateNameFormatExplicitlySet()); cfg.setSetting(Configuration.TEMPLATE_NAME_FORMAT_KEY, "defauLt"); assertFalse(cfg.isTemplateNameFormatExplicitlySet()); @@ -770,10 +772,10 @@ public class ConfigurationTest extends TestCase { public void testTemplateLookupStrategyDefaultAndSet() throws IOException { Configuration cfg = new Configuration(Configuration.VERSION_2_3_22); - assertSame(TemplateLookupStrategy.DEFAULT_2_3_0, cfg.getTemplateLookupStrategy()); + assertSame(DefaultTemplateLookupStrategy.INSTANCE, cfg.getTemplateLookupStrategy()); cfg.setClassForTemplateLoading(ConfigurationTest.class, ""); - assertSame(TemplateLookupStrategy.DEFAULT_2_3_0, cfg.getTemplateLookupStrategy()); + assertSame(DefaultTemplateLookupStrategy.INSTANCE, cfg.getTemplateLookupStrategy()); CacheStorageWithGetSize cache = (CacheStorageWithGetSize) cfg.getCacheStorage(); cfg.setClassForTemplateLoading(ConfigurationTest.class, ""); @@ -781,7 +783,7 @@ public class ConfigurationTest extends TestCase { cfg.getTemplate("toCache1.ftl"); assertEquals(1, cache.getSize()); - cfg.setTemplateLookupStrategy(TemplateLookupStrategy.DEFAULT_2_3_0); + cfg.setTemplateLookupStrategy(DefaultTemplateLookupStrategy.INSTANCE); assertEquals(1, cache.getSize()); final TemplateLookupStrategy myStrategy = new TemplateLookupStrategy() { @@ -799,7 +801,7 @@ public class ConfigurationTest extends TestCase { cfg.setTemplateLookupStrategy(myStrategy); assertEquals(1, cache.getSize()); - cfg.setTemplateLookupStrategy(TemplateLookupStrategy.DEFAULT_2_3_0); + cfg.setTemplateLookupStrategy(DefaultTemplateLookupStrategy.INSTANCE); assertEquals(0, cache.getSize()); } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b0e08ccb/src/test/java/org/apache/freemarker/core/ExceptionTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/freemarker/core/ExceptionTest.java b/src/test/java/org/apache/freemarker/core/ExceptionTest.java index ef907e7..710f0bc 100644 --- a/src/test/java/org/apache/freemarker/core/ExceptionTest.java +++ b/src/test/java/org/apache/freemarker/core/ExceptionTest.java @@ -36,7 +36,7 @@ import org.apache.freemarker.core.Configuration; import org.apache.freemarker.core.Template; import org.apache.freemarker.core.TemplateException; import org.apache.freemarker.core.ast.ParseException; -import org.apache.freemarker.core.templateresolver.StringTemplateLoader; +import org.apache.freemarker.core.templateresolver.impl.StringTemplateLoader; import org.apache.freemarker.core.util.NullWriter; import junit.framework.TestCase; http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b0e08ccb/src/test/java/org/apache/freemarker/core/IncudeFromNamelessTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/freemarker/core/IncudeFromNamelessTest.java b/src/test/java/org/apache/freemarker/core/IncudeFromNamelessTest.java index 8860683..0f887c5 100644 --- a/src/test/java/org/apache/freemarker/core/IncudeFromNamelessTest.java +++ b/src/test/java/org/apache/freemarker/core/IncudeFromNamelessTest.java @@ -26,7 +26,7 @@ import java.io.StringWriter; import org.apache.freemarker.core.Configuration; import org.apache.freemarker.core.Template; import org.apache.freemarker.core.TemplateException; -import org.apache.freemarker.core.templateresolver.StringTemplateLoader; +import org.apache.freemarker.core.templateresolver.impl.StringTemplateLoader; import junit.framework.TestCase; http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b0e08ccb/src/test/java/org/apache/freemarker/core/MistakenlyPublicImportAPIsTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/freemarker/core/MistakenlyPublicImportAPIsTest.java b/src/test/java/org/apache/freemarker/core/MistakenlyPublicImportAPIsTest.java index ae171f2..6c65c5b 100644 --- a/src/test/java/org/apache/freemarker/core/MistakenlyPublicImportAPIsTest.java +++ b/src/test/java/org/apache/freemarker/core/MistakenlyPublicImportAPIsTest.java @@ -34,7 +34,7 @@ import org.apache.freemarker.core.ast.InvalidReferenceException; import org.apache.freemarker.core.ast.LibraryLoad; import org.apache.freemarker.core.ast.Environment.Namespace; import org.apache.freemarker.core.model.TemplateModel; -import org.apache.freemarker.core.templateresolver.StringTemplateLoader; +import org.apache.freemarker.core.templateresolver.impl.StringTemplateLoader; import org.apache.freemarker.core.util.NullWriter; import org.junit.Test; http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b0e08ccb/src/test/java/org/apache/freemarker/core/TemplateLanguageVersionTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/freemarker/core/TemplateLanguageVersionTest.java b/src/test/java/org/apache/freemarker/core/TemplateLanguageVersionTest.java index 0ee6dd4..ab67d25 100644 --- a/src/test/java/org/apache/freemarker/core/TemplateLanguageVersionTest.java +++ b/src/test/java/org/apache/freemarker/core/TemplateLanguageVersionTest.java @@ -27,7 +27,7 @@ import java.io.IOException; import org.apache.freemarker.core.Configuration; import org.apache.freemarker.core.Template; import org.apache.freemarker.core.Version; -import org.apache.freemarker.core.templateresolver.StringTemplateLoader; +import org.apache.freemarker.core.templateresolver.impl.StringTemplateLoader; import org.apache.freemarker.test.util.TestUtil; import org.junit.Test; public class TemplateLanguageVersionTest { http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b0e08ccb/src/test/java/org/apache/freemarker/core/TemplateLookupStrategyTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/freemarker/core/TemplateLookupStrategyTest.java b/src/test/java/org/apache/freemarker/core/TemplateLookupStrategyTest.java index 60b68ae..f8b443a 100644 --- a/src/test/java/org/apache/freemarker/core/TemplateLookupStrategyTest.java +++ b/src/test/java/org/apache/freemarker/core/TemplateLookupStrategyTest.java @@ -29,14 +29,11 @@ import java.io.IOException; import java.io.StringWriter; import java.util.Locale; -import org.apache.freemarker.core.Configuration; -import org.apache.freemarker.core.Template; -import org.apache.freemarker.core.TemplateException; -import org.apache.freemarker.core.TemplateNotFoundException; import org.apache.freemarker.core.ast.ParseException; import org.apache.freemarker.core.templateresolver.TemplateLookupContext; import org.apache.freemarker.core.templateresolver.TemplateLookupResult; import org.apache.freemarker.core.templateresolver.TemplateLookupStrategy; +import org.apache.freemarker.core.templateresolver.impl.DefaultTemplateLookupStrategy; import org.apache.freemarker.test.MonitoredTemplateLoader; import org.junit.Test; @@ -47,13 +44,13 @@ public class TemplateLookupStrategyTest { @Test public void testSetSetting() throws TemplateException { Configuration cfg = new Configuration(Configuration.VERSION_2_3_22); - assertSame(TemplateLookupStrategy.DEFAULT_2_3_0, cfg.getTemplateLookupStrategy()); + assertSame(DefaultTemplateLookupStrategy.INSTANCE, cfg.getTemplateLookupStrategy()); cfg.setSetting(Configuration.TEMPLATE_LOOKUP_STRATEGY_KEY, MyTemplateLookupStrategy.class.getName() + "()"); assertTrue(cfg.getTemplateLookupStrategy() instanceof MyTemplateLookupStrategy); cfg.setSetting(Configuration.TEMPLATE_LOOKUP_STRATEGY_KEY, "dEfault"); - assertSame(TemplateLookupStrategy.DEFAULT_2_3_0, cfg.getTemplateLookupStrategy()); + assertSame(DefaultTemplateLookupStrategy.INSTANCE, cfg.getTemplateLookupStrategy()); } @Test http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b0e08ccb/src/test/java/org/apache/freemarker/core/TemplateNotFoundMessageTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/freemarker/core/TemplateNotFoundMessageTest.java b/src/test/java/org/apache/freemarker/core/TemplateNotFoundMessageTest.java index 6a7084c..7113a2a 100644 --- a/src/test/java/org/apache/freemarker/core/TemplateNotFoundMessageTest.java +++ b/src/test/java/org/apache/freemarker/core/TemplateNotFoundMessageTest.java @@ -31,14 +31,14 @@ import java.io.IOException; import org.apache.freemarker.core.Configuration; import org.apache.freemarker.core.TemplateNotFoundException; -import org.apache.freemarker.core.templateresolver.ClassTemplateLoader; -import org.apache.freemarker.core.templateresolver.FileTemplateLoader; -import org.apache.freemarker.core.templateresolver.MultiTemplateLoader; -import org.apache.freemarker.core.templateresolver.StringTemplateLoader; import org.apache.freemarker.core.templateresolver.TemplateLoader; import org.apache.freemarker.core.templateresolver.TemplateLookupContext; import org.apache.freemarker.core.templateresolver.TemplateLookupResult; import org.apache.freemarker.core.templateresolver.TemplateLookupStrategy; +import org.apache.freemarker.core.templateresolver.impl.ClassTemplateLoader; +import org.apache.freemarker.core.templateresolver.impl.FileTemplateLoader; +import org.apache.freemarker.core.templateresolver.impl.MultiTemplateLoader; +import org.apache.freemarker.core.templateresolver.impl.StringTemplateLoader; import org.apache.freemarker.servlet.WebAppTemplateLoader; import org.junit.Test; http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b0e08ccb/src/test/java/org/apache/freemarker/core/ast/CanonicalFormTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/freemarker/core/ast/CanonicalFormTest.java b/src/test/java/org/apache/freemarker/core/ast/CanonicalFormTest.java index 85e74d6..7c584cd 100644 --- a/src/test/java/org/apache/freemarker/core/ast/CanonicalFormTest.java +++ b/src/test/java/org/apache/freemarker/core/ast/CanonicalFormTest.java @@ -25,8 +25,8 @@ import java.io.StringWriter; import org.apache.freemarker.core.Configuration; import org.apache.freemarker.core.TemplateNotFoundException; import org.apache.freemarker.core.ast.ParseException; -import org.apache.freemarker.core.templateresolver.ClassTemplateLoader; import org.apache.freemarker.core.templateresolver.MalformedTemplateNameException; +import org.apache.freemarker.core.templateresolver.impl.ClassTemplateLoader; import org.apache.freemarker.test.CopyrightCommentRemoverTemplateLoader; import org.apache.freemarker.test.util.FileTestCase;
