1/*2 * Copyright 2000-2001,2004 The Apache Software Foundation.3 * 4 * Licensed under the Apache License, Version 2.0 (the "License");5 * you may not use this file except in compliance with the License.6 * You may obtain a copy of the License at7 * 8 * http://www.apache.org/licenses/LICENSE-2.09 * 10 * Unless required by applicable law or agreed to in writing, software11 * distributed under the License is distributed on an "AS IS" BASIS,12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.13 * See the License for the specific language governing permissions and14 * limitations under the License.15 */1617packageorg.apache.jetspeed.services.template;
1819// java.io20import java.io.File;
2122// java.util23import java.util.Iterator;
24import java.util.List;
25import java.util.ArrayList;
26import java.util.Map;
27import java.util.HashMap;
28import java.util.Locale;
2930import javax.servlet.ServletConfig;
3132// turbine.util33import org.apache.turbine.util.RunData;
3435// turbine.services36import org.apache.turbine.services.TurbineBaseService;
37import org.apache.turbine.services.TurbineServices;
38import org.apache.turbine.services.InitializationException;
39import org.apache.turbine.services.servlet.TurbineServlet;
40import org.apache.turbine.services.jsp.JspService;
41import org.apache.turbine.services.resources.TurbineResources;
42import org.apache.turbine.services.template.TurbineTemplate;
43import org.apache.turbine.services.velocity.VelocityService;
44import org.apache.turbine.services.localization.LocalizationService;
4546// jetspeed.capability47import org.apache.commons.configuration.Configuration;
48import org.apache.jetspeed.capability.CapabilityMap;
4950// jetspeed.services51import org.apache.jetspeed.services.resources.JetspeedResources;
52import org.apache.jetspeed.services.rundata.JetspeedRunData;
53import org.apache.jetspeed.services.Profiler;
54import org.apache.jetspeed.services.logging.JetspeedLogFactoryService;
55import org.apache.jetspeed.services.logging.JetspeedLogger;
56import org.apache.jetspeed.services.customlocalization.CustomLocalizationService;
57import 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 locate63 * 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 */7677publicclassJetspeedTemplateLocatorService78extends TurbineBaseService
79 implements TemplateLocatorService80 {
81/***82 * Static initialization of the logger for this class83 */84privatestaticfinalJetspeedLogger logger = JetspeedLogFactoryService.getLogger(JetspeedTemplateLocatorService.class.getName());
8586privatefinalstatic String CONFIG_TEMPLATE_ROOT = ".templateRoot";
87privatefinalstatic String CONFIG_PORTLET_GLOBAL_SEARCH = ".portlet.global.search";
88privatefinalstatic String CONFIG_HOT_DEPLOY = ".hot.deploy";
89privatefinalstatic String DIR_SCREENS = "/screens";
90privatefinalstatic String DIR_LAYOUTS = "/layouts";
91privatefinalstatic String DIR_PORTLETS = "/portlets";
92privatefinalstatic String DIR_CONTROLS = "/controls";
93privatefinalstatic String DIR_CONTROLLERS = "/controllers";
94privatefinalstatic String DIR_NAVIGATIONS = "/navigations";
95privatefinalstatic String DIR_PARAMETERS = "/parameters";
96privatefinalstatic String DIR_EMAILS = "/emails";
97privatestaticfinal String PATH_SEPARATOR = "/";
9899// messages100privatefinalstatic String MSG_MISSING_PARAMETER =
101"JetspeedTemplateLocatorService initialization failed. Missing parameter:";
102103// Template Service Constants104privatestaticfinal String TEMPLATE_EXTENSION = "template.extension";
105privatestaticfinal String DEFAULT_LAYOUT = "default.layout.template";
106107// Template services108privatestatic VelocityService velocityService;
109110privatestatic JspService jspService;
111112// the template root directories, webapp relative113private String[] templateRoots;
114115// check the file system if template not found in name cache116privateboolean hotDeploy = false;
117118// template name cache119private Map templateMap = null;
120121// include screens when searching for portlet template122privateboolean useGlobalPortletSearch = false;
123124/***125 * This is the early initialization method called by the126 * Turbine <code>Service</code> framework127 * @param conf The <code>ServletConfig</code>128 * @exception throws a <code>InitializationException</code> if the service129 * fails to initialize130 */131publicsynchronizedvoid init(ServletConfig conf) throws InitializationException
132 {
133// already initialized134if (getInit())
135 {
136return;
137 }
138139 initConfiguration();
140141// initialization done142 setInit(true);
143 }
144145publicvoid init() throws InitializationException
146 {
147 logger.info("Late init for JetspeedTemplateLocatorService called");
148while (!getInit())
149 {
150//Not yet...151try152 {
153 Thread.sleep(100);
154 logger.info("Waiting for init of JetspeedTemplateLocatorService...");
155 }
156catch (InterruptedException ie)
157 {
158 logger.error("Exception", ie);
159 }
160 }
161 }
162163/***164 * This is the shutdown method called by the165 * Turbine <code>Service</code> framework166 */167publicvoid shutdown()
168 {
169 }
170171/***172 * Locate a screen template using Jetspeed template location algorithm, searching by173 * 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 */181public String locateScreenTemplate(RunData data, String template)
182 {
183 List templatePaths = localizeTemplateName(data);
184 Iterator i = templatePaths.iterator();
185 String located = null;
186187while (i.hasNext())
188 {
189 String path = (String) i.next();
190 located = locateTemplate(data, DIR_SCREENS, path, template);
191192if (null != located)
193 {
194return located;
195 }
196 }
197198if (null == located)
199 {
200//we have not found the requested sreen but still need to return201//something, search for the default screen202203 i = templatePaths.iterator();
204 template = "/default." + getTemplateExtension(template);
205while (i.hasNext())
206 {
207 String path = (String) i.next();
208 located = locateTemplate(data, DIR_SCREENS, path, template);
209210if (null != located)
211 {
212return located;
213 }
214 }
215 }
216217return located;
218 }
219220/***221 * Locate a layout template using Jetspeed template location algorithm, searching by222 * 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 */230public String locateLayoutTemplate(RunData data, String template)
231 {
232 List templatePaths = localizeTemplateName(data);
233 Iterator i = templatePaths.iterator();
234 String located = null;
235236while (i.hasNext())
237 {
238 String path = (String) i.next();
239 located = locateTemplate(data, DIR_LAYOUTS, path, template);
240241if (null != located)
242 {
243return located;
244 }
245 }
246247if (null == located)
248 {
249//we have not found the requested layout but still need to return250//something, search for the default layout251252 i = templatePaths.iterator();
253254//template = "/default." + getTemplateExtension(template);255 template = getTemplateLayout(getTemplateExtension(template));
256while (i.hasNext())
257 {
258 String path = (String) i.next();
259 located = locateTemplate(data, DIR_LAYOUTS, path, template);
260261if (null != located)
262 {
263return located;
264 }
265 }
266 }
267268return located;
269 }
270271/***272 * Locate a controller template using Jetspeed template location algorithm, searching by273 * 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 */281public String locateNavigationTemplate(RunData data, String template)
282 {
283 List templatePaths = localizeTemplateName(data);
284 Iterator i = templatePaths.iterator();
285286while (i.hasNext())
287 {
288 String path = (String) i.next();
289 String located = locateTemplate(data, DIR_NAVIGATIONS, path, template);
290291if (null != located)
292 {
293return located;
294 }
295 }
296297returnnull;
298 }
299300/***301 * Locate a portlet template using Jetspeed template location algorithm, searching by302 * 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 */310public String locatePortletTemplate(RunData data, String template)
311 {
312 List templatePaths = localizeTemplateName(data);
313 Iterator i = templatePaths.iterator();
314315while (i.hasNext())
316 {
317 String path = (String) i.next();
318 String located = locateTemplate(data, DIR_PORTLETS, path, template);
319320if (null != located)
321 {
322return DIR_PORTLETS + located;
323 }
324 }
325326// Use "wide" search when required327if (useGlobalPortletSearch == true)
328 {
329 String located = locateScreenTemplate(data, template);
330if (located != null)
331 {
332return DIR_SCREENS + located;
333 }
334 }
335336returnnull;
337 }
338339/***340 * Locate a control template using Jetspeed template location algorithm, searching by341 * 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 */349public String locateControlTemplate(RunData data, String template)
350 {
351 List templatePaths = localizeTemplateName(data);
352 Iterator i = templatePaths.iterator();
353354while (i.hasNext())
355 {
356 String path = (String) i.next();
357 String located = locateTemplate(data, DIR_CONTROLS, path, template);
358359if (null != located)
360 {
361return DIR_CONTROLS + located;
362 }
363 }
364365returnnull;
366 }
367368/***369 * Locate a controller template using Jetspeed template location algorithm, searching by370 * 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 */378public String locateControllerTemplate(RunData data, String template)
379 {
380 List templatePaths = localizeTemplateName(data);
381 Iterator i = templatePaths.iterator();
382383while (i.hasNext())
384 {
385 String path = (String) i.next();
386 String located = locateTemplate(data, DIR_CONTROLLERS, path, template);
387388if (null != located)
389 {
390return DIR_CONTROLLERS + located;
391 }
392 }
393394returnnull;
395 }
396397/***398 * Locate an email template using Jetspeed template location algorithm, searching by399 * 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 */407public String locateEmailTemplate(RunData data, String template)
408 {
409CustomLocalizationService locService = (CustomLocalizationService) ServiceUtil.getServiceByName(
410 LocalizationService.SERVICE_NAME);
411return locateEmailTemplate(data, template, locService.getLocale(data));
412 }
413414/***415 * Locate an email template using Jetspeed template location algorithm, searching by416 * 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 */425public String locateEmailTemplate(RunData data, String template, Locale locale)
426 {
427 List templatePaths = localizeTemplateName(data, locale);
428 Iterator i = templatePaths.iterator();
429430while (i.hasNext())
431 {
432 String path = (String) i.next();
433 String located = locateTemplate(data, DIR_EMAILS, path, template);
434435if (null != located)
436 {
437return DIR_EMAILS + located;
438 }
439 }
440441returnnull;
442 }
443444/***445 * Locate a parameter style template using Jetspeed template location algorithm, searching by446 * 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 */454public String locateParameterTemplate(RunData data, String template)
455 {
456 List templatePaths = localizeTemplateName(data);
457 Iterator i = templatePaths.iterator();
458459while (i.hasNext())
460 {
461 String path = (String) i.next();
462 String located = locateTemplate(data, DIR_PARAMETERS, path, template);
463464if (null != located)
465 {
466return DIR_PARAMETERS + located;
467 }
468 }
469470returnnull;
471 }
472473/***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 */484private String locateTemplate(RunData data, String resourceType, String path, String template)
485 {
486 String located = null;
487488// Iterate through each of the template roots489for (int i = 0; i < templateRoots.length; i++)
490 {
491 located = locateTemplate(data, resourceType, path, template, templateRoots[i]);
492if (located != null)
493 {
494break;
495 }
496 }
497498return located;
499 }
500501/***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 */513private String locateTemplate(
514 RunData data,
515 String resourceType,
516 String path,
517 String template,
518 String templateRoot)
519 {
520 String finalPath;
521522// make sure resourceType doesn't end with "/" but starts with "/"523if (resourceType.endsWith(PATH_SEPARATOR))
524 {
525 resourceType = resourceType.substring(0, resourceType.length() - 1);
526 }
527if (!resourceType.startsWith(PATH_SEPARATOR))
528 {
529 resourceType = PATH_SEPARATOR + resourceType;
530 }
531// make sure path doesn't end with "/" but starts with "/"532if (path.endsWith(PATH_SEPARATOR))
533 {
534 path = path.substring(0, path.length() - 1);
535 }
536if (!path.startsWith(PATH_SEPARATOR))
537 {
538 path = PATH_SEPARATOR + path;
539 }
540// make sure template starts with "/"541if (!template.startsWith(PATH_SEPARATOR))
542 {
543 template = PATH_SEPARATOR + template;
544 }
545546 StringBuffer fullPath = new StringBuffer(templateRoot);
547548if (!templateRoot.endsWith(PATH_SEPARATOR))
549 {
550 fullPath.append(PATH_SEPARATOR);
551 }
552553 fullPath.append(getTemplateExtension(template));
554 fullPath.append(resourceType);
555556 String basePath = fullPath.toString();
557 String realPath = null;
558 String workingPath = null;
559560do561 {
562 workingPath = path + template;
563 realPath = TurbineServlet.getRealPath(basePath + workingPath);
564565// the current template exists in cache, return the corresponding path566if (templateExists(realPath, true))
567 {
568if (logger.isDebugEnabled())
569 {
570 logger.debug(
571"TemplateLocator: template exists in cache: "572 + realPath
573 + " returning "574 + workingPath);
575 }
576577return workingPath;
578 }
579elseif (this.hotDeploy == true)
580 {
581// Try to locate it directly on file system, perhaps it was recently added582if (templateExists(realPath, false))
583 {
584if (logger.isDebugEnabled())
585 {
586 logger.debug(
587"TemplateLocator: template exists on the file system: "588 + realPath
589 + " returning "590 + workingPath);
591 }
592593// add it to the map594//templateMap.put(workingPath, null);595 templateMap.put(realPath, null);
596597return workingPath;
598 }
599 }
600// else strip path of one of its components and loop601int pt = path.lastIndexOf(PATH_SEPARATOR);
602if (pt > -1)
603 {
604 path = path.substring(0, pt);
605 }
606else607 {
608 path = null;
609 }
610 }
611while (path != null);
612613returnnull;
614 }
615616/***617 * Helper function for template locator to find a localized (NLS) resource.618 * Considers both language and country resources as well as all the possible619 * media-types for the request620 *621 * @param data The rundata for the request.622 *623 * @return The possible paths to a localized template ordered by624 * descending preference625 */626private List localizeTemplateName(RunData data)
627 {
628return localizeTemplateName(data, null);
629 }
630631/***632 * Helper function for template locator to find a localized (NLS) resource.633 * Considers both language and country resources as well as all the possible634 * media-types for the request635 *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 by640 * descending preference641 */642private List localizeTemplateName(RunData data, Locale inLocale)
643 {
644 List templates = new ArrayList();
645 Locale tmplocale = null;
646647if (inLocale != null)
648 {
649 tmplocale = inLocale;
650 }
651else652 {
653CustomLocalizationService locService = (CustomLocalizationService) ServiceUtil.getServiceByName(
654 LocalizationService.SERVICE_NAME);
655 tmplocale = locService.getLocale(data);
656 }
657658// Get the locale store it in the user object659if (tmplocale == null)
660 {
661 tmplocale =
662new Locale(
663 TurbineResources.getString("locale.default.language", "en"),
664 TurbineResources.getString("locale.default.country", "US"));
665 }
666667 data.getUser().setTemp("locale", tmplocale);
668669 StringBuffer templatePath = new StringBuffer();
670671// retrieve all the possible media types672 String type = data.getParameters().getString(Profiler.PARAM_MEDIA_TYPE, null);
673 List types = new ArrayList();
674CapabilityMap cm = ((JetspeedRunData) data).getCapability();
675676// Grab the Locale from the temporary storage in the User object677 Locale locale = (Locale) data.getUser().getTemp("locale");
678 String language = locale.getLanguage();
679 String country = locale.getCountry();
680681if (null != type)
682 {
683 types.add(type);
684 }
685else686 {
687 Iterator i = cm.listMediaTypes();
688while (i.hasNext())
689 {
690 types.add(i.next());
691 }
692 }
693694 Iterator typeIterator = types.iterator();
695696while (typeIterator.hasNext())
697 {
698 type = (String) typeIterator.next();
699700if ((type != null) && (type.length() > 0))
701 {
702 templatePath.append(PATH_SEPARATOR).append(type);
703 }
704705if ((language != null) && (language.length() > 0))
706 {
707 templatePath.append(PATH_SEPARATOR).append(language);
708 }
709710if ((country != null) && (country.length() > 0))
711 {
712 templatePath.append(PATH_SEPARATOR).append(country);
713 }
714715 templates.add(templatePath.toString());
716 templatePath.setLength(0);
717 }
718719return templates;
720 }
721722/***723 * Returns the extension for the specified template724 *725 * @param template the template name to scan for an extension726 * @return the template extension if it exists or the default727 * template extension728 */729private String getTemplateExtension(String template)
730 {
731 String ext = TurbineTemplate.getDefaultExtension();
732733int idx = template.lastIndexOf(".");
734735if (idx > 0)
736 {
737 ext = template.substring(idx + 1);
738 }
739740return ext;
741 }
742743/***744 * Checks for the existence of a template resource given a key.745 * The key are absolute paths to the templates, and are cached746 * 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 */752publicboolean templateExists(String templateKey, boolean useNameCache)
753 {
754if (null == templateKey)
755 {
756return false;
757 }
758759if (useNameCache == true)
760 {
761return templateMap.containsKey(templateKey);
762 }
763764return (new File(templateKey).exists());
765 }
766767/***768 * Loads the configuration parameters for this service from the769 * JetspeedResources.properties file.770 *771 * @exception throws a <code>InitializationException</code> if the service772 * fails to initialize773 */774privatevoid initConfiguration() throws InitializationException
775 {
776777 templateRoots =
778 JetspeedResources.getStringArray(
779 TurbineServices.SERVICE_PREFIX
780 + TemplateLocatorService.SERVICE_NAME
781 + CONFIG_TEMPLATE_ROOT);
782783if ((templateRoots == null) || (templateRoots.length == 0))
784 {
785thrownew InitializationException(MSG_MISSING_PARAMETER + CONFIG_TEMPLATE_ROOT);
786 }
787788 templateMap = new HashMap();
789790for (int i = 0; i < templateRoots.length; i++)
791 {
792 String templateRoot = templateRoots[i];
793794if (!templateRoot.endsWith(PATH_SEPARATOR))
795 {
796 templateRoot = templateRoot + PATH_SEPARATOR;
797 }
798799if (logger.isDebugEnabled())
800 {
801 logger.debug("TemplateLocator: Adding templateRoot:" + templateRoot);
802 }
803804// traverse starting from the root template directory and add resources805 String templateRootPath = TurbineServlet.getRealPath(templateRoot);
806if (null != templateRootPath)
807 {
808 loadNameCache(templateRootPath, "");
809 }
810 }
811812813 velocityService =
814 (VelocityService) TurbineServices.getInstance().getService(
815 VelocityService.SERVICE_NAME);
816817 jspService = (JspService) TurbineServices.getInstance().getService(JspService.SERVICE_NAME);
818819 useGlobalPortletSearch = JetspeedResources.getBoolean(
820 TurbineServices.SERVICE_PREFIX
821 + TemplateLocatorService.SERVICE_NAME
822 + CONFIG_PORTLET_GLOBAL_SEARCH, false);
823824 hotDeploy = JetspeedResources.getBoolean(
825 TurbineServices.SERVICE_PREFIX
826 + TemplateLocatorService.SERVICE_NAME
827 + CONFIG_HOT_DEPLOY, true);
828829 }
830831/***832 * Loads the template name cache map to accelerate template searches.833 *834 * @param path The template835 * @param name just the name of the resource836 */837privatevoid loadNameCache(String path, String name)
838 {
839 File file = new File(path);
840if (file.isFile())
841 {
842// add it to the map843 templateMap.put(path, null);
844 }
845else846 {
847if (file.isDirectory())
848 {
849if (!path.endsWith(File.separator))
850 {
851 path += File.separator;
852 }
853854 String list[] = file.list();
855856// Process all files recursivly857for (int ix = 0; list != null && ix < list.length; ix++)
858 {
859 loadNameCache(path + list[ix], list[ix]);
860 }
861 }
862 }
863 }
864865/***866 * Correctly locate the default layout based on the 867 * default.layout.template property of the appropriate868 * template service.869 * @author <a href="mailto:sweaver@rippe.com">Scott Weaver</a>870 */871private String getTemplateLayout(String extension)
872 {
873 String dftLayout = "/default." + extension;
874875 Configuration velocityCfg = null;
876 Configuration jspCfg = null;
877if (velocityService != null)
878 {
879 velocityCfg = velocityService.getConfiguration();
880 }
881882if (jspService != null)
883 {
884 jspCfg = jspService.getConfiguration();
885 }
886887if (velocityCfg != null888 && velocityCfg.getString(TEMPLATE_EXTENSION).indexOf(extension) > -1)
889 {
890return velocityCfg.getString(DEFAULT_LAYOUT, dftLayout);
891 }
892elseif (jspCfg != null && jspCfg.getString(TEMPLATE_EXTENSION).indexOf(extension) > -1)
893 {
894return jspCfg.getString(DEFAULT_LAYOUT, dftLayout);
895 }
896else897 {
898return dftLayout;
899 }
900 }
901 }