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