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