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.log.Log;
018    import com.liferay.portal.kernel.log.LogFactoryUtil;
019    import com.liferay.portal.kernel.log.LogUtil;
020    import com.liferay.portal.kernel.portlet.PortletBag;
021    import com.liferay.portal.kernel.portlet.PortletBagPool;
022    import com.liferay.portal.kernel.servlet.DirectRequestDispatcherFactoryUtil;
023    import com.liferay.portal.kernel.servlet.TrackedServletRequest;
024    import com.liferay.portal.kernel.servlet.taglib.TagDynamicIdFactory;
025    import com.liferay.portal.kernel.servlet.taglib.TagDynamicIdFactoryRegistry;
026    import com.liferay.portal.kernel.servlet.taglib.TagDynamicIncludeUtil;
027    import com.liferay.portal.kernel.util.GetterUtil;
028    import com.liferay.portal.kernel.util.PropsKeys;
029    import com.liferay.portal.kernel.util.PropsUtil;
030    import com.liferay.portal.kernel.util.ServerDetector;
031    import com.liferay.portal.kernel.util.StringBundler;
032    import com.liferay.portal.kernel.util.StringPool;
033    import com.liferay.portal.kernel.util.UnicodeProperties;
034    import com.liferay.portal.kernel.util.Validator;
035    import com.liferay.portal.kernel.util.WebKeys;
036    import com.liferay.portal.model.Group;
037    import com.liferay.portal.model.PortletConstants;
038    import com.liferay.portal.model.Theme;
039    import com.liferay.portal.theme.ThemeDisplay;
040    import com.liferay.portal.util.CustomJspRegistryUtil;
041    import com.liferay.portlet.exportimport.staging.StagingUtil;
042    import com.liferay.taglib.FileAvailabilityUtil;
043    import com.liferay.taglib.servlet.JspWriterHttpServletResponse;
044    import com.liferay.taglib.servlet.PipingServletResponse;
045    
046    import java.io.IOException;
047    
048    import javax.servlet.RequestDispatcher;
049    import javax.servlet.ServletContext;
050    import javax.servlet.ServletException;
051    import javax.servlet.http.HttpServletRequest;
052    import javax.servlet.http.HttpServletResponse;
053    import javax.servlet.jsp.JspException;
054    import javax.servlet.jsp.tagext.BodyContent;
055    
056    /**
057     * @author Brian Wing Shun Chan
058     * @author Shuyang Zhou
059     * @author Eduardo Lundgren
060     * @author Raymond Aug??
061     */
062    public class IncludeTag extends AttributesTagSupport {
063    
064            @Override
065            public int doEndTag() throws JspException {
066                    try {
067                            String page = getPage();
068    
069                            if (Validator.isNull(page)) {
070                                    page = getEndPage();
071                            }
072    
073                            callSetAttributes();
074    
075                            if (themeResourceExists(page)) {
076                                    doIncludeTheme(page);
077    
078                                    return EVAL_PAGE;
079                            }
080    
081                            if (!FileAvailabilityUtil.isAvailable(servletContext, page)) {
082                                    logUnavailablePage(page);
083    
084                                    return processEndTag();
085                            }
086    
087                            doInclude(page, false);
088    
089                            return EVAL_PAGE;
090                    }
091                    catch (Exception e) {
092                            throw new JspException(e);
093                    }
094                    finally {
095                            doClearTag();
096                    }
097            }
098    
099            @Override
100            public int doStartTag() throws JspException {
101                    try {
102                            String page = getStartPage();
103    
104                            callSetAttributes();
105    
106                            if (themeResourceExists(page)) {
107                                    doIncludeTheme(page);
108    
109                                    return EVAL_BODY_INCLUDE;
110                            }
111    
112                            if (!FileAvailabilityUtil.isAvailable(servletContext, page)) {
113                                    logUnavailablePage(page);
114    
115                                    return processStartTag();
116                            }
117    
118                            doInclude(page, true);
119    
120                            return EVAL_BODY_INCLUDE;
121                    }
122                    catch (Exception e) {
123                            throw new JspException(e);
124                    }
125            }
126    
127            public void runTag() throws JspException {
128                    doStartTag();
129                    doEndTag();
130            }
131    
132            public void setPage(String page) {
133                    _page = page;
134            }
135    
136            public void setPortletId(String portletId) {
137                    if (Validator.isNotNull(portletId)) {
138                            String rootPortletId = PortletConstants.getRootPortletId(portletId);
139    
140                            PortletBag portletBag = PortletBagPool.get(rootPortletId);
141    
142                            servletContext = portletBag.getServletContext();
143                    }
144            }
145    
146            public void setStrict(boolean strict) {
147                    _strict = strict;
148            }
149    
150            public void setUseCustomPage(boolean useCustomPage) {
151                    _useCustomPage = useCustomPage;
152            }
153    
154            protected void callSetAttributes() {
155                    HttpServletRequest request = getOriginalServletRequest();
156    
157                    if (isCleanUpSetAttributes()) {
158                            _trackedRequest = new TrackedServletRequest(request);
159    
160                            request = _trackedRequest;
161                    }
162    
163                    setNamespacedAttribute(request, "bodyContent", getBodyContentWrapper());
164                    setNamespacedAttribute(
165                            request, "dynamicAttributes", getDynamicAttributes());
166                    setNamespacedAttribute(
167                            request, "scopedAttributes", getScopedAttributes());
168    
169                    setAttributes(request);
170            }
171    
172            protected void cleanUp() {
173            }
174    
175            protected void cleanUpSetAttributes() {
176                    if (isCleanUpSetAttributes() && (_trackedRequest != null)) {
177                            for (String name : _trackedRequest.getSetAttributes()) {
178                                    _trackedRequest.removeAttribute(name);
179                            }
180    
181                            _trackedRequest = null;
182                    }
183            }
184    
185            protected void doClearTag() {
186                    clearDynamicAttributes();
187                    clearParams();
188                    clearProperties();
189    
190                    cleanUpSetAttributes();
191    
192                    if (!ServerDetector.isResin()) {
193                            setPage(null);
194                            setUseCustomPage(true);
195    
196                            cleanUp();
197                    }
198            }
199    
200            protected void doInclude(
201                            String page, boolean dynamicIncludeAscendingPriority)
202                    throws JspException {
203    
204                    try {
205                            include(page, dynamicIncludeAscendingPriority);
206                    }
207                    catch (Exception e) {
208                            String currentURL = (String)request.getAttribute(
209                                    WebKeys.CURRENT_URL);
210    
211                            String message =
212                                    "Current URL " + currentURL + " generates exception: " +
213                                            e.getMessage();
214    
215                            LogUtil.log(_log, e, message);
216    
217                            if (e instanceof JspException) {
218                                    throw (JspException)e;
219                            }
220                    }
221            }
222    
223            protected void doIncludeTheme(String page) throws Exception {
224                    HttpServletResponse response =
225                            (HttpServletResponse)pageContext.getResponse();
226    
227                    Theme theme = (Theme)request.getAttribute(WebKeys.THEME);
228    
229                    ThemeUtil.include(servletContext, request, response, page, theme);
230            }
231    
232            protected Object getBodyContentWrapper() {
233                    final BodyContent bodyContent = getBodyContent();
234    
235                    if (bodyContent == null) {
236                            return null;
237                    }
238    
239                    return new Object() {
240    
241                            @Override
242                            public String toString() {
243                                    return bodyContent.getString();
244                            }
245    
246                    };
247            }
248    
249            protected String getCustomPage(
250                    ServletContext servletContext, HttpServletRequest request,
251                    String page) {
252    
253                    if (Validator.isNull(page)) {
254                            return null;
255                    }
256    
257                    ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
258                            WebKeys.THEME_DISPLAY);
259    
260                    if (themeDisplay == null) {
261                            return null;
262                    }
263    
264                    Group group = null;
265    
266                    try {
267                            group = StagingUtil.getLiveGroup(themeDisplay.getScopeGroupId());
268                    }
269                    catch (Exception e) {
270                            return null;
271                    }
272    
273                    UnicodeProperties typeSettingsProperties =
274                            group.getTypeSettingsProperties();
275    
276                    String customJspServletContextName = typeSettingsProperties.getProperty(
277                            "customJspServletContextName");
278    
279                    if (Validator.isNull(customJspServletContextName)) {
280                            return null;
281                    }
282    
283                    String customPage = CustomJspRegistryUtil.getCustomJspFileName(
284                            customJspServletContextName, page);
285    
286                    if (FileAvailabilityUtil.isAvailable(servletContext, customPage)) {
287                            return customPage;
288                    }
289    
290                    return null;
291            }
292    
293            protected String getEndPage() {
294                    return null;
295            }
296    
297            protected HttpServletRequest getOriginalServletRequest() {
298                    return (HttpServletRequest)pageContext.getRequest();
299            }
300    
301            protected String getPage() {
302                    return _page;
303            }
304    
305            protected String getStartPage() {
306                    return null;
307            }
308    
309            protected void include(String page, boolean doStartTag) throws Exception {
310                    JspWriterHttpServletResponse jspWriterHttpServletResponse = null;
311    
312                    Class<?> clazz = getClass();
313    
314                    String tagClassName = clazz.getName();
315    
316                    String tagDynamicId = null;
317    
318                    String tagPointPrefix = null;
319    
320                    if (doStartTag) {
321                            tagPointPrefix = "doStartTag#";
322                    }
323                    else {
324                            tagPointPrefix = "doEndTag#";
325                    }
326    
327                    TagDynamicIdFactory tagDynamicIdFactory =
328                            TagDynamicIdFactoryRegistry.getTagDynamicIdFactory(tagClassName);
329    
330                    if (tagDynamicIdFactory != null) {
331                            jspWriterHttpServletResponse = new JspWriterHttpServletResponse(
332                                    pageContext);
333    
334                            tagDynamicId = tagDynamicIdFactory.getTagDynamicId(
335                                    request, jspWriterHttpServletResponse, this);
336    
337                            TagDynamicIncludeUtil.include(
338                                    request, jspWriterHttpServletResponse, tagClassName,
339                                    tagDynamicId, tagPointPrefix + "before", doStartTag);
340                    }
341    
342                    if (_useCustomPage) {
343                            String customPage = getCustomPage(servletContext, request, page);
344    
345                            if (Validator.isNotNull(customPage)) {
346                                    page = customPage;
347                            }
348                    }
349    
350                    request.setAttribute(
351                            WebKeys.SERVLET_CONTEXT_INCLUDE_FILTER_STRICT, _strict);
352    
353                    HttpServletResponse response = new PipingServletResponse(pageContext);
354    
355                    includePage(page, response);
356    
357                    request.removeAttribute(WebKeys.SERVLET_CONTEXT_INCLUDE_FILTER_STRICT);
358    
359                    if (tagDynamicIdFactory != null) {
360                            TagDynamicIncludeUtil.include(
361                                    request, jspWriterHttpServletResponse, tagClassName,
362                                    tagDynamicId, tagPointPrefix + "after", doStartTag);
363                    }
364            }
365    
366            protected void includePage(String page, HttpServletResponse response)
367                    throws IOException, ServletException {
368    
369                    RequestDispatcher requestDispatcher =
370                            DirectRequestDispatcherFactoryUtil.getRequestDispatcher(
371                                    servletContext, page);
372    
373                    requestDispatcher.include(request, response);
374            }
375    
376            protected boolean isCleanUpSetAttributes() {
377                    return _CLEAN_UP_SET_ATTRIBUTES;
378            }
379    
380            protected boolean isPortalPage(String page) {
381                    if (page.startsWith("/html/taglib/") &&
382                            (page.endsWith("/end.jsp") || page.endsWith("/page.jsp") ||
383                             page.endsWith("/start.jsp"))) {
384    
385                            return true;
386                    }
387    
388                    return false;
389            }
390    
391            protected boolean isUseCustomPage() {
392                    return _useCustomPage;
393            }
394    
395            protected void logUnavailablePage(String page) {
396                    if ((page == null) || !_log.isWarnEnabled()) {
397                            return;
398                    }
399    
400                    String contextPath = servletContext.getContextPath();
401    
402                    if (contextPath.equals(StringPool.BLANK)) {
403                            contextPath = StringPool.SLASH;
404                    }
405    
406                    StringBundler sb = new StringBundler(8);
407    
408                    sb.append("Unable to find ");
409                    sb.append(page);
410                    sb.append(" in the context ");
411                    sb.append(contextPath);
412                    sb.append(".");
413    
414                    if (isPortalPage(page)) {
415                            if (contextPath.equals(StringPool.SLASH)) {
416                                    sb = null;
417                            }
418                            else {
419                                    sb.append(" You must not use a taglib from a module and ");
420                                    sb.append("set the attribute \"servletContext\". Inline the ");
421                                    sb.append("content directly where the taglib is invoked.");
422                            }
423                    }
424                    else if (contextPath.equals(StringPool.SLASH)) {
425                            Class<?> clazz = getClass();
426    
427                            if (clazz.equals(IncludeTag.class)) {
428                                    sb.append(" You must set the attribute \"servletContext\" ");
429                                    sb.append("with the value \"<%= application %>\" when ");
430                                    sb.append("invoking a taglib from a module.");
431                            }
432                            else {
433                                    sb.append(" You must not use a taglib from a module and ");
434                                    sb.append("set the attribute \"file\". Inline the content ");
435                                    sb.append("directly where the taglib is invoked.");
436                            }
437                    }
438    
439                    if (sb != null) {
440                            _log.warn(sb.toString());
441                    }
442            }
443    
444            protected int processEndTag() throws Exception {
445                    return EVAL_PAGE;
446            }
447    
448            protected int processStartTag() throws Exception {
449                    return EVAL_BODY_INCLUDE;
450            }
451    
452            protected void setAttributes(HttpServletRequest request) {
453            }
454    
455            protected boolean themeResourceExists(String page) throws Exception {
456                    if ((page == null) || !_THEME_JSP_OVERRIDE_ENABLED || _strict) {
457                            return false;
458                    }
459    
460                    Theme theme = (Theme)request.getAttribute(WebKeys.THEME);
461    
462                    if (theme == null) {
463                            ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
464                                    WebKeys.THEME_DISPLAY);
465    
466                            if (themeDisplay != null) {
467                                    theme = themeDisplay.getTheme();
468                            }
469                    }
470    
471                    if (theme == null) {
472                            return false;
473                    }
474    
475                    String portletId = ThemeUtil.getPortletId(request);
476    
477                    boolean exists = theme.resourceExists(servletContext, portletId, page);
478    
479                    if (_log.isDebugEnabled() && exists) {
480                            String resourcePath = theme.getResourcePath(
481                                    servletContext, null, page);
482    
483                            _log.debug(resourcePath);
484                    }
485    
486                    return exists;
487            }
488    
489            private static final boolean _CLEAN_UP_SET_ATTRIBUTES = false;
490    
491            private static final boolean _THEME_JSP_OVERRIDE_ENABLED =
492                    GetterUtil.getBoolean(
493                            PropsUtil.get(PropsKeys.THEME_JSP_OVERRIDE_ENABLED));
494    
495            private static final Log _log = LogFactoryUtil.getLog(IncludeTag.class);
496    
497            private String _page;
498            private boolean _strict;
499            private TrackedServletRequest _trackedRequest;
500            private boolean _useCustomPage = true;
501    
502    }