Velocity

This section discusses how to install and configure Velocity 1.3, and the framework's built-in support for the Velocity template engine.

Installing and Configuring Velocity

Unlike JSP, Velocity isn't part of J2EE and probably won't be shipped with your application server. The /lib/runtime/velocity directory of the sample application includes the two JAR files that Velocity 1.3 requires: velocity-1.3.jar, and velocity-dep-1.3.jar. These must be copied to the /WEB-INF/lib of a web application using Velocity. The sample application's Ant build script handles this.

Velocity must also be configured before individual templates can be used. Velocity is highly configurable and exposes many configuration parameters, but the only configuration parameters that we must set tell Velocity where to find templates at runtime. Velocity offers several strategies for loading templates, such as using the file system or the class loader. In the sample application, I've opted for the class loader, as it's more portable between web containers.

The following keys from the sample application's /WEB-INF/velocity.properties file tell Velocity to load templates from the classpath (that is, under /WEB-INF/classes or /WEB-INF/lib within the WAR), and that it should automatically reload altered templates to ease development:

    resource.loader=class    class.resource.loader.class =      org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader    class.resource.loader.cache = false    class.resource.loader.modificationCheckInterval = 5 

Important 

Velocity template reloading, like JSP compilation, should normally be switched off in production, as it will impact on performance.

Important optional settings include the paths of Velocity macro libraries to be loaded when Velocity starts, and made available to all templates. The following example specifies the location of a Velocity macro library:

    velocimacro.library = myMacros.vm 

Multiple libraries can be included in a CSV list, and they should be located as for ordinary Velocity templates.

We need to apply these properties to Velocity before we attempt to evaluate templates. One way to do this is to use a servlet with a <load-on-startup> subelement to ensure it's loaded before our web application receives requests. However, the bean-based framework we discussed in Chapter 11 provides a more elegant alternative. As a "singleton" bean in our application context is guaranteed to be instantiated, even if it's not accessed by user code, we can simply define a bean that will configure Velocity during application initialization. The framework provides the com.interface21.web.servlet.velocity.VelocityConfigurer bean for this purpose. The following bean definition from the /WEB-INF/ticket-servlet.xml file of our sample application illustrates its use:

    <bean name="velocityConfig"         >         <property name="url">/WEB-INF/velocity.properties</property>    </bean> 

Note the single bean property url, which is the location within the WAR of the standard Velocity properties file. The VelocityConfigurer bean loads the Velocity properties (in this case from a properties file within the WAR), and uses them to initialize the Velocity singleton as follows:

    Velocity.init(properties); 
Note 

Velocity 1.2 introduced support for multiple independent instances of Velocity in the same web application, so the Singleton model is no longer the only choice.

Implementing the View Interface for Velocity

Let's look at the framework's built-in implementation of the com.interface21.web.servlet.View interface, which exposes model data via Velocity templates.

This framework implementation needs to perform the following steps in its render() method:

  • Create a new Velocity context object to build this response.

  • Expose model objects as Velocity context objects, in the same way in which we exposed model objects as request attributes in the InternalResourceView class.

  • Obtain the relevant Velocity template. As each VelocityView object will be associated with a single view, the Velocity view can obtain and cache the necessary Velocity template at startup, enabling it to check that the template exists and is free from syntax errors on framework initialization.

  • Use Velocity to "merge" the template with the context data to write to the HttpServletResponse.

Exposing Model Data to a Velocity Template

Please look at the source of the com.interface21.web.servlet.view.velocity.VelocityView class in the /framework directory of the download for the full implementation. I show the basic steps to achieve each of these steps below.

Like the other view implementations discussed in this chapter, VelocityView extends AbstractView. The first step in the renderMergedOutputModel() method is to is to create a Velocity context to build this response:

    Context vContext = new VelocityContext() ; 

Model objects are exposed to this new, request-specific, Velocity context in the following private method:

    private void exposeModelsAsContextAttributes (Map model, Context vContext) {      if (model != null) {        Set keys = model.keyset();        Iterator itr = keys.iterator();        while (itr.hasNext() ) {          String modelname = (String) itr.next();          Object val = model.get (modelname) ;         vContext.put (modelname, val) ;                                                                                            }      }    } 

The template can be obtained from the Velocity singleton instance and cached when the view is initialized as follows:

    private void loadTemplate() throws ServletException {      String mesg = "Velocity resource loader is: ["+        Velocity.getProperty ("class. resource. loader. class") + "]; ";      try {        this.velocityTemplate =          RuntimeSingleton. getTemplate (this. templateName);                                                                     }      catch (ResourceNotFoundException ex) {        mesg += "Can't load Velocity template "' + this. templateName +                " ': is it on the classpath, under /WEB-INF/classes?" ;        logger.logp(Level. SEVERE, getClass().getName() , "getTemplate" ,                    mesg, ex) ;        throw new ServletException(mesg, ex);      }      catch (ParseErrorException ex) {        mesg += "Error parsing Velocity template "' + this. templateName + " ' " ;        logger.logp(Level.SEVERE, getClass().getName() , "getTemplate" ,                    mesg, ex) ;        throw new ServletException(mesg, ex) ;      }      catch (Exception ex) {        mesg += "Unexpected error getting Velocity template ' " +                this. templateName + " ' " ;        logger.logp (Level.SEVERE, getClass().getName(), "getTemplate",                     mesg, ex) ;        throw new ServletException(mesg, ex) ;      }    } 

With a Velocity context populated with model data, we can generate output to the HttpServletResponse as follows:

    this.velocityTemplate. merge (vContext, response. getWriter() ) ; 

Note 

The actual implementation of the VelocityView class uses a more performant, but slightly more complex approach, based on the VelocityServlet bundled with Velocity. Please see the source for details.

Providing Support for Date and Currency Formatting

Surprisingly, Velocity 1.3 lacks standard support for date and currency formatting. The Velocity Tools project includes some support, although it still seems to be in development. Thus to ensure that our framework's Velocity view isn't tied to the current locale, we need to help Velocity out in this area.

We could opt to expose formatted dates and currency amounts as string properties in each model, but this subverts good design. Localization is usually better handled in views than in models.

The VelocityView class adds java.text.SimpleDateFormat and java.text.NumberFormat objects to the Velocity context if the view definition indicates that the wrapped template needs to perform date or currency formatting. Java's localizable support for number and currency formatting is simple, familiar is and documented in the java.text package Javadocs.

SimpleDateFormat and NumberFormat objects must be constructed for each request, as Velocity template code may alter their configuration - for example, by setting the formatting pattern. These helpers are exposed only if two bean properties on VelocityView - exposeDateFormatter and exposeCurrencyFormatter - are set to true to indicate that a particular View instance is associated with a template that needs to format dates or currency amounts (exposing these helper objects to the majority of templates that don't need them would be wasteful). The helpers are initialized using the HttpServletRequest's locale property, as follows:

    public static final String DATE_FORMAT_KEY = "simpleDateFormat";    public static final String CURRENCY_FORMAT_KEY = "currencyFormat";    private void exposeHelpers(Context vContext, HttpServletRequest request) {      if (this.exposeDateFormatter) {        // Javadocs indicate that this cast will work in most locales        SimpleDateFormat df = (SimpleDateFormat)        DateFormat.getDateTimeInstance(DateFormat.LONG,          DateFormat.LONG, request.getLocale());        vContext. put (DATE_FORMAT_KEY, df) ;                                                                                      }      if (this.exposeCurrencyFormatter) {        NumberFormat nf =          NumberFormat.getCurrencyInstance(request.getLocale());        vContext. put (CURRENCY_FORMAT_KEY, nf) ;                                                                                  }    } 

We can now manipulate patterns if necessary in Velocity templates and use the objects to format model data as follows:

    The date of the performance is    $simpleDateFormat.format($performance.when).    The total cost of these tickets will be    $currencyFormat.format($reservation.totalPrice). 

Defining Velocity Views for Use in an Application

The following example from the /WEB-INF/classes/views.properties file defines a Velocity view bean for the sample page discussed in Chapter 13. Note that it sets the two format helper properties to true, as this page needs to display the performance date and ticket costing:

    showReservation.class=      com.interface21.web.servlet.view.velocity.VelocityView    showReservation.templateName=showReservation.vm    showReservation.exposeDateFormatter=true    showReservation.exposeCurrencyFormatter=true 

The VelocityView class exposes the following bean properties, beyond those inherited from AbstractView:



Expert One-on-One J2EE Design and Development
Microsoft Office PowerPoint 2007 On Demand
ISBN: B0085SG5O4
EAN: 2147483647
Year: 2005
Pages: 183

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net