001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.taglib.util;
016    
017    import com.liferay.portal.kernel.io.unsync.UnsyncStringWriter;
018    import com.liferay.portal.kernel.log.Log;
019    import com.liferay.portal.kernel.log.LogFactoryUtil;
020    import com.liferay.portal.kernel.servlet.ServletContextPool;
021    import com.liferay.portal.kernel.servlet.taglib.DynamicIncludeUtil;
022    import com.liferay.portal.kernel.template.Template;
023    import com.liferay.portal.kernel.template.TemplateConstants;
024    import com.liferay.portal.kernel.template.TemplateContextContributor;
025    import com.liferay.portal.kernel.template.TemplateManager;
026    import com.liferay.portal.kernel.template.TemplateManagerUtil;
027    import com.liferay.portal.kernel.template.TemplateResource;
028    import com.liferay.portal.kernel.template.TemplateResourceLoaderUtil;
029    import com.liferay.portal.kernel.util.GetterUtil;
030    import com.liferay.portal.kernel.util.StringPool;
031    import com.liferay.portal.kernel.util.ThemeHelper;
032    import com.liferay.portal.kernel.util.UnsyncPrintWriterPool;
033    import com.liferay.portal.kernel.util.Validator;
034    import com.liferay.portal.kernel.util.WebKeys;
035    import com.liferay.portal.model.PortletConstants;
036    import com.liferay.portal.model.Theme;
037    import com.liferay.portal.theme.PortletDisplay;
038    import com.liferay.portal.theme.ThemeDisplay;
039    import com.liferay.registry.collections.ServiceTrackerCollections;
040    import com.liferay.registry.collections.ServiceTrackerList;
041    import com.liferay.taglib.servlet.PipingServletResponse;
042    
043    import java.io.Writer;
044    
045    import javax.servlet.RequestDispatcher;
046    import javax.servlet.ServletContext;
047    import javax.servlet.http.HttpServletRequest;
048    import javax.servlet.http.HttpServletResponse;
049    
050    /**
051     * @author Brian Wing Shun Chan
052     * @author Brian Myunghun Kim
053     * @author Raymond Aug??
054     * @author Mika Koivisto
055     * @author Shuyang Zhou
056     */
057    public class ThemeUtil {
058    
059            public static String getPortletId(HttpServletRequest request) {
060                    String portletId = null;
061    
062                    ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
063                            WebKeys.THEME_DISPLAY);
064    
065                    if (themeDisplay != null) {
066                            PortletDisplay portletDisplay = themeDisplay.getPortletDisplay();
067    
068                            portletId = portletDisplay.getId();
069                    }
070    
071                    return portletId;
072            }
073    
074            public static void include(
075                            ServletContext servletContext, HttpServletRequest request,
076                            HttpServletResponse response, String path, Theme theme)
077                    throws Exception {
078    
079                    String extension = theme.getTemplateExtension();
080    
081                    if (extension.equals(ThemeHelper.TEMPLATE_EXTENSION_FTL)) {
082                            includeFTL(servletContext, request, response, path, theme, true);
083                    }
084                    else if (extension.equals(ThemeHelper.TEMPLATE_EXTENSION_VM)) {
085                            includeVM(servletContext, request, response, path, theme, true);
086                    }
087                    else {
088                            path = theme.getTemplatesPath() + StringPool.SLASH + path;
089    
090                            includeJSP(servletContext, request, response, path, theme);
091                    }
092            }
093    
094            public static String includeFTL(
095                            ServletContext servletContext, HttpServletRequest request,
096                            HttpServletResponse response, String path, Theme theme,
097                            boolean write)
098                    throws Exception {
099    
100                    return doDispatch(
101                            servletContext, request, response, path, theme, write,
102                            ThemeHelper.TEMPLATE_EXTENSION_FTL);
103            }
104    
105            public static void includeJSP(
106                            ServletContext servletContext, HttpServletRequest request,
107                            HttpServletResponse response, String path, Theme theme)
108                    throws Exception {
109    
110                    doDispatch(
111                            servletContext, request, response, path, theme, true,
112                            ThemeHelper.TEMPLATE_EXTENSION_JSP);
113            }
114    
115            public static String includeVM(
116                            ServletContext servletContext, HttpServletRequest request,
117                            HttpServletResponse response, String path, Theme theme,
118                            boolean write)
119                    throws Exception {
120    
121                    return doDispatch(
122                            servletContext, request, response, path, theme, write,
123                            ThemeHelper.TEMPLATE_EXTENSION_VM);
124            }
125    
126            protected static String doDispatch(
127                            ServletContext servletContext, HttpServletRequest request,
128                            HttpServletResponse response, String path, Theme theme,
129                            boolean write, String extension)
130                    throws Exception {
131    
132                    String pluginServletContextName = GetterUtil.getString(
133                            theme.getServletContextName());
134    
135                    ServletContext pluginServletContext = ServletContextPool.get(
136                            pluginServletContextName);
137    
138                    ClassLoader pluginClassLoader = null;
139    
140                    if (pluginServletContext != null) {
141                            pluginClassLoader = pluginServletContext.getClassLoader();
142                    }
143    
144                    Thread currentThread = Thread.currentThread();
145    
146                    ClassLoader contextClassLoader = currentThread.getContextClassLoader();
147    
148                    if ((pluginClassLoader != null) &&
149                            (pluginClassLoader != contextClassLoader)) {
150    
151                            currentThread.setContextClassLoader(pluginClassLoader);
152                    }
153    
154                    try {
155                            if (extension.equals(ThemeHelper.TEMPLATE_EXTENSION_FTL)) {
156                                    return doIncludeFTL(
157                                            servletContext, request, response, path, theme, false,
158                                            write);
159                            }
160                            else if (extension.equals(ThemeHelper.TEMPLATE_EXTENSION_JSP)) {
161                                    doIncludeJSP(servletContext, request, response, path, theme);
162                            }
163                            else if (extension.equals(ThemeHelper.TEMPLATE_EXTENSION_VM)) {
164                                    return doIncludeVM(
165                                            servletContext, request, response, path, theme, false,
166                                            write);
167                            }
168    
169                            return null;
170                    }
171                    finally {
172                            if ((pluginClassLoader != null) &&
173                                    (pluginClassLoader != contextClassLoader)) {
174    
175                                    currentThread.setContextClassLoader(contextClassLoader);
176                            }
177                    }
178            }
179    
180            protected static String doIncludeFTL(
181                            ServletContext servletContext, HttpServletRequest request,
182                            HttpServletResponse response, String path, Theme theme,
183                            boolean restricted, boolean write)
184                    throws Exception {
185    
186                    // The servlet context name will be null when the theme is deployed to
187                    // the root directory in Tomcat. See
188                    // com.liferay.portal.servlet.MainServlet and
189                    // com.liferay.portlet.PortletContextImpl for other cases where a null
190                    // servlet context name is also converted to an empty string.
191    
192                    String servletContextName = GetterUtil.getString(
193                            theme.getServletContextName());
194    
195                    if (ServletContextPool.get(servletContextName) == null) {
196    
197                            // This should only happen if the FreeMarker template is the first
198                            // page to be accessed in the system
199    
200                            ServletContextPool.put(servletContextName, servletContext);
201                    }
202    
203                    String portletId = getPortletId(request);
204    
205                    String resourcePath = theme.getResourcePath(
206                            servletContext, portletId, path);
207    
208                    if (Validator.isNotNull(portletId) &&
209                            PortletConstants.hasInstanceId(portletId) &&
210                            !TemplateResourceLoaderUtil.hasTemplateResource(
211                                    TemplateConstants.LANG_TYPE_FTL, resourcePath)) {
212    
213                            String rootPortletId = PortletConstants.getRootPortletId(portletId);
214    
215                            resourcePath = theme.getResourcePath(
216                                    servletContext, rootPortletId, path);
217                    }
218    
219                    if (Validator.isNotNull(portletId) &&
220                            !TemplateResourceLoaderUtil.hasTemplateResource(
221                                    TemplateConstants.LANG_TYPE_FTL, resourcePath)) {
222    
223                            resourcePath = theme.getResourcePath(servletContext, null, path);
224                    }
225    
226                    if (!TemplateResourceLoaderUtil.hasTemplateResource(
227                                    TemplateConstants.LANG_TYPE_FTL, resourcePath)) {
228    
229                            _log.error(resourcePath + " does not exist");
230    
231                            return null;
232                    }
233    
234                    TemplateResource templateResource =
235                            TemplateResourceLoaderUtil.getTemplateResource(
236                                    TemplateConstants.LANG_TYPE_FTL, resourcePath);
237    
238                    Template template = TemplateManagerUtil.getTemplate(
239                            TemplateConstants.LANG_TYPE_FTL, templateResource, restricted);
240    
241                    // FreeMarker variables
242    
243                    template.prepare(request);
244    
245                    // Custom theme variables
246    
247                    for (TemplateContextContributor templateContextContributor :
248                                    _templateContextContributors) {
249    
250                            templateContextContributor.prepare(template, request);
251                    }
252    
253                    // Theme servlet context
254    
255                    ServletContext themeServletContext = ServletContextPool.get(
256                            servletContextName);
257    
258                    template.put("themeServletContext", themeServletContext);
259    
260                    Writer writer = null;
261    
262                    if (write) {
263    
264                            // Wrapping is needed because of a bug in FreeMarker
265    
266                            writer = UnsyncPrintWriterPool.borrow(response.getWriter());
267                    }
268                    else {
269                            writer = new UnsyncStringWriter();
270                    }
271    
272                    TemplateManager templateManager =
273                            TemplateManagerUtil.getTemplateManager(
274                                    TemplateConstants.LANG_TYPE_FTL);
275    
276                    templateManager.addTaglibApplication(
277                            template, "Application", request.getServletContext());
278                    templateManager.addTaglibFactory(
279                            template, "PortalJspTagLibs", servletContext);
280                    templateManager.addTaglibFactory(
281                            template, "ThemeJspTaglibs", themeServletContext);
282                    templateManager.addTaglibRequest(
283                            template, "Request", request, response);
284                    templateManager.addTaglibTheme(
285                            template, "taglibLiferay", request,
286                            new PipingServletResponse(response, writer));
287    
288                    template.put(TemplateConstants.WRITER, writer);
289    
290                    // Merge templates
291    
292                    template.processTemplate(writer);
293    
294                    if (write) {
295                            return null;
296                    }
297                    else {
298                            return writer.toString();
299                    }
300            }
301    
302            protected static void doIncludeJSP(
303                            ServletContext servletContext, HttpServletRequest request,
304                            HttpServletResponse response, String path, Theme theme)
305                    throws Exception {
306    
307                    DynamicIncludeUtil.include(
308                            request, response, ThemeUtil.class.getName() + "#doIncludeJSP",
309                            true);
310    
311                    if (theme.isWARFile()) {
312                            ServletContext themeServletContext = servletContext.getContext(
313                                    theme.getContextPath());
314    
315                            if (themeServletContext == null) {
316                                    _log.error(
317                                            "Theme " + theme.getThemeId() + " cannot find its " +
318                                                    "servlet context at " + theme.getServletContextName());
319                            }
320                            else {
321                                    RequestDispatcher requestDispatcher =
322                                            themeServletContext.getRequestDispatcher(path);
323    
324                                    if (requestDispatcher == null) {
325                                            _log.error(
326                                                    "Theme " + theme.getThemeId() + " does not have " +
327                                                            path);
328                                    }
329                                    else {
330                                            requestDispatcher.include(request, response);
331                                    }
332                            }
333                    }
334                    else {
335                            RequestDispatcher requestDispatcher =
336                                    servletContext.getRequestDispatcher(path);
337    
338                            if (requestDispatcher == null) {
339                                    _log.error(
340                                            "Theme " + theme.getThemeId() + " does not have " + path);
341                            }
342                            else {
343                                    requestDispatcher.include(request, response);
344                            }
345                    }
346            }
347    
348            protected static String doIncludeVM(
349                            ServletContext servletContext, HttpServletRequest request,
350                            HttpServletResponse response, String page, Theme theme,
351                            boolean restricted, boolean write)
352                    throws Exception {
353    
354                    // The servlet context name will be null when the theme is deployed to
355                    // the root directory in Tomcat. See
356                    // com.liferay.portal.servlet.MainServlet and
357                    // com.liferay.portlet.PortletContextImpl for other cases where a null
358                    // servlet context name is also converted to an empty string.
359    
360                    String servletContextName = GetterUtil.getString(
361                            theme.getServletContextName());
362    
363                    if (ServletContextPool.get(servletContextName) == null) {
364    
365                            // This should only happen if the Velocity template is the first
366                            // page to be accessed in the system
367    
368                            ServletContextPool.put(servletContextName, servletContext);
369                    }
370    
371                    String portletId = getPortletId(request);
372    
373                    String resourcePath = theme.getResourcePath(
374                            servletContext, portletId, page);
375    
376                    boolean checkResourceExists = true;
377    
378                    if (Validator.isNotNull(portletId)) {
379                            if (PortletConstants.hasInstanceId(portletId) &&
380                                    (checkResourceExists !=
381                                            TemplateResourceLoaderUtil.hasTemplateResource(
382                                                    TemplateConstants.LANG_TYPE_VM, resourcePath))) {
383    
384                                    String rootPortletId = PortletConstants.getRootPortletId(
385                                            portletId);
386    
387                                    resourcePath = theme.getResourcePath(
388                                            servletContext, rootPortletId, page);
389                            }
390    
391                            if (checkResourceExists &&
392                                    (checkResourceExists !=
393                                            TemplateResourceLoaderUtil.hasTemplateResource(
394                                                    TemplateConstants.LANG_TYPE_VM, resourcePath))) {
395    
396                                    resourcePath = theme.getResourcePath(
397                                            servletContext, null, page);
398                            }
399                    }
400    
401                    if (checkResourceExists &&
402                            !TemplateResourceLoaderUtil.hasTemplateResource(
403                                    TemplateConstants.LANG_TYPE_VM, resourcePath)) {
404    
405                            _log.error(resourcePath + " does not exist");
406    
407                            return null;
408                    }
409    
410                    TemplateResource templateResource =
411                            TemplateResourceLoaderUtil.getTemplateResource(
412                                    TemplateConstants.LANG_TYPE_VM, resourcePath);
413    
414                    if (templateResource == null) {
415                            throw new Exception(
416                                    "Unable to load template resource " + resourcePath);
417                    }
418    
419                    TemplateManager templateManager =
420                            TemplateManagerUtil.getTemplateManager(
421                                    TemplateConstants.LANG_TYPE_VM);
422    
423                    Template template = TemplateManagerUtil.getTemplate(
424                            TemplateConstants.LANG_TYPE_VM, templateResource, restricted);
425    
426                    // Velocity variables
427    
428                    template.prepare(request);
429    
430                    // Custom theme variables
431    
432                    for (TemplateContextContributor templateContextContributor :
433                                    _templateContextContributors) {
434    
435                            templateContextContributor.prepare(template, request);
436                    }
437    
438                    // Theme servlet context
439    
440                    ServletContext themeServletContext = ServletContextPool.get(
441                            servletContextName);
442    
443                    template.put("themeServletContext", themeServletContext);
444    
445                    // Tag libraries
446    
447                    Writer writer = null;
448    
449                    if (write) {
450                            writer = response.getWriter();
451                    }
452                    else {
453                            writer = new UnsyncStringWriter();
454                    }
455    
456                    templateManager.addTaglibTheme(
457                            template, "taglibLiferay", request,
458                            new PipingServletResponse(response, writer));
459    
460                    template.put(TemplateConstants.WRITER, writer);
461    
462                    // Merge templates
463    
464                    template.processTemplate(writer);
465    
466                    if (write) {
467                            return null;
468                    }
469                    else {
470                            return ((UnsyncStringWriter)writer).toString();
471                    }
472            }
473    
474            private static final Log _log = LogFactoryUtil.getLog(ThemeUtil.class);
475    
476            private static final ServiceTrackerList<TemplateContextContributor>
477                    _templateContextContributors = ServiceTrackerCollections.openList(
478                            TemplateContextContributor.class,
479                            "(type=" + TemplateContextContributor.TYPE_THEME + ")");
480    
481    }