001    /**
002     * Copyright (c) 2000-2012 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.portlet;
016    
017    import com.liferay.portal.kernel.language.LanguageUtil;
018    import com.liferay.portal.kernel.log.Log;
019    import com.liferay.portal.kernel.log.LogFactoryUtil;
020    import com.liferay.portal.kernel.portlet.LiferayPortletRequest;
021    import com.liferay.portal.kernel.portlet.LiferayPortletResponse;
022    import com.liferay.portal.kernel.portlet.PortletFilterUtil;
023    import com.liferay.portal.kernel.servlet.BufferCacheServletResponse;
024    import com.liferay.portal.kernel.servlet.PluginContextListener;
025    import com.liferay.portal.kernel.servlet.PortletServlet;
026    import com.liferay.portal.kernel.util.ClassUtil;
027    import com.liferay.portal.kernel.util.GetterUtil;
028    import com.liferay.portal.kernel.util.JavaConstants;
029    import com.liferay.portal.kernel.util.StringBundler;
030    import com.liferay.portal.kernel.util.StringPool;
031    import com.liferay.portal.kernel.util.StringUtil;
032    import com.liferay.portal.kernel.util.Time;
033    import com.liferay.portal.model.Layout;
034    import com.liferay.portal.model.PortletApp;
035    import com.liferay.portal.model.PortletConstants;
036    import com.liferay.portal.tools.deploy.PortletDeployer;
037    import com.liferay.portal.util.ClassLoaderUtil;
038    import com.liferay.portal.util.WebKeys;
039    
040    import java.io.IOException;
041    import java.io.PrintWriter;
042    
043    import java.util.ArrayList;
044    import java.util.List;
045    import java.util.Map;
046    import java.util.Set;
047    import java.util.concurrent.ConcurrentHashMap;
048    
049    import javax.portlet.ActionRequest;
050    import javax.portlet.ActionResponse;
051    import javax.portlet.EventRequest;
052    import javax.portlet.EventResponse;
053    import javax.portlet.Portlet;
054    import javax.portlet.PortletConfig;
055    import javax.portlet.PortletContext;
056    import javax.portlet.PortletException;
057    import javax.portlet.PortletRequest;
058    import javax.portlet.PortletSession;
059    import javax.portlet.RenderRequest;
060    import javax.portlet.RenderResponse;
061    import javax.portlet.ResourceRequest;
062    import javax.portlet.ResourceResponse;
063    import javax.portlet.filter.ActionFilter;
064    import javax.portlet.filter.EventFilter;
065    import javax.portlet.filter.FilterChain;
066    import javax.portlet.filter.PortletFilter;
067    import javax.portlet.filter.RenderFilter;
068    import javax.portlet.filter.ResourceFilter;
069    
070    import javax.servlet.RequestDispatcher;
071    import javax.servlet.ServletException;
072    import javax.servlet.http.HttpServletRequest;
073    import javax.servlet.http.HttpServletResponse;
074    import javax.servlet.http.HttpSession;
075    
076    import org.apache.commons.lang.time.StopWatch;
077    
078    /**
079     * @author Brian Wing Shun Chan
080     * @author Brian Myunghun Kim
081     */
082    public class InvokerPortletImpl implements InvokerPortlet {
083    
084            public static void clearResponse(
085                    HttpSession session, long plid, String portletId, String languageId) {
086    
087                    String sesResponseId = encodeResponseKey(plid, portletId, languageId);
088    
089                    getResponses(session).remove(sesResponseId);
090            }
091    
092            public static void clearResponses(HttpSession session) {
093                    getResponses(session).clear();
094            }
095    
096            public static void clearResponses(PortletSession session) {
097                    getResponses(session).clear();
098            }
099    
100            public static String encodeResponseKey(
101                    long plid, String portletId, String languageId) {
102    
103                    StringBundler sb = new StringBundler(5);
104    
105                    sb.append(StringUtil.toHexString(plid));
106                    sb.append(StringPool.UNDERLINE);
107                    sb.append(portletId);
108                    sb.append(StringPool.UNDERLINE);
109                    sb.append(languageId);
110    
111                    return sb.toString();
112            }
113    
114            public static Map<String, InvokerPortletResponse> getResponses(
115                    HttpSession session) {
116    
117                    Map<String, InvokerPortletResponse> responses =
118                            (Map<String, InvokerPortletResponse>)session.getAttribute(
119                                    WebKeys.CACHE_PORTLET_RESPONSES);
120    
121                    if (responses == null) {
122                            responses = new ConcurrentHashMap<String, InvokerPortletResponse>();
123    
124                            session.setAttribute(WebKeys.CACHE_PORTLET_RESPONSES, responses);
125                    }
126    
127                    return responses;
128            }
129    
130            public static Map<String, InvokerPortletResponse> getResponses(
131                    PortletSession portletSession) {
132    
133                    return getResponses(
134                            ((PortletSessionImpl)portletSession).getHttpSession());
135            }
136    
137            public InvokerPortletImpl(
138                            com.liferay.portal.model.Portlet portletModel, Portlet portlet,
139                            PortletConfig portletConfig, PortletContext portletContext,
140                            boolean checkAuthToken, boolean facesPortlet, boolean strutsPortlet,
141                            boolean strutsBridgePortlet)
142                    throws PortletException {
143    
144                    _initialize(
145                            portletModel, portlet, portletConfig, portletContext,
146                            checkAuthToken, facesPortlet, strutsPortlet, strutsBridgePortlet);
147            }
148    
149            public InvokerPortletImpl(
150                            com.liferay.portal.model.Portlet portletModel, Portlet portlet,
151                            PortletContext portletContext)
152                    throws PortletException {
153    
154                    Map<String, String> initParams = portletModel.getInitParams();
155    
156                    boolean checkAuthToken = GetterUtil.getBoolean(
157                            initParams.get("check-auth-token"), true);
158    
159                    boolean facesPortlet = false;
160    
161                    if (ClassUtil.isSubclass(
162                                    portlet.getClass(), PortletDeployer.JSF_MYFACES) ||
163                            ClassUtil.isSubclass(
164                                    portlet.getClass(), PortletDeployer.JSF_STANDARD) ||
165                            ClassUtil.isSubclass(
166                                    portlet.getClass(), PortletDeployer.JSF_SUN)) {
167    
168                            facesPortlet = true;
169                    }
170    
171                    boolean strutsPortlet = ClassUtil.isSubclass(
172                            portlet.getClass(), StrutsPortlet.class);
173    
174                    boolean strutsBridgePortlet = ClassUtil.isSubclass(
175                            portlet.getClass(),
176                            "org.apache.portals.bridges.struts.StrutsPortlet");
177    
178                    _initialize(
179                            portletModel, portlet, null, portletContext, checkAuthToken,
180                            facesPortlet, strutsPortlet, strutsBridgePortlet);
181            }
182    
183            public void destroy() {
184                    if (PortletConstants.hasInstanceId(_portletModel.getPortletId())) {
185                            if (_log.isWarnEnabled()) {
186                                    _log.warn("Destroying an instanced portlet is not allowed");
187                            }
188    
189                            return;
190                    }
191    
192                    ClassLoader contextClassLoader =
193                            ClassLoaderUtil.getContextClassLoader();
194    
195                    ClassLoader portletClassLoader = getPortletClassLoader();
196    
197                    try {
198                            if (portletClassLoader != null) {
199                                    ClassLoaderUtil.setContextClassLoader(portletClassLoader);
200                            }
201    
202                            removePortletFilters();
203    
204                            _portlet.destroy();
205                    }
206                    finally {
207                            if (portletClassLoader != null) {
208                                    ClassLoaderUtil.setContextClassLoader(contextClassLoader);
209                            }
210                    }
211            }
212    
213            public Integer getExpCache() {
214                    return _expCache;
215            }
216    
217            public Portlet getPortlet() {
218                    return _portlet;
219            }
220    
221            public ClassLoader getPortletClassLoader() {
222                    ClassLoader classLoader = (ClassLoader)_portletContextImpl.getAttribute(
223                            PluginContextListener.PLUGIN_CLASS_LOADER);
224    
225                    if (classLoader == null) {
226                            classLoader = ClassLoaderUtil.getPortalClassLoader();
227                    }
228    
229                    return classLoader;
230            }
231    
232            public PortletConfigImpl getPortletConfig() {
233                    return _portletConfigImpl;
234            }
235    
236            public PortletContextImpl getPortletContext() {
237                    return _portletContextImpl;
238            }
239    
240            public Portlet getPortletInstance() {
241                    return _portlet;
242            }
243    
244            public void init(PortletConfig portletConfig) throws PortletException {
245                    _portletConfigImpl = (PortletConfigImpl)portletConfig;
246    
247                    ClassLoader contextClassLoader =
248                            ClassLoaderUtil.getContextClassLoader();
249    
250                    ClassLoader portletClassLoader = getPortletClassLoader();
251    
252                    try {
253                            if (portletClassLoader != null) {
254                                    ClassLoaderUtil.setContextClassLoader(portletClassLoader);
255                            }
256    
257                            _portlet.init(portletConfig);
258                    }
259                    finally {
260                            if (portletClassLoader != null) {
261                                    ClassLoaderUtil.setContextClassLoader(contextClassLoader);
262                            }
263                    }
264            }
265    
266            public boolean isCheckAuthToken() {
267                    return _checkAuthToken;
268            }
269    
270            public boolean isFacesPortlet() {
271                    return _facesPortlet;
272            }
273    
274            public boolean isStrutsBridgePortlet() {
275                    return _strutsBridgePortlet;
276            }
277    
278            public boolean isStrutsPortlet() {
279                    return _strutsPortlet;
280            }
281    
282            public void processAction(
283                            ActionRequest actionRequest, ActionResponse actionResponse)
284                    throws IOException {
285    
286                    StopWatch stopWatch = null;
287    
288                    if (_log.isDebugEnabled()) {
289                            stopWatch = new StopWatch();
290    
291                            stopWatch.start();
292                    }
293    
294                    try {
295                            invokeAction(actionRequest, actionResponse);
296                    }
297                    catch (PortletException pe) {
298                            actionRequest.setAttribute(
299                                    _portletId + PortletException.class.getName(), pe);
300                    }
301    
302                    if (_log.isDebugEnabled()) {
303                            if (stopWatch != null) {
304                                    _log.debug(
305                                            "processAction for " + _portletId + " takes " +
306                                                    stopWatch.getTime() + " ms");
307                            }
308                            else {
309                                    _log.debug("processAction for " + _portletId + " is finished");
310                            }
311                    }
312            }
313    
314            public void processEvent(
315                            EventRequest eventRequest, EventResponse eventResponse)
316                    throws IOException, PortletException {
317    
318                    StopWatch stopWatch = null;
319    
320                    if (_log.isDebugEnabled()) {
321                            stopWatch = new StopWatch();
322    
323                            stopWatch.start();
324                    }
325    
326                    invokeEvent(eventRequest, eventResponse);
327    
328                    if (_log.isDebugEnabled()) {
329                            _log.debug(
330                                    "processEvent for " + _portletId + " takes " +
331                                            stopWatch.getTime() + " ms");
332                    }
333            }
334    
335            public void render(
336                            RenderRequest renderRequest, RenderResponse renderResponse)
337                    throws IOException, PortletException {
338    
339                    PortletException portletException =
340                            (PortletException)renderRequest.getAttribute(
341                                    _portletId + PortletException.class.getName());
342    
343                    if (portletException != null) {
344                            throw portletException;
345                    }
346    
347                    StopWatch stopWatch = null;
348    
349                    if (_log.isDebugEnabled()) {
350                            stopWatch = new StopWatch();
351    
352                            stopWatch.start();
353                    }
354    
355                    String remoteUser = renderRequest.getRemoteUser();
356    
357                    if ((remoteUser == null) || (_expCache == null) ||
358                            (_expCache.intValue() == 0)) {
359    
360                            invokeRender(renderRequest, renderResponse);
361                    }
362                    else {
363                            RenderResponseImpl renderResponseImpl =
364                                    (RenderResponseImpl)renderResponse;
365    
366                            BufferCacheServletResponse bufferCacheServletResponse =
367                                    (BufferCacheServletResponse)
368                                            renderResponseImpl.getHttpServletResponse();
369    
370                            PortletSession portletSession = renderRequest.getPortletSession();
371    
372                            long now = System.currentTimeMillis();
373    
374                            Layout layout = (Layout)renderRequest.getAttribute(WebKeys.LAYOUT);
375    
376                            Map<String, InvokerPortletResponse> sessionResponses = getResponses(
377                                    portletSession);
378    
379                            String sessionResponseId = encodeResponseKey(
380                                    layout.getPlid(), _portletId,
381                                    LanguageUtil.getLanguageId(renderRequest));
382    
383                            InvokerPortletResponse response = sessionResponses.get(
384                                    sessionResponseId);
385    
386                            if (response == null) {
387                                    String title = invokeRender(renderRequest, renderResponse);
388    
389                                    response = new InvokerPortletResponse(
390                                            title, bufferCacheServletResponse.getString(),
391                                            now + Time.SECOND * _expCache.intValue());
392    
393                                    sessionResponses.put(sessionResponseId, response);
394                            }
395                            else if ((response.getTime() < now) && (_expCache.intValue() > 0)) {
396                                    String title = invokeRender(renderRequest, renderResponse);
397    
398                                    response.setTitle(title);
399                                    response.setContent(bufferCacheServletResponse.getString());
400                                    response.setTime(now + Time.SECOND * _expCache.intValue());
401                            }
402                            else {
403                                    renderResponseImpl.setTitle(response.getTitle());
404    
405                                    PrintWriter printWriter =
406                                            bufferCacheServletResponse.getWriter();
407    
408                                    printWriter.print(response.getContent());
409                            }
410                    }
411    
412                    Map<String, String[]> properties =
413                            ((RenderResponseImpl)renderResponse).getProperties();
414    
415                    if (properties.containsKey("clear-request-parameters")) {
416                            Map<String, String[]> renderParameters =
417                                    ((RenderRequestImpl)renderRequest).getRenderParameters();
418    
419                            renderParameters.clear();
420                    }
421    
422                    if (_log.isDebugEnabled()) {
423                            _log.debug(
424                                    "render for " + _portletId + " takes " + stopWatch.getTime() +
425                                            " ms");
426                    }
427            }
428    
429            public void serveResource(
430                            ResourceRequest resourceRequest, ResourceResponse resourceResponse)
431                    throws IOException {
432    
433                    StopWatch stopWatch = null;
434    
435                    if (_log.isDebugEnabled()) {
436                            stopWatch = new StopWatch();
437    
438                            stopWatch.start();
439                    }
440    
441                    try {
442                            invokeResource(resourceRequest, resourceResponse);
443                    }
444                    catch (PortletException pe) {
445                            resourceRequest.setAttribute(
446                                    _portletId + PortletException.class.getName(), pe);
447                    }
448    
449                    if (_log.isDebugEnabled()) {
450                            _log.debug(
451                                    "serveResource for " + _portletId + " takes " +
452                                            stopWatch.getTime() + " ms");
453                    }
454            }
455    
456            public void setPortletFilters() throws PortletException {
457                    PortletApp portletApp = _portletModel.getPortletApp();
458    
459                    PortletContextBag portletContextBag = PortletContextBagPool.get(
460                            portletApp.getServletContextName());
461    
462                    if (portletApp.isWARFile() && (portletContextBag == null)) {
463                            return;
464                    }
465    
466                    removePortletFilters();
467    
468                    Map<String, com.liferay.portal.model.PortletFilter> portletFilters =
469                            _portletModel.getPortletFilters();
470    
471                    for (Map.Entry<String, com.liferay.portal.model.PortletFilter> entry :
472                                    portletFilters.entrySet()) {
473    
474                            com.liferay.portal.model.PortletFilter portletFilterModel =
475                                    entry.getValue();
476    
477                            PortletFilter portletFilter = PortletFilterFactory.create(
478                                    portletFilterModel, _portletContextImpl);
479    
480                            Set<String> lifecycles = portletFilterModel.getLifecycles();
481    
482                            if (lifecycles.contains(PortletRequest.ACTION_PHASE)) {
483                                    _actionFilters.add((ActionFilter)portletFilter);
484                            }
485    
486                            if (lifecycles.contains(PortletRequest.EVENT_PHASE)) {
487                                    _eventFilters.add((EventFilter)portletFilter);
488                            }
489    
490                            if (lifecycles.contains(PortletRequest.RENDER_PHASE)) {
491                                    _renderFilters.add((RenderFilter)portletFilter);
492                            }
493    
494                            if (lifecycles.contains(PortletRequest.RESOURCE_PHASE)) {
495                                    _resourceFilters.add((ResourceFilter)portletFilter);
496                            }
497                    }
498            }
499    
500            protected void invoke(
501                            LiferayPortletRequest portletRequest,
502                            LiferayPortletResponse portletResponse, String lifecycle,
503                            List<? extends PortletFilter> filters)
504                    throws IOException, PortletException {
505    
506                    FilterChain filterChain = new FilterChainImpl(_portlet, filters);
507    
508                    if (_portletConfigImpl.isWARFile()) {
509                            String invokerPortletName = _portletConfigImpl.getInitParameter(
510                                    INIT_INVOKER_PORTLET_NAME);
511    
512                            if (invokerPortletName == null) {
513                                    invokerPortletName = _portletConfigImpl.getPortletName();
514                            }
515    
516                            String path = StringPool.SLASH + invokerPortletName + "/invoke";
517    
518                            RequestDispatcher requestDispatcher =
519                                    _portletContextImpl.getServletContext().getRequestDispatcher(
520                                            path);
521    
522                            HttpServletRequest request = portletRequest.getHttpServletRequest();
523                            HttpServletResponse response =
524                                    portletResponse.getHttpServletResponse();
525    
526                            request.setAttribute(JavaConstants.JAVAX_PORTLET_PORTLET, _portlet);
527                            request.setAttribute(PortletRequest.LIFECYCLE_PHASE, lifecycle);
528                            request.setAttribute(
529                                    PortletServlet.PORTLET_SERVLET_FILTER_CHAIN, filterChain);
530    
531                            try {
532    
533                                    // Resource phase must be a forward because includes do not
534                                    // allow you to specify the content type or headers
535    
536                                    if (lifecycle.equals(PortletRequest.RESOURCE_PHASE)) {
537                                            requestDispatcher.forward(request, response);
538                                    }
539                                    else {
540                                            requestDispatcher.include(request, response);
541                                    }
542                            }
543                            catch (ServletException se) {
544                                    Throwable cause = se.getRootCause();
545    
546                                    if (cause instanceof PortletException) {
547                                            throw (PortletException)cause;
548                                    }
549    
550                                    throw new PortletException(cause);
551                            }
552                    }
553                    else {
554                            PortletFilterUtil.doFilter(
555                                    portletRequest, portletResponse, lifecycle, filterChain);
556                    }
557    
558                    portletResponse.transferMarkupHeadElements();
559    
560                    Map<String, String[]> properties = portletResponse.getProperties();
561    
562                    if ((properties != null) && (properties.size() > 0)) {
563                            if (_expCache != null) {
564                                    String[] expCache = properties.get(
565                                            RenderResponse.EXPIRATION_CACHE);
566    
567                                    if ((expCache != null) && (expCache.length > 0) &&
568                                            (expCache[0] != null)) {
569    
570                                            _expCache = new Integer(GetterUtil.getInteger(expCache[0]));
571                                    }
572                            }
573                    }
574            }
575    
576            protected void invokeAction(
577                            ActionRequest actionRequest, ActionResponse actionResponse)
578                    throws IOException, PortletException {
579    
580                    LiferayPortletRequest portletRequest =
581                            (LiferayPortletRequest)actionRequest;
582                    LiferayPortletResponse portletResponse =
583                            (LiferayPortletResponse)actionResponse;
584    
585                    invoke(
586                            portletRequest, portletResponse, PortletRequest.ACTION_PHASE,
587                            _actionFilters);
588            }
589    
590            protected void invokeEvent(
591                            EventRequest eventRequest, EventResponse eventResponse)
592                    throws IOException, PortletException {
593    
594                    LiferayPortletRequest portletRequest =
595                            (LiferayPortletRequest)eventRequest;
596                    LiferayPortletResponse portletResponse =
597                            (LiferayPortletResponse)eventResponse;
598    
599                    invoke(
600                            portletRequest, portletResponse, PortletRequest.EVENT_PHASE,
601                            _eventFilters);
602            }
603    
604            protected String invokeRender(
605                            RenderRequest renderRequest, RenderResponse renderResponse)
606                    throws IOException, PortletException {
607    
608                    LiferayPortletRequest portletRequest =
609                            (LiferayPortletRequest)renderRequest;
610                    LiferayPortletResponse portletResponse =
611                            (LiferayPortletResponse)renderResponse;
612    
613                    invoke(
614                            portletRequest, portletResponse, PortletRequest.RENDER_PHASE,
615                            _renderFilters);
616    
617                    RenderResponseImpl renderResponseImpl =
618                            (RenderResponseImpl)renderResponse;
619    
620                    return renderResponseImpl.getTitle();
621            }
622    
623            protected void invokeResource(
624                            ResourceRequest resourceRequest, ResourceResponse resourceResponse)
625                    throws IOException, PortletException {
626    
627                    LiferayPortletRequest portletRequest =
628                            (LiferayPortletRequest)resourceRequest;
629                    LiferayPortletResponse portletResponse =
630                            (LiferayPortletResponse)resourceResponse;
631    
632                    invoke(
633                            portletRequest, portletResponse, PortletRequest.RESOURCE_PHASE,
634                            _resourceFilters);
635            }
636    
637            protected void removePortletFilters() {
638                    _actionFilters.clear();
639                    _eventFilters.clear();
640                    _renderFilters.clear();
641                    _resourceFilters.clear();
642            }
643    
644            private void _initialize(
645                            com.liferay.portal.model.Portlet portletModel, Portlet portlet,
646                            PortletConfig portletConfig, PortletContext portletContext,
647                            boolean checkAuthToken, boolean facesPortlet, boolean strutsPortlet,
648                            boolean strutsBridgePortlet)
649                    throws PortletException {
650    
651                    _portletModel = portletModel;
652                    _portlet = portlet;
653                    _portletConfigImpl = (PortletConfigImpl)portletConfig;
654                    _portletId = _portletModel.getPortletId();
655                    _portletContextImpl = (PortletContextImpl)portletContext;
656                    _checkAuthToken = checkAuthToken;
657                    _facesPortlet = facesPortlet;
658                    _strutsPortlet = strutsPortlet;
659                    _strutsBridgePortlet = strutsBridgePortlet;
660                    _expCache = portletModel.getExpCache();
661                    setPortletFilters();
662    
663                    if (_log.isDebugEnabled()) {
664                            _log.debug(
665                                    "Create instance cache wrapper for " +
666                                            _portletContextImpl.getPortlet().getPortletId());
667                    }
668            }
669    
670            private static Log _log = LogFactoryUtil.getLog(InvokerPortletImpl.class);
671    
672            private List<ActionFilter> _actionFilters = new ArrayList<ActionFilter>();
673            private boolean _checkAuthToken;
674            private List<EventFilter> _eventFilters = new ArrayList<EventFilter>();
675            private Integer _expCache;
676            private boolean _facesPortlet;
677            private Portlet _portlet;
678            private PortletConfigImpl _portletConfigImpl;
679            private PortletContextImpl _portletContextImpl;
680            private String _portletId;
681            private com.liferay.portal.model.Portlet _portletModel;
682            private List<RenderFilter> _renderFilters = new ArrayList<RenderFilter>();
683            private List<ResourceFilter> _resourceFilters =
684                    new ArrayList<ResourceFilter>();
685            private boolean _strutsBridgePortlet;
686            private boolean _strutsPortlet;
687    
688    }