001    /**
002     * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved.
003     *
004     * The contents of this file are subject to the terms of the Liferay Enterprise
005     * Subscription License ("License"). You may not use this file except in
006     * compliance with the License. You can obtain a copy of the License by
007     * contacting Liferay, Inc. See the License for the specific language governing
008     * permissions and limitations under the License, including but not limited to
009     * distribution rights of the Software.
010     *
011     *
012     *
013     */
014    
015    package com.liferay.portlet.layoutconfiguration.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.PipingPageContext;
021    import com.liferay.portal.kernel.servlet.PipingServletResponse;
022    import com.liferay.portal.kernel.servlet.PluginContextListener;
023    import com.liferay.portal.kernel.servlet.ServletContextPool;
024    import com.liferay.portal.kernel.util.GetterUtil;
025    import com.liferay.portal.kernel.util.JavaConstants;
026    import com.liferay.portal.kernel.util.StringBundler;
027    import com.liferay.portal.kernel.util.StringPool;
028    import com.liferay.portal.kernel.util.StringUtil;
029    import com.liferay.portal.kernel.util.Validator;
030    import com.liferay.portal.kernel.velocity.VelocityContext;
031    import com.liferay.portal.kernel.velocity.VelocityEngineUtil;
032    import com.liferay.portal.kernel.velocity.VelocityVariablesUtil;
033    import com.liferay.portal.model.LayoutTemplate;
034    import com.liferay.portal.model.LayoutTemplateConstants;
035    import com.liferay.portal.model.Portlet;
036    import com.liferay.portal.model.PortletConstants;
037    import com.liferay.portal.security.lang.PortalSecurityManagerThreadLocal;
038    import com.liferay.portal.security.pacl.PACLClassLoaderUtil;
039    import com.liferay.portal.security.pacl.PACLPolicy;
040    import com.liferay.portal.security.pacl.PACLPolicyManager;
041    import com.liferay.portal.service.LayoutTemplateLocalServiceUtil;
042    import com.liferay.portal.service.PortletLocalServiceUtil;
043    import com.liferay.portal.theme.PortletDisplay;
044    import com.liferay.portal.theme.PortletDisplayFactory;
045    import com.liferay.portal.theme.ThemeDisplay;
046    import com.liferay.portal.util.PortalUtil;
047    import com.liferay.portal.util.WebKeys;
048    import com.liferay.portlet.layoutconfiguration.util.velocity.CustomizationSettingsProcessor;
049    import com.liferay.portlet.layoutconfiguration.util.velocity.TemplateProcessor;
050    import com.liferay.portlet.layoutconfiguration.util.xml.ActionURLLogic;
051    import com.liferay.portlet.layoutconfiguration.util.xml.PortletLogic;
052    import com.liferay.portlet.layoutconfiguration.util.xml.RenderURLLogic;
053    import com.liferay.portlet.layoutconfiguration.util.xml.RuntimeLogic;
054    import com.liferay.taglib.util.VelocityTaglib;
055    
056    import java.lang.reflect.Constructor;
057    
058    import java.util.HashMap;
059    import java.util.Map;
060    
061    import javax.portlet.PortletConfig;
062    import javax.portlet.PortletRequest;
063    import javax.portlet.RenderRequest;
064    import javax.portlet.RenderResponse;
065    
066    import javax.servlet.ServletContext;
067    import javax.servlet.http.HttpServletRequest;
068    import javax.servlet.http.HttpServletResponse;
069    import javax.servlet.jsp.JspWriter;
070    import javax.servlet.jsp.PageContext;
071    
072    /**
073     * @author Brian Wing Shun Chan
074     * @author Raymond Augé
075     * @author Shuyang Zhou
076     */
077    public class RuntimePortletImpl implements RuntimePortlet {
078    
079            public String processCustomizationSettings(
080                            ServletContext servletContext, HttpServletRequest request,
081                            HttpServletResponse response, PageContext pageContext,
082                            String velocityTemplateId, String velocityTemplateContent)
083                    throws Exception {
084    
085                    return doDispatch(
086                            servletContext, request, response, pageContext, null, null,
087                            velocityTemplateId, velocityTemplateContent, false);
088            }
089    
090            public String processPortlet(
091                            ServletContext servletContext, HttpServletRequest request,
092                            HttpServletResponse response, Portlet portlet, String queryString,
093                            String columnId, Integer columnPos, Integer columnCount,
094                            String path, boolean writeOutput)
095                    throws Exception {
096    
097                    return processPortlet(
098                            servletContext, request, response, null, null, portlet,
099                            portlet.getPortletId(), queryString, columnId, columnPos,
100                            columnCount, path, writeOutput);
101            }
102    
103            public String processPortlet(
104                            ServletContext servletContext, HttpServletRequest request,
105                            HttpServletResponse response, RenderRequest renderRequest,
106                            RenderResponse renderResponse, Portlet portlet, String portletId,
107                            String queryString, String columnId, Integer columnPos,
108                            Integer columnCount, String path, boolean writeOutput)
109                    throws Exception {
110    
111                    ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
112                            WebKeys.THEME_DISPLAY);
113    
114                    if (portlet == null) {
115                            portlet = PortletLocalServiceUtil.getPortletById(
116                                    themeDisplay.getCompanyId(), portletId);
117                    }
118    
119                    if ((portlet != null) && portlet.isInstanceable() &&
120                            !portlet.isAddDefaultResource()) {
121    
122                            String instanceId = portlet.getInstanceId();
123    
124                            if (Validator.isNotNull(instanceId) &&
125                                    Validator.isPassword(instanceId) &&
126                                    (instanceId.length() >= 4)) {
127    
128                                    /*portletId += PortletConstants.INSTANCE_SEPARATOR + instanceId;
129    
130                                    portlet = PortletLocalServiceUtil.getPortletById(
131                                            themeDisplay.getCompanyId(), portletId);*/
132                            }
133                            else {
134                                    if (_log.isDebugEnabled()) {
135                                            _log.debug(
136                                                    "Portlet " + portlet.getPortletId() +
137                                                            " is instanceable but does not have a " +
138                                                                    "valid instance id");
139                                    }
140    
141                                    portlet = null;
142                            }
143                    }
144    
145                    if (portlet == null) {
146                            return StringPool.BLANK;
147                    }
148    
149                    // Capture the current portlet's settings to reset them once the child
150                    // portlet is rendered
151    
152                    PortletDisplay portletDisplay = themeDisplay.getPortletDisplay();
153    
154                    PortletDisplay portletDisplayClone = PortletDisplayFactory.create();
155    
156                    portletDisplay.copyTo(portletDisplayClone);
157    
158                    PortletConfig portletConfig = (PortletConfig)request.getAttribute(
159                            JavaConstants.JAVAX_PORTLET_CONFIG);
160    
161                    String lifecycle = (String)request.getAttribute(
162                            PortletRequest.LIFECYCLE_PHASE);
163    
164                    try {
165                            return PortalUtil.renderPortlet(
166                                    servletContext, request, response, portlet, queryString,
167                                    columnId, columnPos, columnCount, path, writeOutput);
168                    }
169                    finally {
170                            portletDisplay.copyFrom(portletDisplayClone);
171    
172                            portletDisplayClone.recycle();
173    
174                            _defineObjects(
175                                    request, portletConfig, renderRequest, renderResponse);
176    
177                            if (lifecycle != null) {
178                                    request.setAttribute(PortletRequest.LIFECYCLE_PHASE, lifecycle);
179                            }
180                    }
181            }
182    
183            public String processPortlet(
184                            ServletContext servletContext, HttpServletRequest request,
185                            HttpServletResponse response, RenderRequest renderRequest,
186                            RenderResponse renderResponse, String portletId, String queryString,
187                            boolean writeOutput)
188                    throws Exception {
189    
190                    return processPortlet(
191                            servletContext, request, response, renderRequest, renderResponse,
192                            portletId, queryString, null, null, null, writeOutput);
193            }
194    
195            public String processPortlet(
196                            ServletContext servletContext, HttpServletRequest request,
197                            HttpServletResponse response, RenderRequest renderRequest,
198                            RenderResponse renderResponse, String portletId, String queryString,
199                            String columnId, Integer columnPos, Integer columnCount,
200                            boolean writeOutput)
201                    throws Exception {
202    
203                    return processPortlet(
204                            servletContext, request, response, renderRequest, renderResponse,
205                            null, portletId, queryString, columnId, columnPos, columnCount,
206                            null, writeOutput);
207            }
208    
209            public void processTemplate(
210                            ServletContext servletContext, HttpServletRequest request,
211                            HttpServletResponse response, PageContext pageContext,
212                            JspWriter jspWriter, String velocityTemplateId,
213                            String velocityTemplateContent)
214                    throws Exception {
215    
216                    processTemplate(
217                            servletContext, request, response, pageContext, jspWriter, null,
218                            velocityTemplateId, velocityTemplateContent);
219            }
220    
221            public void processTemplate(
222                            ServletContext servletContext, HttpServletRequest request,
223                            HttpServletResponse response, PageContext pageContext,
224                            JspWriter jspWriter, String portletId, String velocityTemplateId,
225                            String velocityTemplateContent)
226                    throws Exception {
227    
228                    doDispatch(
229                            servletContext, request, response, pageContext, jspWriter,
230                            portletId, velocityTemplateId, velocityTemplateContent, true);
231            }
232    
233            public String processXML(
234                            HttpServletRequest request, String content,
235                            RuntimeLogic runtimeLogic)
236                    throws Exception {
237    
238                    if (Validator.isNull(content)) {
239                            return StringPool.BLANK;
240                    }
241    
242                    int index = content.indexOf(runtimeLogic.getOpenTag());
243    
244                    if (index == -1) {
245                            return content;
246                    }
247    
248                    Portlet renderPortlet = (Portlet)request.getAttribute(
249                            WebKeys.RENDER_PORTLET);
250    
251                    Boolean renderPortletResource = (Boolean)request.getAttribute(
252                            WebKeys.RENDER_PORTLET_RESOURCE);
253    
254                    String outerPortletId = (String)request.getAttribute(
255                            WebKeys.OUTER_PORTLET_ID);
256    
257                    if (outerPortletId == null) {
258                            request.setAttribute(
259                                    WebKeys.OUTER_PORTLET_ID, renderPortlet.getPortletId());
260                    }
261    
262                    try {
263                            request.setAttribute(WebKeys.RENDER_PORTLET_RESOURCE, Boolean.TRUE);
264    
265                            StringBundler sb = new StringBundler();
266    
267                            int x = 0;
268                            int y = index;
269    
270                            while (y != -1) {
271                                    sb.append(content.substring(x, y));
272    
273                                    int close1 = content.indexOf(runtimeLogic.getClose1Tag(), y);
274                                    int close2 = content.indexOf(runtimeLogic.getClose2Tag(), y);
275    
276                                    if ((close2 == -1) || ((close1 != -1) && (close1 < close2))) {
277                                            x = close1 + runtimeLogic.getClose1Tag().length();
278                                    }
279                                    else {
280                                            x = close2 + runtimeLogic.getClose2Tag().length();
281                                    }
282    
283                                    sb.append(runtimeLogic.processXML(content.substring(y, x)));
284    
285                                    y = content.indexOf(runtimeLogic.getOpenTag(), x);
286                            }
287    
288                            if (y == -1) {
289                                    sb.append(content.substring(x));
290                            }
291    
292                            return sb.toString();
293                    }
294                    finally {
295                            if (outerPortletId == null) {
296                                    request.removeAttribute(WebKeys.OUTER_PORTLET_ID);
297                            }
298    
299                            request.setAttribute(WebKeys.RENDER_PORTLET, renderPortlet);
300    
301                            if (renderPortletResource == null) {
302                                    request.removeAttribute(WebKeys.RENDER_PORTLET_RESOURCE);
303                            }
304                            else {
305                                    request.setAttribute(
306                                            WebKeys.RENDER_PORTLET_RESOURCE, renderPortletResource);
307                            }
308                    }
309            }
310    
311            public String processXML(
312                            ServletContext servletContext, HttpServletRequest request,
313                            HttpServletResponse response, RenderRequest renderRequest,
314                            RenderResponse renderResponse, String content)
315                    throws Exception {
316    
317                    RuntimeLogic portletLogic = new PortletLogic
318                            (servletContext, request, response, renderRequest, renderResponse);
319                    RuntimeLogic actionURLLogic = new ActionURLLogic(renderResponse);
320                    RuntimeLogic renderURLLogic = new RenderURLLogic(renderResponse);
321    
322                    content = RuntimePortletUtil.processXML(request, content, portletLogic);
323                    content = RuntimePortletUtil.processXML(
324                            request, content, actionURLLogic);
325                    content = RuntimePortletUtil.processXML(
326                            request, content, renderURLLogic);
327    
328                    return content;
329            }
330    
331            protected Object buildVelocityTaglib(
332                            HttpServletRequest request, HttpServletResponse response,
333                            PageContext pageContext)
334                    throws Exception {
335    
336                    // We have to load this class from the plugin class loader (context
337                    // class loader) or we will throw a ClassCastException
338    
339                    Class<?> clazz = Class.forName(VelocityTaglib.class.getName());
340    
341                    Constructor<?> constructor = clazz.getConstructor(
342                            ServletContext.class, HttpServletRequest.class,
343                            HttpServletResponse.class, PageContext.class);
344    
345                    return constructor.newInstance(
346                            pageContext.getServletContext(), request, response, pageContext);
347            }
348    
349            protected String doDispatch(
350                            ServletContext servletContext, HttpServletRequest request,
351                            HttpServletResponse response, PageContext pageContext,
352                            JspWriter jspWriter, String portletId, String velocityTemplateId,
353                            String velocityTemplateContent, boolean processTemplate)
354                    throws Exception {
355    
356                    if (Validator.isNull(velocityTemplateContent)) {
357                            return null;
358                    }
359    
360                    LayoutTemplate layoutTemplate = getLayoutTemplate(velocityTemplateId);
361    
362                    String pluginServletContextName = GetterUtil.getString(
363                            layoutTemplate.getServletContextName());
364    
365                    ServletContext pluginServletContext = ServletContextPool.get(
366                            pluginServletContextName);
367    
368                    ClassLoader pluginClassLoader = null;
369    
370                    if (pluginServletContext != null) {
371                            pluginClassLoader =
372                                    (ClassLoader)pluginServletContext.getAttribute(
373                                            PluginContextListener.PLUGIN_CLASS_LOADER);
374                    }
375    
376                    ClassLoader contextClassLoader =
377                            PACLClassLoaderUtil.getContextClassLoader();
378    
379                    PACLPolicy contextClassLoaderPACLPolicy =
380                            PACLPolicyManager.getPACLPolicy(contextClassLoader);
381                    PACLPolicy pluginClassLoaderPACLPolicy =
382                            PACLPolicyManager.getPACLPolicy(pluginClassLoader);
383    
384                    try {
385                            if ((pluginClassLoader != null) &&
386                                    (pluginClassLoader != contextClassLoader)) {
387    
388                                    PACLClassLoaderUtil.setContextClassLoader(pluginClassLoader);
389                                    PortalSecurityManagerThreadLocal.setPACLPolicy(
390                                            pluginClassLoaderPACLPolicy);
391                            }
392    
393                            if (processTemplate) {
394                                    doProcessTemplate(
395                                            servletContext, request, response, pageContext, jspWriter,
396                                            portletId, velocityTemplateId, velocityTemplateContent);
397                            }
398                            else {
399                                    return doProcessCustomizationSettings(
400                                            servletContext, request, response, pageContext,
401                                            velocityTemplateId, velocityTemplateContent);
402                            }
403    
404                            return null;
405                    }
406                    finally {
407                            if ((pluginClassLoader != null) &&
408                                    (pluginClassLoader != contextClassLoader)) {
409    
410                                    PortalSecurityManagerThreadLocal.setPACLPolicy(
411                                            contextClassLoaderPACLPolicy);
412                                    PACLClassLoaderUtil.setContextClassLoader(contextClassLoader);
413                            }
414                    }
415            }
416    
417            protected String doProcessCustomizationSettings(
418                            ServletContext servletContext, HttpServletRequest request,
419                            HttpServletResponse response, PageContext pageContext,
420                            String velocityTemplateId, String velocityTemplateContent)
421                    throws Exception {
422    
423                    if (Validator.isNull(velocityTemplateContent)) {
424                            return StringPool.BLANK;
425                    }
426    
427                    UnsyncStringWriter unsyncStringWriter = new UnsyncStringWriter();
428    
429                    CustomizationSettingsProcessor processor =
430                            new CustomizationSettingsProcessor(
431                                    request, new PipingPageContext(pageContext, unsyncStringWriter),
432                                    unsyncStringWriter);
433    
434                    VelocityContext velocityContext =
435                            VelocityEngineUtil.getWrappedClassLoaderToolsContext();
436    
437                    velocityContext.put("processor", processor);
438    
439                    // Velocity variables
440    
441                    VelocityVariablesUtil.insertVariables(velocityContext, request);
442    
443                    // liferay:include tag library
444    
445                    Object velocityTaglib = buildVelocityTaglib(
446                            request, response, pageContext);
447    
448                    velocityContext.put("taglibLiferay", velocityTaglib);
449                    velocityContext.put("theme", velocityTaglib);
450    
451                    try {
452                            VelocityEngineUtil.mergeTemplate(
453                                    velocityTemplateId, velocityTemplateContent, velocityContext,
454                                    unsyncStringWriter);
455                    }
456                    catch (Exception e) {
457                            _log.error(e, e);
458    
459                            throw e;
460                    }
461    
462                    return unsyncStringWriter.toString();
463            }
464    
465            protected void doProcessTemplate(
466                            ServletContext servletContext, HttpServletRequest request,
467                            HttpServletResponse response, PageContext pageContext,
468                            JspWriter jspWriter, String portletId, String velocityTemplateId,
469                            String velocityTemplateContent)
470                    throws Exception {
471    
472                    if (Validator.isNull(velocityTemplateContent)) {
473                            return;
474                    }
475    
476                    TemplateProcessor processor = new TemplateProcessor(
477                            servletContext, request, response, portletId);
478    
479                    VelocityContext velocityContext =
480                            VelocityEngineUtil.getWrappedClassLoaderToolsContext();
481    
482                    velocityContext.put("processor", processor);
483    
484                    // Velocity variables
485    
486                    VelocityVariablesUtil.insertVariables(velocityContext, request);
487    
488                    // liferay:include tag library
489    
490                    UnsyncStringWriter unsyncStringWriter = new UnsyncStringWriter();
491    
492                    Object velocityTaglib = buildVelocityTaglib(
493                            request, response, pageContext);
494    
495                    velocityContext.put("taglibLiferay", velocityTaglib);
496                    velocityContext.put("theme", velocityTaglib);
497    
498                    try {
499                            VelocityEngineUtil.mergeTemplate(
500                                    velocityTemplateId, velocityTemplateContent, velocityContext,
501                                    unsyncStringWriter);
502                    }
503                    catch (Exception e) {
504                            _log.error(e, e);
505    
506                            throw e;
507                    }
508    
509                    String output = unsyncStringWriter.toString();
510    
511                    Map<Portlet, Object[]> portletsMap = processor.getPortletsMap();
512    
513                    Map<String, StringBundler> contentsMap =
514                            new HashMap<String, StringBundler>(portletsMap.size());
515    
516                    for (Map.Entry<Portlet, Object[]> entry : portletsMap.entrySet()) {
517                            Portlet portlet = entry.getKey();
518                            Object[] value = entry.getValue();
519    
520                            String queryString = (String)value[0];
521                            String columnId = (String)value[1];
522                            Integer columnPos = (Integer)value[2];
523                            Integer columnCount = (Integer)value[3];
524    
525                            UnsyncStringWriter portletUnsyncStringWriter =
526                                    new UnsyncStringWriter();
527    
528                            PipingServletResponse pipingServletResponse =
529                                    new PipingServletResponse(response, portletUnsyncStringWriter);
530    
531                            processPortlet(
532                                    servletContext, request, pipingServletResponse, portlet,
533                                    queryString, columnId, columnPos, columnCount, null, true);
534    
535                            contentsMap.put(
536                                    portlet.getPortletId(),
537                                    portletUnsyncStringWriter.getStringBundler());
538                    }
539    
540                    StringBundler sb = StringUtil.replaceWithStringBundler(
541                            output, "[$TEMPLATE_PORTLET_", "$]", contentsMap);
542    
543                    sb.writeTo(jspWriter);
544            }
545    
546            protected LayoutTemplate getLayoutTemplate(String velocityTemplateId) {
547                    String separator = LayoutTemplateConstants.CUSTOM_SEPARATOR;
548                    boolean standard = false;
549    
550                    if (velocityTemplateId.contains(
551                                    LayoutTemplateConstants.STANDARD_SEPARATOR)) {
552    
553                            separator = LayoutTemplateConstants.STANDARD_SEPARATOR;
554                            standard = true;
555                    }
556    
557                    String layoutTemplateId = null;
558    
559                    String themeId = null;
560    
561                    int pos = velocityTemplateId.indexOf(separator);
562    
563                    if (pos != -1) {
564                            layoutTemplateId = velocityTemplateId.substring(
565                                    pos + separator.length());
566    
567                            themeId = velocityTemplateId.substring(0, pos);
568                    }
569    
570                    pos = layoutTemplateId.indexOf(PortletConstants.INSTANCE_SEPARATOR);
571    
572                    if (pos != -1) {
573                            layoutTemplateId = layoutTemplateId.substring(
574                                    pos + PortletConstants.INSTANCE_SEPARATOR.length() + 1);
575    
576                            pos = layoutTemplateId.indexOf(StringPool.UNDERLINE);
577    
578                            layoutTemplateId = layoutTemplateId.substring(pos + 1);
579                    }
580    
581                    return LayoutTemplateLocalServiceUtil.getLayoutTemplate(
582                            layoutTemplateId, standard, themeId);
583            }
584    
585            private static void _defineObjects(
586                    HttpServletRequest request, PortletConfig portletConfig,
587                    RenderRequest renderRequest, RenderResponse renderResponse) {
588    
589                    if (portletConfig != null) {
590                            request.setAttribute(
591                                    JavaConstants.JAVAX_PORTLET_CONFIG, portletConfig);
592                    }
593    
594                    if (renderRequest != null) {
595                            request.setAttribute(
596                                    JavaConstants.JAVAX_PORTLET_REQUEST, renderRequest);
597                    }
598    
599                    if (renderResponse != null) {
600                            request.setAttribute(
601                                    JavaConstants.JAVAX_PORTLET_RESPONSE, renderResponse);
602                    }
603            }
604    
605            private static Log _log = LogFactoryUtil.getLog(RuntimePortletUtil.class);
606    
607    }