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