From cac5073d200af56bedf7c95c8b975cbab0c905db Mon Sep 17 00:00:00 2001 From: Mitchell Bosecke Date: Mon, 30 Sep 2013 21:18:18 -0600 Subject: [PATCH] Pebble now works within an application container. Well at least glassfish --- .../mitchellbosecke/pebble/PebbleEngine.java | 58 +++------ .../pebble/compiler/ClassFileManager.java | 16 ++- .../pebble/compiler/CompilerImpl.java | 37 +++--- .../mitchellbosecke/pebble/loader/Loader.java | 9 +- .../pebble/loader/PebbleDefaultLoader.java | 119 ++++++++++++++++++ .../mitchellbosecke/pebble/AbstractTest.java | 6 +- .../mitchellbosecke/pebble/CompilerTest.java | 2 - 7 files changed, 180 insertions(+), 67 deletions(-) create mode 100644 src/main/java/com/mitchellbosecke/pebble/loader/PebbleDefaultLoader.java diff --git a/src/main/java/com/mitchellbosecke/pebble/PebbleEngine.java b/src/main/java/com/mitchellbosecke/pebble/PebbleEngine.java index 365dbf452..a8b26afd7 100644 --- a/src/main/java/com/mitchellbosecke/pebble/PebbleEngine.java +++ b/src/main/java/com/mitchellbosecke/pebble/PebbleEngine.java @@ -12,7 +12,6 @@ import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import java.util.Collection; import java.util.HashMap; import java.util.Map; @@ -26,7 +25,6 @@ import com.mitchellbosecke.pebble.lexer.Lexer; import com.mitchellbosecke.pebble.lexer.LexerImpl; import com.mitchellbosecke.pebble.lexer.TokenStream; -import com.mitchellbosecke.pebble.loader.ResourceLoader; import com.mitchellbosecke.pebble.loader.Loader; import com.mitchellbosecke.pebble.node.Node; import com.mitchellbosecke.pebble.node.NodeRoot; @@ -56,7 +54,6 @@ public class PebbleEngine { private final Class templateInterfaceClass; private final Class templateAbstractClass; private final String templateClassPrefix; - private String compiledTemplateDirectory; /* * Templates that have already been compiled into Java @@ -77,16 +74,9 @@ public class PebbleEngine { private Map binaryOperators; private Map filters; private Map tests; - - /** - * Constructor for the Pebble Engine give file system paths to templates. - * - * @param paths - * File system paths where the templates are being stored - */ - public PebbleEngine(Collection templatePaths, String compiledTemplateDirectory) { - this(new ResourceLoader(templatePaths), compiledTemplateDirectory); + public PebbleEngine() { + this(null); } /** @@ -95,13 +85,11 @@ public PebbleEngine(Collection templatePaths, String compiledTemplateDir * @param loader * The template loader for this engine */ - public PebbleEngine(Loader loader, String compiledTemplateDirectory) { + public PebbleEngine(Loader loader) { this.setLoader(loader); lexer = new LexerImpl(this); parser = new ParserImpl(this); compiler = new CompilerImpl(this); - - this.compiledTemplateDirectory = compiledTemplateDirectory; this.addExtension(new CoreExtension()); @@ -118,8 +106,8 @@ public PebbleEngine(Loader loader, String compiledTemplateDirectory) { * @param templateName * The name of the template to load * @return An instance of the template that has been compiled into Java - * @throws SyntaxException - * @throws LoaderException + * @throws SyntaxException + * @throws LoaderException */ public PebbleTemplate loadTemplate(String templateName) throws SyntaxException, LoaderException { String className = this.getTemplateClassName(templateName); @@ -127,23 +115,24 @@ public PebbleTemplate loadTemplate(String templateName) throws SyntaxException, if (loadedTemplates.containsKey(className)) { instance = loadedTemplates.get(className); } else if (!requiresCompilation(templateName)) { - + /* try to load class file if it has already been compiled */ // TODO instance = null; - + } else { /* template has not been compiled, we must compile it */ String templateSource = loader.getSource(templateName); NodeRoot root = parse(tokenize(templateSource, templateName)); - + String javaSource = compile(root); - - // if this template has a parent, lets make sure the parent is compiled first - if(root.hasParent()){ + + // if this template has a parent, lets make sure the parent is + // compiled first + if (root.hasParent()) { this.loadTemplate(root.getParentFileName()); } - + instance = getCompiler().compileToJava(javaSource, className); instance.setEngine(this); loadedTemplates.put(className, instance); @@ -152,7 +141,7 @@ public PebbleTemplate loadTemplate(String templateName) throws SyntaxException, } private boolean requiresCompilation(String templateName) { - + return true; } @@ -165,7 +154,7 @@ private boolean requiresCompilation(String templateName) { * @param filename * The name of the template (used for meaningful error messages) * @return The TokenStream which is ready for parsing - * @throws SyntaxException + * @throws SyntaxException */ private TokenStream tokenize(String source, String filename) throws SyntaxException { return getLexer().tokenize(source, filename); @@ -178,7 +167,7 @@ private TokenStream tokenize(String source, String filename) throws SyntaxExcept * @param stream * The TokenStream which is ready for parsing * @return The root Node of the AST - * @throws SyntaxException + * @throws SyntaxException */ private NodeRoot parse(TokenStream stream) throws SyntaxException { return getParser().parse(stream); @@ -346,10 +335,11 @@ public Map getTests() { * @return The final name that would be used for creating a Java class */ public String getTemplateClassName(String templateName) { - - // if tempalteName is part of a directory path, get just the last segment + + // if tempalteName is part of a directory path, get just the last + // segment templateName = templateName.replaceFirst(".*/([^/]+).*", "$1"); - + String classNameHash = ""; byte[] bytesOfName; MessageDigest md; @@ -379,12 +369,4 @@ public Class getTemplateInterfaceClass() { public Class getTemplateAbstractClass() { return templateAbstractClass; } - - public String getCompiledTemplateDirectory() { - return compiledTemplateDirectory; - } - - public void setCompiledTemplateDirectory(String compiledTemplateDirectory) { - this.compiledTemplateDirectory = compiledTemplateDirectory; - } } diff --git a/src/main/java/com/mitchellbosecke/pebble/compiler/ClassFileManager.java b/src/main/java/com/mitchellbosecke/pebble/compiler/ClassFileManager.java index e6fd26fe9..39d9cc5cd 100644 --- a/src/main/java/com/mitchellbosecke/pebble/compiler/ClassFileManager.java +++ b/src/main/java/com/mitchellbosecke/pebble/compiler/ClassFileManager.java @@ -48,11 +48,18 @@ public static ClassFileManager getInstance(StandardJavaFileManager manager) { @Override public ClassLoader getClassLoader(Location location) { - return new SecureClassLoader() { + return new SecureClassLoader(ClassFileManager.class.getClassLoader()) { @Override protected Class findClass(String name) throws ClassNotFoundException { - byte[] b = classObjects.get(name).getBytes(); - return super.defineClass(name, b, 0, b.length); + ByteArrayJavaFileObject classObject = classObjects.get(name); + + if (classObject == null) { + return super.loadClass(name); + } else { + byte[] b = classObject.getBytes(); + return super.defineClass(name, b, 0, b.length); + } + } }; } @@ -84,10 +91,9 @@ public Iterable list(Location location, String packageName, Set< public String inferBinaryName(Location location, JavaFileObject file) { if (file instanceof ByteArrayJavaFileObject) { return ((ByteArrayJavaFileObject) file).getBinaryName(); - } else { + } else { return fileManager.inferBinaryName(location, file); } } - } \ No newline at end of file diff --git a/src/main/java/com/mitchellbosecke/pebble/compiler/CompilerImpl.java b/src/main/java/com/mitchellbosecke/pebble/compiler/CompilerImpl.java index 8640abe7e..1cf872d3f 100644 --- a/src/main/java/com/mitchellbosecke/pebble/compiler/CompilerImpl.java +++ b/src/main/java/com/mitchellbosecke/pebble/compiler/CompilerImpl.java @@ -9,8 +9,11 @@ ******************************************************************************/ package com.mitchellbosecke.pebble.compiler; +import java.io.File; import java.io.IOException; +import java.net.URISyntaxException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Locale; @@ -147,13 +150,22 @@ public PebbleTemplate compileToJava(String javaSource, String className) { List compilationUnits = new ArrayList<>(); compilationUnits.add(new StringSourceFileObject(fullClassName, javaSource)); - /* Prepare any compilation options to be used during compilation */ - // compiledTemplatesPath = compiledTemplatesPath.replace(" ", "\\ "); + // prepare compilation options + List compilationOptions = new ArrayList<>(); - // logger.info(String.format("Compiling to %s", compiledTemplatesPath)); - // String[] compileOptions = new String[] { "-d", compiledTemplatesPath - // }; - // Iterable compilationOptions = Arrays.asList(compileOptions); + // build classpath + StringBuilder sb = new StringBuilder(); + + try { + sb.append(System.getProperty("java.class.path")).append(File.pathSeparator) + .append(PebbleTemplate.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath()); + } catch (URISyntaxException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + + String classpath = sb.toString(); + compilationOptions.addAll(Arrays.asList("-classpath", classpath)); /* Create a diagnostic controller, which holds the compilation problems */ DiagnosticCollector diagnostics = new DiagnosticCollector(); @@ -162,7 +174,8 @@ public PebbleTemplate compileToJava(String javaSource, String className) { * Create a compilation task from compiler by passing in the required * input objects prepared above */ - CompilationTask compilerTask = compiler.getTask(null, fileManager, diagnostics, null, null, compilationUnits); + CompilationTask compilerTask = compiler.getTask(null, fileManager, diagnostics, compilationOptions, null, + compilationUnits); // Perform the compilation by calling the call method on compilerTask // object. @@ -181,14 +194,8 @@ public PebbleTemplate compileToJava(String javaSource, String className) { } try { - - // PebbleClassLoader pebbleClassLoader = - // PebbleClassLoader.getInstance(); - // pebbleClassLoader.register(fullClassName, - // fileManager.getCompiledJavaClassObject()); - - AbstractPebbleTemplate template = (AbstractPebbleTemplate) fileManager.getClassLoader(null) - .loadClass(fullClassName).newInstance(); + ClassLoader cl = fileManager.getClassLoader(null); + AbstractPebbleTemplate template = (AbstractPebbleTemplate) cl.loadClass(fullClassName).newInstance(); template.setSourceCode(getSource()); return template; } catch (IllegalAccessException | InstantiationException e) { diff --git a/src/main/java/com/mitchellbosecke/pebble/loader/Loader.java b/src/main/java/com/mitchellbosecke/pebble/loader/Loader.java index 8c4432728..9b79ac239 100644 --- a/src/main/java/com/mitchellbosecke/pebble/loader/Loader.java +++ b/src/main/java/com/mitchellbosecke/pebble/loader/Loader.java @@ -9,15 +9,14 @@ ******************************************************************************/ package com.mitchellbosecke.pebble.loader; -import java.util.Date; - import com.mitchellbosecke.pebble.error.LoaderException; - public interface Loader { - + public String getSource(String templateName) throws LoaderException; - //boolean isFresh(String templateName, Date timestamp) throws LoaderException; + public void setPrefix(String prefix); + + public void setSuffix(String suffix); } diff --git a/src/main/java/com/mitchellbosecke/pebble/loader/PebbleDefaultLoader.java b/src/main/java/com/mitchellbosecke/pebble/loader/PebbleDefaultLoader.java new file mode 100644 index 000000000..c1c99efed --- /dev/null +++ b/src/main/java/com/mitchellbosecke/pebble/loader/PebbleDefaultLoader.java @@ -0,0 +1,119 @@ +/******************************************************************************* + * This file is part of Pebble. + * + * Copyright (c) 2012 Mitchell Bosecke. + * + * This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 + * Unported License. To view a copy of this license, visit + * http://creativecommons.org/licenses/by-sa/3.0/ + ******************************************************************************/ +package com.mitchellbosecke.pebble.loader; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.UnsupportedEncodingException; +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.io.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.mitchellbosecke.pebble.error.LoaderException; + +public class PebbleDefaultLoader implements Loader { + + private static final Logger logger = LoggerFactory.getLogger(PebbleDefaultLoader.class); + + private String prefix; + + private String suffix; + + private Map readerCache = new HashMap<>(); + + @Override + public String getSource(String templateName) throws LoaderException { + Reader location = getReader(templateName); + String source = null; + + try { + source = IOUtils.toString(location); + } catch (IOException e) { + throw new LoaderException("Template can not be found."); + } + return source; + } + + protected Reader getReader(String templateName) throws LoaderException { + + Reader reader = readerCache.containsKey(templateName) ? readerCache.get(templateName) : null; + + if (reader == null) { + InputStream is = null; + + String path = getPrefix().endsWith(String.valueOf(File.separatorChar)) ? getPrefix() : getPrefix() + + File.separatorChar; + + String location = path + templateName + (getSuffix() == null ? "" : getSuffix()); + logger.info("Looking for template in {}.", location); + + // try ContextClassLoader + ClassLoader ccl = Thread.currentThread().getContextClassLoader(); + is = ccl.getResourceAsStream(location); + + // try ResourceLoader's class loader + ClassLoader rcl = PebbleDefaultLoader.class.getClassLoader(); + if (is == null) { + is = rcl.getResourceAsStream(location); + } + + // try to load File + if (is == null) { + File file = new File(path, templateName); + if (file.exists() && file.isFile()) { + try { + is = new FileInputStream(file); + } catch (FileNotFoundException e) { + // TODO: throw exception? + } + } + } + + if (is == null) { + throw new LoaderException("Could not find template \"" + templateName + "\""); + } + + try { + reader = new BufferedReader(new InputStreamReader(is, "UTF-8")); + } catch (UnsupportedEncodingException e) { + } + } + + readerCache.put(templateName, reader); + + return reader; + } + + public String getSuffix() { + return suffix; + } + + public void setSuffix(String suffix) { + this.suffix = suffix; + } + + public String getPrefix() { + return prefix; + } + + public void setPrefix(String prefix) { + this.prefix = prefix; + } + +} diff --git a/src/test/java/com/mitchellbosecke/pebble/AbstractTest.java b/src/test/java/com/mitchellbosecke/pebble/AbstractTest.java index cf978f911..97a2c1612 100644 --- a/src/test/java/com/mitchellbosecke/pebble/AbstractTest.java +++ b/src/test/java/com/mitchellbosecke/pebble/AbstractTest.java @@ -15,6 +15,7 @@ import com.mitchellbosecke.pebble.compiler.Compiler; import com.mitchellbosecke.pebble.lexer.Lexer; import com.mitchellbosecke.pebble.loader.Loader; +import com.mitchellbosecke.pebble.loader.PebbleDefaultLoader; import com.mitchellbosecke.pebble.parser.Parser; public abstract class AbstractTest { @@ -28,8 +29,9 @@ public abstract class AbstractTest { public AbstractTest() { Collection paths = new ArrayList<>(); paths.add("templates"); - paths.add("misc"); - pebble = new PebbleEngine(paths, "target/classes"); + Loader templateLoader = new PebbleDefaultLoader(); + templateLoader.setPrefix("templates"); + pebble = new PebbleEngine(templateLoader); loader = pebble.getLoader(); lexer = pebble.getLexer(); parser = pebble.getParser(); diff --git a/src/test/java/com/mitchellbosecke/pebble/CompilerTest.java b/src/test/java/com/mitchellbosecke/pebble/CompilerTest.java index 20a97b2b1..85d9eefcc 100644 --- a/src/test/java/com/mitchellbosecke/pebble/CompilerTest.java +++ b/src/test/java/com/mitchellbosecke/pebble/CompilerTest.java @@ -16,9 +16,7 @@ import org.junit.Test; -import com.mitchellbosecke.pebble.error.LoaderException; import com.mitchellbosecke.pebble.error.PebbleException; -import com.mitchellbosecke.pebble.error.SyntaxException; import com.mitchellbosecke.pebble.template.PebbleTemplate; public class CompilerTest extends AbstractTest {