1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
143 super.init(config);
144
145
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
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
171 eventCartridge.addEventHandler(new NullSetEventHandler()
172 {
173 public boolean shouldLogOnNullSet(String lhs, String rhs) { return false; }
174 });
175
176
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
190
191 LogSystemCommonsLog.setVelocityEngine(velocity);
192
193 velocity.setApplicationAttribute(SERVLET_CONTEXT_KEY, getServletContext());
194
195
196 velocity.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS, "org.apache.velocity.tools.view.servlet.ServletLogger");
197
198
199 velocity.setProperty(RuntimeConstants.RESOURCE_LOADER, "webapp");
200 velocity.setProperty("webapp.resource.loader.class",
201 WebappLoader.class.getName());
202
203
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
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
249 eventCartridge.attachToContext(ctx);
250
251 JetspeedDesktopContext desktopContext = (JetspeedDesktopContext)request.getAttribute(JetspeedDesktopContext.DESKTOP_CONTEXT_ATTRIBUTE);
252 if (desktopContext != null)
253 {
254
255 ctx.put(JetspeedDesktopContext.DESKTOP_CONTEXT_ATTRIBUTE, desktopContext);
256 ctx.put("JS2RequestContext", requestContext);
257
258
259 handlingRequestContext.set(ctx);
260 return super.handleRequest(request, response, ctx);
261 }
262
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
301 handlingRequestContext.set(ctx);
302
303
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
322 Context ctx = (Context) handlingRequestContext.get();
323 if (ctx != null)
324 {
325
326 VelocityEngine velocity = getVelocityEngine(ctx);
327 if (velocity != null)
328 {
329
330 return velocity.getTemplate(name);
331 }
332 }
333
334
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
354 Context ctx = (Context) handlingRequestContext.get();
355 if (ctx != null)
356 {
357
358 VelocityEngine velocity = getVelocityEngine(ctx);
359 if (velocity != null)
360 {
361
362 return velocity.getTemplate(name, encoding);
363 }
364 }
365
366
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
389 RenderRequest renderRequest = (RenderRequest) ctx.get("renderRequest");
390 JetspeedVelocityPowerTool jpt = (JetspeedVelocityPowerTool) ctx.get("jetspeed");
391 if ((renderRequest != null) && (requestContext != null))
392 {
393
394
395 Fragment layout = (Fragment) renderRequest.getAttribute(JetspeedVelocityPowerTool.LAYOUT_ATTR);
396 if (layout == null)
397 {
398
399 layout = jpt.getCurrentFragment();
400 }
401 String layoutType = layout.getType();
402 String layoutDecoration = layout.getDecorator();
403 if (layoutDecoration == null)
404 {
405
406 Page page = requestContext.getPage();
407 layoutDecoration = page.getEffectiveDefaultDecorator(layoutType);
408 }
409
410
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
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
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
434 VelocityEngine velocity = (VelocityEngine) velocityEngineCache.get(config.macros.getAbsolutePath());
435 if (velocity != null)
436 {
437 return velocity;
438 }
439 }
440 }
441 else
442 {
443
444 synchronized (this)
445 {
446
447 if (defaultVelocityEngine == null)
448 {
449 defaultVelocityEngine = initVelocity((TemplateDescriptor)null);
450 }
451 return defaultVelocityEngine;
452 }
453 }
454 }
455
456
457 TemplateDescriptor macrosDescriptor = null;
458
459
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
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
484 try
485 {
486 descriptor.setType(JetspeedVelocityPowerTool.GENERIC_TEMPLATE_TYPE);
487 propertiesDescriptor = decorationLocator.locateTemplate(descriptor);
488 }
489 catch (TemplateLocatorException tleFallback)
490 {
491 }
492 }
493
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
509 String ext = configuration.getString("template.extension");
510 String macros = configuration.getString("template.macros");
511
512
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
523
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
541
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
560 config.lastValidated = now;
561 if (macrosDescriptor != null)
562 {
563
564 config.macros = new File(macrosDescriptor.getAbsolutePath());
565 config.macrosLastModified = config.macros.lastModified();
566 }
567 else
568 {
569
570 config.macros = null;
571 config.macrosLastModified = -1;
572 }
573
574
575
576 forceVelocityEngineRefresh = !newVelocityEngineConfig;
577 }
578 else
579 {
580
581 config.lastValidated = now;
582 }
583
584
585
586 VelocityEngine velocity = null;
587 if ((macrosDescriptor != null) && (config.macros != null))
588 {
589 synchronized (velocityEngineCache)
590 {
591 if (!forceVelocityEngineRefresh)
592 {
593
594 velocity = (VelocityEngine) velocityEngineCache.get(config.macros.getAbsolutePath());
595 }
596 if (velocity == null)
597 {
598
599 velocity = initVelocity(macrosDescriptor);
600 if (velocity != null)
601 {
602 velocityEngineCache.put(config.macros.getAbsolutePath(), velocity);
603 }
604 }
605 }
606 }
607
608
609 if (velocity == null)
610 {
611 synchronized (this)
612 {
613
614 if (defaultVelocityEngine == null)
615 {
616 defaultVelocityEngine = initVelocity((TemplateDescriptor)null);
617 }
618 velocity = defaultVelocityEngine;
619 }
620 }
621
622
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
641 VelocityEngine velocity = new VelocityEngine();
642
643
644
645
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
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 }