I thought I would share the following code with the Apache Velocity
community.


Problem Description:  I wanted to use Dynamic Templates though I found the
following had massive performance implications.

-- Velocity Call Snippet --
String template = "message.to:  ${message.to} ";
StringWriter velocityWriter = new StringWriter();
velocityEngine.evaluate( velocityContext, velocityWriter, "LOG", template );
return velocityWriter.toString();
-- Velocity Call Snippet --


As such, I needed an alternate solution and guessed that if I could cache
the Velocity templates used I could see big performance gains.

I initially looked to the
org.apache.velocity.runtime.resource.loader.StringResourceLoader class to
solve my problems.   The StringResourceLoader class requires an instance of
the StringResourceRepository class, and the only implementation of
StringResourceRepository is StringResourceRepositoryImpl.  Unfortunately,
since the number of unique templates will grow over time, in time the
StringResourceRepositoryImpl.resources Map will cause an out of memory
error.

Why doesn't StringResourceRepositoryImpl use a cache with a maximum size? 
Is a cache even needed as the Velocity ResourceCacheImpl will cache
Templates?

+++++++++++++++++++++++++++++++++++++
In the end I settled on the following solution.  From my tests using YourKit
Java Profiler I invoked the static Velocity wrapping method 100,000 times,
and discovered a performance difference of around 75 fold (146,937ms vs
1,981ms).

Below is my solution.  Feedback appreciated.

+++++++++++++++++++++++++++++++++++++ 


Regards,
Mark

-- Velocity Engine Setup Snippet --
Properties p = new Properties();

p.setProperty( RuntimeConstants.INPUT_ENCODING, DEFAULT_ENCODING );
p.setProperty( RuntimeConstants.OUTPUT_ENCODING, DEFAULT_ENCODING );

p.setProperty( RuntimeConstants.RESOURCE_MANAGER_CLASS,
       "org.apache.velocity.runtime.resource.ResourceManagerImpl" );
p.setProperty( RuntimeConstants.RESOURCE_MANAGER_CACHE_CLASS,
       "org.apache.velocity.runtime.resource.ResourceCacheImpl" );
p.setProperty( RuntimeConstants.RESOURCE_MANAGER_DEFAULTCACHE_SIZE, "100" );

p.setProperty( RuntimeConstants.RESOURCE_LOADER, "string" );
p.setProperty( "string.resource.loader.class",
       "custom.SimpleStringResourceLoader" );
p.setProperty( "string.resource.loader.encoding", DEFAULT_ENCODING );
p.setProperty( "string.resource.loader.cache", Boolean.TRUE.toString() );
p.setProperty( "string.resource.loader.modificationCheckInterval", "-1" );

VelocityEngine engine = new VelocityEngine( p );
engine.init();
-- Velocity Engine Setup Snippet --

-- Velocity Call Snippet --
String template = "message.to:  ${message.to} ";
StringWriter velocityWriter = new StringWriter();
Template veTemplate = velocityEngine.getTemplate( template );
veTemplate.merge( velocityContext, velocityWriter );
return velocityWriter.toString();
-- Velocity Call Snippet --




-- Velocity Supporting Class Snippet --

package custom;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.concurrent.atomic.AtomicLong;

import org.apache.commons.collections.ExtendedProperties;
import org.apache.velocity.exception.VelocityException;
import org.apache.velocity.runtime.resource.Resource;
import org.apache.velocity.runtime.resource.loader.ResourceLoader;
import org.apache.velocity.runtime.resource.util.StringResource;

public class SimpleStringResourceLoader extends ResourceLoader
{

    protected static int classMetricsLevel;
    protected static AtomicLong totalClassInstancesCreatedCount;
    protected static AtomicLong totalStringsGeneratedCount;

    static {
        classMetricsLevel = 1;
        totalClassInstancesCreatedCount = new AtomicLong();
        totalStringsGeneratedCount = new AtomicLong();
    }


    protected String encoding;


    public SimpleStringResourceLoader()
    {

        if ( classMetricsLevel > 0 )
        {
            totalClassInstancesCreatedCount.incrementAndGet();
        }

        this.encoding = "UTF-8";
    }

    @Override
    public void init( ExtendedProperties paramExtendedProperties )
    {
        String paramEncoding = paramExtendedProperties.getString( "encoding"
);

        if ( null != encoding && encoding.trim().length() > 0 )
        {
            this.encoding = paramEncoding;
        }
    }

    @Override
    public InputStream getResourceStream( String contents )
    {
        if ( classMetricsLevel > 1 )
        {
            totalStringsGeneratedCount.incrementAndGet();
        }

        StringResource resource = new StringResource( contents, encoding );

        byte[] byteArray = null;
        try
        {
            byteArray = resource.getBody().getBytes( resource.getEncoding()
);
            return new ByteArrayInputStream( byteArray );
        }
        catch ( UnsupportedEncodingException ue )
        {
            throw new VelocityException( "Failed to convert contents to
String "
                    + resource.getEncoding(), ue );
        }

    }

    @SuppressWarnings( "unused" )
    @Override
    public boolean isSourceModified( Resource paramResource )
    {
        return false;
    }

    @SuppressWarnings( "unused" )
    @Override
    public long getLastModified( Resource paramResource )
    {
        return 0;
    }


    public String getEncoding()
    {
        return encoding;
    }

    public void setEncoding( String encoding )
    {
        this.encoding = encoding;
    }


    public static int getClassMetricsLevel()
    {
        return classMetricsLevel;
    }

    public static void setClassMetricsLevel( int classMetricsLevel )
    {
        SimpleStringResourceLoader.classMetricsLevel = classMetricsLevel;
    }

    public static void setTotalClassInstancesCreatedCount( long counterValue
)
    {
       
SimpleStringResourceLoader.totalClassInstancesCreatedCount.getAndSet(
counterValue );
    }

    public static long getTotalClassInstancesCreatedCount()
    {
        return
SimpleStringResourceLoader.totalClassInstancesCreatedCount.get();
    }


    public static void setTotalStringsGeneratedCount( long counterValue )
    {
        SimpleStringResourceLoader.totalStringsGeneratedCount.getAndSet(
counterValue );
    }

    public static long getTotalStringsGeneratedCount()
    {
        return SimpleStringResourceLoader.totalStringsGeneratedCount.get();
    }

    @Override
    public String toString()
    {
        return "SimpleStringResourceLoader [encoding=" + encoding + "]";
    }

}

-- Velocity Supporting Class Snippet --

-- 
View this message in context: 
http://old.nabble.com/Dynamic-Templates-tp33297076p33297076.html
Sent from the Velocity - User mailing list archive at Nabble.com.


---------------------------------------------------------------------
To unsubscribe, e-mail: user-unsubscr...@velocity.apache.org
For additional commands, e-mail: user-h...@velocity.apache.org

Reply via email to