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