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