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