View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.jetspeed.velocity;
18  
19  import java.io.File;
20  import java.util.Locale;
21  import java.util.Map;
22  
23  import javax.portlet.PortletConfig;
24  import javax.portlet.PortletMode;
25  import javax.portlet.PortletRequest;
26  import javax.portlet.RenderRequest;
27  import javax.portlet.RenderResponse;
28  import javax.portlet.WindowState;
29  import javax.servlet.ServletConfig;
30  import javax.servlet.ServletException;
31  import javax.servlet.http.HttpServletRequest;
32  import javax.servlet.http.HttpServletResponse;
33  
34  import org.apache.commons.collections.ExtendedProperties;
35  import org.apache.commons.collections.map.LRUMap;
36  import org.apache.commons.configuration.Configuration;
37  import org.apache.commons.configuration.ConfigurationException;
38  import org.apache.commons.configuration.PropertiesConfiguration;
39  import org.apache.commons.logging.Log;
40  import org.apache.commons.logging.LogFactory;
41  import org.apache.jetspeed.Jetspeed;
42  import org.apache.jetspeed.PortalReservedParameters;
43  import org.apache.jetspeed.capabilities.CapabilityMap;
44  import org.apache.jetspeed.components.ComponentManager;
45  import org.apache.jetspeed.desktop.JetspeedDesktopContext;
46  import org.apache.jetspeed.locator.LocatorDescriptor;
47  import org.apache.jetspeed.locator.TemplateDescriptor;
48  import org.apache.jetspeed.locator.TemplateLocator;
49  import org.apache.jetspeed.locator.TemplateLocatorException;
50  import org.apache.jetspeed.om.page.Fragment;
51  import org.apache.jetspeed.om.page.Page;
52  import org.apache.jetspeed.request.RequestContext;
53  import org.apache.pluto.Constants;
54  import org.apache.portals.bridges.velocity.BridgesVelocityViewServlet;
55  import org.apache.velocity.Template;
56  import org.apache.velocity.app.VelocityEngine;
57  import org.apache.velocity.app.event.EventCartridge;
58  import org.apache.velocity.app.event.NullSetEventHandler;
59  import org.apache.velocity.context.Context;
60  import org.apache.velocity.exception.ParseErrorException;
61  import org.apache.velocity.exception.ResourceNotFoundException;
62  import org.apache.velocity.runtime.RuntimeConstants;
63  import org.apache.velocity.tools.generic.log.LogSystemCommonsLog;
64  import org.apache.velocity.tools.view.servlet.WebappLoader;
65  
66  /***
67   * @version $Id: JetspeedVelocityViewServlet.java 550655 2007-06-26 01:41:35Z taylor $
68   */
69  public class JetspeedVelocityViewServlet extends BridgesVelocityViewServlet
70  {
71      /*** logging */
72      private static final Log log = LogFactory.getLog(JetspeedVelocityViewServlet.class);
73  
74      /*** default cache size */
75      private static final long DEFAULT_CACHE_SIZE = 50;
76  
77      /*** default cache validation interval */
78      private static final String CACHE_SIZE_PARAMETER = "org.apache.jetspeed.cache.size";
79  
80      /*** default cache validation interval */
81      private static final long DEFAULT_CACHE_VALIDATION_INTERVAL = 10000;
82  
83      /*** default cache validation interval */
84      private static final String CACHE_VALIDATION_INTERVAL_PARAMETER = "org.apache.jetspeed.cache.validation.interval";
85  
86      /*** TLS for Context propagation */
87      private static ThreadLocal handlingRequestContext = new ThreadLocal();
88  
89      /*** decoration locators */
90      private TemplateLocator decorationLocator;
91  
92      /*** velocity engine configuration caching object */
93      private class VelocityEngineConfig
94      {
95          public String decoration;
96          public String type;
97          public String mediaType;
98          public String language;
99          public String country;
100 
101         public File macros;
102         public long macrosLastModified;
103         public long lastValidated;
104 
105         public VelocityEngineConfig(String decoration, String type, String mediaType, String language, String country)
106         {
107             this.decoration = decoration;
108             this.type = type;
109             this.mediaType = mediaType;
110             this.language = language;
111             this.country = country;
112             
113             this.macrosLastModified = -1;
114             this.lastValidated = System.currentTimeMillis();
115         }
116     }
117 
118     /*** VelocityEngine configuration cache by decoration */
119     private Map velocityEngineConfigCache;
120 
121     /*** VelocityEngine cache by macros locators */
122     private Map velocityEngineCache;
123 
124     /*** cache validation interval */
125     private long cacheValidationInterval;
126 
127     /*** default velocity engine */
128     private VelocityEngine defaultVelocityEngine;
129     
130     /*** Velocity EventCartridge for handling event */
131     EventCartridge eventCartridge;
132 
133     /***
134      * Initialize servlet, BridgesVelocityViewServlet, and VelocityViewServlet.
135      *
136      * @see org.apache.velocity.tools.view.servlet.VelocityViewServlet.init()
137      *
138      * @param config servlet configuation
139      */
140     public void init(ServletConfig config) throws ServletException
141     {
142         // initialize
143         super.init(config);
144 
145         // get jetspeed component manager configuration for decorations
146         ComponentManager cm = Jetspeed.getComponentManager();
147         int count =0;
148         while(cm == null) {
149             try {
150                 Thread.sleep(200);
151             } catch(InterruptedException ie) {
152                 
153             }
154             cm = Jetspeed.getComponentManager();
155             if( count > 5 ) {
156                 if (null == cm)
157                     throw new ServletException("Could not get Jetspeed Component Manager after "+count+"tries");
158             }
159             count++;
160         
161         }
162         decorationLocator = (TemplateLocator) cm.getComponent("DecorationLocator");
163 
164         // initialize thread safe velocity engine cache
165         int cacheSize = (int) getLongInitParameter(config, CACHE_SIZE_PARAMETER, DEFAULT_CACHE_SIZE);
166         velocityEngineConfigCache = new LRUMap(cacheSize);
167         velocityEngineCache = new LRUMap(cacheSize/2);
168         
169         eventCartridge = new EventCartridge();
170         // setup NullSetEventHandler to ignore those pesky "ERROR velocity - RHS of #set statement is null. Context will not be modified."
171         eventCartridge.addEventHandler(new NullSetEventHandler()
172         {
173             public boolean shouldLogOnNullSet(String lhs, String rhs) { return false; }
174         });
175 
176         // initialize velocity engine cache validation interval
177         cacheValidationInterval = getLongInitParameter(config, CACHE_VALIDATION_INTERVAL_PARAMETER, DEFAULT_CACHE_VALIDATION_INTERVAL);
178     }
179 
180     /***
181      * overriding VelocityViewServlet initialization of global Velocity to properly provide our own velocity.properties
182      * so to prevent an ERROR logging for not finding the default global VM_global_library.vm (which isn't available).
183      */
184     protected void initVelocity(ServletConfig config) throws ServletException
185     {
186         VelocityEngine velocity = new VelocityEngine();
187         setVelocityEngine(velocity);
188 
189         // register this engine to be the default handler of log messages
190         // if the user points commons-logging to the LogSystemCommonsLog
191         LogSystemCommonsLog.setVelocityEngine(velocity);
192 
193         velocity.setApplicationAttribute(SERVLET_CONTEXT_KEY, getServletContext());
194 
195         // default to servletlogger, which logs to the servlet engines log
196         velocity.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS, "org.apache.velocity.tools.view.servlet.ServletLogger");
197 
198         // by default, load resources with webapp resource loader
199         velocity.setProperty(RuntimeConstants.RESOURCE_LOADER, "webapp");
200         velocity.setProperty("webapp.resource.loader.class", 
201                              WebappLoader.class.getName());
202 
203         // Try reading an overriding Velocity configuration
204         try
205         {
206             ExtendedProperties p = loadConfiguration(config);
207             p.addProperty("velocimacro.library", "/WEB-INF/jetspeed_macros.vm");
208             p.setProperty("file.resource.loader.path", getServletContext().getRealPath("/"));
209             velocity.setExtendedProperties(p);
210         }
211         catch(Exception e)
212         {
213             getServletContext().log("VelocityViewServlet: Unable to read Velocity configuration file: "+e);
214             getServletContext().log("VelocityViewServlet: Using default Velocity configuration.");
215         }   
216 
217         // now all is ready - init Velocity
218         try
219         {
220             velocity.init();
221         }
222         catch(Exception e)
223         {
224             getServletContext().log("VelocityViewServlet: PANIC! unable to init() - "+e);
225             throw new ServletException(e);
226         }
227     }
228     
229     /***
230      * Handle the template processing request.
231      *
232      * @see org.apache.velocity.tools.view.servlet.VelocityViewServlet.handleRequest()
233      *
234      * @param request client request
235      * @param response client response
236      * @param ctx  VelocityContext to fill
237      * @return Velocity Template object or null
238      */
239     protected Template handleRequest(HttpServletRequest request, HttpServletResponse response, Context ctx) throws Exception
240     {
241         RequestContext requestContext = (RequestContext)request.getAttribute(PortalReservedParameters.REQUEST_CONTEXT_ATTRIBUTE);
242         if(requestContext == null)
243         {
244             throw new IllegalStateException("JetspeedVelocityViewServlet unable to handle request because there is no RequestContext in "+
245                    "the HttpServletRequest.");
246         }
247         
248         // hook up eventHandlers to the context, specifically our own IgnoringNullSetEventHandling
249         eventCartridge.attachToContext(ctx);
250         
251         JetspeedDesktopContext desktopContext = (JetspeedDesktopContext)request.getAttribute(JetspeedDesktopContext.DESKTOP_CONTEXT_ATTRIBUTE);
252         if (desktopContext != null)
253         {
254             // standard render request and response also available in context
255             ctx.put(JetspeedDesktopContext.DESKTOP_CONTEXT_ATTRIBUTE, desktopContext);
256             ctx.put("JS2RequestContext", requestContext);
257             
258             // setup TLS for Context propagation
259             handlingRequestContext.set(ctx);            
260             return super.handleRequest(request, response, ctx);            
261         }
262         // configure velocity context
263         PortletRequest renderRequest = (PortletRequest) request.getAttribute(Constants.PORTLET_REQUEST);
264         RenderResponse renderResponse = (RenderResponse) request.getAttribute(Constants.PORTLET_RESPONSE);
265         PortletConfig portletConfig = (PortletConfig) request.getAttribute(Constants.PORTLET_CONFIG);
266         if (renderRequest != null)
267         {
268             renderRequest.setAttribute(VELOCITY_CONTEXT_ATTR, ctx);
269         }
270                 
271         JetspeedVelocityPowerTool jpt = (JetspeedVelocityPowerTool) renderRequest.getAttribute(PortalReservedParameters.JETSPEED_POWER_TOOL_REQ_ATTRIBUTE);
272         if(jpt == null)
273         {
274             throw new IllegalStateException("JetspeedVelocityViewServlet unable to handle request because there is no JetspeedPowerTool in "+
275                    "the HttpServletRequest.");
276         }
277         
278         jpt.setVelocityContext(ctx);
279         ctx.put("jetspeed", jpt);  
280         ctx.put("JS2RequestContext", requestContext);
281         ctx.put("renderRequest", renderRequest);
282         ctx.put("renderResponse", renderResponse);
283         ctx.put("portletConfig", portletConfig);
284         ctx.put("portletModeView", PortletMode.VIEW);
285         ctx.put("portletModeEdit", PortletMode.EDIT);
286         ctx.put("portletModeHelp", PortletMode.HELP);
287         ctx.put("windowStateNormal", WindowState.NORMAL);
288         ctx.put("windowStateMinimized", WindowState.MINIMIZED);
289         ctx.put("windowStateMaximized", WindowState.MAXIMIZED);
290         ctx.put("rco", requestContext.getObjects());
291         StringBuffer appRoot = new StringBuffer();
292         if (!requestContext.getPortalURL().isRelativeOnly())
293         {
294             appRoot.append(request.getScheme()).append("://").append(request.getServerName()).append(":").append(request.getServerPort());
295         }
296         appRoot.append(renderRequest.getContextPath());
297         ctx.put("appRoot", appRoot.toString());        
298         
299         
300         // setup TLS for Context propagation
301         handlingRequestContext.set(ctx);
302 
303         // handle request normally        
304         return super.handleRequest(request, response, ctx);
305     }
306 
307     /***
308      * Retrieves the requested template.
309      *
310      * @see org.apache.velocity.tools.view.servlet.VelocityViewServlet.getTemplate()
311      *
312      * @param name The file name of the template to retrieve relative to the template root.
313      * @return The requested template.
314      * @throws ResourceNotFoundException if template not found from any available source.
315      * @throws ParseErrorException if template cannot be parsed due to syntax (or other) error.
316      * @throws Exception if an error occurs in template initialization
317      */
318     public Template getTemplate(String name)
319         throws ResourceNotFoundException, ParseErrorException, Exception
320     {
321         // retrieve Context to lookup appropriate velocity engine
322         Context ctx = (Context) handlingRequestContext.get();
323         if (ctx != null)
324         {
325             // create or lookup cached velocity engine
326             VelocityEngine velocity = getVelocityEngine(ctx);
327             if (velocity != null)
328             {
329                 // get template from velocity engine
330                 return velocity.getTemplate(name);
331             }
332         }
333 
334         // no velocity engine available
335         throw new Exception("No velocity engine available for request context.");
336     }
337 
338     /***
339      * Retrieves the requested template with the specified character encoding.
340      *
341      * @see org.apache.velocity.tools.view.servlet.VelocityViewServlet.getTemplate()
342      *
343      * @param name The file name of the template to retrieve relative to the template root.
344      * @param encoding the character encoding of the template
345      * @return The requested template.
346      * @throws ResourceNotFoundException if template not found from any available source.
347      * @throws ParseErrorException if template cannot be parsed due to syntax (or other) error.
348      * @throws Exception if an error occurs in template initialization
349      */
350     public Template getTemplate(String name, String encoding)
351         throws ResourceNotFoundException, ParseErrorException, Exception
352     {
353         // retrieve Context to lookup appropriate velocity engine
354         Context ctx = (Context) handlingRequestContext.get();
355         if (ctx != null)
356         {
357             // create or lookup cached velocity engine
358             VelocityEngine velocity = getVelocityEngine(ctx);
359             if (velocity != null)
360             {
361                 // get template from velocity engine
362                 return velocity.getTemplate(name, encoding);
363             }
364         }
365 
366         // no velocity engine available
367         throw new Exception("No velocity engine available for request context.");
368     }
369 
370     /***
371      * Get VelocityEngine for template access.
372      *
373      * @param ctx the velocity context.
374      * @return The VelocityEngine or null.
375      */
376     private VelocityEngine getVelocityEngine(Context ctx)
377     {
378         RequestContext requestContext = (RequestContext) ctx.get("JS2RequestContext");        
379         JetspeedDesktopContext desktopContext = (JetspeedDesktopContext)requestContext.getRequest().getAttribute(JetspeedDesktopContext.DESKTOP_CONTEXT_ATTRIBUTE);        
380         if (desktopContext != null)
381         {
382             if (defaultVelocityEngine == null)
383             {
384                 defaultVelocityEngine = initVelocity((TemplateDescriptor)null);
385             }
386             return defaultVelocityEngine;            
387         }                
388         // get render request and request context from Context
389         RenderRequest renderRequest = (RenderRequest) ctx.get("renderRequest");
390         JetspeedVelocityPowerTool jpt = (JetspeedVelocityPowerTool) ctx.get("jetspeed");
391         if ((renderRequest != null) && (requestContext != null))
392         {
393             // get layout type and decoration, fallback to
394             // page default decorations
395             Fragment layout = (Fragment) renderRequest.getAttribute(JetspeedVelocityPowerTool.LAYOUT_ATTR);
396             if (layout == null)
397             {
398                // layout = (Fragment) renderRequest.getAttribute(JetspeedPowerTool.FRAGMENT_ATTR);
399                 layout = jpt.getCurrentFragment();
400             }
401             String layoutType = layout.getType();
402             String layoutDecoration = layout.getDecorator();
403             if (layoutDecoration == null)
404             {
405                 //Page page = (Page) renderRequest.getAttribute(PortalReservedParameters.PAGE_ATTRIBUTE_KEY);
406                 Page page = requestContext.getPage();
407                 layoutDecoration = page.getEffectiveDefaultDecorator(layoutType);
408             }
409             
410             // get layout capabilites and locale
411             CapabilityMap capabilityMap = requestContext.getCapabilityMap();
412             Locale locale = requestContext.getLocale();
413             String layoutMediaType = capabilityMap.getPreferredMediaType().getName();
414             String layoutLanguage = locale.getLanguage();
415             String layoutCountry = locale.getCountry();
416             
417             // lookup cache config based on decoration cache key
418             String cacheKey = layoutDecoration + ":" + layoutType + ":" + layoutMediaType + ":" + layoutLanguage + ":" + layoutCountry;
419             VelocityEngineConfig config = null;
420             synchronized (velocityEngineConfigCache)
421             {
422                config = (VelocityEngineConfig) velocityEngineConfigCache.get(cacheKey);
423             }
424             
425             // validate cached configuration and return VelocityEngine if cached
426             long now = System.currentTimeMillis();
427             if ((config != null) && ((cacheValidationInterval == -1) || (now <= (config.lastValidated + cacheValidationInterval)))) 
428             {
429                 if (config.macros != null)
430                 {
431                     synchronized (velocityEngineCache)
432                     {
433                         // use cached velocity engine if available
434                         VelocityEngine velocity = (VelocityEngine) velocityEngineCache.get(config.macros.getAbsolutePath());
435                         if (velocity != null)
436                         {
437                             return velocity;
438                         }
439                     }
440                 }
441                 else
442                 {
443                     // use default velocity engine
444                     synchronized (this)
445                     {
446                         // construct and cache default velocity engine
447                         if (defaultVelocityEngine == null)
448                         {
449                             defaultVelocityEngine = initVelocity((TemplateDescriptor)null);
450                         }
451                         return defaultVelocityEngine;
452                     }
453                 }
454             }
455            
456             // load and/or verify decorator macros configuration
457             TemplateDescriptor macrosDescriptor = null;
458             
459             // create reusable decoration base descriptor
460             LocatorDescriptor descriptor = null;
461             try
462             {
463                 descriptor = decorationLocator.createLocatorDescriptor(null);
464             }
465             catch (TemplateLocatorException tle)
466             {
467                 log.error("getVelocityEngine(): unable create base descriptor", tle);
468             }
469             descriptor.setMediaType(layoutMediaType);
470             descriptor.setCountry(layoutCountry);
471             descriptor.setLanguage(layoutLanguage);
472             descriptor.setType(layoutType);
473             
474             // get decoration configuration properties descriptor
475             descriptor.setName(layoutDecoration + "/" + JetspeedVelocityPowerTool.DECORATOR_TYPE + ".properties");
476             TemplateDescriptor propertiesDescriptor = null;
477             try
478             {
479                 propertiesDescriptor = decorationLocator.locateTemplate(descriptor);
480             }
481             catch (TemplateLocatorException tle)
482             {
483                 // fallback to generic template type
484                 try
485                 {
486                     descriptor.setType(JetspeedVelocityPowerTool.GENERIC_TEMPLATE_TYPE);
487                     propertiesDescriptor = decorationLocator.locateTemplate(descriptor);
488                 }
489                 catch (TemplateLocatorException tleFallback)
490                 {
491                 }
492             }
493             // load configuration properties
494             Configuration configuration = null;
495             if (propertiesDescriptor != null)
496             {
497                 try
498                 {
499                     configuration = new PropertiesConfiguration(propertiesDescriptor.getAbsolutePath());
500                 }
501                 catch (ConfigurationException ce)
502                 {
503                     log.warn("getVelocityEngine(): unable read decorator properties from " + propertiesDescriptor.getAbsolutePath(), ce);
504                 }
505             }
506             if (configuration != null)
507             {
508                 // get decoration template macros extension and suffix
509                 String ext = configuration.getString("template.extension");
510                 String macros = configuration.getString("template.macros");
511                 
512                 // get decoration template macros descriptor if defined
513                 if ((ext != null) && (ext.length() > 0) && (macros != null) && (macros.length() > 0))
514                 {
515                     descriptor.setName(layoutDecoration + "/" + JetspeedVelocityPowerTool.DECORATOR_TYPE + macros + ext);
516                     try
517                     {
518                         macrosDescriptor = decorationLocator.locateTemplate(descriptor);
519                     }
520                     catch (TemplateLocatorException tle)
521                     {
522                         // fallback to extends decoration, (assume macros named the
523                         // same in the parent decoration as configured here)
524                         try
525                         {
526                             String parent = configuration.getString("extends");
527                             if ((parent != null) && (parent.length() > 0))
528                             {
529                                 descriptor.setName(parent + "/" + JetspeedVelocityPowerTool.DECORATOR_TYPE + macros + ext);
530                                 macrosDescriptor = decorationLocator.locateTemplate(descriptor);
531                             }
532                         }
533                         catch (TemplateLocatorException tleExtends)
534                         {
535                         }
536                     }
537                 }
538             }
539             
540             // compare located macros file with cached version
541             // to validate/refresh cached config and velocity engine
542             boolean newVelocityEngineConfig = false;
543             boolean forceVelocityEngineRefresh = false;
544             if (config == null)
545             {
546                 config = new VelocityEngineConfig(layoutDecoration, layoutType, layoutMediaType, layoutLanguage, layoutCountry);
547                 synchronized (velocityEngineConfigCache)
548                 {
549                     velocityEngineConfigCache.put(cacheKey, config);
550                 }
551                 newVelocityEngineConfig = true;
552             }
553             if (((macrosDescriptor == null) && (config.macros != null)) ||
554                 ((macrosDescriptor != null) && (config.macros == null)) ||
555                 ((macrosDescriptor != null) && (config.macros != null) &&
556                  (!macrosDescriptor.getAbsolutePath().equals(config.macros.getAbsolutePath()) ||
557                   (config.macros.lastModified() != config.macrosLastModified))))
558             {
559                 // set or reset configuration cache entry
560                 config.lastValidated = now;
561                 if (macrosDescriptor != null)
562                 {
563                     // save macros file
564                     config.macros = new File(macrosDescriptor.getAbsolutePath());
565                     config.macrosLastModified = config.macros.lastModified();
566                 }
567                 else
568                 {
569                     // clear macros file
570                     config.macros = null;
571                     config.macrosLastModified = -1;
572                 }
573 
574                 // aggressively force creation of new velocity engine
575                 // if any configuration change detected
576                 forceVelocityEngineRefresh = !newVelocityEngineConfig;
577             }
578             else
579             {
580                 // config validated
581                 config.lastValidated = now;
582             }
583 
584             // get or create new velocity engine intialized with
585             // validated macros configuration
586             VelocityEngine velocity = null;
587             if ((macrosDescriptor != null) && (config.macros != null))
588             {
589                 synchronized (velocityEngineCache)
590                 {
591                     if (!forceVelocityEngineRefresh)
592                     {
593                         // use cached velocity engine
594                         velocity = (VelocityEngine) velocityEngineCache.get(config.macros.getAbsolutePath());
595                     }
596                     if (velocity == null)
597                     {
598                         // create and cache new velocity engine
599                         velocity = initVelocity(macrosDescriptor);
600                         if (velocity != null)
601                         {
602                             velocityEngineCache.put(config.macros.getAbsolutePath(), velocity);
603                         }
604                     }
605                 }
606             }
607 
608             // fallback to default velocity engine
609             if (velocity == null)
610             {
611                 synchronized (this)
612                 {
613                     // construct and cache default velocity engine
614                     if (defaultVelocityEngine == null)
615                     {
616                         defaultVelocityEngine = initVelocity((TemplateDescriptor)null);
617                     }
618                     velocity = defaultVelocityEngine;
619                 }
620             }
621             
622             // return velocity engine for validated configuration
623             return velocity;
624         }
625         return null;
626     }
627 
628     /***
629      * Initialize new velocity instance using specified macros template.
630      *
631      * @see org.apache.velocity.tools.view.servlet.VelocityViewServlet.initVelocity()
632      *
633      * @param macros template descriptor.
634      * @return new VelocityEngine instance.
635      */
636     private VelocityEngine initVelocity(TemplateDescriptor macros)
637     {
638         try
639         {
640             // create new instance to initialize
641             VelocityEngine velocity = new VelocityEngine();
642             
643             // initialize new instance as is done with the default
644             // velocity singleton, appending macros template to the
645             // base configuration velocimacro.library property
646             velocity.setApplicationAttribute(SERVLET_CONTEXT_KEY, getServletContext());
647             velocity.setProperty(VelocityEngine.RUNTIME_LOG_LOGSYSTEM_CLASS, "org.apache.velocity.tools.view.servlet.ServletLogger");
648             ExtendedProperties configuration = loadConfiguration(getServletConfig());
649             if (macros != null)
650             {
651                 configuration.addProperty("velocimacro.library", macros.getAppRelativePath());
652             }
653             configuration.setProperty("file.resource.loader.path", getServletContext().getRealPath("/"));
654             velocity.setExtendedProperties(configuration);
655 
656             // initialize and return velocity engine
657             velocity.init();
658             if (macros != null)
659             {
660                 log.debug("initVelocity(): create new VelocityEngine instance to support " + macros.getAppRelativePath() + " decoration template macros");
661             }
662             else
663             {
664                 log.debug("initVelocity(): create new default VelocityEngine instance");
665             }
666             return velocity;
667         }
668         catch (Exception e)
669         {
670             log.error("initVelocity(): unable to initialize velocity engine instance, using default singleton", e);
671         }
672         return null;
673     }
674 
675     /***
676      * Utility to get long init parameters.
677      *
678      * @param config servlet config
679      * @param name of init parameter
680      * @param defaultValue value
681      * @return parameter value
682      */
683     private long getLongInitParameter(ServletConfig config, String name, long defaultValue)
684     {
685         String value = config.getInitParameter(name);
686         if ((value == null) || (value.length() == 0))
687         {
688             value = config.getServletContext().getInitParameter(name);
689         }
690         if ((value != null) && (value.length() > 0))
691         {
692             try
693             {
694                 return Long.parseLong(value);
695             }
696             catch (Exception e)
697             {
698             }
699         }
700         return defaultValue;
701     }
702     
703     protected void error(HttpServletRequest request, 
704             HttpServletResponse response, 
705             Exception e)
706     throws ServletException
707     {
708         try
709         {
710             StringBuffer html = new StringBuffer();
711             html.append("<b>\n");
712             html.append("Content is not available");
713             html.append("<b>\n");
714             getResponseWriter(response).write(html.toString());
715             log.error("Error processing vm template ", e);
716         }
717         catch (Exception e2)
718         {
719             log.error("Error writing error message to vm template ", e2);            
720         }        
721     }
722     
723 }