All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.webpieces.templatingdev.impl.DevTemplateService Maven / Gradle / Ivy

There is a newer version: 2.1.109
Show newest version
package org.webpieces.templatingdev.impl;

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 javax.inject.Singleton;

import org.apache.commons.io.IOUtils;
import org.webpieces.templating.api.HtmlTagLookup;
import org.webpieces.templating.api.RouterLookup;
import org.webpieces.templating.api.Template;
import org.webpieces.templating.impl.ProdTemplateService;
import org.webpieces.templating.impl.TemplateImpl;
import org.webpieces.templatingdev.api.TemplateCompileConfig;
import org.digitalforge.sneakythrow.SneakyThrow;
import org.webpieces.util.file.VirtualFile;
import org.webpieces.util.file.VirtualFileClasspath;

@Singleton
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) {
		super(urlLookup, htmlTagLookup);
		this.htmlTagLookup = htmlTagLookup;
		this.compiler = compiler;
		this.config = config;
	}

	@Override
	public void initialize() {
		lookup.initialize(this);
	}
	
	@Override
	public void loadAndRunTemplateImpl(String templatePath, StringWriter out, Map pageArgs) {
		//TODO: big nit and for fun, we should look into recreating OurGroovyClassLoader ONLY when
		//the html files have changed.  to do this, I think we would have to save a Holder object with the first 
		//classloader and only swap him on changes to the html files instead of on every request.  I think if we
		//do that, the ThreadLocal may go away as well.  This may need some manual testing and we may have to add
		// more change files during test like we did for the controller testing in TestDevRefreshPageWithNoRestarting.java
		
		//prod knows nothing about groovy or the Groovy classloader so we keep it that way 
		//by setting the classloader used on the thread.  This is NOT A recursive function like the below.
		currentCl.set(new OurGroovyClassLoader());
		
		try {
			super.loadAndRunTemplateImpl(templatePath, out, pageArgs);
		} finally {
			currentCl.set(null);
		}
	}
	
	protected Template loadTemplate(String fullTemplatePath, String fullClassName) {
		//this is a recursive function.  Run TestFieldTag.java to see
		try {
			return loadTemplateImpl(currentCl.get(), fullTemplatePath, fullClassName);
		} catch (ClassNotFoundException | IOException e) {
			throw SneakyThrow.sneak(e);
		}
	}
	
	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.
			Class compiledTemplate = DevTemplateService.class.getClassLoader().loadClass(templateFullClassName);
			return new TemplateImpl(urlLookup, htmlTagLookup, compiledTemplate);
		}
		
		VirtualFile theResource = null;
		List srcPaths = config.getSrcPaths();
		for(VirtualFile f : srcPaths) {
			VirtualFile child = f.child(fullTemplatePath.substring(1));//take off the leading '/' so it is a relative path
			if(child.exists()) {
				theResource = child;
				break;
			}
		}

		if(theResource == null) 
			theResource = new VirtualFileClasspath(fullTemplatePath, DevTemplateService.class, false);
		
		if(!theResource.exists()) {
			try {
				//This is for plugins in the jar file that someone's webapp is using so there is no *.html files in that jar
				//and it only has xxxxx_html.class files(precompiled templates)
				return super.loadTemplate(fullTemplatePath, templateFullClassName);
			} catch(ClassNotFoundException e) {
				//ok, class is not found, throw original, file not found exception
				FileNotFoundException exc = new FileNotFoundException("resource="+fullTemplatePath+" was not found in classpath AND corresponding *.class file not found too");
				exc.initCause(e);
				throw exc;
			}
		}
		
		try(InputStream resource = theResource.openInputStream()) {
			String viewSource = IOUtils.toString(resource, config.getFileEncoding().name());
	
			Class compiledTemplate = createTemplate(cl, templateFullClassName, viewSource);
			
			return new TemplateImpl(urlLookup, htmlTagLookup, compiledTemplate);
		}
	}

	private Class createTemplate(OurGroovyClassLoader cl, String fullClassName, String source) throws ClassNotFoundException {
		if(!cl.isClassDefined(fullClassName))
			compiler.compile(cl, fullClassName, source);
		return cl.loadClass(fullClassName);
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy