Create a reusable parse method which uses velocity

958 Views Asked by At

We are using velocity to parse our templates.

Velocity developer guide suggests to create a new VelocityContext for every parsing

But what about the VelocityEngine and the RuntimeInstances?

Can we reuse them or is it better to create new instances every call ? Will the new instances of VelocityEngine cause memory leaks ?

  public String parse(String templateStr, Map<String, String> params) {
        StringWriter writer = new StringWriter();
        try {
            VelocityEngine velocityEngine = new VelocityEngine();
            velocityEngine.init();
            RuntimeServices rs = RuntimeSingleton.getRuntimeServices();
            StringReader sr = new StringReader(templateStr);
            SimpleNode sn = rs.parse(sr, "template");

            Template t = new Template();
            t.setRuntimeServices(rs);
            t.setData(sn);
            t.initDocument();

            VelocityContext context = new VelocityContext();

            if (params != null && !params.isEmpty()) {
                for (Entry<String, String> entry : params.entrySet()) {
                    context.put(entry.getKey(), entry.getValue());
                }
            }
            t.merge(context, writer);
        } catch (Exception e) {
            LOGGER.error("Exception in velocity parsing", e);

        }
        return writer.toString();

    }
3

There are 3 best solutions below

0
On BEST ANSWER

Velocity allows you to use Singleton model

Velocity.setProperty(Velocity.RUNTIME_LOG_NAME, "mylog");
Velocity.init();
Template t = Velocity.getTemplate("foo.vm");

Developers have two options for using the Velocity engine, the singleton model and the separate instance model. The same core Velocity code is used for both approaches, which are provided to make Velocity easier to integrate into your Java application.

Basically, instead of VelocityEngine, you can use Velocity class:

This class provides a separate new-able instance of the Velocity template engine. The alternative model for use is using the Velocity class which employs the singleton model.

0
On

RuntimeInstance is an internal class you don't have to deal with.

VelocityEngine, as well as the singleton class Velocity (which relies on VelocityEngine), are re-entrant (as well as their associated template resource loaders). It means that they can safely be used in a multi-threaded environment.

0
On

Each time you instantiate a VelocityEngine, you'll need to call init() once on it. Reusing a previous VelocityEngine instance avoids the need for additional init() calls.

But the key benefit in reusing a VelocityEngine instance is to make use of automatic template parsing and caching via its resource loaders. In the code in your question, you're manually instantiating Template objects and loading them with data from strings. A cleaner way is to configure your VelocityEngine to support a "string resource loader", then store each string containing a template body within a StringResourceRepository associated with your VelocityEngine instance. This is all demonstrated for Velocity 2.1 at this StackOverflow answer.

A few notes in addition:

  1. Velocity will take care of parsing your templates automatically with the approach shown in the link, above. No need to manually parse or call initDocument() on a Template.

  2. With this approach Velocity will take care of caching your templates (in parsed form) automatically if you've configured your string resource loader (in your VelocityEngine instance) with caching enabled.

  3. The above two points will allow your code to be simpler and faster. You can further simplify the code: instead of calling yourVelocityEngineInstance.getTemplate() and then merge(), you can call yourVelocityEngineInstance.mergeTemplate().

  4. When adding a template to a StringResourceRepository via a call to yourRepo.putStringResource(), you specify a template name as well as the body of the template. If there is no obvious name to use you can write an MD5 hashing function to quickly generate an MD5 hash string of the template body and use that as a unique name (got this tip from another Velocity user). If the template body ever changes that would generate a distinct name. There is a corresponding removeStringResource() method to delete templates from the string resource repository.

  5. Once a template is placed in the string resource repository Velocity can locate it "magically" by name. You can call yourVelocityEngineInstance.resourceExists("YourTemplateName") to see if a given template exists in the repository or not.

I do not know the performance cost of instantiating and initializing a VelocityEngine, a RuntimeInstance or a VelocityContext. Until the Velocity documentation provides clearer guidance on optimizing performance, you'll have to do your own performance testing to find out.