[
https://issues.apache.org/jira/browse/VELOCITY-817?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Claude Brisson closed VELOCITY-817.
-----------------------------------
> Dynamic Templates with Caching
> ------------------------------
>
> Key: VELOCITY-817
> URL: https://issues.apache.org/jira/browse/VELOCITY-817
> Project: Velocity
> Issue Type: Improvement
> Components: Engine
> Affects Versions: 1.7
> Reporter: Mark S
> Assignee: Claude Brisson
> Priority: Major
> Labels: performance
> Original Estimate: 2h
> Remaining Estimate: 2h
>
> 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.
> {code:title=Velocity Call Snippet|borderStyle=solid}
> String template = "message.to: $\{message.to} ";
> StringWriter velocityWriter = new StringWriter();
> velocityEngine.evaluate( velocityContext, velocityWriter, "LOG", template );
> return velocityWriter.toString();
> {code}
> 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.
> +++++++++++++++++++++++++++++++++++++
> {code:title=Velocity Engine Setup Snippet|borderStyle=solid}
> 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();
> {code}
> {code:title=Velocity Call Snippet|borderStyle=solid}
> String template = "message.to: ${message.to} ";
> StringWriter velocityWriter = new StringWriter();
> Template veTemplate = velocityEngine.getTemplate( template );
> veTemplate.merge( velocityContext, velocityWriter );
> return velocityWriter.toString();
> {code}
> {code:title=Velocity Supporting Class Snippet|borderStyle=solid}
> 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 + "]";
> }
> }
> {code}
--
This message was sent by Atlassian JIRA
(v7.6.3#76005)
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]