diff --git a/runAllTesting.sh b/runAllTesting.sh index 661fb0d80..e7401215a 100755 --- a/runAllTesting.sh +++ b/runAllTesting.sh @@ -42,7 +42,7 @@ unzip mytest.zip test_result=$? if [ $test_result -eq 0 ] then - echo "Successfully Unzipped Production Server " + echo "Successfully Unzipped Production Server to `pwd`" else echo "Unzip Production server FAILED" exit $test_result diff --git a/webserver/gradle-plugin-htmlcompiler/src/main/java/org/webpieces/gradle/compiler/TemplateCompilerTask.java b/webserver/gradle-plugin-htmlcompiler/src/main/java/org/webpieces/gradle/compiler/TemplateCompilerTask.java index f5dda79a9..fc11fe690 100644 --- a/webserver/gradle-plugin-htmlcompiler/src/main/java/org/webpieces/gradle/compiler/TemplateCompilerTask.java +++ b/webserver/gradle-plugin-htmlcompiler/src/main/java/org/webpieces/gradle/compiler/TemplateCompilerTask.java @@ -28,6 +28,8 @@ import com.google.inject.Guice; import com.google.inject.Injector; +import groovy.lang.GroovyClassLoader; + public class TemplateCompilerTask extends AbstractCompile { // private TemplateCompileOptions options = new TemplateCompileOptions(); @@ -69,8 +71,6 @@ public void compileImpl(TemplateCompileOptions options) throws IOException { config.setGroovySrcWriteDirectory(groovySrcGen); System.out.println("custom tags="+options.getCustomTags()); config.setCustomTagsFromPlugin(options.getCustomTags()); - Injector injector = Guice.createInjector(new RouterLookupModule(), new DevTemplateModule(config)); - HtmlToJavaClassCompiler compiler = injector.getInstance(HtmlToJavaClassCompiler.class); LogLevel logLevel = getProject().getGradle().getStartParameter().getLogLevel(); @@ -92,6 +92,14 @@ public void compileImpl(TemplateCompileOptions options) throws IOException { OutputStreamWriter write = new OutputStreamWriter(routeOut, encoding.name()); BufferedWriter bufWrite = new BufferedWriter(write) ) { + + Injector injector = Guice.createInjector( + new RouterLookupModule(), + new DevTemplateModule(config, new PluginCompileCallback(destinationDir, bufWrite)) + ); + HtmlToJavaClassCompiler compiler = injector.getInstance(HtmlToJavaClassCompiler.class); + GroovyClassLoader cl = new GroovyClassLoader(); + for(File f : files) { System.out.println("file="+f); @@ -100,7 +108,7 @@ public void compileImpl(TemplateCompileOptions options) throws IOException { String source = readSource(f); - compiler.compile(fullName, source, new PluginCompileCallback(destinationDir, bufWrite)); + compiler.compile(cl, fullName, source); } } @@ -164,8 +172,8 @@ public PluginCompileCallback(File destinationDir, BufferedWriter bufWrite) { this.destinationDir = destinationDir; this.routeOut = bufWrite; } - - public void compiledGroovyClass(GroovyClass clazz) { + + public void compiledGroovyClass(GroovyClassLoader groovyCl, GroovyClass clazz) { String name = clazz.getName(); String path = name.replace('.', '/'); String fullPathName = path+".class"; diff --git a/webserver/http-router-dev/src/main/java/org/webpieces/devrouter/impl/DevCompressionCacheSetup.java b/webserver/http-router-dev/src/main/java/org/webpieces/devrouter/impl/DevCompressionCacheSetup.java index a0d05fdbc..e5c57a04d 100644 --- a/webserver/http-router-dev/src/main/java/org/webpieces/devrouter/impl/DevCompressionCacheSetup.java +++ b/webserver/http-router-dev/src/main/java/org/webpieces/devrouter/impl/DevCompressionCacheSetup.java @@ -6,6 +6,7 @@ import org.webpieces.util.logging.LoggerFactory; import org.webpieces.router.impl.StaticRoute; import org.webpieces.router.impl.compression.CompressionCacheSetup; +import org.webpieces.router.impl.compression.FileMeta; public class DevCompressionCacheSetup implements CompressionCacheSetup { @@ -17,7 +18,7 @@ public void setupCache(List staticRoutes) { } @Override - public String relativeUrlToHash(String path) { + public FileMeta relativeUrlToHash(String path) { return null; } } diff --git a/webserver/http-router/src/main/java/org/webpieces/router/api/RoutingService.java b/webserver/http-router/src/main/java/org/webpieces/router/api/RoutingService.java index ace199e2f..2f3932e49 100644 --- a/webserver/http-router/src/main/java/org/webpieces/router/api/RoutingService.java +++ b/webserver/http-router/src/main/java/org/webpieces/router/api/RoutingService.java @@ -3,6 +3,7 @@ import java.util.Map; import org.webpieces.ctx.api.RouterRequest; +import org.webpieces.router.impl.compression.FileMeta; public interface RoutingService { @@ -22,5 +23,5 @@ public interface RoutingService { */ String convertToUrl(String routeId, Map notUrlEncodedArgs); - String relativeUrlToHash(String urlPath); + FileMeta relativeUrlToHash(String urlPath); } diff --git a/webserver/http-router/src/main/java/org/webpieces/router/impl/AbstractRouterService.java b/webserver/http-router/src/main/java/org/webpieces/router/impl/AbstractRouterService.java index 01c2bc2f4..1fdafc038 100644 --- a/webserver/http-router/src/main/java/org/webpieces/router/impl/AbstractRouterService.java +++ b/webserver/http-router/src/main/java/org/webpieces/router/impl/AbstractRouterService.java @@ -8,6 +8,7 @@ import org.webpieces.router.api.RoutingService; import org.webpieces.router.api.Startable; import org.webpieces.router.api.exceptions.BadCookieException; +import org.webpieces.router.impl.compression.FileMeta; import org.webpieces.util.logging.Logger; import org.webpieces.util.logging.LoggerFactory; @@ -48,7 +49,7 @@ public String convertToUrl(String routeId, Map args) { } @Override - public String relativeUrlToHash(String urlPath) { + public FileMeta relativeUrlToHash(String urlPath) { if(!urlPath.startsWith("/")) urlPath = "/"+urlPath; diff --git a/webserver/http-router/src/main/java/org/webpieces/router/impl/RouteLoader.java b/webserver/http-router/src/main/java/org/webpieces/router/impl/RouteLoader.java index 7cbd66106..3a42b3640 100644 --- a/webserver/http-router/src/main/java/org/webpieces/router/impl/RouteLoader.java +++ b/webserver/http-router/src/main/java/org/webpieces/router/impl/RouteLoader.java @@ -19,6 +19,7 @@ import org.webpieces.router.api.routing.RouteModule; import org.webpieces.router.api.routing.WebAppMeta; import org.webpieces.router.impl.compression.CompressionCacheSetup; +import org.webpieces.router.impl.compression.FileMeta; import org.webpieces.router.impl.hooks.ClassForName; import org.webpieces.router.impl.loader.ControllerLoader; import org.webpieces.router.impl.model.AbstractRouteBuilder; @@ -231,7 +232,7 @@ public String convertToUrl(String routeId, Map args) { return invoker.convertToUrl(routeId, args); } - public String relativeUrlToHash(String urlPath) { + public FileMeta relativeUrlToHash(String urlPath) { return compressionCacheSetup.relativeUrlToHash(urlPath); } diff --git a/webserver/http-router/src/main/java/org/webpieces/router/impl/compression/CompressionCacheSetup.java b/webserver/http-router/src/main/java/org/webpieces/router/impl/compression/CompressionCacheSetup.java index 0883e9528..0c3e194fa 100644 --- a/webserver/http-router/src/main/java/org/webpieces/router/impl/compression/CompressionCacheSetup.java +++ b/webserver/http-router/src/main/java/org/webpieces/router/impl/compression/CompressionCacheSetup.java @@ -8,6 +8,6 @@ public interface CompressionCacheSetup { void setupCache(List staticRoutes); - String relativeUrlToHash(String path); + FileMeta relativeUrlToHash(String path); } diff --git a/webserver/http-router/src/main/java/org/webpieces/router/impl/compression/FileMeta.java b/webserver/http-router/src/main/java/org/webpieces/router/impl/compression/FileMeta.java new file mode 100644 index 000000000..f2ae05d3f --- /dev/null +++ b/webserver/http-router/src/main/java/org/webpieces/router/impl/compression/FileMeta.java @@ -0,0 +1,19 @@ +package org.webpieces.router.impl.compression; + +public class FileMeta { + + private String hash; + + public FileMeta() { + this(null); + } + + public FileMeta(String hash) { + this.hash = hash; + } + + public String getHash() { + return hash; + } + +} diff --git a/webserver/http-router/src/main/java/org/webpieces/router/impl/compression/ProdCompressionCacheSetup.java b/webserver/http-router/src/main/java/org/webpieces/router/impl/compression/ProdCompressionCacheSetup.java index 07b004644..44a0d5dd7 100644 --- a/webserver/http-router/src/main/java/org/webpieces/router/impl/compression/ProdCompressionCacheSetup.java +++ b/webserver/http-router/src/main/java/org/webpieces/router/impl/compression/ProdCompressionCacheSetup.java @@ -30,7 +30,7 @@ public class ProdCompressionCacheSetup implements CompressionCacheSetup { private MimeTypes mimeTypes; private List encodings = new ArrayList<>(); private FileUtil fileUtil; - private Map pathToHash = new HashMap<>(); + private Map pathToFileMeta = new HashMap<>(); @Inject public ProdCompressionCacheSetup(CompressionLookup lookup, RouterConfig config, MimeTypes mimeTypes, FileUtil fileUtil) { @@ -116,14 +116,18 @@ private void transferAndCompress(Properties p, File directory, File destination, private void maybeAddFileToCache(Properties properties, File src, File destination, String urlPath) { String name = src.getName(); int indexOf = name.lastIndexOf("."); - if(indexOf < 0) + if(indexOf < 0) { + pathToFileMeta.put(urlPath, new FileMeta()); return; //do nothing + } String extension = name.substring(indexOf+1); MimeTypeResult mimeType = mimeTypes.extensionToContentType(extension, "application/octet-stream"); Compression compression = lookup.createCompressionStream(encodings, extension, mimeType); - if(compression == null) + if(compression == null) { + pathToFileMeta.put(urlPath, new FileMeta()); return; + } //before we do the below, do a quick timestamp check to avoid reading in the files when not necessary long lastModifiedSrc = src.lastModified(); @@ -133,7 +137,7 @@ private void maybeAddFileToCache(Properties properties, File src, File destinati String previousHash = properties.getProperty(urlPath); if(lastModified > lastModifiedSrc && previousHash != null) { log.info("timestamp later than src so skipping writing to="+destination); - pathToHash.put(urlPath, previousHash); + pathToFileMeta.put(urlPath, new FileMeta(previousHash)); return; //no need to check anything as destination was written after this source file } @@ -148,7 +152,7 @@ private void maybeAddFileToCache(Properties properties, File src, File destinati + "corrupted. You will need to delete the whole cache directory"); log.info("Previous file is the same, no need to compress to="+destination+" hash="+hash); - pathToHash.put(urlPath, previousHash); + pathToFileMeta.put(urlPath, new FileMeta(previousHash)); return; } } @@ -158,12 +162,12 @@ private void maybeAddFileToCache(Properties properties, File src, File destinati //if file writing succeeded, set the hash properties.setProperty(urlPath, hash); - String existing = pathToHash.get(urlPath); + FileMeta existing = pathToFileMeta.get(urlPath); if(existing != null) - throw new IllegalStateException("this urlpath="+urlPath+" is referencing two files. hash1="+existing+" hash2="+hash + throw new IllegalStateException("this urlpath="+urlPath+" is referencing two files. hash1="+existing.getHash()+" hash2="+hash +" You should search your logs for this hash"); - pathToHash.put(urlPath, hash); + pathToFileMeta.put(urlPath, new FileMeta(hash)); log.info("compressed "+src.length()+" bytes to="+destination.length()+" to file="+destination+" hash="+hash); @@ -196,7 +200,7 @@ private void createDirectory(File directoryToCreate) { } @Override - public String relativeUrlToHash(String path) { - return pathToHash.get(path); + public FileMeta relativeUrlToHash(String path) { + return pathToFileMeta.get(path); } } diff --git a/webserver/http-templating-dev/src/main/java/org/webpieces/templating/api/AbstractTag.java b/webserver/http-templating-dev/src/main/java/org/webpieces/templating/api/AbstractTag.java index 664916ba2..3d391cd4b 100644 --- a/webserver/http-templating-dev/src/main/java/org/webpieces/templating/api/AbstractTag.java +++ b/webserver/http-templating-dev/src/main/java/org/webpieces/templating/api/AbstractTag.java @@ -3,7 +3,7 @@ public abstract class AbstractTag implements GroovyGen { @Override - public void generateStartAndEnd(ScriptOutput sourceCode, Token token, int uniqueId, CompileCallback callback) { + public void generateStartAndEnd(ScriptOutput sourceCode, Token token, int uniqueId) { String name = getName(); throw new IllegalArgumentException(name+" tag can only be used with a body so" + " #{"+name+"/} is not usable. "+token.getSourceLocation(true)); diff --git a/webserver/http-templating-dev/src/main/java/org/webpieces/templating/api/CompileCallback.java b/webserver/http-templating-dev/src/main/java/org/webpieces/templating/api/CompileCallback.java index 939e402bd..bcc34c8c8 100644 --- a/webserver/http-templating-dev/src/main/java/org/webpieces/templating/api/CompileCallback.java +++ b/webserver/http-templating-dev/src/main/java/org/webpieces/templating/api/CompileCallback.java @@ -4,10 +4,12 @@ import org.codehaus.groovy.tools.GroovyClass; -public interface CompileCallback { +import groovy.lang.GroovyClassLoader; - void compiledGroovyClass(GroovyClass clazz); +public interface CompileCallback { + void compiledGroovyClass(GroovyClassLoader groovyCl, GroovyClass clazz); + /** * Allows the compiler to write a file out with all routeids from html files that can be used at startup time * to validate all routes so we catch any errors on mistyped route ids before going to production (ie. build time as your @@ -20,5 +22,4 @@ public interface CompileCallback { void recordRouteId(String routeId, List argNames, String sourceLocation); void recordPath(String relativeUrlPath, String sourceLocation); - } diff --git a/webserver/http-templating-dev/src/main/java/org/webpieces/templating/api/DevTemplateModule.java b/webserver/http-templating-dev/src/main/java/org/webpieces/templating/api/DevTemplateModule.java index 320939fa6..42bdfe000 100644 --- a/webserver/http-templating-dev/src/main/java/org/webpieces/templating/api/DevTemplateModule.java +++ b/webserver/http-templating-dev/src/main/java/org/webpieces/templating/api/DevTemplateModule.java @@ -1,5 +1,6 @@ package org.webpieces.templating.api; +import org.webpieces.templating.impl.DevTemplateCompileCallback; import org.webpieces.templating.impl.DevTemplateService; import com.google.inject.Binder; @@ -8,14 +9,21 @@ public class DevTemplateModule implements Module { private TemplateCompileConfig config; + private CompileCallback callback; public DevTemplateModule(TemplateCompileConfig config) { + this(config, new DevTemplateCompileCallback()); + } + + public DevTemplateModule(TemplateCompileConfig config, CompileCallback callback) { this.config = config; + this.callback = callback; } @Override public void configure(Binder binder) { binder.bind(TemplateService.class).to(DevTemplateService.class).asEagerSingleton(); binder.bind(TemplateCompileConfig.class).toInstance(config); + binder.bind(CompileCallback.class).toInstance(callback); } } diff --git a/webserver/http-templating-dev/src/main/java/org/webpieces/templating/api/GroovyGen.java b/webserver/http-templating-dev/src/main/java/org/webpieces/templating/api/GroovyGen.java index ec13154a1..c2db41e08 100644 --- a/webserver/http-templating-dev/src/main/java/org/webpieces/templating/api/GroovyGen.java +++ b/webserver/http-templating-dev/src/main/java/org/webpieces/templating/api/GroovyGen.java @@ -4,9 +4,9 @@ public interface GroovyGen { String getName(); - void generateStartAndEnd(ScriptOutput sourceCode, Token token, int uniqueId, CompileCallback callback); + void generateStartAndEnd(ScriptOutput sourceCode, Token token, int uniqueId); - void generateStart(ScriptOutput sourceCode, Token token, int uniqueId, CompileCallback callback); + void generateStart(ScriptOutput sourceCode, Token token, int uniqueId); void generateEnd(ScriptOutput sourceCode, Token token, int uniqueId); diff --git a/webserver/http-templating-dev/src/main/java/org/webpieces/templating/api/RouterLookupModule.java b/webserver/http-templating-dev/src/main/java/org/webpieces/templating/api/RouterLookupModule.java index 7afa580dd..1fca8b994 100644 --- a/webserver/http-templating-dev/src/main/java/org/webpieces/templating/api/RouterLookupModule.java +++ b/webserver/http-templating-dev/src/main/java/org/webpieces/templating/api/RouterLookupModule.java @@ -6,6 +6,7 @@ import com.google.inject.Module; public class RouterLookupModule implements Module { + @Override public void configure(Binder binder) { binder.bind(RouterLookup.class).to(NullRouterLookup.class).asEagerSingleton(); diff --git a/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/DevTemplateCompileCallback.java b/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/DevTemplateCompileCallback.java new file mode 100644 index 000000000..c9e80c297 --- /dev/null +++ b/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/DevTemplateCompileCallback.java @@ -0,0 +1,25 @@ +package org.webpieces.templating.impl; + +import java.util.List; + +import org.codehaus.groovy.tools.GroovyClass; +import org.webpieces.templating.api.CompileCallback; + +import groovy.lang.GroovyClassLoader; + +public class DevTemplateCompileCallback implements CompileCallback { + + @Override + public void compiledGroovyClass(GroovyClassLoader cl, GroovyClass groovyClass) { + cl.defineClass(groovyClass.getName(), groovyClass.getBytes()); + } + + @Override + public void recordRouteId(String routeId, List argNames, String sourceLocation) { + } + + @Override + public void recordPath(String relativeUrlPath, String sourceLocation) { + } + +} \ No newline at end of file diff --git a/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/DevTemplateService.java b/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/DevTemplateService.java index e61d16ec6..9f7306a6e 100644 --- a/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/DevTemplateService.java +++ b/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/DevTemplateService.java @@ -3,32 +3,37 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.io.StringWriter; import java.util.List; +import java.util.Map; import javax.inject.Inject; import org.apache.commons.io.IOUtils; -import org.codehaus.groovy.tools.GroovyClass; -import org.webpieces.templating.api.CompileCallback; import org.webpieces.templating.api.HtmlTagLookup; import org.webpieces.templating.api.RouterLookup; import org.webpieces.templating.api.Template; import org.webpieces.templating.api.TemplateCompileConfig; -import org.webpieces.templating.api.TemplateService; import org.webpieces.templating.impl.source.ScriptOutputImpl; import org.webpieces.util.file.VirtualFile; import org.webpieces.util.file.VirtualFileClasspath; import groovy.lang.GroovyClassLoader; -public class DevTemplateService extends ProdTemplateService implements TemplateService { +public class DevTemplateService extends ProdTemplateService { private HtmlToJavaClassCompiler compiler; private TemplateCompileConfig config; private HtmlTagLookup htmlTagLookup; + private ThreadLocal currentCl = new ThreadLocal<>(); + @Inject - public DevTemplateService(RouterLookup urlLookup, HtmlTagLookup htmlTagLookup, HtmlToJavaClassCompiler compiler, TemplateCompileConfig config) { + public DevTemplateService( + RouterLookup urlLookup, + HtmlTagLookup htmlTagLookup, + HtmlToJavaClassCompiler compiler, + TemplateCompileConfig config) { super(urlLookup, htmlTagLookup); this.htmlTagLookup = htmlTagLookup; this.compiler = compiler; @@ -36,9 +41,27 @@ public DevTemplateService(RouterLookup urlLookup, HtmlTagLookup htmlTagLookup, H } @Override + public void loadAndRunTemplate(String templatePath, StringWriter out, Map pageArgs) { + // + GroovyClassLoader startingCl = currentCl.get(); + if(startingCl == null) + currentCl.set(new OurGroovyClassLoader()); + + try { + Template template = loadTemplate(templatePath); + runTemplate(template, out, pageArgs); + } finally { + if(startingCl == null) { + //startingCl is the flag for when we created the original Cl + currentCl.set(null); + } + } + } + protected Template loadTemplate(String fullTemplatePath, String fullClassName) { + //this is a recursive function. Run TestFieldTag.java to see try { - return loadTemplateImpl(fullTemplatePath, fullClassName); + return loadTemplateImpl(currentCl.get(), fullTemplatePath, fullClassName); } catch (IOException e) { throw new RuntimeException(e); } catch (ClassNotFoundException e) { @@ -46,7 +69,7 @@ protected Template loadTemplate(String fullTemplatePath, String fullClassName) { } } - private Template loadTemplateImpl(String fullTemplatePath, String templateFullClassName) throws IOException, ClassNotFoundException { + private Template loadTemplateImpl(OurGroovyClassLoader cl, String fullTemplatePath, String templateFullClassName) throws IOException, ClassNotFoundException { if(config.isMustReadClassFromFileSystem()) { //shortcut for tests that use PlatformOverridesForTest to ensure we test the production groovy class files AND //it ensures we give code coverage numbers on those class files as well. @@ -73,40 +96,15 @@ private Template loadTemplateImpl(String fullTemplatePath, String templateFullCl try(InputStream resource = theResource.openInputStream()) { String viewSource = IOUtils.toString(resource, config.getFileEncoding().name()); - Class compiledTemplate = createTemplate(templateFullClassName, viewSource); + Class compiledTemplate = createTemplate(cl, templateFullClassName, viewSource); return new TemplateImpl(urlLookup, htmlTagLookup, compiledTemplate); } } - private Class createTemplate(String fullClassName, String source) throws ClassNotFoundException { - GroovyClassLoader cl = new GroovyClassLoader(); - - ScriptOutputImpl scriptCode = compiler.compile(fullClassName, source, new DevTemplateCompileCallback(cl)); - - return cl.loadClass(scriptCode.getFullClassName()); - } - - private static class DevTemplateCompileCallback implements CompileCallback { - - private GroovyClassLoader cl; - - public DevTemplateCompileCallback(GroovyClassLoader cl) { - this.cl = cl; - } - - @Override - public void compiledGroovyClass(GroovyClass groovyClass) { - cl.defineClass(groovyClass.getName(), groovyClass.getBytes()); - } - - @Override - public void recordRouteId(String routeId, List argNames, String sourceLocation) { - } - - @Override - public void recordPath(String relativeUrlPath, String sourceLocation) { - } - + private Class createTemplate(OurGroovyClassLoader cl, String fullClassName, String source) throws ClassNotFoundException { + if(!cl.isClassDefined(fullClassName)) + compiler.compile(cl, fullClassName, source); + return cl.loadClass(fullClassName); } } diff --git a/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/GroovyToJavaClassCompiler.java b/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/GroovyToJavaClassCompiler.java index 6da93fb8f..9ad8af2ea 100644 --- a/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/GroovyToJavaClassCompiler.java +++ b/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/GroovyToJavaClassCompiler.java @@ -1,5 +1,7 @@ package org.webpieces.templating.impl; +import javax.inject.Inject; + import org.codehaus.groovy.control.CompilationUnit; import org.codehaus.groovy.control.Phases; import org.codehaus.groovy.tools.GroovyClass; @@ -10,9 +12,16 @@ public class GroovyToJavaClassCompiler { - public void compile(ScriptOutputImpl scriptCode, CompileCallback callbacks) { + private CompileCallback callbacks; + + @Inject + public GroovyToJavaClassCompiler(CompileCallback callbacks) { + this.callbacks = callbacks; + } + + public void compile(GroovyClassLoader cl, ScriptOutputImpl scriptCode) { try { - compileImpl(scriptCode, callbacks); + compileImpl(cl, scriptCode); //F'ing checked exceptions should have been runtime so I don't have all this cruft in my app... } catch (SecurityException e) { throw new RuntimeException(e); @@ -21,16 +30,15 @@ public void compile(ScriptOutputImpl scriptCode, CompileCallback callbacks) { } } - private void compileImpl(ScriptOutputImpl scriptCode, CompileCallback callbacks) { - GroovyClassLoader cl = new GroovyClassLoader(); + private void compileImpl(GroovyClassLoader groovyCl, ScriptOutputImpl scriptCode) { CompilationUnit compileUnit = new CompilationUnit(); compileUnit.addSource(scriptCode.getFullClassName(), scriptCode.getScriptSourceCode()); compileUnit.compile(Phases.CLASS_GENERATION); - compileUnit.setClassLoader(cl); - + compileUnit.setClassLoader(groovyCl); + for (Object compileClass : compileUnit.getClasses()) { GroovyClass groovyClass = (GroovyClass) compileClass; - callbacks.compiledGroovyClass(groovyClass); + callbacks.compiledGroovyClass(groovyCl, groovyClass); } } diff --git a/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/HtmlToJavaClassCompiler.java b/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/HtmlToJavaClassCompiler.java index f62d7cee1..462f74e6e 100644 --- a/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/HtmlToJavaClassCompiler.java +++ b/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/HtmlToJavaClassCompiler.java @@ -9,11 +9,12 @@ import javax.inject.Inject; -import org.webpieces.templating.api.CompileCallback; import org.webpieces.templating.api.TemplateCompileConfig; import org.webpieces.templating.impl.source.GroovyScriptGenerator; import org.webpieces.templating.impl.source.ScriptOutputImpl; +import groovy.lang.GroovyClassLoader; + public class HtmlToJavaClassCompiler { private GroovyScriptGenerator scriptGen; private GroovyToJavaClassCompiler groovyCompile; @@ -29,16 +30,16 @@ public HtmlToJavaClassCompiler( this.config = config; } - public ScriptOutputImpl compile(String fullClassName, String source, CompileCallback callbacks) { + public ScriptOutputImpl compile(GroovyClassLoader cl, String fullClassName, String source) { String filePath = fullClassName.replace(".", "/").replace("_", "."); - ScriptOutputImpl scriptCode = scriptGen.generate(filePath, source, fullClassName, callbacks); + ScriptOutputImpl scriptCode = scriptGen.generate(filePath, source, fullClassName); if(config.getGroovySrcWriteDirectory() != null) writeSourceFile(scriptCode, fullClassName); try { - groovyCompile.compile(scriptCode, callbacks); + groovyCompile.compile(cl, scriptCode); } catch(Exception e) { throw new RuntimeException("Generated a groovy script file but compilation failed for file=" +filePath+" Script code generated=\n\n"+scriptCode.getScriptSourceCode(), e); diff --git a/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/OurGroovyClassLoader.java b/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/OurGroovyClassLoader.java new file mode 100644 index 000000000..561949534 --- /dev/null +++ b/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/OurGroovyClassLoader.java @@ -0,0 +1,22 @@ +package org.webpieces.templating.impl; + +import java.util.HashSet; +import java.util.Set; + +import groovy.lang.GroovyClassLoader; + +public class OurGroovyClassLoader extends GroovyClassLoader { + + private Set definedClasses = new HashSet<>(); + + @SuppressWarnings("rawtypes") + @Override + public Class defineClass(String name, byte[] b) { + definedClasses.add(name); + return super.defineClass(name, b, 0, b.length); + } + + public boolean isClassDefined(String name) { + return definedClasses.contains(name); + } +} diff --git a/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/source/GenLookup.java b/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/source/GenLookup.java index e38a70e86..44b1e1832 100644 --- a/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/source/GenLookup.java +++ b/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/source/GenLookup.java @@ -3,6 +3,8 @@ import java.util.HashMap; import java.util.Map; +import javax.inject.Inject; + import org.webpieces.templating.api.GroovyGen; import org.webpieces.templating.impl.tags.ElseIfGen; import org.webpieces.templating.impl.tags.ElseGen; @@ -14,14 +16,17 @@ public class GenLookup { private Map generators = new HashMap<>(); - public GenLookup() { + @Inject + private ListGen listGen; + + protected void init() { put(new VerbatimGen()); put(new IfGen()); put(new ElseIfGen()); put(new ElseGen()); - put(new ListGen()); + put(listGen); } - + protected void put(GroovyGen generator) { generators.put(generator.getName(), generator); } diff --git a/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/source/GroovyScriptGenerator.java b/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/source/GroovyScriptGenerator.java index 7c74d2c6c..63ef49535 100644 --- a/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/source/GroovyScriptGenerator.java +++ b/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/source/GroovyScriptGenerator.java @@ -6,7 +6,6 @@ import org.webpieces.util.logging.Logger; import org.webpieces.util.logging.LoggerFactory; -import org.webpieces.templating.api.CompileCallback; public class GroovyScriptGenerator { @@ -20,7 +19,9 @@ public GroovyScriptGenerator(TemplateTokenizer tokenizer, ScriptWriter creator) this.creator = creator; } - public ScriptOutputImpl generate(String filePath, String source, String fullClassName, CompileCallback callbacks) { + public ScriptOutputImpl generate(String filePath, String source, String fullClassName) { + creator.init(); + long start = System.currentTimeMillis(); source = source.replace("\r", ""); @@ -41,7 +42,7 @@ public ScriptOutputImpl generate(String filePath, String source, String fullClas // Class header creator.printHead(sourceCode, packageStr, className); - generateBody(sourceCode, tokens, callbacks); + generateBody(sourceCode, tokens); // Class end creator.printEnd(sourceCode); @@ -57,7 +58,7 @@ public ScriptOutputImpl generate(String filePath, String source, String fullClas return sourceCode; } - private void generateBody(ScriptOutputImpl sourceCode, List tokens, CompileCallback callbacks) { + private void generateBody(ScriptOutputImpl sourceCode, List tokens) { for(int i = 0; i < tokens.size(); i++) { TokenImpl token = tokens.get(i); TokenImpl previousToken = null; @@ -75,6 +76,9 @@ private void generateBody(ScriptOutputImpl sourceCode, List tokens, C case SCRIPT: creator.printScript(token, sourceCode); break; + case FILE_VERIFY: + creator.printFilePath(token, sourceCode); + break; case EXPR: creator.printExpression(token, sourceCode); break; @@ -82,19 +86,19 @@ private void generateBody(ScriptOutputImpl sourceCode, List tokens, C creator.printMessage(token, sourceCode); break; case ACTION: - creator.printAction(token, sourceCode, false, callbacks); + creator.printAction(token, sourceCode, false); break; case ABSOLUTE_ACTION: - creator.printAction(token, sourceCode, true, callbacks); + creator.printAction(token, sourceCode, true); break; case COMMENT: creator.unprintUpToLastNewLine(); break; case START_END_TAG: - creator.printStartEndTag(token, sourceCode, callbacks); + creator.printStartEndTag(token, sourceCode); break; case START_TAG: - creator.printStartTag(token, previousToken, sourceCode, callbacks); + creator.printStartTag(token, previousToken, sourceCode); break; case END_TAG: creator.printEndTag(token, sourceCode); diff --git a/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/source/ScriptWriter.java b/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/source/ScriptWriter.java index 8dcc04b10..2b9481ab2 100644 --- a/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/source/ScriptWriter.java +++ b/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/source/ScriptWriter.java @@ -6,12 +6,11 @@ import javax.inject.Inject; import org.webpieces.templating.api.AbstractTag; -import org.webpieces.templating.api.CompileCallback; import org.webpieces.templating.api.GroovyGen; import org.webpieces.templating.api.HtmlTag; import org.webpieces.templating.api.HtmlTagLookup; import org.webpieces.templating.api.TemplateCompileConfig; -import org.webpieces.templating.impl.tags.ParseTagArgs; +import org.webpieces.templating.impl.tags.RoutePathTranslator; import org.webpieces.templating.impl.tags.TagGen; public class ScriptWriter { @@ -26,15 +25,31 @@ public class ScriptWriter { private HtmlTagLookup htmlTagLookup; private UniqueIdGenerator uniqueIdGen; private TemplateCompileConfig config; + private RoutePathTranslator translator; + private boolean initialized; @Inject - public ScriptWriter(HtmlTagLookup htmlTagLookup, GenLookup lookup, UniqueIdGenerator generator, TemplateCompileConfig config) { + public ScriptWriter( + HtmlTagLookup htmlTagLookup, + GenLookup lookup, + UniqueIdGenerator generator, + RoutePathTranslator translator, + TemplateCompileConfig config) { this.htmlTagLookup = htmlTagLookup; generatorLookup = lookup; this.uniqueIdGen = generator; + this.translator = translator; this.config = config; } + public void init() { + if(initialized) + return; + + initialized = true; + generatorLookup.init(); + } + public void printHead(ScriptOutputImpl sourceCode, String packageStr, String className) { tagStack.set(new Stack<>()); @@ -157,13 +172,13 @@ private String replaceNewLinesBetweenQuotes(String value, TokenImpl token) { } } - public void printAction(TokenImpl token, ScriptOutputImpl sourceCode, boolean isAbsolute, CompileCallback callbacks) { + public void printAction(TokenImpl token, ScriptOutputImpl sourceCode, boolean isAbsolute) { if(isAbsolute) throw new UnsupportedOperationException("not supported yet. Need to modify to use the Host header as input as the domain"); String expr = token.getCleanValue(); - String groovySnippet = ParseTagArgs.translateRouteId(expr, token, callbacks); + String groovySnippet = translator.translateRouteId(expr, token); //fetchUrl('VERBATIM_ROUTE_ID', [:], 'at org.webpieces.webserver.tags.app.aHrefTag.html(aHrefTag.html:5)') sourceCode.println(" __out.print("+groovySnippet+");", token); } @@ -175,7 +190,7 @@ public void printAction(TokenImpl token, ScriptOutputImpl sourceCode, boolean is * @param sourceCode * @param callbacks */ - public void printStartEndTag(TokenImpl token, ScriptOutputImpl sourceCode, CompileCallback callbacks) { + public void printStartEndTag(TokenImpl token, ScriptOutputImpl sourceCode) { String expr = token.getCleanValue(); int indexOfSpace = expr.indexOf(" "); String tagName = expr; @@ -187,15 +202,15 @@ public void printStartEndTag(TokenImpl token, ScriptOutputImpl sourceCode, Compi GroovyGen generator = generatorLookup.lookup(tagName, token); HtmlTag htmltag = htmlTagLookup.lookup(tagName); if(generator != null) { - generator.generateStartAndEnd(sourceCode, token, id, callbacks); + generator.generateStartAndEnd(sourceCode, token, id); } else if(htmltag == null) { throw new IllegalArgumentException("Unknown tag="+tagName+" location="+token.getSourceLocation(true)); } else { - new TagGen(tagName, token).generateStartAndEnd(sourceCode, token, id, callbacks); + new TagGen(tagName, token, translator).generateStartAndEnd(sourceCode, token, id); } } - public void printStartTag(TokenImpl token, TokenImpl previousToken, ScriptOutputImpl sourceCode, CompileCallback callbacks) { + public void printStartTag(TokenImpl token, TokenImpl previousToken, ScriptOutputImpl sourceCode) { String tagName = token.getTagName(); GroovyGen generator = generatorLookup.lookup(tagName, token); @@ -211,11 +226,11 @@ public void printStartTag(TokenImpl token, TokenImpl previousToken, ScriptOutput throw new IllegalArgumentException("Unknown tag=#{"+tagName+"}# OR you didn't add '" +tagName+"' to list of customTags in build.gradle file. "+token.getSourceLocation(true)); - generator = new TagGen(tagName, token); + generator = new TagGen(tagName, token, translator); } int id = uniqueIdGen.generateId(); - generator.generateStart(sourceCode, token, id, callbacks); + generator.generateStart(sourceCode, token, id); tagStack.get().push(new TagState(token, generator, id)); } @@ -242,4 +257,16 @@ public void cleanup() { tagStack.set(null); } + public void printFilePath(TokenImpl token, ScriptOutputImpl sourceCode) { + //TODO: record the script to pre-emptively send by calling into the groovy + //superclass recording all these scripts to send + //THEN, after script is run, client can call getScriptsToPreemptivelySend and send + //all those scripts before the browser client asks for them(in http2 at least) + //BUT we must also RECORD all these on that connection and not send them a + //second time which would be a waste of our CPU + String value = token.getCleanValue(); + String path = translator.recordPath(value, token.getSourceLocation(false)); + sourceCode.println(" __out.print(\""+path+"\");", token); + } + } diff --git a/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/source/TemplateToken.java b/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/source/TemplateToken.java index 2a9b7d97f..fe47988f1 100644 --- a/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/source/TemplateToken.java +++ b/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/source/TemplateToken.java @@ -12,6 +12,7 @@ public enum TemplateToken { EOF(null, null), //end of file PLAIN(null, null), //normal text SCRIPT("%{", "}%"), // %{...}% + FILE_VERIFY("%[", "]%"), // %[...]% verify the file exists so we don't deploy with missing files EXPR("${", "}$"), // ${...}$ START_TAG("#{", "}#"), // #{...}# END_TAG("#{/", "}#"), // #{/...}# diff --git a/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/source/TemplateTokenizerTask.java b/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/source/TemplateTokenizerTask.java index 47141da55..8095aaa32 100644 --- a/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/source/TemplateTokenizerTask.java +++ b/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/source/TemplateTokenizerTask.java @@ -94,6 +94,11 @@ private void processEnd(int lineNumber, char c, char c1, char c2) { found(PLAIN, 2, lineNumber); } break; + case FILE_VERIFY: + if (FILE_VERIFY.matchesEnd(c, c1, c2)) { + found(PLAIN, 2, lineNumber); + } + break; case COMMENT: if (COMMENT.matchesEnd(c, c1, c2)) { found(PLAIN, 2, lineNumber); @@ -145,6 +150,8 @@ private void processEnd(int lineNumber, char c, char c1, char c2) { private void processStartTagMatches(int lineNumber, char c, char c1, char c2) { if (SCRIPT.matchesStart(c, c1, c2)) { found(SCRIPT, 2, lineNumber); + } else if (FILE_VERIFY.matchesStart(c, c1, c2)) { + found(FILE_VERIFY, 2, lineNumber); } else if (EXPR.matchesStart(c, c1, c2)) { found(EXPR, 2, lineNumber); } else if (END_TAG.matchesStart(c, c1, c2)) { diff --git a/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/tags/ElseGen.java b/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/tags/ElseGen.java index e2108c638..626c96a39 100644 --- a/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/tags/ElseGen.java +++ b/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/tags/ElseGen.java @@ -1,7 +1,6 @@ package org.webpieces.templating.impl.tags; import org.webpieces.templating.api.AbstractTag; -import org.webpieces.templating.api.CompileCallback; import org.webpieces.templating.api.ScriptOutput; import org.webpieces.templating.api.Token; @@ -13,7 +12,7 @@ public String getName() { } @Override - public void generateStart(ScriptOutput sourceCode, Token token, int uniqueId, CompileCallback callback) { + public void generateStart(ScriptOutput sourceCode, Token token, int uniqueId) { sourceCode.println(" else {", token); sourceCode.println(); } diff --git a/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/tags/ElseIfGen.java b/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/tags/ElseIfGen.java index 9490c4079..3f754d975 100644 --- a/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/tags/ElseIfGen.java +++ b/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/tags/ElseIfGen.java @@ -1,7 +1,6 @@ package org.webpieces.templating.impl.tags; import org.webpieces.templating.api.AbstractTag; -import org.webpieces.templating.api.CompileCallback; import org.webpieces.templating.api.ScriptOutput; import org.webpieces.templating.api.Token; @@ -13,7 +12,7 @@ public String getName() { } @Override - public void generateStart(ScriptOutput sourceCode, Token token, int uniqueId, CompileCallback callback) { + public void generateStart(ScriptOutput sourceCode, Token token, int uniqueId) { String cleanValue = token.getCleanValue(); int indexOf = cleanValue.indexOf(" "); if(indexOf < 0) diff --git a/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/tags/IfGen.java b/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/tags/IfGen.java index dc4865a6c..37f6d8baa 100644 --- a/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/tags/IfGen.java +++ b/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/tags/IfGen.java @@ -1,7 +1,6 @@ package org.webpieces.templating.impl.tags; import org.webpieces.templating.api.AbstractTag; -import org.webpieces.templating.api.CompileCallback; import org.webpieces.templating.api.ScriptOutput; import org.webpieces.templating.api.Token; @@ -12,7 +11,7 @@ public String getName() { return "if"; } @Override - public void generateStart(ScriptOutput sourceCode, Token token, int uniqueId, CompileCallback callback) { + public void generateStart(ScriptOutput sourceCode, Token token, int uniqueId) { String cleanValue = token.getCleanValue(); int indexOf = cleanValue.indexOf(" "); if(indexOf < 0) diff --git a/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/tags/ListGen.java b/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/tags/ListGen.java index d16c4a407..2812aeecc 100644 --- a/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/tags/ListGen.java +++ b/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/tags/ListGen.java @@ -1,19 +1,25 @@ package org.webpieces.templating.impl.tags; -import org.webpieces.templating.api.CompileCallback; +import javax.inject.Inject; + import org.webpieces.templating.api.ScriptOutput; import org.webpieces.templating.api.Token; public class ListGen extends ParseTagArgs { + @Inject + public ListGen(RoutePathTranslator callback) { + super(callback); + } + @Override public String getName() { return "list"; } @Override - public void generateStart(ScriptOutput sourceCode, Token token, int uniqueId, CompileCallback callback) { - super.generateStartAttrs(sourceCode, token, uniqueId, callback); + public void generateStart(ScriptOutput sourceCode, Token token, int uniqueId) { + super.generateStartAttrs(sourceCode, token, uniqueId); String tagBody = token.getCleanValue(); if(!tagBody.contains("items:")) @@ -69,7 +75,7 @@ public void generateEnd(ScriptOutput sourceCode, Token token, int uniqueId) { } @Override - public void generateStartAndEnd(ScriptOutput sourceCode, Token token, int uniqueId, CompileCallback callback) { + public void generateStartAndEnd(ScriptOutput sourceCode, Token token, int uniqueId) { throw new UnsupportedOperationException("#{list}# tag must have body and didn't. "+token.getSourceLocation(true)); } diff --git a/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/tags/ParseTagArgs.java b/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/tags/ParseTagArgs.java index 5a2b7d759..e54fb144e 100644 --- a/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/tags/ParseTagArgs.java +++ b/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/tags/ParseTagArgs.java @@ -1,17 +1,20 @@ package org.webpieces.templating.impl.tags; -import java.util.ArrayList; -import java.util.List; - -import org.webpieces.templating.api.CompileCallback; import org.webpieces.templating.api.GroovyGen; import org.webpieces.templating.api.ScriptOutput; import org.webpieces.templating.api.Token; +import org.webpieces.templating.impl.source.TemplateToken; public abstract class ParseTagArgs implements GroovyGen { - protected void generateStartAttrs(ScriptOutput sourceCode, Token token, int uniqueId, CompileCallback callback) { - String tagArgs = fetchArgs(token, callback); + private RoutePathTranslator callback; + + public ParseTagArgs(RoutePathTranslator callback) { + this.callback = callback; + } + + protected void generateStartAttrs(ScriptOutput sourceCode, Token token, int uniqueId) { + String tagArgs = fetchArgs(token); sourceCode.println("enterExpression('"+token.getSourceLocation(false)+"');", token); //purely so we can add info to missing properties sourceCode.println("_attrs" + uniqueId + " = [" + tagArgs + "];", token); @@ -19,7 +22,7 @@ protected void generateStartAttrs(ScriptOutput sourceCode, Token token, int uniq sourceCode.println(); } - private String fetchArgs(Token token, CompileCallback callback) { + private String fetchArgs(Token token) { String expr = token.getCleanValue(); int indexOfSpace = expr.indexOf(" "); String tagArgs; @@ -31,12 +34,12 @@ private String fetchArgs(Token token, CompileCallback callback) { } //record the tag used and source location to be verified at unit test time - while(tagArgs.contains("@[")) { - tagArgs = replaceRouteIds(token, tagArgs, indexOfSpace, callback); + while(tagArgs.contains(TemplateToken.ACTION.getStart())) { + tagArgs = replaceRouteIds(token, tagArgs, indexOfSpace); } while(tagArgs.contains("%[")) { - tagArgs = replacePaths(token, tagArgs, indexOfSpace, callback); + tagArgs = replacePaths(token, tagArgs, indexOfSpace); } @@ -46,7 +49,7 @@ private String fetchArgs(Token token, CompileCallback callback) { return tagArgs; } - private String replacePaths(Token token, String tagArgs, int indexOfSpace, CompileCallback callback) { + private String replacePaths(Token token, String tagArgs, int indexOfSpace) { int atIndex = tagArgs.indexOf("%["); int nextAtIndex = tagArgs.indexOf("]%"); if(nextAtIndex < 0) @@ -56,22 +59,15 @@ private String replacePaths(Token token, String tagArgs, int indexOfSpace, Compi String relativeUrlPath = tagArgs.substring(atIndex+2, nextAtIndex); String leftover = tagArgs.substring(nextAtIndex+2); - String groovyCode = translatePath(relativeUrlPath, token, callback); + String groovyCode = callback.recordPath(relativeUrlPath, token.getSourceLocation(false)); String groovy = prefix + groovyCode + leftover; return groovy; } - private String translatePath(String relativeUrlPath, Token token, CompileCallback callback) { - - callback.recordPath(relativeUrlPath, token.getSourceLocation(false)); - - return relativeUrlPath; - } - - public static String replaceRouteIds(Token token, String tagArgs, int indexOfSpace, CompileCallback callback) { - int atIndex = tagArgs.indexOf("@["); - int nextAtIndex = tagArgs.indexOf("]@"); + private String replaceRouteIds(Token token, String tagArgs, int indexOfSpace) { + int atIndex = tagArgs.indexOf(TemplateToken.ACTION.getStart()); + int nextAtIndex = tagArgs.indexOf(TemplateToken.ACTION.getEnd()); if(nextAtIndex < 0) throw new IllegalArgumentException("Missing closing ]@ on the route."+token.getSourceLocation(true)); @@ -79,51 +75,10 @@ public static String replaceRouteIds(Token token, String tagArgs, int indexOfSpa String routeInfo = tagArgs.substring(atIndex+2, nextAtIndex); String leftover = tagArgs.substring(nextAtIndex+2); - String groovyCode = translateRouteId(routeInfo, token, callback); + String groovyCode = callback.translateRouteId(routeInfo, token); String groovy = prefix + groovyCode + leftover; return groovy; } - public static String translateRouteId(String routeText, Token token, CompileCallback callback) { - - int firstCommaLocation = routeText.indexOf(","); - String route; - String args; - if(firstCommaLocation > 0) { - route = routeText.substring(0, firstCommaLocation); - args = routeText.substring(firstCommaLocation+1); - args = "["+args+"]"; - } else { - route = routeText; - args = "[:]"; - } - - //TODO: This is not proper as it will break if there is a Map in a Map...but it works for now on validating the key names - //add tests eventually and fix - List argNames = fetchArgNames(args, token); - callback.recordRouteId(route, argNames, token.getSourceLocation(false)); - - return "fetchUrl('"+route+"', "+args+", '"+token.getSourceLocation(false)+"')"; - } - - private static List fetchArgNames(String args, Token token) { - List names = new ArrayList<>(); - if("[:]".equals(args)) - return names; - - String noBracketsArgs = args.substring(1, args.length()-1); - String[] split = noBracketsArgs.split("[:,]"); - if(split.length % 2 != 0) - throw new IllegalArgumentException("One of a few issues occurred with your code\n" - + " 1. You forgot to close with ]@ token. The body of the token looks like this(if this looks wrong, fix it)='''"+token.getCleanValue()+"'''\n" - + " 2. The groovy Map appears to be invalid as splitting on [:,] results in" - + " an odd amount of elements and it shold be key:value,key2:value "+token.getSourceLocation(true)); - for(int i = 0; i < split.length; i+=2) { - names.add(split[i]); - } - - return names; - } - } diff --git a/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/tags/RoutePathTranslator.java b/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/tags/RoutePathTranslator.java new file mode 100644 index 000000000..9b3724545 --- /dev/null +++ b/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/tags/RoutePathTranslator.java @@ -0,0 +1,65 @@ +package org.webpieces.templating.impl.tags; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +import org.webpieces.templating.api.CompileCallback; +import org.webpieces.templating.api.Token; + +public class RoutePathTranslator { + + private CompileCallback callback; + + @Inject + public RoutePathTranslator(CompileCallback callback) { + this.callback = callback; + } + + public String translateRouteId(String routeText, Token token) { + + int firstCommaLocation = routeText.indexOf(","); + String route; + String args; + if(firstCommaLocation > 0) { + route = routeText.substring(0, firstCommaLocation); + args = routeText.substring(firstCommaLocation+1); + args = "["+args+"]"; + } else { + route = routeText; + args = "[:]"; + } + + //TODO: This is not proper as it will break if there is a Map in a Map...but it works for now on validating the key names + //add tests eventually and fix + List argNames = fetchArgNames(args, token); + callback.recordRouteId(route, argNames, token.getSourceLocation(false)); + + return "fetchUrl('"+route+"', "+args+", '"+token.getSourceLocation(false)+"')"; + } + + private static List fetchArgNames(String args, Token token) { + List names = new ArrayList<>(); + if("[:]".equals(args)) + return names; + + String noBracketsArgs = args.substring(1, args.length()-1); + String[] split = noBracketsArgs.split("[:,]"); + if(split.length % 2 != 0) + throw new IllegalArgumentException("One of a few issues occurred with your code\n" + + " 1. You forgot to close with ]@ token. The body of the token looks like this(if this looks wrong, fix it)='''"+token.getCleanValue()+"'''\n" + + " 2. The groovy Map appears to be invalid as splitting on [:,] results in" + + " an odd amount of elements and it shold be key:value,key2:value "+token.getSourceLocation(true)); + for(int i = 0; i < split.length; i+=2) { + names.add(split[i]); + } + + return names; + } + + public String recordPath(String relativeUrlPath, String sourceLocation) { + callback.recordPath(relativeUrlPath, sourceLocation); + return relativeUrlPath; + } +} diff --git a/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/tags/TagGen.java b/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/tags/TagGen.java index 77e129a79..61753253e 100644 --- a/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/tags/TagGen.java +++ b/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/tags/TagGen.java @@ -1,16 +1,15 @@ package org.webpieces.templating.impl.tags; -import org.webpieces.templating.api.CompileCallback; -import org.webpieces.templating.api.GroovyGen; import org.webpieces.templating.api.ScriptOutput; import org.webpieces.templating.api.Token; -public class TagGen extends ParseTagArgs implements GroovyGen { +public class TagGen extends ParseTagArgs { private String name; private Token startToken; - public TagGen(String tagName, Token startToken) { + public TagGen(String tagName, Token startToken, RoutePathTranslator callback) { + super(callback); this.name = tagName; this.startToken = startToken; } @@ -21,15 +20,15 @@ public String getName() { } @Override - public void generateStartAndEnd(ScriptOutput sourceCode, Token token, int uniqueId, CompileCallback callback) { - super.generateStartAttrs(sourceCode, token, uniqueId, callback); + public void generateStartAndEnd(ScriptOutput sourceCode, Token token, int uniqueId) { + super.generateStartAttrs(sourceCode, token, uniqueId); sourceCode.println("runTag('" + name + "', _attrs" + uniqueId + ", null, '"+token.getSourceLocation(false)+"');", token); sourceCode.println(); } @Override - public void generateStart(ScriptOutput sourceCode, Token token, int uniqueId, CompileCallback callback) { - super.generateStartAttrs(sourceCode, token, uniqueId, callback); + public void generateStart(ScriptOutput sourceCode, Token token, int uniqueId) { + super.generateStartAttrs(sourceCode, token, uniqueId); sourceCode.println("_body" + uniqueId + " = {", token); sourceCode.println(); } diff --git a/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/tags/VerbatimGen.java b/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/tags/VerbatimGen.java index a51026f3d..b5d38a679 100644 --- a/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/tags/VerbatimGen.java +++ b/webserver/http-templating-dev/src/main/java/org/webpieces/templating/impl/tags/VerbatimGen.java @@ -1,7 +1,6 @@ package org.webpieces.templating.impl.tags; import org.webpieces.templating.api.AbstractTag; -import org.webpieces.templating.api.CompileCallback; import org.webpieces.templating.api.ScriptOutput; import org.webpieces.templating.api.Token; @@ -13,7 +12,7 @@ public String getName() { } @Override - public void generateStart(ScriptOutput sourceCode, Token token, int uniqueId, CompileCallback callback) { + public void generateStart(ScriptOutput sourceCode, Token token, int uniqueId) { sourceCode.println(" installNullFormatter();", token); sourceCode.println(); } diff --git a/webserver/http-templating-dev/src/test/java/org/webpieces/templating/api/TestSimpleTemplate.java b/webserver/http-templating-dev/src/test/java/org/webpieces/templating/api/TestSimpleTemplate.java index b987e6797..aa491d9c1 100644 --- a/webserver/http-templating-dev/src/test/java/org/webpieces/templating/api/TestSimpleTemplate.java +++ b/webserver/http-templating-dev/src/test/java/org/webpieces/templating/api/TestSimpleTemplate.java @@ -28,30 +28,30 @@ public void setup() { @Test public void testBasicTemplate() throws IOException { - Template template = svc.loadTemplate("/mytestfile.html"); + String templateName = "/mytestfile.html"; Map properties = createArgs(new UserBean("Dean Hiller")); StringWriter out = new StringWriter(); - svc.runTemplate(template, out, properties); + svc.loadAndRunTemplate(templateName, out, properties); //NOTE: We should be able to run with UserBean2 as well(this shows if //a Class was recompiled on-demand with our runtimecompiler we won't have issues in development mode Map args = createArgs(new UserBean2("Cooler Guy")); - svc.runTemplate(template, new StringWriter(), args); + svc.loadAndRunTemplate(templateName, new StringWriter(), args); System.out.println("HTML=\n"+out.toString()); } @Test public void testWithPackage() throws IOException { - Template template = svc.loadTemplate("/org/webpieces/mytestfile.html"); + String templateName = "/org/webpieces/mytestfile.html"; Map properties = createArgs(new UserBean("Dean Hiller")); StringWriter out = new StringWriter(); - svc.runTemplate(template, out, properties); + svc.loadAndRunTemplate(templateName, out, properties); //NOTE: We should be able to run with UserBean2 as well(this shows if //a Class was recompiled on-demand with our runtimecompiler we won't have issues in development mode Map args = createArgs(new UserBean2("Cooler Guy")); - svc.runTemplate(template, new StringWriter(), args); + svc.loadAndRunTemplate(templateName, new StringWriter(), args); String html = out.toString(); Assert.assertTrue("Html was="+html, html.contains("Hi there, my name is Dean Hiller and my favorite color is green")); diff --git a/webserver/http-templating/src/main/java/org/webpieces/templating/api/TemplateService.java b/webserver/http-templating/src/main/java/org/webpieces/templating/api/TemplateService.java index 8447b1328..62930582c 100644 --- a/webserver/http-templating/src/main/java/org/webpieces/templating/api/TemplateService.java +++ b/webserver/http-templating/src/main/java/org/webpieces/templating/api/TemplateService.java @@ -10,9 +10,12 @@ @ImplementedBy(ProdTemplateService.class) public interface TemplateService { - Template loadTemplate(String templatePath); + void loadAndRunTemplate(String templatePath, StringWriter out, Map pageArgs); + + //Template loadTemplate(String templatePath); - void runTemplate(Template template, StringWriter out, Map pageArgs); + //void runTemplate(Template template, StringWriter out, Map pageArgs); - String runTemplate(Template template, Map pageArgs, Map setTagProps); + String loadAndRunTemplate(String templatePath, Map pageArgs, Map setTagProps); + //String runTemplate(Template template, Map pageArgs, Map setTagProps); } diff --git a/webserver/http-templating/src/main/java/org/webpieces/templating/impl/ProdTemplateService.java b/webserver/http-templating/src/main/java/org/webpieces/templating/impl/ProdTemplateService.java index 266020eeb..1c6bce01d 100644 --- a/webserver/http-templating/src/main/java/org/webpieces/templating/impl/ProdTemplateService.java +++ b/webserver/http-templating/src/main/java/org/webpieces/templating/impl/ProdTemplateService.java @@ -27,7 +27,19 @@ public ProdTemplateService(RouterLookup urlLookup, HtmlTagLookup lookup) { } @Override - public final Template loadTemplate(String templatePath) { + public void loadAndRunTemplate(String templatePath, StringWriter out, Map pageArgs) { + Template template = loadTemplate(templatePath); + runTemplate(template, out, pageArgs); + } + + @Override + public String loadAndRunTemplate(String templatePath, Map pageArgs, + Map setTagProps) { + Template template = loadTemplate(templatePath); + return runTemplate(template, pageArgs, setTagProps); + } + + protected final Template loadTemplate(String templatePath) { if(!templatePath.startsWith("/")) throw new IllegalArgumentException("templatePath must start with / and be absolute reference from base of classpath"); else if(templatePath.contains("_")) @@ -52,13 +64,11 @@ protected Template loadTemplate(String fullTemplatePath, String fullClassName) { } } - @Override - public final void runTemplate(Template template, StringWriter out, Map pageArgs) { + protected final void runTemplate(Template template, StringWriter out, Map pageArgs) { String result = runTemplate(template, pageArgs, new HashMap<>()); out.write(result); } - @Override public String runTemplate(Template template, Map pageArgs, Map setTagProps) { Map copy = new HashMap<>(pageArgs); diff --git a/webserver/http-templating/src/main/java/org/webpieces/templating/impl/tags/TemplateLoaderTag.java b/webserver/http-templating/src/main/java/org/webpieces/templating/impl/tags/TemplateLoaderTag.java index 1381ee6b6..f748d19fb 100644 --- a/webserver/http-templating/src/main/java/org/webpieces/templating/impl/tags/TemplateLoaderTag.java +++ b/webserver/http-templating/src/main/java/org/webpieces/templating/impl/tags/TemplateLoaderTag.java @@ -4,7 +4,6 @@ import java.util.Map; import org.webpieces.templating.api.HtmlTag; -import org.webpieces.templating.api.Template; import org.webpieces.templating.api.TemplateService; import org.webpieces.templating.api.TemplateUtil; import org.webpieces.templating.impl.GroovyTemplateSuperclass; @@ -26,10 +25,9 @@ public void runTag(Map tagArgs, Closure body, PrintWriter out Map customTagArgs = convertTagArgs(tagArgs, pageArgs, body, srcLocation); String filePath = getFilePath(parentTemplate, tagArgs, srcLocation); - Template template = svc.loadTemplate(filePath); Map setTagProps = parentTemplate.getSetTagProperties(); - String s = svc.runTemplate(template, customTagArgs, setTagProps); + String s = svc.loadAndRunTemplate(filePath, customTagArgs, setTagProps); out.print(s); } diff --git a/webserver/http-webserver-test/src/main/java/org/webpieces/webserver/test/PlatformOverridesForTest.java b/webserver/http-webserver-test/src/main/java/org/webpieces/webserver/test/PlatformOverridesForTest.java index 160999b6c..ca4ed1247 100644 --- a/webserver/http-webserver-test/src/main/java/org/webpieces/webserver/test/PlatformOverridesForTest.java +++ b/webserver/http-webserver-test/src/main/java/org/webpieces/webserver/test/PlatformOverridesForTest.java @@ -1,9 +1,8 @@ package org.webpieces.webserver.test; import org.webpieces.frontend.api.HttpFrontendManager; +import org.webpieces.templating.api.DevTemplateModule; import org.webpieces.templating.api.TemplateCompileConfig; -import org.webpieces.templating.api.TemplateService; -import org.webpieces.templating.impl.DevTemplateService; import org.webpieces.util.logging.Logger; import org.webpieces.util.logging.LoggerFactory; @@ -31,8 +30,8 @@ public void configure(Binder binder) { //That said, there is a setting when this test runs in gradle that skips this step and runs the //production groovy *.class file that will be run in production (ie. the test run in the IDE //and run in gradle differ just a little :( ) - binder.bind(TemplateService.class).to(DevTemplateService.class); - binder.bind(TemplateCompileConfig.class).toInstance(templateConfig); + + binder.install(new DevTemplateModule(templateConfig)); } /** diff --git a/webserver/http-webserver-test/src/main/java/org/webpieces/webserver/test/SeleniumOverridesForTest.java b/webserver/http-webserver-test/src/main/java/org/webpieces/webserver/test/SeleniumOverridesForTest.java index b50a0c4ff..823d64dab 100644 --- a/webserver/http-webserver-test/src/main/java/org/webpieces/webserver/test/SeleniumOverridesForTest.java +++ b/webserver/http-webserver-test/src/main/java/org/webpieces/webserver/test/SeleniumOverridesForTest.java @@ -1,8 +1,7 @@ package org.webpieces.webserver.test; +import org.webpieces.templating.api.DevTemplateModule; import org.webpieces.templating.api.TemplateCompileConfig; -import org.webpieces.templating.api.TemplateService; -import org.webpieces.templating.impl.DevTemplateService; import com.google.inject.Binder; import com.google.inject.Module; @@ -26,7 +25,6 @@ public void configure(Binder binder) { //That said, there is a setting when this test runs in gradle that skips this step and runs the //production groovy *.class file that will be run in production (ie. the test run in the IDE //and run in gradle differ just a little :( ) - binder.bind(TemplateService.class).to(DevTemplateService.class); - binder.bind(TemplateCompileConfig.class).toInstance(templateConfig); + binder.install(new DevTemplateModule(templateConfig)); } } diff --git a/webserver/http-webserver/src/main/java/org/webpieces/webserver/impl/ProxyResponse.java b/webserver/http-webserver/src/main/java/org/webpieces/webserver/impl/ProxyResponse.java index 7f41e782d..1fd5bf7d8 100644 --- a/webserver/http-webserver/src/main/java/org/webpieces/webserver/impl/ProxyResponse.java +++ b/webserver/http-webserver/src/main/java/org/webpieces/webserver/impl/ProxyResponse.java @@ -33,7 +33,6 @@ import org.webpieces.router.api.exceptions.IllegalReturnValueException; import org.webpieces.router.impl.compression.Compression; import org.webpieces.router.impl.compression.CompressionLookup; -import org.webpieces.templating.api.Template; import org.webpieces.templating.api.TemplateService; import org.webpieces.templating.api.TemplateUtil; import org.webpieces.util.logging.Logger; @@ -156,15 +155,11 @@ public void sendRenderHtml(RenderResponse resp) { String templatePath = getTemplatePath(packageStr, templateClassName, extension); - //TODO: get html from the request such that we look up the correct template? AND if not found like they request only json, than - //we send back a 404 rather than a 500 - Template template = templatingService.loadTemplate(templatePath); - //TODO: stream this out with chunked response instead??.... StringWriter out = new StringWriter(); try { - templatingService.runTemplate(template, out, resp.pageArgs); + templatingService.loadAndRunTemplate(templatePath, out, resp.pageArgs); } catch(MissingPropertyException e) { Set keys = resp.pageArgs.keySet(); throw new ControllerPageArgsException("Controller.method="+view.getControllerName()+"."+view.getMethodName()+" did\nnot" diff --git a/webserver/http-webserver/src/main/java/org/webpieces/webserver/impl/RouterLookupProxy.java b/webserver/http-webserver/src/main/java/org/webpieces/webserver/impl/RouterLookupProxy.java index d1826e7cf..3bbee2136 100644 --- a/webserver/http-webserver/src/main/java/org/webpieces/webserver/impl/RouterLookupProxy.java +++ b/webserver/http-webserver/src/main/java/org/webpieces/webserver/impl/RouterLookupProxy.java @@ -8,6 +8,7 @@ import javax.inject.Inject; import org.webpieces.router.api.RoutingService; +import org.webpieces.router.impl.compression.FileMeta; import org.webpieces.templating.api.RouterLookup; public class RouterLookupProxy implements RouterLookup { @@ -26,12 +27,12 @@ public String fetchUrl(String routeId, Map args) { @Override public String pathToUrlEncodedHash(String relativeUrlPath) { - String hash = router.relativeUrlToHash(relativeUrlPath); - if(hash == null) + FileMeta hashMeta = router.relativeUrlToHash(relativeUrlPath); + if(hashMeta == null || hashMeta.getHash() == null) return null; try { - return URLEncoder.encode(hash, StandardCharsets.UTF_8.name()); + return URLEncoder.encode(hashMeta.getHash(), StandardCharsets.UTF_8.name()); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } diff --git a/webserver/http-webserver/src/main/java/org/webpieces/webserver/impl/WebServerImpl.java b/webserver/http-webserver/src/main/java/org/webpieces/webserver/impl/WebServerImpl.java index 633fcf0e1..9cf03d6e1 100644 --- a/webserver/http-webserver/src/main/java/org/webpieces/webserver/impl/WebServerImpl.java +++ b/webserver/http-webserver/src/main/java/org/webpieces/webserver/impl/WebServerImpl.java @@ -22,6 +22,7 @@ import org.webpieces.router.api.RoutingService; import org.webpieces.router.api.exceptions.RouteNotFoundException; import org.webpieces.router.api.routing.Nullable; +import org.webpieces.router.impl.compression.FileMeta; import org.webpieces.templating.api.ProdTemplateModule; import org.webpieces.util.logging.Logger; import org.webpieces.util.logging.LoggerFactory; @@ -100,12 +101,12 @@ private void validateRouteIdsFromHtmlFilesImpl() throws IOException { try (InputStream in = url.openStream(); InputStreamReader reader = new InputStreamReader(in); BufferedReader bufReader = new BufferedReader(reader)) { - loopThroughFile(bufReader); + loopThroughFile(url, bufReader); } log.info("Validation of routeIds complete"); } - private void loopThroughFile(BufferedReader bufReader) throws IOException { + private void loopThroughFile(URL url, BufferedReader bufReader) throws IOException { RouteNotFoundException firstException = null; int count = 1; String errorMsg = ""; @@ -126,7 +127,7 @@ private void loopThroughFile(BufferedReader bufReader) throws IOException { if(ProdTemplateModule.ROUTE_TYPE.equals(type)) { processRoute(line, location, meta); } else if(ProdTemplateModule.PATH_TYPE.equals(type)) { - processPath(line, location, meta); + processPath(url, line, location, meta); } else throw new IllegalStateException("wrong type. corrupt line="+line); } catch(RouteNotFoundException e) { @@ -141,11 +142,11 @@ private void loopThroughFile(BufferedReader bufReader) throws IOException { throw new RuntimeException("There were one or more invalid routeIds in html files="+errorMsg, firstException); } - private void processPath(String line, String location, String urlPath) throws UnsupportedEncodingException { + private void processPath(URL url, String line, String location, String urlPath) throws UnsupportedEncodingException { String path = URLDecoder.decode(urlPath, StandardCharsets.UTF_8.name()); - String hash = routingService.relativeUrlToHash(path); - if(hash == null) - throw new RouteNotFoundException("backing file for urlPath="+path+" was not found or route is missing to connect url to path"); + FileMeta meta = routingService.relativeUrlToHash(path); + if(meta == null) + throw new RouteNotFoundException("backing file for urlPath="+path+" was not found or route is missing to connect url to path. url="+url); } private void processRoute(String line, String location, String meta) throws UnsupportedEncodingException { diff --git a/webserver/http-webserver/src/test/java/org/webpieces/webserver/tags/TestStylesheetScriptTags.java b/webserver/http-webserver/src/test/java/org/webpieces/webserver/tags/TestStylesheetScriptTags.java index e4e7fd7f4..e948a25f7 100644 --- a/webserver/http-webserver/src/test/java/org/webpieces/webserver/tags/TestStylesheetScriptTags.java +++ b/webserver/http-webserver/src/test/java/org/webpieces/webserver/tags/TestStylesheetScriptTags.java @@ -40,6 +40,7 @@ public void testStylesheet() { FullResponse response = responses.get(0); response.assertStatusCode(KnownStatusCode.HTTP_200_OK); + response.assertContains("`hi`".replace('`', '"')); response.assertContains("".replace('`', '"')); response.assertContains("