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]