taylor 2004/12/01 21:17:30 Modified: portal/src/java/org/apache/jetspeed/velocity JetspeedVelocityViewServlet.java Log: Supports individual VelocityEngine instances for decorators to allow local macro definitions. Caching of VelocityEngine is controlled via web.xml init parameters for JetspeedVelocityViewServlet. See decorator.properties and decorator-macros.vm files in tigris and jetspeed layout decorators for use of this facility. Submitted by: Randy Watler Reviewed by: David Sean Taylor CVS: ---------------------------------------------------------------------- CVS: PR: CVS: If this change addresses a PR in the problem report tracking CVS: database, then enter the PR number(s) here. CVS: Obtained from: CVS: If this change has been taken from another system, such as NCSA, CVS: then name the system in this line, otherwise delete it. CVS: Submitted by: CVS: If this code has been contributed to Apache by someone else; i.e., CVS: they sent us a patch or a new module, then include their name/email CVS: address here. If this is your work then delete this line. CVS: Reviewed by: CVS: If we are doing pre-commit code reviews and someone else has CVS: reviewed your changes, include their name(s) here. CVS: If you have not had it reviewed then delete this line. Revision Changes Path 1.9 +486 -8 jakarta-jetspeed-2/portal/src/java/org/apache/jetspeed/velocity/JetspeedVelocityViewServlet.java Index: JetspeedVelocityViewServlet.java =================================================================== RCS file: /home/cvs/jakarta-jetspeed-2/portal/src/java/org/apache/jetspeed/velocity/JetspeedVelocityViewServlet.java,v retrieving revision 1.8 retrieving revision 1.9 diff -u -r1.8 -r1.9 --- JetspeedVelocityViewServlet.java 24 Oct 2004 19:33:43 -0000 1.8 +++ JetspeedVelocityViewServlet.java 2 Dec 2004 05:17:15 -0000 1.9 @@ -1,41 +1,168 @@ /* - * Created on Oct 24, 2004 - * - * TODO To change the template for this generated file go to - * Window - Preferences - Java - Code Style - Code Templates + * Copyright 2000-2004 The Apache Software Foundation. + * + * 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 org.apache.jetspeed.velocity; +import java.io.File; +import java.util.Locale; +import java.util.Map; + import javax.portlet.PortletConfig; import javax.portlet.PortletMode; import javax.portlet.PortletRequest; +import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; import javax.portlet.WindowState; +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.apache.commons.collections.ExtendedProperties; +import org.apache.commons.collections.map.LRUMap; +import org.apache.commons.configuration.Configuration; +import org.apache.commons.configuration.ConfigurationException; +import org.apache.commons.configuration.PropertiesConfiguration; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.jetspeed.Jetspeed; +import org.apache.jetspeed.PortalReservedParameters; +import org.apache.jetspeed.capabilities.CapabilityMap; +import org.apache.jetspeed.components.ComponentManager; +import org.apache.jetspeed.locator.LocatorDescriptor; +import org.apache.jetspeed.locator.TemplateDescriptor; +import org.apache.jetspeed.locator.TemplateLocator; +import org.apache.jetspeed.locator.TemplateLocatorException; +import org.apache.jetspeed.om.page.Fragment; +import org.apache.jetspeed.om.page.Page; import org.apache.jetspeed.request.RequestContext; import org.apache.pluto.Constants; import org.apache.portals.bridges.velocity.BridgesVelocityViewServlet; import org.apache.velocity.Template; +import org.apache.velocity.app.Velocity; +import org.apache.velocity.app.VelocityEngine; import org.apache.velocity.context.Context; +import org.apache.velocity.exception.ResourceNotFoundException; +import org.apache.velocity.exception.ParseErrorException; /** * @version $Id$ */ public class JetspeedVelocityViewServlet extends BridgesVelocityViewServlet { + /** logging */ + private static final Log log = LogFactory.getLog(JetspeedVelocityViewServlet.class); + + /** default cache size */ + private static final long DEFAULT_CACHE_SIZE = 50; + + /** default cache validation interval */ + private static final String CACHE_SIZE_PARAMETER = "org.apache.jetspeed.cache.size"; + + /** default cache validation interval */ + private static final long DEFAULT_CACHE_VALIDATION_INTERVAL = 10000; + + /** default cache validation interval */ + private static final String CACHE_VALIDATION_INTERVAL_PARAMETER = "org.apache.jetspeed.cache.validation.interval"; + + /** TLS for Context propagation */ + private static ThreadLocal handlingRequestContext = new ThreadLocal(); + + /** decoration locators */ + private TemplateLocator decorationLocator; + + /** velocity engine configuration caching object */ + private class VelocityEngineConfig + { + public String decoration; + public String type; + public String mediaType; + public String language; + public String country; + + public File macros; + public long macrosLastModified; + public long lastValidated; + + public VelocityEngineConfig(String decoration, String type, String mediaType, String language, String country) + { + this.decoration = decoration; + this.type = type; + this.mediaType = mediaType; + this.language = language; + this.country = country; + + this.macrosLastModified = -1; + this.lastValidated = System.currentTimeMillis(); + } + } + /** VelocityEngine configuration cache by decoration */ + private Map velocityEngineConfigCache; + + /** VelocityEngine cache by macros locators */ + private Map velocityEngineCache; + + /** cache validation interval */ + private long cacheValidationInterval; + + /** + * Initialize servlet, BridgesVelocityViewServlet, and VelocityViewServlet. + * + * @see org.apache.velocity.tools.view.servlet.VelocityViewServlet.init() + * + * @param config servlet configuation + */ + public void init(ServletConfig config) throws ServletException + { + // initialize + super.init(config); + + // get jetspeed component manager configuration for decorations + ComponentManager cm = Jetspeed.getComponentManager(); + decorationLocator = (TemplateLocator) cm.getComponent("DecorationLocator"); + + // initialize thread safe velocity engine cache + int cacheSize = (int) getLongInitParameter(config, CACHE_SIZE_PARAMETER, DEFAULT_CACHE_SIZE); + velocityEngineConfigCache = new LRUMap(cacheSize); + velocityEngineCache = new LRUMap(cacheSize/2); + + // initialize velocity engine cache validation interval + cacheValidationInterval = getLongInitParameter(config, CACHE_VALIDATION_INTERVAL_PARAMETER, DEFAULT_CACHE_VALIDATION_INTERVAL); + } + + /** + * Handle the template processing request. + * + * @see org.apache.velocity.tools.view.servlet.VelocityViewServlet.handleRequest() + * + * @param request client request + * @param response client response + * @param ctx VelocityContext to fill + * @return Velocity Template object or null + */ protected Template handleRequest(HttpServletRequest request, HttpServletResponse response, Context ctx) throws Exception { + // configure velocity context PortletRequest renderRequest = (PortletRequest) request.getAttribute(Constants.PORTLET_REQUEST); RenderResponse renderResponse = (RenderResponse) request.getAttribute(Constants.PORTLET_RESPONSE); PortletConfig portletConfig = (PortletConfig) request.getAttribute(Constants.PORTLET_CONFIG); if (renderRequest != null) { renderRequest.setAttribute(VELOCITY_CONTEXT_ATTR, ctx); - } - + } ctx.put("JS2RequestContext", request.getAttribute(RequestContext.REQUEST_PORTALENV)); ctx.put("renderRequest", renderRequest); ctx.put("renderResponse", renderResponse); @@ -50,6 +177,357 @@ .append(request.getServerName()).append(":") .append(request.getServerPort()).append(renderRequest.getContextPath()); ctx.put("appRoot", appRoot.toString()); - return super.handleRequest(request, response, ctx); + + + // setup TLS for Context propagation + handlingRequestContext.set(ctx); + + // handle request normally + return super.handleRequest(request, response, ctx); + } + + /** + * Retrieves the requested template. + * + * @see org.apache.velocity.tools.view.servlet.VelocityViewServlet.getTemplate() + * + * @param name The file name of the template to retrieve relative to the template root. + * @return The requested template. + * @throws ResourceNotFoundException if template not found from any available source. + * @throws ParseErrorException if template cannot be parsed due to syntax (or other) error. + * @throws Exception if an error occurs in template initialization + */ + public Template getTemplate(String name) + throws ResourceNotFoundException, ParseErrorException, Exception + { + // retrieve Context to lookup appropriate velocity engine + Context ctx = (Context) handlingRequestContext.get(); + if (ctx != null) + { + // create or lookup cached velocity engine + VelocityEngine velocity = getVelocityEngine(ctx); + if (velocity != null) + { + // get template from velocity engine + return velocity.getTemplate(name); + } + } + + // fallback to global velocity engine singleton + return super.getTemplate(name); + } + + /** + * Retrieves the requested template with the specified character encoding. + * + * @see org.apache.velocity.tools.view.servlet.VelocityViewServlet.getTemplate() + * + * @param name The file name of the template to retrieve relative to the template root. + * @param encoding the character encoding of the template + * @return The requested template. + * @throws ResourceNotFoundException if template not found from any available source. + * @throws ParseErrorException if template cannot be parsed due to syntax (or other) error. + * @throws Exception if an error occurs in template initialization + */ + public Template getTemplate(String name, String encoding) + throws ResourceNotFoundException, ParseErrorException, Exception + { + // retrieve Context to lookup appropriate velocity engine + Context ctx = (Context) handlingRequestContext.get(); + if (ctx != null) + { + // create or lookup cached velocity engine + VelocityEngine velocity = getVelocityEngine(ctx); + if (velocity != null) + { + // get template from velocity engine + return velocity.getTemplate(name, encoding); + } + } + + // fallback to global velocity engine singleton + return super.getTemplate(name, encoding); + } + + /** + * Get VelocityEngine for template access. + * + * @param ctx the velocity context. + * @return The VelocityEngine or null. + */ + private VelocityEngine getVelocityEngine(Context ctx) + { + // get render request and request context from Context + RenderRequest renderRequest = (RenderRequest) ctx.get("renderRequest"); + RequestContext requestContext = (RequestContext) ctx.get("JS2RequestContext"); + if ((renderRequest != null) && (requestContext != null)) + { + // get fragment type and decoration, fallback to + // fragment page default decorations + Fragment fragment = (Fragment) renderRequest.getAttribute(JetspeedPowerTool.FRAGMENT_ATTR); + String fragmentType = fragment.getType(); + String fragmentDecoration = fragment.getDecorator(); + if (fragmentDecoration == null) + { + Page fragmentPage = (Page) renderRequest.getAttribute(PortalReservedParameters.PAGE_ATTRIBUTE_KEY); + fragmentDecoration = fragmentPage.getDefaultDecorator(fragmentType); + } + + // get fragment capabilites and locale + CapabilityMap capabilityMap = requestContext.getCapabilityMap(); + Locale locale = requestContext.getLocale(); + String fragmentMediaType = capabilityMap.getPreferredMediaType().getName(); + String fragmentLanguage = locale.getLanguage(); + String fragmentCountry = locale.getCountry(); + + // lookup cache config based on decoration cache key + String cacheKey = fragmentDecoration + ":" + fragmentType + ":" + fragmentMediaType + ":" + fragmentLanguage + ":" + fragmentCountry; + VelocityEngineConfig config = null; + synchronized (velocityEngineConfigCache) + { + config = (VelocityEngineConfig) velocityEngineConfigCache.get(cacheKey); + } + + // validate cached configuration and return VelocityEngine if cached + long now = System.currentTimeMillis(); + if ((config != null) && (now <= (config.lastValidated + cacheValidationInterval))) + { + if (config.macros != null) + { + synchronized (velocityEngineCache) + { + // use cached velocity engine if available + VelocityEngine velocity = (VelocityEngine) velocityEngineCache.get(config.macros.getPath()); + if (velocity != null) + { + return velocity; + } + } + } + else + { + return null; + } + } + + // load and/or verify decorator macros configuration + TemplateDescriptor macrosDescriptor = null; + + // create reusable decoration base descriptor + LocatorDescriptor descriptor = null; + try + { + descriptor = decorationLocator.createLocatorDescriptor(null); + } + catch (TemplateLocatorException tle) + { + log.error("getVelocityEngine(): unable create base descriptor", tle); + } + descriptor.setMediaType(fragmentMediaType); + descriptor.setCountry(fragmentCountry); + descriptor.setLanguage(fragmentLanguage); + descriptor.setType(fragmentType); + + // get decoration configuration properties descriptor + descriptor.setName(fragmentDecoration + "/" + JetspeedPowerTool.DECORATOR_TYPE + ".properties"); + TemplateDescriptor propertiesDescriptor = null; + try + { + propertiesDescriptor = decorationLocator.locateTemplate(descriptor); + } + catch (TemplateLocatorException tle) + { + // fallback to generic template type + try + { + descriptor.setType(JetspeedPowerTool.GENERIC_TEMPLATE_TYPE); + propertiesDescriptor = decorationLocator.locateTemplate(descriptor); + } + catch (TemplateLocatorException tleFallback) + { + } + } + // load configuration properties + Configuration configuration = null; + if (propertiesDescriptor != null) + { + try + { + configuration = new PropertiesConfiguration(propertiesDescriptor.getAbsolutePath()); + } + catch (ConfigurationException ce) + { + log.warn("getVelocityEngine(): unable read decorator properties from " + propertiesDescriptor.getAbsolutePath(), ce); + } + } + if (configuration != null) + { + // get decoration template macros extension and suffix + String ext = configuration.getString("template.extension"); + String macros = configuration.getString("template.macros"); + + // get decoration template macros descriptor if defined + if ((ext != null) && (ext.length() > 0) && (macros != null) && (macros.length() > 0)) + { + descriptor.setName(fragmentDecoration + "/" + JetspeedPowerTool.DECORATOR_TYPE + macros + ext); + try + { + macrosDescriptor = decorationLocator.locateTemplate(descriptor); + } + catch (TemplateLocatorException tle) + { + // fallback to extends decoration, (assume macros named the + // same in the parent decoration as configured here) + try + { + String parent = configuration.getString("extends"); + if ((parent != null) && (parent.length() > 0)) + { + descriptor.setName(parent + "/" + JetspeedPowerTool.DECORATOR_TYPE + macros + ext); + macrosDescriptor = decorationLocator.locateTemplate(descriptor); + } + } + catch (TemplateLocatorException tleExtends) + { + } + } + } + } + + // compare located macros file with cached version + // to validate/refresh cached config and velocity engine + boolean newVelocityEngineConfig = false; + boolean forceVelocityEngineRefresh = false; + if (config == null) + { + config = new VelocityEngineConfig(fragmentDecoration, fragmentType, fragmentMediaType, fragmentLanguage, fragmentCountry); + synchronized (velocityEngineConfigCache) + { + velocityEngineConfigCache.put(cacheKey, config); + } + newVelocityEngineConfig = true; + } + if (((macrosDescriptor == null) && (config.macros != null)) || + ((macrosDescriptor != null) && (config.macros == null)) || + ((macrosDescriptor != null) && (config.macros != null) && + (!macrosDescriptor.getAbsolutePath().equals(config.macros.getPath()) || + (config.macros.lastModified() != config.macrosLastModified)))) + { + // set or reset configuration cache entry + config.lastValidated = now; + if (macrosDescriptor != null) + { + // save macros file + config.macros = new File(macrosDescriptor.getAbsolutePath()); + config.macrosLastModified = config.macros.lastModified(); + } + else + { + // clear macros file + config.macros = null; + config.macrosLastModified = -1; + } + + // aggressively force creation of new velocity engine + // if any configuration change detected + forceVelocityEngineRefresh = !newVelocityEngineConfig; + } + else + { + // config validated + config.lastValidated = now; + } + + // get or create new velocity engine intialized with + // validated macros configuration + VelocityEngine velocity = null; + if ((macrosDescriptor != null) && (config.macros != null)) + { + synchronized (velocityEngineCache) + { + if (!forceVelocityEngineRefresh) + { + // use cached velocity engine + velocity = (VelocityEngine) velocityEngineCache.get(config.macros.getPath()); + } + if (velocity == null) + { + // create and cache new velocity engine + velocity = initVelocity(macrosDescriptor); + velocityEngineCache.put(config.macros.getPath(), velocity); + } + } + } + + // return velocity engine for validated configuration + return velocity; + } + return null; + } + + /** + * Initialize new velocity instance using specified macros template. + * + * @see org.apache.velocity.tools.view.servlet.VelocityViewServlet.initVelocity() + * + * @param macros template descriptor. + * @return new VelocityEngine instance. + */ + private VelocityEngine initVelocity(TemplateDescriptor macros) + { + try + { + // create new instance to initialize + VelocityEngine velocity = new VelocityEngine(); + + // initialize new instance as is done with the default + // velocity singleton, appending macros template to the + // base configuration velocimacro.library property + velocity.setApplicationAttribute(SERVLET_CONTEXT_KEY, getServletContext()); + velocity.setProperty(VelocityEngine.RUNTIME_LOG_LOGSYSTEM_CLASS, "org.apache.velocity.tools.view.servlet.ServletLogger"); + velocity.setProperty(VelocityEngine.RESOURCE_LOADER, "webapp"); + velocity.setProperty("webapp.resource.loader.class", "org.apache.velocity.tools.view.servlet.WebappLoader"); + ExtendedProperties configuration = loadConfiguration(getServletConfig()); + configuration.addProperty("velocimacro.library", macros.getAppRelativePath()); + velocity.setExtendedProperties(configuration); + + // initialize and return velocity engine + velocity.init(); + log.debug("initVelocity(): create new VelocityEngine instance to support " + macros.getAppRelativePath() + " decoration template macros"); + return velocity; + } + catch (Exception e) + { + log.error("initVelocity(): unable to initialize velocity engine instance, using default singleton", e); + } + return null; + } + + /** + * Utility to get long init parameters. + * + * @param config servlet config + * @param name of init parameter + * @param defaultValue value + * @return parameter value + */ + private long getLongInitParameter(ServletConfig config, String name, long defaultValue) + { + String value = config.getInitParameter(name); + if ((value == null) || (value.length() == 0)) + { + value = config.getServletContext().getInitParameter(name); + } + if ((value != null) && (value.length() > 0)) + { + try + { + return Long.parseLong(value); + } + catch (Exception e) + { + } + } + return defaultValue; } }
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]