Package org.andromda.templateengines.velocity

Source Code of org.andromda.templateengines.velocity.VelocityTemplateEngine$VelocityLoggingReceiver

package org.andromda.templateengines.velocity;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.io.Writer;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.andromda.core.common.AndroMDALogger;
import org.andromda.core.common.Constants;
import org.andromda.core.common.ExceptionUtils;
import org.andromda.core.common.Merger;
import org.andromda.core.common.ResourceUtils;
import org.andromda.core.common.ResourceWriter;
import org.andromda.core.templateengine.TemplateEngine;
import org.andromda.core.templateengine.TemplateEngineException;
import org.apache.commons.collections.ExtendedProperties;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.FileAppender;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.runtime.RuntimeServices;
import org.apache.velocity.runtime.log.LogSystem;
import org.apache.velocity.runtime.resource.loader.FileResourceLoader;


/**
* The TemplateEngine implementation for VelocityTemplateEngine template processor.
*
* @author <a href="http://www.mbohlen.de">Matthias Bohlen </a>
* @author Chad Brandon
* @see http://jakarta.apache.org/velocity/
*/
public class VelocityTemplateEngine
    implements TemplateEngine
{
    protected static Logger logger = null;

    /**
     * The directory we look in to find velocity properties.
     */
    private static final String PROPERTIES_DIR = "META-INF/";

    /**
     * The suffix for the the velocity properties.
     */
    private static final String PROPERTIES_SUFFIX = "-velocity.properties";

    /**
     * The location to which temporary templates are written
     */
    private static final String TEMPORARY_TEMPLATE_LOCATION = Constants.TEMPORARY_DIRECTORY + "velocity/merged";

    /**
     * The location of external templates
     */
    private String mergeLocation;

    /**
     * Stores additional properties specified within the plugin within the file META-INF/'plugin
     * name'-velocity.properties
     */
    private Properties properties = null;

    /**
     * The current namespace this template engine is running within.
     */
    private String namespace;

    /**
     * the VelocityEngine instance to use
     */
    private VelocityEngine velocityEngine;
    private VelocityContext velocityContext;
    private final List macroLibraries = new ArrayList();

    /**
     * Stores a collection of templates that have already been
     * discovered by the velocity engine
     */
    private final Map discoveredTemplates = new HashMap();

    /**
     * Stores the merged template files that are deleted at shutdown.
     */
    private final Collection mergedTemplateFiles = new ArrayList();

    /**
     * @see org.andromda.core.templateengine.TemplateEngine#init(java.lang.String)
     */
    public void initialize(final String namespace)
        throws Exception
    {
        this.namespace = namespace;
        this.initLogger(namespace);

        ExtendedProperties engineProperties = new ExtendedProperties();

        // Tell VelocityTemplateEngine it should also use the
        // classpath when searching for templates
        // IMPORTANT: file,andromda.plugins the ordering of these
        // two things matters, the ordering allows files to override
        // the resources found on the classpath.
        engineProperties.setProperty(VelocityEngine.RESOURCE_LOADER, "file,classpath");

        engineProperties.setProperty(
            "file." + VelocityEngine.RESOURCE_LOADER + ".class",
            FileResourceLoader.class.getName());

        engineProperties.setProperty(
            "classpath." + VelocityEngine.RESOURCE_LOADER + ".class",
            ClasspathResourceLoader.class.getName());

        // Tell VelocityTemplateEngine not to use its own logger
        // the logger but to use of this plugin.
        engineProperties.setProperty(
            VelocityEngine.RUNTIME_LOG_LOGSYSTEM,
            new VelocityLoggingReceiver());

        // Let this template engine know about the macro libraries.
        for (final Iterator iterator = getMacroLibraries().iterator(); iterator.hasNext();)
        {
            engineProperties.addProperty(
                VelocityEngine.VM_LIBRARY,
                iterator.next());
        }

        this.velocityEngine = new VelocityEngine();
        this.velocityEngine.setExtendedProperties(engineProperties);

        if (this.mergeLocation != null)
        {
            // set the file resource path (to the merge location)
            velocityEngine.addProperty(VelocityEngine.FILE_RESOURCE_LOADER_PATH, this.mergeLocation);
        }

        // if the namespace requires a merge add the temporary template
        // location to which merged templates are written
        if (Merger.instance().requiresMerge(this.namespace))
        {
            velocityEngine.addProperty(
                VelocityEngine.FILE_RESOURCE_LOADER_PATH,
                this.getMergedTemplatesLocation());
        }

        this.addProperties(namespace);
        this.velocityEngine.init();
    }

    /**
     * Adds any properties found within META-INF/'plugin name'-velocity.properties
     */
    private void addProperties(String pluginName)
        throws IOException
    {
        // reset any properties from previous processing
        this.properties = null;

        // see if the velocity properties exist for the current
        // plugin
        URL propertiesUri =
            ResourceUtils.getResource(PROPERTIES_DIR + StringUtils.trimToEmpty(pluginName) + PROPERTIES_SUFFIX);

        if (propertiesUri != null)
        {
            if (logger.isDebugEnabled())
            {
                logger.debug("loading properties from --> '" + propertiesUri + "'");
            }

            this.properties = new Properties();
            this.properties.load(propertiesUri.openStream());

            for (final Iterator iterator = this.properties.keySet().iterator(); iterator.hasNext();)
            {
                final String property = (String)iterator.next();
                final String value = this.properties.getProperty(property);
                if (logger.isDebugEnabled())
                {
                    logger.debug("setting property '" + property + "' with --> '" + value + "'");
                }
                this.velocityEngine.setProperty(property, value);
            }
        }
    }

    /**
     * @see org.andromda.core.templateengine.TemplateEngine#processTemplate(java.lang.String, java.util.Map,
     *      java.io.StringWriter)
     */
    public void processTemplate(
        final String templateFile,
        final Map templateObjects,
        final Writer output)
        throws Exception
    {
        final String methodName = "VelocityTemplateEngine.processTemplate";

        if (logger.isDebugEnabled())
        {
            logger.debug(
                "performing " + methodName + " with templateFile '" + templateFile + "' and templateObjects '" +
                templateObjects + "'");
        }
        ExceptionUtils.checkEmpty("templateFile", templateFile);
        ExceptionUtils.checkNull("output", output);
        this.velocityContext = new VelocityContext();
        this.loadVelocityContext(templateObjects);

        Template template = (Template)this.discoveredTemplates.get(templateFile);
        if (template == null)
        {
            template = this.velocityEngine.getTemplate(templateFile);

            // We check to see if the namespace requires a merge, and if so
            final Merger merger = Merger.instance();
            if (merger.requiresMerge(this.namespace))
            {
                final String mergedTemplateLocation = this.getMergedTemplateLocation(templateFile);
                final InputStream resource = template.getResourceLoader().getResourceStream(templateFile);
                ResourceWriter.instance().writeStringToFile(
                    merger.getMergedString(resource, this.namespace),
                    mergedTemplateLocation);
                template = this.velocityEngine.getTemplate(templateFile);
                this.mergedTemplateFiles.add(new File(mergedTemplateLocation));
            }
            this.discoveredTemplates.put(templateFile, template);
        }
        template.merge(velocityContext, output);
    }
   
    /**
     * Loads the internal {@link #velocityContext} from the
     * given Map of template objects.
     *
     * @param a Map containing objects to add to the template context.
     */
    private final void loadVelocityContext(final Map templateObjects)
    {
        if (templateObjects != null && !templateObjects.isEmpty())
        {   
            // copy the templateObjects to the velocityContext
            if (templateObjects != null)
            {
                for (final Iterator namesIterator = templateObjects.keySet().iterator(); namesIterator.hasNext();)
                {
                    final String name = (String)namesIterator.next();
                    final Object value = templateObjects.get(name);
                    this.velocityContext.put(name, value);
                }
            }
        }
    }

    /**
     * Gets location to which the given <code>templateName</code>
     * has its merged output written.
     * @param templatePath the relative path to the template.
     * @return the complete merged template location.
     */
    private String getMergedTemplateLocation(String templatePath)
    {
        return this.getMergedTemplatesLocation() + "/" + templatePath;
    }

    /**
     * Gets the location to which merge templates are written.  These
     * must be written in order to replace the unmerged ones when Velocity
     * performs its template search.
     *
     * @return the merged templates location.
     */
    private String getMergedTemplatesLocation()
    {
        return TEMPORARY_TEMPLATE_LOCATION + "/" + this.namespace;
    }

    /**
     * The log tag used for evaluation (this can be any abitrary name).
     */
    private static final String LOG_TAG = "logtag";
   
    /**
     * @see org.andromda.core.templateengine.TemplateEngine#getEvaluatedExpression(java.lang.String, java.util.Map)
     */
    public String getEvaluatedExpression(final String expression, final Map templateObjects)
    {
        String evaluatedExpression = null;
        if (StringUtils.isNotEmpty(expression) && templateObjects != null && !templateObjects.isEmpty())
        {
            if (this.velocityContext == null)
            {
                this.velocityContext = new VelocityContext();
                this.loadVelocityContext(templateObjects);
            }
            try
            {
                final StringWriter writer = new StringWriter();
                this.velocityEngine.evaluate(this.velocityContext, writer, LOG_TAG, expression);
                evaluatedExpression = writer.toString();
            }
            catch (final Throwable throwable)
            {
                throw new TemplateEngineException(throwable);
            }
        }
        return evaluatedExpression;
    }

    /**
     * @see org.andromda.core.templateengine.TemplateEngine#getMacroLibraries()
     */
    public List getMacroLibraries()
    {
        return this.macroLibraries;
    }

    /**
     * @see org.andromda.core.templateengine.TemplateEngine#addMacroLibrary(java.lang.String)
     */
    public void addMacroLibrary(String libraryName)
    {
        this.macroLibraries.add(libraryName);
    }

    /**
     * @see org.andromda.core.templateengine.TemplateEngine#setMergeLocation(java.lang.String)
     */
    public void setMergeLocation(String mergeLocation)
    {
        this.mergeLocation = mergeLocation;
    }

    /**
     * @see org.andromda.core.templateengine.TemplateEngine#shutdown()
     */
    public void shutdown()
    {
        this.deleteMergedTemplatesLocation();
        this.discoveredTemplates.clear();
        this.velocityEngine = null;
    }

    /**
     * Deletes the merged templates location (these
     * are the templates that were created just for merging
     * purposes and so therefore are no longer needed after
     * the engine is shutdown).
     */
    private final void deleteMergedTemplatesLocation()
    {
        File directory = new File(TEMPORARY_TEMPLATE_LOCATION);
        if (directory.getParentFile().isDirectory())
        {
            directory = directory.getParentFile();
            ResourceUtils.deleteDirectory(directory);
            directory.delete();
        }
    }

    /**
     * Opens a log file for this namespace.
     *
     * @throws IOException if the file cannot be opened
     */
    private final void initLogger(final String pluginName)
        throws IOException
    {
        logger = AndroMDALogger.getNamespaceLogger(pluginName);
        logger.setAdditivity(false);

        final FileAppender appender =
            new FileAppender(
                new PatternLayout("%-5p %d - %m%n"),
                AndroMDALogger.getNamespaceLogFileName(pluginName),
                true);
        logger.addAppender(appender);
    }

    /**
     * <p/>
     * This class receives log messages from VelocityTemplateEngine and forwards them to the concrete logger that is
     * configured for this cartridge. </p>
     * <p/>
     * This avoids creation of one large VelocityTemplateEngine log file where errors are difficult to find and track.
     * </p>
     * <p/>
     * Error messages can now be traced to plugin activities. </p>
     */
    static class VelocityLoggingReceiver
        implements LogSystem
    {
        /**
         * @see org.apache.velocity.runtime.log.LogSystem#init(org.apache.velocity.runtime.RuntimeServices)
         */
        public void init(RuntimeServices services)
            throws Exception
        {
        }

        /**
         * @see org.apache.velocity.runtime.log.LogSystem#logVelocityMessage(int, java.lang.String)
         */
        public void logVelocityMessage(
            int level,
            String message)
        {
            switch (level)
            {
            case LogSystem.WARN_ID:
                logger.info(message);
                break;
            case LogSystem.INFO_ID:
                logger.info(message);
                break;
            case LogSystem.DEBUG_ID:
                logger.debug(message);
                break;
            case LogSystem.ERROR_ID:
                logger.info(message);
                break;
            default:
                logger.debug(message);
                break;
            }
        }
    }
}
TOP

Related Classes of org.andromda.templateengines.velocity.VelocityTemplateEngine$VelocityLoggingReceiver

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.