1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.jetspeed.services.template;
18
19
20 import java.io.File;
21
22
23 import java.util.Iterator;
24 import java.util.List;
25 import java.util.ArrayList;
26 import java.util.Map;
27 import java.util.HashMap;
28 import java.util.Locale;
29
30 import javax.servlet.ServletConfig;
31
32
33 import org.apache.turbine.util.RunData;
34
35
36 import org.apache.turbine.services.TurbineBaseService;
37 import org.apache.turbine.services.TurbineServices;
38 import org.apache.turbine.services.InitializationException;
39 import org.apache.turbine.services.servlet.TurbineServlet;
40 import org.apache.turbine.services.jsp.JspService;
41 import org.apache.turbine.services.resources.TurbineResources;
42 import org.apache.turbine.services.template.TurbineTemplate;
43 import org.apache.turbine.services.velocity.VelocityService;
44 import org.apache.turbine.services.localization.LocalizationService;
45
46
47 import org.apache.commons.configuration.Configuration;
48 import org.apache.jetspeed.capability.CapabilityMap;
49
50
51 import org.apache.jetspeed.services.resources.JetspeedResources;
52 import org.apache.jetspeed.services.rundata.JetspeedRunData;
53 import org.apache.jetspeed.services.Profiler;
54 import org.apache.jetspeed.services.logging.JetspeedLogFactoryService;
55 import org.apache.jetspeed.services.logging.JetspeedLogger;
56 import org.apache.jetspeed.services.customlocalization.CustomLocalizationService;
57 import org.apache.jetspeed.util.ServiceUtil;
58 /***
59 * <p>Implements all template location related operations.
60 * Template location algorithms are different from the Velocity template location,
61 * since Jetspeed has a specialized template directory structure.
62 * This is a fix to get us through unti the TurbineTemplateService can locate
63 * resources by NLS and mediatype. Then it can be removed</p>
64 *
65 * <p>The directory structure is currently layout out in the following order:
66 * /templateType/mediaType/LanguageCode/CountryCode</p>
67 * <p>Example: /screens/html/en/US/resource.vm</p>
68 *
69 * @author <a href="mailto:taylor@apache.org">David Sean Taylor</a>
70 * @author <a href="mailto:rapahel@apache.org">Raphael Luta</a>
71 * @author <a href="mailto:paulsp@apache.org">Paul Spener</a>
72 * @author <a href="mailto:kimptoc_mail@yahoo.com">Chris Kimpton</a>
73 * @author <a href="mailto:weaver@apache.org">Scott T. Weaver</a>
74 * @version $Id: JetspeedTemplateLocatorService.java,v 1.22 2004/02/23 03:38:54 jford Exp $
75 */
76
77 public class JetspeedTemplateLocatorService
78 extends TurbineBaseService
79 implements TemplateLocatorService
80 {
81 /***
82 * Static initialization of the logger for this class
83 */
84 private static final JetspeedLogger logger = JetspeedLogFactoryService.getLogger(JetspeedTemplateLocatorService.class.getName());
85
86 private final static String CONFIG_TEMPLATE_ROOT = ".templateRoot";
87 private final static String CONFIG_PORTLET_GLOBAL_SEARCH = ".portlet.global.search";
88 private final static String CONFIG_HOT_DEPLOY = ".hot.deploy";
89 private final static String DIR_SCREENS = "/screens";
90 private final static String DIR_LAYOUTS = "/layouts";
91 private final static String DIR_PORTLETS = "/portlets";
92 private final static String DIR_CONTROLS = "/controls";
93 private final static String DIR_CONTROLLERS = "/controllers";
94 private final static String DIR_NAVIGATIONS = "/navigations";
95 private final static String DIR_PARAMETERS = "/parameters";
96 private final static String DIR_EMAILS = "/emails";
97 private static final String PATH_SEPARATOR = "/";
98
99
100 private final static String MSG_MISSING_PARAMETER =
101 "JetspeedTemplateLocatorService initialization failed. Missing parameter:";
102
103
104 private static final String TEMPLATE_EXTENSION = "template.extension";
105 private static final String DEFAULT_LAYOUT = "default.layout.template";
106
107
108 private static VelocityService velocityService;
109
110 private static JspService jspService;
111
112
113 private String[] templateRoots;
114
115
116 private boolean hotDeploy = false;
117
118
119 private Map templateMap = null;
120
121
122 private boolean useGlobalPortletSearch = false;
123
124 /***
125 * This is the early initialization method called by the
126 * Turbine <code>Service</code> framework
127 * @param conf The <code>ServletConfig</code>
128 * @exception throws a <code>InitializationException</code> if the service
129 * fails to initialize
130 */
131 public synchronized void init(ServletConfig conf) throws InitializationException
132 {
133
134 if (getInit())
135 {
136 return;
137 }
138
139 initConfiguration();
140
141
142 setInit(true);
143 }
144
145 public void init() throws InitializationException
146 {
147 logger.info("Late init for JetspeedTemplateLocatorService called");
148 while (!getInit())
149 {
150
151 try
152 {
153 Thread.sleep(100);
154 logger.info("Waiting for init of JetspeedTemplateLocatorService...");
155 }
156 catch (InterruptedException ie)
157 {
158 logger.error("Exception", ie);
159 }
160 }
161 }
162
163 /***
164 * This is the shutdown method called by the
165 * Turbine <code>Service</code> framework
166 */
167 public void shutdown()
168 {
169 }
170
171 /***
172 * Locate a screen template using Jetspeed template location algorithm, searching by
173 * mediatype and language criteria extracted from the request state in rundata.
174 *
175 * @param data The rundata for the request.
176 * @param template The name of the template.
177 *
178 * @return The path relative to the screens directory for the requested screen template,
179 * or null if not found.
180 */
181 public String locateScreenTemplate(RunData data, String template)
182 {
183 List templatePaths = localizeTemplateName(data);
184 Iterator i = templatePaths.iterator();
185 String located = null;
186
187 while (i.hasNext())
188 {
189 String path = (String) i.next();
190 located = locateTemplate(data, DIR_SCREENS, path, template);
191
192 if (null != located)
193 {
194 return located;
195 }
196 }
197
198 if (null == located)
199 {
200
201
202
203 i = templatePaths.iterator();
204 template = "/default." + getTemplateExtension(template);
205 while (i.hasNext())
206 {
207 String path = (String) i.next();
208 located = locateTemplate(data, DIR_SCREENS, path, template);
209
210 if (null != located)
211 {
212 return located;
213 }
214 }
215 }
216
217 return located;
218 }
219
220 /***
221 * Locate a layout template using Jetspeed template location algorithm, searching by
222 * mediatype and language criteria extracted from the request state in rundata.
223 *
224 * @param data The rundata for the request.
225 * @param template The name of the template.
226 *
227 * @return The path relative to the layouts directory for the requested layout template,
228 * or null if not found.
229 */
230 public String locateLayoutTemplate(RunData data, String template)
231 {
232 List templatePaths = localizeTemplateName(data);
233 Iterator i = templatePaths.iterator();
234 String located = null;
235
236 while (i.hasNext())
237 {
238 String path = (String) i.next();
239 located = locateTemplate(data, DIR_LAYOUTS, path, template);
240
241 if (null != located)
242 {
243 return located;
244 }
245 }
246
247 if (null == located)
248 {
249
250
251
252 i = templatePaths.iterator();
253
254
255 template = getTemplateLayout(getTemplateExtension(template));
256 while (i.hasNext())
257 {
258 String path = (String) i.next();
259 located = locateTemplate(data, DIR_LAYOUTS, path, template);
260
261 if (null != located)
262 {
263 return located;
264 }
265 }
266 }
267
268 return located;
269 }
270
271 /***
272 * Locate a controller template using Jetspeed template location algorithm, searching by
273 * mediatype and language criteria extracted from the request state in rundata.
274 *
275 * @param data The rundata for the request.
276 * @param template The name of the template.
277 *
278 * @return The path relative to the controllers directory for the requested controller template,
279 * or null if not found.
280 */
281 public String locateNavigationTemplate(RunData data, String template)
282 {
283 List templatePaths = localizeTemplateName(data);
284 Iterator i = templatePaths.iterator();
285
286 while (i.hasNext())
287 {
288 String path = (String) i.next();
289 String located = locateTemplate(data, DIR_NAVIGATIONS, path, template);
290
291 if (null != located)
292 {
293 return located;
294 }
295 }
296
297 return null;
298 }
299
300 /***
301 * Locate a portlet template using Jetspeed template location algorithm, searching by
302 * mediatype and language criteria extracted from the request state in rundata.
303 *
304 * @param data The rundata for the request.
305 * @param template The name of the template.
306 *
307 * @return The path relative to the portlets directory for the requested portlet template,
308 * or null if not found.
309 */
310 public String locatePortletTemplate(RunData data, String template)
311 {
312 List templatePaths = localizeTemplateName(data);
313 Iterator i = templatePaths.iterator();
314
315 while (i.hasNext())
316 {
317 String path = (String) i.next();
318 String located = locateTemplate(data, DIR_PORTLETS, path, template);
319
320 if (null != located)
321 {
322 return DIR_PORTLETS + located;
323 }
324 }
325
326
327 if (useGlobalPortletSearch == true)
328 {
329 String located = locateScreenTemplate(data, template);
330 if (located != null)
331 {
332 return DIR_SCREENS + located;
333 }
334 }
335
336 return null;
337 }
338
339 /***
340 * Locate a control template using Jetspeed template location algorithm, searching by
341 * mediatype and language criteria extracted from the request state in rundata.
342 *
343 * @param data The rundata for the request.
344 * @param template The name of the template.
345 *
346 * @return The path relative to the controls directory for the requested control template,
347 * or null if not found.
348 */
349 public String locateControlTemplate(RunData data, String template)
350 {
351 List templatePaths = localizeTemplateName(data);
352 Iterator i = templatePaths.iterator();
353
354 while (i.hasNext())
355 {
356 String path = (String) i.next();
357 String located = locateTemplate(data, DIR_CONTROLS, path, template);
358
359 if (null != located)
360 {
361 return DIR_CONTROLS + located;
362 }
363 }
364
365 return null;
366 }
367
368 /***
369 * Locate a controller template using Jetspeed template location algorithm, searching by
370 * mediatype and language criteria extracted from the request state in rundata.
371 *
372 * @param data The rundata for the request.
373 * @param template The name of the template.
374 *
375 * @return The path relative to the controllers directory for the requested controller template,
376 * or null if not found.
377 */
378 public String locateControllerTemplate(RunData data, String template)
379 {
380 List templatePaths = localizeTemplateName(data);
381 Iterator i = templatePaths.iterator();
382
383 while (i.hasNext())
384 {
385 String path = (String) i.next();
386 String located = locateTemplate(data, DIR_CONTROLLERS, path, template);
387
388 if (null != located)
389 {
390 return DIR_CONTROLLERS + located;
391 }
392 }
393
394 return null;
395 }
396
397 /***
398 * Locate an email template using Jetspeed template location algorithm, searching by
399 * mediatype and language criteria extracted from the request state in rundata.
400 *
401 * @param data The rundata for the request.
402 * @param template The name of the template.
403 *
404 * @return The path relative to the emails directory for the requested email template,
405 * or null if not found.
406 */
407 public String locateEmailTemplate(RunData data, String template)
408 {
409 CustomLocalizationService locService = (CustomLocalizationService) ServiceUtil.getServiceByName(
410 LocalizationService.SERVICE_NAME);
411 return locateEmailTemplate(data, template, locService.getLocale(data));
412 }
413
414 /***
415 * Locate an email template using Jetspeed template location algorithm, searching by
416 * mediatype and language.
417 *
418 * @param data The rundata for the request.
419 * @param template The name of the template.
420 * @param locale The name of the language.
421 *
422 * @return The path relative to the emails directory for the requested email template,
423 * or null if not found.
424 */
425 public String locateEmailTemplate(RunData data, String template, Locale locale)
426 {
427 List templatePaths = localizeTemplateName(data, locale);
428 Iterator i = templatePaths.iterator();
429
430 while (i.hasNext())
431 {
432 String path = (String) i.next();
433 String located = locateTemplate(data, DIR_EMAILS, path, template);
434
435 if (null != located)
436 {
437 return DIR_EMAILS + located;
438 }
439 }
440
441 return null;
442 }
443
444 /***
445 * Locate a parameter style template using Jetspeed template location algorithm, searching by
446 * mediatype and language criteria extracted from the request state in rundata.
447 *
448 * @param data The rundata for the request.
449 * @param template The name of the template.
450 *
451 * @return The path relative to the portlets directory for the requested portlet template,
452 * or null if not found.
453 */
454 public String locateParameterTemplate(RunData data, String template)
455 {
456 List templatePaths = localizeTemplateName(data);
457 Iterator i = templatePaths.iterator();
458
459 while (i.hasNext())
460 {
461 String path = (String) i.next();
462 String located = locateTemplate(data, DIR_PARAMETERS, path, template);
463
464 if (null != located)
465 {
466 return DIR_PARAMETERS + located;
467 }
468 }
469
470 return null;
471 }
472
473 /***
474 * General template location algorithm. Starts with the most specific resource,
475 * including mediatype + nls specification, and fallsback to least specific.
476 *
477 * @param data The rundata for the request.
478 * @param resourceType The path specific to the resource type sought (eg /screens).
479 * @param path The fullest path to the template based on simple NLS/mediatype directory.
480 * @param template The name of the template.
481 *
482 * @return the exact path to the template, or null if not found.
483 */
484 private String locateTemplate(RunData data, String resourceType, String path, String template)
485 {
486 String located = null;
487
488
489 for (int i = 0; i < templateRoots.length; i++)
490 {
491 located = locateTemplate(data, resourceType, path, template, templateRoots[i]);
492 if (located != null)
493 {
494 break;
495 }
496 }
497
498 return located;
499 }
500
501 /***
502 * General template location algorithm. Starts with the most specific resource,
503 * including mediatype + nls specification, and fallsback to least specific.
504 *
505 * @param data The rundata for the request.
506 * @param resourceType The path specific to the resource type sought (eg /screens).
507 * @param path The fullest path to the template based on simple NLS/mediatype directory.
508 * @param template The name of the template.
509 * @param templateRoot The template root to be searched.
510 *
511 * @return the exact path to the template, or null if not found.
512 */
513 private String locateTemplate(
514 RunData data,
515 String resourceType,
516 String path,
517 String template,
518 String templateRoot)
519 {
520 String finalPath;
521
522
523 if (resourceType.endsWith(PATH_SEPARATOR))
524 {
525 resourceType = resourceType.substring(0, resourceType.length() - 1);
526 }
527 if (!resourceType.startsWith(PATH_SEPARATOR))
528 {
529 resourceType = PATH_SEPARATOR + resourceType;
530 }
531
532 if (path.endsWith(PATH_SEPARATOR))
533 {
534 path = path.substring(0, path.length() - 1);
535 }
536 if (!path.startsWith(PATH_SEPARATOR))
537 {
538 path = PATH_SEPARATOR + path;
539 }
540
541 if (!template.startsWith(PATH_SEPARATOR))
542 {
543 template = PATH_SEPARATOR + template;
544 }
545
546 StringBuffer fullPath = new StringBuffer(templateRoot);
547
548 if (!templateRoot.endsWith(PATH_SEPARATOR))
549 {
550 fullPath.append(PATH_SEPARATOR);
551 }
552
553 fullPath.append(getTemplateExtension(template));
554 fullPath.append(resourceType);
555
556 String basePath = fullPath.toString();
557 String realPath = null;
558 String workingPath = null;
559
560 do
561 {
562 workingPath = path + template;
563 realPath = TurbineServlet.getRealPath(basePath + workingPath);
564
565
566 if (templateExists(realPath, true))
567 {
568 if (logger.isDebugEnabled())
569 {
570 logger.debug(
571 "TemplateLocator: template exists in cache: "
572 + realPath
573 + " returning "
574 + workingPath);
575 }
576
577 return workingPath;
578 }
579 else if (this.hotDeploy == true)
580 {
581
582 if (templateExists(realPath, false))
583 {
584 if (logger.isDebugEnabled())
585 {
586 logger.debug(
587 "TemplateLocator: template exists on the file system: "
588 + realPath
589 + " returning "
590 + workingPath);
591 }
592
593
594
595 templateMap.put(realPath, null);
596
597 return workingPath;
598 }
599 }
600
601 int pt = path.lastIndexOf(PATH_SEPARATOR);
602 if (pt > -1)
603 {
604 path = path.substring(0, pt);
605 }
606 else
607 {
608 path = null;
609 }
610 }
611 while (path != null);
612
613 return null;
614 }
615
616 /***
617 * Helper function for template locator to find a localized (NLS) resource.
618 * Considers both language and country resources as well as all the possible
619 * media-types for the request
620 *
621 * @param data The rundata for the request.
622 *
623 * @return The possible paths to a localized template ordered by
624 * descending preference
625 */
626 private List localizeTemplateName(RunData data)
627 {
628 return localizeTemplateName(data, null);
629 }
630
631 /***
632 * Helper function for template locator to find a localized (NLS) resource.
633 * Considers both language and country resources as well as all the possible
634 * media-types for the request
635 *
636 * @param data The rundata for the request.
637 * @param locale The locale for the request.
638 *
639 * @return The possible paths to a localized template ordered by
640 * descending preference
641 */
642 private List localizeTemplateName(RunData data, Locale inLocale)
643 {
644 List templates = new ArrayList();
645 Locale tmplocale = null;
646
647 if (inLocale != null)
648 {
649 tmplocale = inLocale;
650 }
651 else
652 {
653 CustomLocalizationService locService = (CustomLocalizationService) ServiceUtil.getServiceByName(
654 LocalizationService.SERVICE_NAME);
655 tmplocale = locService.getLocale(data);
656 }
657
658
659 if (tmplocale == null)
660 {
661 tmplocale =
662 new Locale(
663 TurbineResources.getString("locale.default.language", "en"),
664 TurbineResources.getString("locale.default.country", "US"));
665 }
666
667 data.getUser().setTemp("locale", tmplocale);
668
669 StringBuffer templatePath = new StringBuffer();
670
671
672 String type = data.getParameters().getString(Profiler.PARAM_MEDIA_TYPE, null);
673 List types = new ArrayList();
674 CapabilityMap cm = ((JetspeedRunData) data).getCapability();
675
676
677 Locale locale = (Locale) data.getUser().getTemp("locale");
678 String language = locale.getLanguage();
679 String country = locale.getCountry();
680
681 if (null != type)
682 {
683 types.add(type);
684 }
685 else
686 {
687 Iterator i = cm.listMediaTypes();
688 while (i.hasNext())
689 {
690 types.add(i.next());
691 }
692 }
693
694 Iterator typeIterator = types.iterator();
695
696 while (typeIterator.hasNext())
697 {
698 type = (String) typeIterator.next();
699
700 if ((type != null) && (type.length() > 0))
701 {
702 templatePath.append(PATH_SEPARATOR).append(type);
703 }
704
705 if ((language != null) && (language.length() > 0))
706 {
707 templatePath.append(PATH_SEPARATOR).append(language);
708 }
709
710 if ((country != null) && (country.length() > 0))
711 {
712 templatePath.append(PATH_SEPARATOR).append(country);
713 }
714
715 templates.add(templatePath.toString());
716 templatePath.setLength(0);
717 }
718
719 return templates;
720 }
721
722 /***
723 * Returns the extension for the specified template
724 *
725 * @param template the template name to scan for an extension
726 * @return the template extension if it exists or the default
727 * template extension
728 */
729 private String getTemplateExtension(String template)
730 {
731 String ext = TurbineTemplate.getDefaultExtension();
732
733 int idx = template.lastIndexOf(".");
734
735 if (idx > 0)
736 {
737 ext = template.substring(idx + 1);
738 }
739
740 return ext;
741 }
742
743 /***
744 * Checks for the existence of a template resource given a key.
745 * The key are absolute paths to the templates, and are cached
746 * in a template cache for performance.
747 *
748 * @param key The absolute path to the template resource.
749 *
750 * @return True when the template is found, otherwise false.
751 */
752 public boolean templateExists(String templateKey, boolean useNameCache)
753 {
754 if (null == templateKey)
755 {
756 return false;
757 }
758
759 if (useNameCache == true)
760 {
761 return templateMap.containsKey(templateKey);
762 }
763
764 return (new File(templateKey).exists());
765 }
766
767 /***
768 * Loads the configuration parameters for this service from the
769 * JetspeedResources.properties file.
770 *
771 * @exception throws a <code>InitializationException</code> if the service
772 * fails to initialize
773 */
774 private void initConfiguration() throws InitializationException
775 {
776
777 templateRoots =
778 JetspeedResources.getStringArray(
779 TurbineServices.SERVICE_PREFIX
780 + TemplateLocatorService.SERVICE_NAME
781 + CONFIG_TEMPLATE_ROOT);
782
783 if ((templateRoots == null) || (templateRoots.length == 0))
784 {
785 throw new InitializationException(MSG_MISSING_PARAMETER + CONFIG_TEMPLATE_ROOT);
786 }
787
788 templateMap = new HashMap();
789
790 for (int i = 0; i < templateRoots.length; i++)
791 {
792 String templateRoot = templateRoots[i];
793
794 if (!templateRoot.endsWith(PATH_SEPARATOR))
795 {
796 templateRoot = templateRoot + PATH_SEPARATOR;
797 }
798
799 if (logger.isDebugEnabled())
800 {
801 logger.debug("TemplateLocator: Adding templateRoot:" + templateRoot);
802 }
803
804
805 String templateRootPath = TurbineServlet.getRealPath(templateRoot);
806 if (null != templateRootPath)
807 {
808 loadNameCache(templateRootPath, "");
809 }
810 }
811
812
813 velocityService =
814 (VelocityService) TurbineServices.getInstance().getService(
815 VelocityService.SERVICE_NAME);
816
817 jspService = (JspService) TurbineServices.getInstance().getService(JspService.SERVICE_NAME);
818
819 useGlobalPortletSearch = JetspeedResources.getBoolean(
820 TurbineServices.SERVICE_PREFIX
821 + TemplateLocatorService.SERVICE_NAME
822 + CONFIG_PORTLET_GLOBAL_SEARCH, false);
823
824 hotDeploy = JetspeedResources.getBoolean(
825 TurbineServices.SERVICE_PREFIX
826 + TemplateLocatorService.SERVICE_NAME
827 + CONFIG_HOT_DEPLOY, true);
828
829 }
830
831 /***
832 * Loads the template name cache map to accelerate template searches.
833 *
834 * @param path The template
835 * @param name just the name of the resource
836 */
837 private void loadNameCache(String path, String name)
838 {
839 File file = new File(path);
840 if (file.isFile())
841 {
842
843 templateMap.put(path, null);
844 }
845 else
846 {
847 if (file.isDirectory())
848 {
849 if (!path.endsWith(File.separator))
850 {
851 path += File.separator;
852 }
853
854 String list[] = file.list();
855
856
857 for (int ix = 0; list != null && ix < list.length; ix++)
858 {
859 loadNameCache(path + list[ix], list[ix]);
860 }
861 }
862 }
863 }
864
865 /***
866 * Correctly locate the default layout based on the
867 * default.layout.template property of the appropriate
868 * template service.
869 * @author <a href="mailto:sweaver@rippe.com">Scott Weaver</a>
870 */
871 private String getTemplateLayout(String extension)
872 {
873 String dftLayout = "/default." + extension;
874
875 Configuration velocityCfg = null;
876 Configuration jspCfg = null;
877 if (velocityService != null)
878 {
879 velocityCfg = velocityService.getConfiguration();
880 }
881
882 if (jspService != null)
883 {
884 jspCfg = jspService.getConfiguration();
885 }
886
887 if (velocityCfg != null
888 && velocityCfg.getString(TEMPLATE_EXTENSION).indexOf(extension) > -1)
889 {
890 return velocityCfg.getString(DEFAULT_LAYOUT, dftLayout);
891 }
892 else if (jspCfg != null && jspCfg.getString(TEMPLATE_EXTENSION).indexOf(extension) > -1)
893 {
894 return jspCfg.getString(DEFAULT_LAYOUT, dftLayout);
895 }
896 else
897 {
898 return dftLayout;
899 }
900 }
901 }