package nl.ipo.cds.etl.reporting.velocity; import java.io.Writer; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.Map; import org.apache.velocity.VelocityContext; import org.apache.velocity.app.VelocityEngine; import org.apache.velocity.context.Context; import org.apache.velocity.exception.MethodInvocationException; import org.apache.velocity.exception.ParseErrorException; import org.apache.velocity.exception.ResourceNotFoundException; import org.apache.velocity.runtime.RuntimeConstants; import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader; import nl.ipo.cds.etl.reporting.JobFaseLogWriter; import nl.ipo.cds.etl.reporting.LogWriterContext; import nl.ipo.cds.etl.reporting.WriterException; /** * An implementation of JobFaseLogWriter that uses velocity as a template engine. * * Templates are loaded in the form: * [templatePath]/[templateName].[templateContext].vm * * @author Erik Orbons */ public class VelocityJobFaseLogWriter extends JobFaseLogWriter { private VelocityEngine velocityEngine; private String templatePath = ""; private Class<? extends Context> contextClass = VelocityContext.class; /** * Constructs a VelocityJobFaseLogWriter using a new instance of the Velocity engine. The engine * is initialized to use the classpath loader and the UTF-8 character encoding. */ public VelocityJobFaseLogWriter () { velocityEngine = new VelocityEngine (); velocityEngine.setProperty (RuntimeConstants.RESOURCE_LOADER, "classpath"); velocityEngine.setProperty ("classpath.resource.loader.class", ClasspathResourceLoader.class.getName ()); velocityEngine.setProperty ("input.encoding", "UTF-8"); velocityEngine.init (); } /** * Constructs a VelocityJobFaseLogWriter using an existing velocity engine. The engine must have been initialized (e.g. the init * method must have been invoked). * * @param velocityEngine The velocity engine to be used by this writer. */ public VelocityJobFaseLogWriter (VelocityEngine velocityEngine) { this.velocityEngine = velocityEngine; } /** * Returns the velocity engine that is used by this writer. * * @return The velocity engine instance. */ public VelocityEngine getVelocityEngine () { return velocityEngine; } /** * Returns the template path. The template path serves as a prefix when resolving template names and using most velocity resource * loaders the prefix corresponds to a path. * * @return The velocity template path. */ public String getTemplatePath () { return templatePath; } /** * Sets the template path. The template path serves as a prefix when resolving template names and using most velocity resource * loaders the prefix corresponds to a path. * * @param templatePath The new velocity template path. */ public void setTemplatePath (String templatePath) { this.templatePath = templatePath; } /** * Returns the velocity context class. * * @return The current context class to use in this writer. */ public Class<? extends Context> getContextClass () { return contextClass; } /** * * @param contextClass */ public void setContextClass (Class<? extends Context> contextClass) { try { contextClass.getConstructor (Context.class); } catch (SecurityException e) { throw new IllegalArgumentException ("Context class has no accessible constructor that takes a Velocity context as the only argument.", e); } catch (NoSuchMethodException e) { throw new IllegalArgumentException ("Context class has no accessible constructor that takes a Velocity context as the only argument.", e); } this.contextClass = contextClass; } @Override public void write (LogWriterContext context, String template, String templateContext, String encoding, Writer writer) throws WriterException { final Map<String, Object> parameters = context.getParameters (); final String velocityTemplate = String.format ("%s/%s.%s.vm", getTemplatePath (), template, templateContext); // Create a velocity context: Context velocityContext; if (parameters != null) { // Use an inner context to protect the parameter map from mutations: try { Constructor<? extends Context> ctr = contextClass.getConstructor (Context.class); velocityContext = ctr.newInstance (new VelocityContext (parameters)); } catch (SecurityException e) { throw new WriterException ("Error instantiating Velocity context class: constructor inaccessible", e); } catch (NoSuchMethodException e) { throw new WriterException ("Error instantiating Velocity context class: no suitable constructor found", e); } catch (InvocationTargetException e) { throw new WriterException ("Error instantiating Velocity context class: error invoking constructor", e); } catch (IllegalAccessException e) { throw new WriterException ("Error instantiating Velocity context class: error invoking constructor", e); } catch (InstantiationException e) { throw new WriterException ("Error instantiating Velocity context class: error instantiating context", e); } } else { try { velocityContext = contextClass.newInstance (); } catch (InstantiationException e) { throw new WriterException ("Error instantiating Velocity context class", e); } catch (IllegalAccessException e) { throw new WriterException ("Error instantiating Velocity context class", e); } velocityContext = new VelocityContext (); } velocityContext.put ("logItems", context.getLogItems ()); // Merge the context and the template: try { getVelocityEngine ().mergeTemplate (velocityTemplate, encoding, velocityContext, writer); } catch (ResourceNotFoundException e) { throw new WriterException (e); } catch (ParseErrorException e) { throw new WriterException (e); } catch (MethodInvocationException e) { throw new WriterException (e); } } }