BSJamonTemplateProcessorImpl.java :  » UML » MetaBoss » com » metaboss » sdlctools » services » jdktools » impl » Java Open Source

Java Open Source » UML » MetaBoss 
MetaBoss » com » metaboss » sdlctools » services » jdktools » impl » BSJamonTemplateProcessorImpl.java
// THIS SOFTWARE IS PROVIDED BY SOFTARIS PTY.LTD. AND OTHER METABOSS
// CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
// BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SOFTARIS PTY.LTD.
// OR OTHER METABOSS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
// EVEN IF SOFTARIS PTY.LTD. OR OTHER METABOSS CONTRIBUTORS ARE ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// Copyright 2000-2005  Softaris Pty.Ltd. All Rights Reserved.
package com.metaboss.sdlctools.services.jdktools.impl;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jamon.TemplateProcessor;
import org.jamon.codegen.JamonParseException;
import org.jamon.emit.EmitMode;

import com.metaboss.enterprise.bs.BSException;
import com.metaboss.enterprise.bs.BSNamingAndDirectoryServiceInvocationException;
import com.metaboss.enterprise.bs.BSUnexpectedProgramConditionException;
import com.metaboss.javatemplate.JavaTemplateContext;
import com.metaboss.javatemplate.JavaTemplateContextMapImpl;
import com.metaboss.javatemplate.JavaTemplateException;
import com.metaboss.sdlctools.services.jdktools.BSJamonTemplateProcessor;
import com.metaboss.sdlctools.services.jdktools.BSJavaCompiler;
import com.metaboss.sdlctools.services.jdktools.CompilationResult;
import com.metaboss.sdlctools.services.jdktools.MergeResult;
import com.metaboss.sdlctools.services.jdktools.SourceType;
import com.metaboss.util.DirectoryUtils;
import com.metaboss.util.JarClassLoader;
import com.metaboss.util.StringUtils;

/** This class implements template merging functionality based on Jamon */
public class BSJamonTemplateProcessorImpl implements BSJamonTemplateProcessor
{
  // Commons Logging instance.
  private static final Log sLogger = LogFactory.getLog(BSJamonTemplateProcessorImpl.class);
  
  /** Thread safe store of template classes used before.
   * We need to specify parent classloader so the template classes will be able to
   * address Jamon library */
  private JarClassLoader mJarClassLoader = null;

    // Default constructor
    public BSJamonTemplateProcessorImpl() throws BSException
    {
    try
    {
      mJarClassLoader = new JarClassLoader(BSJamonTemplateProcessorImpl.class.getClassLoader());
    }
    catch(IOException e)
    {
      throw new BSException("Unable to initialise Jamon template processor",e);
    }
    }
    
    /* Merges given template with given set of properties. */
    public MergeResult mergeTemplate(String pSourceTemplate, String pSourceTemplateName, java.util.Map pContextMap) throws BSException
    {
    if (!mJarClassLoader.hasClass(pSourceTemplateName))
    {
      sLogger.debug("Template " + pSourceTemplateName + " not found in templates cache. Performing full template compilation before running it.");
      try
      {
        CompilationResult lCompilationResult = compileTemplate(pSourceTemplate, pSourceTemplateName);;
        if (!lCompilationResult.isSuccessful())
        {
          // Return error in form of merge failure
          return MergeResult.createMergeFailure(lCompilationResult.getCompilerPrintout());
        }
        mJarClassLoader.addJar(lCompilationResult.getResultJar());
      }
      catch(IOException e)
      {
        throw new BSException("Jamon template source processing error. Template class name: " + pSourceTemplateName, e);
      }
    }
    else
      sLogger.debug("Template " + pSourceTemplateName + " found in templates cache. There is no need to perform full template compilation before running it.");
      
    try
    {
      Class lTemplateClass = mJarClassLoader.loadClass(pSourceTemplateName);
      Method lRenderMethod = lTemplateClass.getMethod("render", new Class[] { java.io.Writer.class, JavaTemplateContext.class}); 
      Object lTemplateInstance = lTemplateClass.newInstance();
      
      JavaTemplateContext lContext = new JavaTemplateContextMapImpl(pContextMap);
      StringWriter lStringWriter = new StringWriter();
      lRenderMethod.invoke(lTemplateInstance, new Object[] { lStringWriter, lContext});
      return MergeResult.createMergeSuccess(lStringWriter.toString());
    }
    catch(NoSuchMethodException e)
    {
      // Generator always generates render method - so this error is unexpected
      throw new BSUnexpectedProgramConditionException("Jamon template class loading error. Template class name: " + pSourceTemplateName, e);
    }
    catch(IllegalAccessException e)
    {
      // Generator always generates public constructor and render method - so this error is unexpected
      throw new BSUnexpectedProgramConditionException("Jamon template rendering error. Template class name: " + pSourceTemplateName, e);
    }
    catch(ClassNotFoundException e)
    {
      // We do check if class is loaded in the jar loader before loading it in VM, so this is unexpected
      throw new BSUnexpectedProgramConditionException("Java class loading error. Template class name: " + pSourceTemplateName, e);
    }
    catch(InstantiationException e)
    {
      // Generator always generates public constructor - so this error is unexpected
      throw new BSUnexpectedProgramConditionException("Jamon template instantiation error. Template class name: " + pSourceTemplateName, e);
    }
    catch(InvocationTargetException e)
    {
      Throwable lTargetException = e.getTargetException();
      // Allow for exceptions from inside template itself. These are arriving as java.io.WriteAbortedException
      if (lTargetException instanceof java.io.WriteAbortedException)
        lTargetException = ((java.io.WriteAbortedException)lTargetException).detail;
      sLogger.error("Jamon template execution error",lTargetException);  
      // Return error in form of merge failure
      return MergeResult.createMergeFailure(lTargetException.toString());
    }
    catch(JavaTemplateException e)
    {
      sLogger.error("Jamon template execution error",e);  
      // Return error in form of merge failure
      return MergeResult.createMergeFailure(e.getMessage());
    }
    }
    
    // Helper method. Does everything necessary to create executable jar for the template, loads it and returns it 
    private CompilationResult compileTemplate(String pSourceTemplate, String pSourceTemplateName) throws BSException
    {
    // Step 1. Create template file in the temporary directory
    String lBaseDir = DirectoryUtils.getUniqueTempDirectoryAbsolutePath();
    sLogger.debug("Template " + pSourceTemplateName + " will be generated and built in " + lBaseDir + " directory.");
    String lTemplateSourceRootDirPath = lBaseDir + File.separator + "template"; 
    File lTemplateSourceRootDir = new File(lTemplateSourceRootDirPath);
    String lTemplateFileRelativeName = StringUtils.replace(pSourceTemplateName,".",File.separator) + ".jamon";
    String lTemplateFileAbsoluteName = lTemplateSourceRootDirPath + File.separator + lTemplateFileRelativeName;
    File lTemplateFile = new File(lTemplateFileAbsoluteName);
    FileWriter lFileWriter = null;
    PrintWriter lWriter = null;
    try
    {
      DirectoryUtils.ensureNewCleanDirectory(lTemplateFile.getParentFile().getAbsolutePath());
      lWriter = new PrintWriter(lFileWriter = new FileWriter(lTemplateFile));
      // This bit is to pass standard parameters
      lWriter.println("<%args>");
      lWriter.println("com.metaboss.javatemplate.JavaTemplateContext pContext;");
      lWriter.println("</%args>");

      // This bit is the front of the wrapper used to catch all sorts of exceptions
      // Jamon's generator only allows to throw java.io.IOExceptions out of render methods. Not only it
      // forces template writers to write lots of exception handling code, it also forces everyone to somehow
      // "squese" all checked exceptions into the java.io.Exception. We add automatically code to do that
      // Any checked exception will automatically be packaged into the java.io.WriteAbortedException 
      // (yes we could have created the dedicated one, but this one seemed to have almost right name).
      // The renderer (see somewhere in this file) will expect this kind of exception and interpret it's contents 
      lWriter.println("<%java>");
      lWriter.println("try");
      lWriter.println("{");
      lWriter.println("</%java>");

      lWriter.print(pSourceTemplate);

      // This bit is the bottom of the wrapper used to catch all sorts of exceptions sorts of exceptions
      lWriter.println("<%java>");
      lWriter.println("}");
      lWriter.println("catch (Exception e)");
      lWriter.println("{");
      lWriter.println("    throw new java.io.WriteAbortedException(\"Exception caught during rendering of the '" + pSourceTemplateName + "' template.\",e);");
      lWriter.println("}");
      lWriter.println("</%java>");
    }
    catch(IOException e)
    {
      throw new BSException("Error while preparing template for compilation",e);
    }
    finally
    {
      if (lFileWriter != null)
      {
        try
        {
          lFileWriter.flush();
        }
        catch(IOException e)
        {
          // Ignore
        }
        try
        {
          lFileWriter.close();
        }
        catch(IOException e)
        {
          // Ignore
        }
      }
      if (lWriter != null)
      {
        lWriter.flush();
        lWriter.close();
      }
    }

    // Step 2.  Invoke Jamon java class generator and generate java source. Use inprocess invocation, so it is faster
    String lGeneratedJavaCodeDirPath = lBaseDir + File.separator + "gensrc"; 
    File lGeneratedJavaCodeDir = new File(lGeneratedJavaCodeDirPath);
    try
    {
      DirectoryUtils.ensureNewCleanDirectory(lGeneratedJavaCodeDirPath);
      new TemplateProcessor(lGeneratedJavaCodeDir, lTemplateSourceRootDir, TemplateProcessor.class.getClassLoader(),EmitMode.STANDARD).generateSource(lTemplateFileRelativeName);
    }
    catch(JamonParseException e)
    {
      throw new BSException("Parsing error while generating java source from the '" + pSourceTemplateName + "' template.",e);
    }
    catch(IOException e)
    {
      throw new BSException("IO Error while generating java source from the '" + pSourceTemplateName + "' template.",e);
    }
    
    // Step 3. Compile generated source
    try
    {
      Context lContext = new InitialContext();
      BSJavaCompiler lJavaCompiler = (BSJavaCompiler)lContext.lookup(BSJavaCompiler.COMPONENT_URL);
      CompilationResult lCompilationResult = lJavaCompiler.compileLocalSource(lGeneratedJavaCodeDirPath, SourceType.METABOSS_DEVTIME);
      if (lCompilationResult.isSuccessful())
      {
        // We have reached the end of last step and everything is still successfull
        // we can now safely delete the temporary directory
        DirectoryUtils.deleteDirectory(new File(lBaseDir));
      }
      return lCompilationResult;
    }
    catch(IOException e)
    {
      throw new BSException("Error while compiling generated source.",e);
    }
    catch(NamingException e)
    {
      throw new BSNamingAndDirectoryServiceInvocationException("Error while compiling generated source.",e);
    }
    }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.