package org.apache.velocity.tools.view.jsp; /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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. */ import java.io.StringWriter; import java.io.Writer; import javax.servlet.http.HttpServletRequest; import javax.servlet.jsp.JspException; import javax.servlet.jsp.PageContext; import javax.servlet.jsp.tagext.BodyTagSupport; import org.apache.velocity.Template; import org.apache.velocity.app.VelocityEngine; import org.apache.velocity.runtime.resource.loader.StringResourceLoader; import org.apache.velocity.runtime.resource.util.StringResourceRepository; import org.apache.velocity.tools.view.ServletUtils; import org.apache.velocity.tools.view.ViewToolContext; import org.apache.velocity.tools.view.VelocityView; /** * <p>This tag enables use of Velocity and VelocityTools within JSP files and tags. * This makes it trivial to render embedded VTL (Velocity Template Language) * or include a separate Velocity template within a JSP using the current * page context. This also automatically provides the typical * {@link VelocityView} toolbox support, much like the VelocityViewServlet * and VelocityLayoutServlets have. In fact, this will by default share * the {@link VelocityView} instance used with those servlets. This allows * for consistent configuration and shared resources (better performance). * </p> * * @author Nathan Bubna * @version $Id: VelocityViewTag.java,v 1.1 2001/08/14 00:07:39 geirm Exp $ * @since VelocityTools 2.0 */ public class VelocityViewTag extends BodyTagSupport { public static final String DEFAULT_BODY_CONTENT_KEY = "bodyContent"; private static final long serialVersionUID = -3329444102562079189L; protected transient VelocityView view; protected transient ViewToolContext context; protected transient StringResourceRepository repository; protected String var; protected String scope; protected String template; protected String bodyContentKey = DEFAULT_BODY_CONTENT_KEY; private boolean cache = false; /** * Release any per-invocation resources, resetting any resources or state * that should be cleared between successive invocations of * {@link javax.servlet.jsp.tagext.Tag#doEndTag()} and * {@link javax.servlet.jsp.tagext.Tag#doStartTag()}. */ protected void reset() { super.setId(null); var = null; scope = null; template = null; bodyContentKey = DEFAULT_BODY_CONTENT_KEY; cache = false; } public void setId(String id) { if (id == null) { throw new NullPointerException("id cannot be null"); } super.setId(id); // assume they want this cached cache = true; } protected String getLogId() { String id = super.getId(); if (id == null) { id = getClass().getSimpleName(); } return id; } public void setVar(String var) { this.var = var; } public String getVar() { return this.var; } public void setScope(String scope) { this.scope = scope; } public String getScope() { return this.scope; } public void setTemplate(String template) { this.template = template; } public String getTemplate() { return this.template; } public void setBodyContentKey(String key) { this.bodyContentKey = DEFAULT_BODY_CONTENT_KEY; } public String getBodyContentKey() { return this.bodyContentKey; } public void setCache(String s) { this.cache = "true".equalsIgnoreCase(s); } public String getCache() { return String.valueOf(this.cache); } public VelocityView getVelocityView() { return this.view; } public void setVelocityView(VelocityView view) { this.view = view; } public ViewToolContext getViewToolContext() { return this.context; } public void setViewToolContext(ViewToolContext context) { this.context = context; } public StringResourceRepository getRepository() { if (this.repository == null) { setRepository(StringResourceLoader.getRepository()); } return this.repository; } public void setRepository(StringResourceRepository repo) { this.repository = repo; } public int doStartTag() throws JspException { initializeView(); return EVAL_BODY_BUFFERED; } public int doEndTag() throws JspException { if (hasContent()) { try { // check for a var attribute String varname = getVar(); if (varname == null) { // if none, render into the pageout renderContent(this.pageContext.getOut()); } else { // if we have a var, render into a string StringWriter out = new StringWriter(); renderContent(out); // and insert the string into the specified scope // if none specified, default is PAGE_SCOPE this.pageContext.setAttribute(varname, out.toString(), toScopeInt(getScope())); } } catch (Exception e) { throw new JspException("Failed to render " + getClass() + ": "+getLogId(), e); } } return EVAL_PAGE; } protected void initializeView() { // get the VelocityView for this app VelocityView view = ServletUtils.getVelocityView(this.pageContext.getServletConfig()); // now make a Context ViewToolContext context = new JspToolContext(view.getVelocityEngine(), this.pageContext); view.prepareContext(context, (HttpServletRequest)this.pageContext.getRequest()); setVelocityView(view); setViewToolContext(context); } protected boolean hasContent() { return (getBodyContent() != null || getTemplate() != null); } protected void renderContent(Writer out) throws Exception { if (getTemplate() != null) { VelocityView view = getVelocityView(); ViewToolContext context = getViewToolContext(); // get the actual Template Template template = view.getTemplate(getTemplate()); if (getBodyContent() != null) { context.put(getBodyContentKey(), getRenderedBody()); } // render the template into the writer template.merge(context, out); } else { // render the body into the writer renderBody(out); } } protected String getRenderedBody() throws Exception { // render the body into a string StringWriter out = new StringWriter(); renderBody(out); return out.toString(); } protected boolean isCached() { return getRepository().getStringResource(getId()) != null; } protected void renderBody(Writer out) throws Exception { String name = getId(); // if it hasn't been cached, try that if (cache && !isCached()) { String template = getBodyContent().getString(); // if no id was set, use the template as the id if (name == null) { name = template; } cache(name, template); } // if it can't be cached, eval it if (!cache) { evalBody(out); } else { // load template from cache Template template = getVelocityView().getTemplate(name); template.merge(getViewToolContext(), out); } } protected void evalBody(Writer out) throws Exception { VelocityEngine engine = getVelocityView().getVelocityEngine(); engine.evaluate(getViewToolContext(), out, getLogId(), getBodyContent().getReader()); } protected static int toScopeInt(String scope) { if (scope == null) { return PageContext.PAGE_SCOPE; } if (scope.equalsIgnoreCase("request")) { return PageContext.REQUEST_SCOPE; } if (scope.equalsIgnoreCase("session")) { return PageContext.SESSION_SCOPE; } if (scope.equalsIgnoreCase("application")) { return PageContext.APPLICATION_SCOPE; } if (scope.equalsIgnoreCase("page")) { return PageContext.PAGE_SCOPE; } throw new IllegalArgumentException("Unknown scope: "+scope); } protected void cache(String name, String template) { try { getRepository().putStringResource(name, template); } catch (Exception cnfe) { getVelocityView().getLog() .error("Could not cache body in a StringResourceRepository", cnfe); cache = false; } } /** * Release any per-instance resources, releasing any resources or state * before this tag instance is disposed. * * @see javax.servlet.jsp.tagext.Tag#release() */ @Override public void release() { super.release(); reset(); } }