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