org.apache.jetspeed.services.template.JetspeedTemplateLocatorService.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.jetspeed.services.template.JetspeedTemplateLocatorService.java

Source

/*
 * Copyright 2000-2001,2004 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.jetspeed.services.template;

// java.io
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import javax.servlet.ServletConfig;

import org.apache.commons.configuration.Configuration;
import org.apache.jetspeed.capability.CapabilityMap;
import org.apache.jetspeed.services.Profiler;
import org.apache.jetspeed.services.customlocalization.CustomLocalizationService;
import org.apache.jetspeed.services.logging.JetspeedLogFactoryService;
import org.apache.jetspeed.services.logging.JetspeedLogger;
import org.apache.jetspeed.services.resources.JetspeedResources;
import org.apache.jetspeed.services.rundata.JetspeedRunData;
import org.apache.jetspeed.util.ServiceUtil;
import org.apache.turbine.services.InitializationException;
import org.apache.turbine.services.TurbineBaseService;
import org.apache.turbine.services.TurbineServices;
import org.apache.turbine.services.jsp.JspService;
import org.apache.turbine.services.localization.LocalizationService;
import org.apache.turbine.services.resources.TurbineResources;
import org.apache.turbine.services.servlet.TurbineServlet;
import org.apache.turbine.services.template.TurbineTemplate;
import org.apache.turbine.services.velocity.VelocityService;
import org.apache.turbine.util.RunData;

/**
 * <p>
 * Implements all template location related operations. Template location
 * algorithms are different from the Velocity template location, since Jetspeed
 * has a specialized template directory structure. This is a fix to get us
 * through unti the TurbineTemplateService can locate resources by NLS and
 * mediatype. Then it can be removed
 * </p>
 * 
 * <p>
 * The directory structure is currently layout out in the following order:
 * /templateType/mediaType/LanguageCode/CountryCode
 * </p>
 * <p>
 * Example: /screens/html/en/US/resource.vm
 * </p>
 * 
 * @author <a href="mailto:taylor@apache.org">David Sean Taylor</a>
 * @author <a href="mailto:rapahel@apache.org">Raphael Luta</a>
 * @author <a href="mailto:paulsp@apache.org">Paul Spener</a>
 * @author <a href="mailto:kimptoc_mail@yahoo.com">Chris Kimpton</a>
 * @author <a href="mailto:weaver@apache.org">Scott T. Weaver</a>
 * @version $Id: JetspeedTemplateLocatorService.java,v 1.22 2004/02/23 03:38:54
 *          jford Exp $
 */

public class JetspeedTemplateLocatorService extends TurbineBaseService implements TemplateLocatorService {
    /**
     * Static initialization of the logger for this class
     */
    private static final JetspeedLogger logger = JetspeedLogFactoryService
            .getLogger(JetspeedTemplateLocatorService.class.getName());

    private final static String CONFIG_TEMPLATE_ROOT = ".templateRoot";

    private final static String CONFIG_PORTLET_GLOBAL_SEARCH = ".portlet.global.search";

    private final static String CONFIG_HOT_DEPLOY = ".hot.deploy";

    private final static String DIR_SCREENS = "/screens";

    private final static String DIR_LAYOUTS = "/layouts";

    private final static String DIR_PORTLETS = "/portlets";

    private final static String DIR_CONTROLS = "/controls";

    private final static String DIR_CONTROLLERS = "/controllers";

    private final static String DIR_NAVIGATIONS = "/navigations";

    private final static String DIR_PARAMETERS = "/parameters";

    private final static String DIR_EMAILS = "/emails";

    private static final String PATH_SEPARATOR = "/";

    // messages
    private final static String MSG_MISSING_PARAMETER = "JetspeedTemplateLocatorService initialization failed. Missing parameter:";

    // Template Service Constants
    private static final String TEMPLATE_EXTENSION = "template.extension";

    private static final String DEFAULT_LAYOUT = "default.layout.template";

    // Template services
    private static VelocityService velocityService;

    private static JspService jspService;

    // the template root directories, webapp relative
    private String[] templateRoots;

    // check the file system if template not found in name cache
    private boolean hotDeploy = false;

    // template name cache
    private Map templateMap = null;

    // include screens when searching for portlet template
    private boolean useGlobalPortletSearch = false;

    /**
     * This is the early initialization method called by the Turbine
     * <code>Service</code> framework
     * 
     * @param conf
     *          The <code>ServletConfig</code>
     * @exception throws a <code>InitializationException</code> if the service
     *            fails to initialize
     */
    @Override
    public synchronized void init(ServletConfig conf) throws InitializationException {
        // already initialized
        if (getInit()) {
            return;
        }

        initConfiguration();

        // initialization done
        setInit(true);
    }

    @Override
    public void init() throws InitializationException {
        logger.info("Late init for JetspeedTemplateLocatorService called");
        while (!getInit()) {
            // Not yet...
            try {
                Thread.sleep(100);
                logger.info("Waiting for init of JetspeedTemplateLocatorService...");
            } catch (InterruptedException ie) {
                logger.error("Exception", ie);
            }
        }
    }

    /**
     * This is the shutdown method called by the Turbine <code>Service</code>
     * framework
     */
    @Override
    public void shutdown() {
    }

    /**
     * Locate a screen template using Jetspeed template location algorithm,
     * searching by mediatype and language criteria extracted from the request
     * state in rundata.
     * 
     * @param data
     *          The rundata for the request.
     * @param template
     *          The name of the template.
     * 
     * @return The path relative to the screens directory for the requested screen
     *         template, or null if not found.
     */
    @Override
    public String locateScreenTemplate(RunData data, String template) {
        List templatePaths = localizeTemplateName(data);
        Iterator i = templatePaths.iterator();
        String located = null;

        while (i.hasNext()) {
            String path = (String) i.next();
            located = locateTemplate(data, DIR_SCREENS, path, template);

            if (null != located) {
                return located;
            }
        }

        if (null == located) {
            // we have not found the requested sreen but still need to return
            // something, search for the default screen

            i = templatePaths.iterator();
            template = "/default." + getTemplateExtension(template);
            while (i.hasNext()) {
                String path = (String) i.next();
                located = locateTemplate(data, DIR_SCREENS, path, template);

                if (null != located) {
                    return located;
                }
            }
        }

        return located;
    }

    /**
     * Locate a layout template using Jetspeed template location algorithm,
     * searching by mediatype and language criteria extracted from the request
     * state in rundata.
     * 
     * @param data
     *          The rundata for the request.
     * @param template
     *          The name of the template.
     * 
     * @return The path relative to the layouts directory for the requested layout
     *         template, or null if not found.
     */
    @Override
    public String locateLayoutTemplate(RunData data, String template) {
        List templatePaths = localizeTemplateName(data);
        Iterator i = templatePaths.iterator();
        String located = null;

        while (i.hasNext()) {
            String path = (String) i.next();
            located = locateTemplate(data, DIR_LAYOUTS, path, template);

            if (null != located) {
                return located;
            }
        }

        if (null == located) {
            // we have not found the requested layout but still need to return
            // something, search for the default layout

            i = templatePaths.iterator();

            // template = "/default." + getTemplateExtension(template);
            template = getTemplateLayout(getTemplateExtension(template));
            while (i.hasNext()) {
                String path = (String) i.next();
                located = locateTemplate(data, DIR_LAYOUTS, path, template);

                if (null != located) {
                    return located;
                }
            }
        }

        return located;
    }

    /**
     * Locate a controller template using Jetspeed template location algorithm,
     * searching by mediatype and language criteria extracted from the request
     * state in rundata.
     * 
     * @param data
     *          The rundata for the request.
     * @param template
     *          The name of the template.
     * 
     * @return The path relative to the controllers directory for the requested
     *         controller template, or null if not found.
     */
    @Override
    public String locateNavigationTemplate(RunData data, String template) {
        List templatePaths = localizeTemplateName(data);
        Iterator i = templatePaths.iterator();

        while (i.hasNext()) {
            String path = (String) i.next();
            String located = locateTemplate(data, DIR_NAVIGATIONS, path, template);

            if (null != located) {
                return located;
            }
        }

        return null;
    }

    /**
     * Locate a portlet template using Jetspeed template location algorithm,
     * searching by mediatype and language criteria extracted from the request
     * state in rundata.
     * 
     * @param data
     *          The rundata for the request.
     * @param template
     *          The name of the template.
     * 
     * @return The path relative to the portlets directory for the requested
     *         portlet template, or null if not found.
     */
    @Override
    public String locatePortletTemplate(RunData data, String template) {
        List templatePaths = localizeTemplateName(data);
        Iterator i = templatePaths.iterator();

        while (i.hasNext()) {
            String path = (String) i.next();
            String located = locateTemplate(data, DIR_PORTLETS, path, template);

            if (null != located) {
                return DIR_PORTLETS + located;
            }
        }

        // Use "wide" search when required
        if (useGlobalPortletSearch == true) {
            String located = locateScreenTemplate(data, template);
            if (located != null) {
                return DIR_SCREENS + located;
            }
        }

        return null;
    }

    /**
     * Locate a control template using Jetspeed template location algorithm,
     * searching by mediatype and language criteria extracted from the request
     * state in rundata.
     * 
     * @param data
     *          The rundata for the request.
     * @param template
     *          The name of the template.
     * 
     * @return The path relative to the controls directory for the requested
     *         control template, or null if not found.
     */
    @Override
    public String locateControlTemplate(RunData data, String template) {
        List templatePaths = localizeTemplateName(data);
        Iterator i = templatePaths.iterator();

        while (i.hasNext()) {
            String path = (String) i.next();
            String located = locateTemplate(data, DIR_CONTROLS, path, template);

            if (null != located) {
                return DIR_CONTROLS + located;
            }
        }

        return null;
    }

    /**
     * Locate a controller template using Jetspeed template location algorithm,
     * searching by mediatype and language criteria extracted from the request
     * state in rundata.
     * 
     * @param data
     *          The rundata for the request.
     * @param template
     *          The name of the template.
     * 
     * @return The path relative to the controllers directory for the requested
     *         controller template, or null if not found.
     */
    @Override
    public String locateControllerTemplate(RunData data, String template) {
        List templatePaths = localizeTemplateName(data);
        Iterator i = templatePaths.iterator();

        while (i.hasNext()) {
            String path = (String) i.next();
            String located = locateTemplate(data, DIR_CONTROLLERS, path, template);

            if (null != located) {
                return DIR_CONTROLLERS + located;
            }
        }

        return null;
    }

    /**
     * Locate an email template using Jetspeed template location algorithm,
     * searching by mediatype and language criteria extracted from the request
     * state in rundata.
     * 
     * @param data
     *          The rundata for the request.
     * @param template
     *          The name of the template.
     * 
     * @return The path relative to the emails directory for the requested email
     *         template, or null if not found.
     */
    @Override
    public String locateEmailTemplate(RunData data, String template) {
        CustomLocalizationService locService = (CustomLocalizationService) ServiceUtil
                .getServiceByName(LocalizationService.SERVICE_NAME);
        return locateEmailTemplate(data, template, locService.getLocale(data));
    }

    /**
     * Locate an email template using Jetspeed template location algorithm,
     * searching by mediatype and language.
     * 
     * @param data
     *          The rundata for the request.
     * @param template
     *          The name of the template.
     * @param locale
     *          The name of the language.
     * 
     * @return The path relative to the emails directory for the requested email
     *         template, or null if not found.
     */
    @Override
    public String locateEmailTemplate(RunData data, String template, Locale locale) {
        List templatePaths = localizeTemplateName(data, locale);
        Iterator i = templatePaths.iterator();

        while (i.hasNext()) {
            String path = (String) i.next();
            String located = locateTemplate(data, DIR_EMAILS, path, template);

            if (null != located) {
                return DIR_EMAILS + located;
            }
        }

        return null;
    }

    /**
     * Locate a parameter style template using Jetspeed template location
     * algorithm, searching by mediatype and language criteria extracted from the
     * request state in rundata.
     * 
     * @param data
     *          The rundata for the request.
     * @param template
     *          The name of the template.
     * 
     * @return The path relative to the portlets directory for the requested
     *         portlet template, or null if not found.
     */
    @Override
    public String locateParameterTemplate(RunData data, String template) {
        List templatePaths = localizeTemplateName(data);
        Iterator i = templatePaths.iterator();

        while (i.hasNext()) {
            String path = (String) i.next();
            String located = locateTemplate(data, DIR_PARAMETERS, path, template);

            if (null != located) {
                return DIR_PARAMETERS + located;
            }
        }

        return null;
    }

    /**
     * General template location algorithm. Starts with the most specific
     * resource, including mediatype + nls specification, and fallsback to least
     * specific.
     * 
     * @param data
     *          The rundata for the request.
     * @param resourceType
     *          The path specific to the resource type sought (eg /screens).
     * @param path
     *          The fullest path to the template based on simple NLS/mediatype
     *          directory.
     * @param template
     *          The name of the template.
     * 
     * @return the exact path to the template, or null if not found.
     */
    private String locateTemplate(RunData data, String resourceType, String path, String template) {
        String located = null;

        // Iterate through each of the template roots
        for (int i = 0; i < templateRoots.length; i++) {
            located = locateTemplate(data, resourceType, path, template, templateRoots[i]);
            if (located != null) {
                break;
            }
        }

        return located;
    }

    /**
     * General template location algorithm. Starts with the most specific
     * resource, including mediatype + nls specification, and fallsback to least
     * specific.
     * 
     * @param data
     *          The rundata for the request.
     * @param resourceType
     *          The path specific to the resource type sought (eg /screens).
     * @param path
     *          The fullest path to the template based on simple NLS/mediatype
     *          directory.
     * @param template
     *          The name of the template.
     * @param templateRoot
     *          The template root to be searched.
     * 
     * @return the exact path to the template, or null if not found.
     */
    private String locateTemplate(RunData data, String resourceType, String path, String template,
            String templateRoot) {
        String finalPath;

        // make sure resourceType doesn't end with "/" but starts with "/"
        if (resourceType.endsWith(PATH_SEPARATOR)) {
            resourceType = resourceType.substring(0, resourceType.length() - 1);
        }
        if (!resourceType.startsWith(PATH_SEPARATOR)) {
            resourceType = PATH_SEPARATOR + resourceType;
        }
        // make sure path doesn't end with "/" but starts with "/"
        if (path.endsWith(PATH_SEPARATOR)) {
            path = path.substring(0, path.length() - 1);
        }
        if (!path.startsWith(PATH_SEPARATOR)) {
            path = PATH_SEPARATOR + path;
        }
        // make sure template starts with "/"
        if (!template.startsWith(PATH_SEPARATOR)) {
            template = PATH_SEPARATOR + template;
        }

        StringBuffer fullPath = new StringBuffer(templateRoot);

        if (!templateRoot.endsWith(PATH_SEPARATOR)) {
            fullPath.append(PATH_SEPARATOR);
        }

        fullPath.append(getTemplateExtension(template));
        fullPath.append(resourceType);

        String basePath = fullPath.toString();
        String realPath = null;
        String workingPath = null;

        do {
            workingPath = path + template;
            realPath = TurbineServlet.getRealPath(basePath + workingPath);

            // the current template exists in cache, return the corresponding path
            if (templateExists(realPath, true)) {
                if (logger.isDebugEnabled()) {
                    logger.debug(
                            "TemplateLocator: template exists in cache: " + realPath + " returning " + workingPath);
                }

                return workingPath;
            } else if (this.hotDeploy == true) {
                // Try to locate it directly on file system, perhaps it was recently
                // added
                if (templateExists(realPath, false)) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("TemplateLocator: template exists on the file system: " + realPath
                                + " returning " + workingPath);
                    }

                    // add it to the map
                    // templateMap.put(workingPath, null);
                    templateMap.put(realPath, null);

                    return workingPath;
                }
            }
            // else strip path of one of its components and loop
            int pt = path.lastIndexOf(PATH_SEPARATOR);
            if (pt > -1) {
                path = path.substring(0, pt);
            } else {
                path = null;
            }
        } while (path != null);

        return null;
    }

    /**
     * Helper function for template locator to find a localized (NLS) resource.
     * Considers both language and country resources as well as all the possible
     * media-types for the request
     * 
     * @param data
     *          The rundata for the request.
     * 
     * @return The possible paths to a localized template ordered by descending
     *         preference
     */
    private List localizeTemplateName(RunData data) {
        return localizeTemplateName(data, null);
    }

    /**
     * Helper function for template locator to find a localized (NLS) resource.
     * Considers both language and country resources as well as all the possible
     * media-types for the request
     * 
     * @param data
     *          The rundata for the request.
     * @param locale
     *          The locale for the request.
     * 
     * @return The possible paths to a localized template ordered by descending
     *         preference
     */
    private List localizeTemplateName(RunData data, Locale inLocale) {
        List templates = new ArrayList();
        Locale tmplocale = null;

        if (inLocale != null) {
            tmplocale = inLocale;
        } else {
            CustomLocalizationService locService = (CustomLocalizationService) ServiceUtil
                    .getServiceByName(LocalizationService.SERVICE_NAME);
            tmplocale = locService.getLocale(data);
        }

        // Get the locale store it in the user object
        if (tmplocale == null) {
            tmplocale = new Locale(TurbineResources.getString("locale.default.language", "en"),
                    TurbineResources.getString("locale.default.country", "US"));
        }

        if (data.getUser() != null) {
            data.getUser().setTemp("locale", tmplocale);
        }

        StringBuffer templatePath = new StringBuffer();

        // retrieve all the possible media types
        String type = data.getParameters().getString(Profiler.PARAM_MEDIA_TYPE, null);
        List types = new ArrayList();
        CapabilityMap cm = ((JetspeedRunData) data).getCapability();

        // Grab the Locale from the temporary storage in the User object
        Locale locale = data.getUser() != null ? (Locale) data.getUser().getTemp("locale") : tmplocale;
        String language = locale.getLanguage();
        String country = locale.getCountry();

        if (null != type) {
            types.add(type);
        } else {
            Iterator i = cm.listMediaTypes();
            while (i.hasNext()) {
                types.add(i.next());
            }
        }

        Iterator typeIterator = types.iterator();

        while (typeIterator.hasNext()) {
            type = (String) typeIterator.next();

            if ((type != null) && (type.length() > 0)) {
                templatePath.append(PATH_SEPARATOR).append(type);
            }

            if ((language != null) && (language.length() > 0)) {
                templatePath.append(PATH_SEPARATOR).append(language);
            }

            if ((country != null) && (country.length() > 0)) {
                templatePath.append(PATH_SEPARATOR).append(country);
            }

            templates.add(templatePath.toString());
            templatePath.setLength(0);
        }

        return templates;
    }

    /**
     * Returns the extension for the specified template
     * 
     * @param template
     *          the template name to scan for an extension
     * @return the template extension if it exists or the default template
     *         extension
     */
    private String getTemplateExtension(String template) {
        String ext = TurbineTemplate.getDefaultExtension();

        int idx = template.lastIndexOf(".");

        if (idx > 0) {
            ext = template.substring(idx + 1);
        }

        return ext;
    }

    /**
     * Checks for the existence of a template resource given a key. The key are
     * absolute paths to the templates, and are cached in a template cache for
     * performance.
     * 
     * @param key
     *          The absolute path to the template resource.
     * 
     * @return True when the template is found, otherwise false.
     */
    public boolean templateExists(String templateKey, boolean useNameCache) {
        if (null == templateKey) {
            return false;
        }

        if (useNameCache == true) {
            return templateMap.containsKey(templateKey);
        }

        return (new File(templateKey).exists());
    }

    /**
     * Loads the configuration parameters for this service from the
     * JetspeedResources.properties file.
     * 
     * @exception throws a <code>InitializationException</code> if the service
     *            fails to initialize
     */
    private void initConfiguration() throws InitializationException {

        templateRoots = JetspeedResources.getStringArray(
                TurbineServices.SERVICE_PREFIX + TemplateLocatorService.SERVICE_NAME + CONFIG_TEMPLATE_ROOT);

        if ((templateRoots == null) || (templateRoots.length == 0)) {
            throw new InitializationException(MSG_MISSING_PARAMETER + CONFIG_TEMPLATE_ROOT);
        }

        templateMap = new HashMap();

        for (int i = 0; i < templateRoots.length; i++) {
            String templateRoot = templateRoots[i];

            if (!templateRoot.endsWith(PATH_SEPARATOR)) {
                templateRoot = templateRoot + PATH_SEPARATOR;
            }

            if (logger.isDebugEnabled()) {
                logger.debug("TemplateLocator: Adding templateRoot:" + templateRoot);
            }

            // traverse starting from the root template directory and add resources
            String templateRootPath = TurbineServlet.getRealPath(templateRoot);
            if (null != templateRootPath) {
                loadNameCache(templateRootPath, "");
            }
        }

        velocityService = (VelocityService) TurbineServices.getInstance().getService(VelocityService.SERVICE_NAME);

        jspService = (JspService) TurbineServices.getInstance().getService(JspService.SERVICE_NAME);

        useGlobalPortletSearch = JetspeedResources.getBoolean(
                TurbineServices.SERVICE_PREFIX + TemplateLocatorService.SERVICE_NAME + CONFIG_PORTLET_GLOBAL_SEARCH,
                false);

        hotDeploy = JetspeedResources.getBoolean(
                TurbineServices.SERVICE_PREFIX + TemplateLocatorService.SERVICE_NAME + CONFIG_HOT_DEPLOY, true);

    }

    /**
     * Loads the template name cache map to accelerate template searches.
     * 
     * @param path
     *          The template
     * @param name
     *          just the name of the resource
     */
    private void loadNameCache(String path, String name) {
        File file = new File(path);
        if (file.isFile()) {
            // add it to the map
            templateMap.put(path, null);
        } else {
            if (file.isDirectory()) {
                if (!path.endsWith(File.separator)) {
                    path += File.separator;
                }

                String list[] = file.list();

                // Process all files recursivly
                for (int ix = 0; list != null && ix < list.length; ix++) {
                    loadNameCache(path + list[ix], list[ix]);
                }
            }
        }
    }

    /**
     * Correctly locate the default layout based on the default.layout.template
     * property of the appropriate template service.
     * 
     * @author <a href="mailto:sweaver@rippe.com">Scott Weaver</a>
     */
    private String getTemplateLayout(String extension) {
        String dftLayout = "/default." + extension;

        Configuration velocityCfg = null;
        Configuration jspCfg = null;
        if (velocityService != null) {
            velocityCfg = velocityService.getConfiguration();
        }

        if (jspService != null) {
            jspCfg = jspService.getConfiguration();
        }

        if (velocityCfg != null && velocityCfg.getString(TEMPLATE_EXTENSION).indexOf(extension) > -1) {
            return velocityCfg.getString(DEFAULT_LAYOUT, dftLayout);
        } else if (jspCfg != null && jspCfg.getString(TEMPLATE_EXTENSION).indexOf(extension) > -1) {
            return jspCfg.getString(DEFAULT_LAYOUT, dftLayout);
        } else {
            return dftLayout;
        }
    }
}