velocity-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Nathan Bubna <nbu...@gmail.com>
Subject Re: Velocity Tools kills spring macros
Date Tue, 10 Feb 2009 20:29:50 GMT
Hi Paul,

I'm still not sure why this is happening and haven't found the time to
figure it out.  Really, the spring-velocity code is not up to speed
with VelocityTools 2, so it's a bit rough.  Here's some code i wrote a
couple months back for Tools 2 to work better with Spring.  I haven't
run it lately, but it may be helpful.  I've put it inline as well, in
case the attachments are stripped.

/*
 * Copyright 2002-2007 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.esha.cara.web;
//package org.springframework.web.servlet.view.velocity;

import java.util.Iterator;
import java.util.Map;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.context.Context;
import org.apache.velocity.tools.config.FactoryConfiguration;
import org.apache.velocity.tools.view.VelocityView;
import org.apache.velocity.tools.view.ServletUtils;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContextException;
import org.springframework.web.servlet.view.velocity.VelocityToolboxView;

/**
 * {@link VelocityView} subclass which adds support for Velocity Tools toolboxes
 * and Velocity Tools ViewTool callbacks / Velocity Tools 1.3 init methods.
 *
 * <p>Specify a "toolboxConfigLocation", for example "/WEB-INF/tools.xml",
 * to automatically load a Velocity Tools toolbox definition file and expose
 * all defined tools in the specified scopes. If no config location is
 * specified, no toolbox will be loaded and exposed.
 *
 * <p>This view will always create a special Velocity context, namely an
 * instance of the ChainedContext class which is part of the view package
 * of Velocity tools. This allows to use tools from the view package of
 * Velocity Tools, like LinkTool, which need to be initialized with a special
 * context that implements the ViewContext interface (i.e. a ChainedContext).
 *
 * <p>This view also checks tools that are specified as "toolAttributes":
 * If they implement the ViewTool interface, they will get initialized with
 * the Velocity context. This allows tools from the view package of Velocity
 * Tools, such as LinkTool, to be defined as
 * {@link #setToolAttributes "toolAttributes"} on a VelocityToolboxView,
 * instead of in a separate toolbox XML file.
 *
 * <p>This is a separate class mainly to avoid a required dependency on
 * the view package of Velocity Tools in {@link VelocityView} itself.
 * This class requires Velocity Tools 2.0 or higher.
 *
 * @author Juergen Hoeller
 * @author Nathan Bubna
 * @see #setFactoryConfiguration
 * @see #initTool
 * @see org.apache.velocity.tools.view.context.ViewContext
 * @see org.apache.velocity.tools.view.tools.ViewTool
 * @see org.apache.velocity.tools.view.tools.LinkTool
 */
public class VelocityToolsView extends VelocityToolboxView {

    private FactoryConfiguration factoryConfig;
    private org.apache.velocity.tools.view.VelocityView view;

    public void setFactoryConfiguration(FactoryConfiguration config) {
        this.factoryConfig = config;
    }

    /**
     * Puts the FactoryConfiguration, if any, into the ServletContext's
     * attributes for the VelocityView to find and then tries to create
     * a org.apache.velocity.tools.view.VelocityView instance.
     *
     * @see FactoryConfiguration
     * @see VelocityView
     * @see #getServletContext()
     */
    protected void initApplicationContext() throws BeansException {
        ServletContext app = getServletContext();

        if (getToolboxConfigLocation() != null) {
            FactoryConfiguration config =

ServletUtils.getConfiguration(getToolboxConfigLocation(), app, true);
            if (this.factoryConfig != null) {
                this.factoryConfig.addConfiguration(config);
            } else {
                this.factoryConfig = config;
            }
        }

        if (this.factoryConfig != null) {
            // push the factory configuration into the servlet context
            // where the VelocityView can find and use it
            app.setAttribute(VelocityView.TOOLS_KEY, this.factoryConfig);
        }

        // get/create the VelocityView
        this.view = ServletUtils.getVelocityView(app);

        super.initApplicationContext();
    }

    /**
     * Autodetect a VelocityEngine via the ApplicationContext or
     * the org.apache.velocity.tools.view.VelocityView for this instance.
     * Called if no explicit VelocityEngine has been specified.
     * @return the VelocityEngine to use for VelocityViews
     * @throws BeansException if no VelocityEngine could be found
     * @see #getApplicationContext
     * @see #setVelocityEngine
     * @see org.apache.velocity.tools.view.VelocityView
     * @see ServletUtils#getVelocityView(ServletContext,boolean)
     */
    protected VelocityEngine autodetectVelocityEngine() throws BeansException {
        try {
            VelocityEngine engine = super.autodetectVelocityEngine();
            if (engine != null) {
                // make sure VelocityView and Spring use the same engine
                view.setVelocityEngine(engine);
            }
            return engine;
        }
        catch (ApplicationContextException ace) {
            // use the VelocityView-created engine
            return view.getVelocityEngine();
        }
    }

    protected org.apache.velocity.tools.view.VelocityView getVelocityView() {
        return this.view;
    }

    /**
     * Overridden to create a ChainedContext, which is part of the view package
     * of Velocity Tools, as special context. ChainedContext is needed for
     * initialization of ViewTool instances.
     * @see #initTool
     */
    protected Context createVelocityContext(Map model,
                                            HttpServletRequest request,
                                            HttpServletResponse
response) throws Exception {
        Context ctx = view.createContext(request, response);

        //NOTE: this assumes that the model is ready to go...
        if (!model.isEmpty()) {
            for (Iterator i = model.entrySet().iterator(); i.hasNext(); ) {
                Map.Entry entry = (Map.Entry)i.next();
                ctx.put(String.valueOf(entry.getKey()), entry.getValue());
            }
        }
        return ctx;
    }

}


/*
 * Copyright 2002-2006 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.esha.cara.web;
//package org.springframework.web.servlet.view.velocity;

import java.io.StringWriter;
import javax.servlet.http.HttpServletResponse;
import org.apache.velocity.Template;
import org.apache.velocity.context.Context;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.springframework.context.ApplicationContextException;

/**
 * VelocityToolsLayoutView emulates the functionality offered by Velocity's
 * VelocityLayoutServlet to ease page composition from different templates.
 *
 * <p>The <code>url</code> property should be set to the content template
 * for the view, and the layout template location should be specified as
 * <code>layoutUrl</code> property. A view can override the configured
 * layout template location by setting the appropriate key (the default
 * is "layout") in the content template.
 *
 * <p>When the view is rendered, the VelocityContext is first merged with
 * the content template (specified by the <code>url</code> property) and
 * then merged with the layout template to produce the final output.
 *
 * <p>The layout template can include the screen content through a
 * VelocityContext variable (the default is "screen_content").
 * At runtime, this variable will contain the rendered content template.
 *
 * @author Darren Davison
 * @author Juergen Hoeller
 * @author Nathan Bubna
 * @see #setLayoutUrl
 * @see #setLayoutKey
 * @see #setScreenContentKey
 */
public class VelocityToolsLayoutView extends VelocityToolsView {

    /**
     * The default {@link #setLayoutUrl(String) layout url}.
     */
    public static final String DEFAULT_LAYOUT_URL = "layout.vm";

    /**
     * The default {@link #setLayoutKey(String) layout key}.
     */
    public static final String DEFAULT_LAYOUT_KEY = "layout";

    /**
     * The default {@link #setScreenContentKey(String) screen content key}.
     */
    public static final String DEFAULT_SCREEN_CONTENT_KEY = "screen_content";


    private String layoutUrl = DEFAULT_LAYOUT_URL;

    private String layoutKey = DEFAULT_LAYOUT_KEY;

    private String screenContentKey = DEFAULT_SCREEN_CONTENT_KEY;


    /**
     * Set the layout template to use. Default is {@link
#DEFAULT_LAYOUT_URL "layout.vm"}.
     * @param layoutUrl the template location (relative to the template
     * root directory)
     */
    public void setLayoutUrl(String layoutUrl) {
        this.layoutUrl = layoutUrl;
    }

    /**
     * Set the context key used to specify an alternate layout to be
used instead
     * of the default layout. Screen content templates can override the layout
     * template that they wish to be wrapped with by setting this value in the
     * template, for example:<br>
     * <code>#set( $layout = "MyLayout.vm" )</code>
     * <p>Default key is {@link #DEFAULT_LAYOUT_KEY "layout"}, as
illustrated above.
     * @param layoutKey the name of the key you wish to use in your
     * screen content templates to override the layout template
     */
    public void setLayoutKey(String layoutKey) {
        this.layoutKey = layoutKey;
    }

    /**
     * Set the name of the context key that will hold the content of
     * the screen within the layout template. This key must be present
     * in the layout template for the current screen to be rendered.
     * <p>Default is {@link #DEFAULT_SCREEN_CONTENT_KEY "screen_content"}:
     * accessed in VTL as <code>$screen_content</code>.
     * @param screenContentKey the name of the screen content key to use
     */
    public void setScreenContentKey(String screenContentKey) {
        this.screenContentKey = screenContentKey;
    }


    /**
     * Overrides <code>VelocityView.checkTemplate()</code> to additionally check
     * that both the layout template and the screen content template
can be loaded.
     * Note that during rendering of the screen content, the layout template
     * can be changed which may invalidate any early checking done here.
     */
    protected void checkTemplate() throws ApplicationContextException {
        super.checkTemplate();

        try {
            // Check that we can get the template, even if we might
subsequently get it again.
            getTemplate(this.layoutUrl);
        }
        catch (ResourceNotFoundException ex) {
            throw new ApplicationContextException("Cannot find
Velocity template for URL [" + this.layoutUrl +
                    "]: Did you specify the correct resource loader path?", ex);
        }
        catch (Exception ex) {
            throw new ApplicationContextException(
                    "Could not load Velocity template for URL [" +
this.layoutUrl + "]", ex);
        }
    }

    /**
     * Overrides the normal rendering process in order to pre-process
the Context,
     * merging it with the screen template into a single value
(identified by the
     * value of screenContentKey). The layout template is then merged with the
     * modified Context in the super class.
     */
    protected void doRender(Context context, HttpServletResponse
response) throws Exception {
        renderScreenContent(context);

        // Velocity context now includes any mappings that were defined
        // (via #set) in screen content template.
        // The screen template can overrule the layout by doing
        // #set( $layout = "MyLayout.vm" )
        String layoutUrlToUse = (String) context.get(this.layoutKey);
        if (layoutUrlToUse != null) {
            if (logger.isDebugEnabled()) {
                logger.debug("Screen content template has requested
layout [" + layoutUrlToUse + "]");
            }
        }
        else {
            // No explicit layout URL given -> use default layout of this view.
            layoutUrlToUse = this.layoutUrl;
        }

        mergeTemplate(getTemplate(layoutUrlToUse), context, response);
    }

    /**
     * The resulting context contains any mappings from render, plus
screen content.
     */
    private void renderScreenContent(Context velocityContext) throws Exception {
        if (logger.isDebugEnabled()) {
            logger.debug("Rendering screen content template [" +
getUrl() + "]");
        }

        StringWriter sw = new StringWriter();
        Template screenContentTemplate = getTemplate(getUrl());
        screenContentTemplate.merge(velocityContext, sw);

        // Put rendered content into Velocity context.
        velocityContext.put(this.screenContentKey, sw.toString());
    }

}


Mime
View raw message