Package org.jasig.portal.portlet.rendering

Source Code of org.jasig.portal.portlet.rendering.PortletRendererImpl

/**
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.jasig.portal.portlet.rendering;

import java.io.IOException;

import javax.portlet.CacheControl;
import javax.portlet.Event;
import javax.portlet.PortletException;
import javax.portlet.PortletMode;
import javax.portlet.PortletRequest;
import javax.portlet.PortletSession;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.pluto.container.PortletContainer;
import org.apache.pluto.container.PortletContainerException;
import org.jasig.portal.AuthorizationException;
import org.jasig.portal.EntityIdentifier;
import org.jasig.portal.api.portlet.PortletDelegationLocator;
import org.jasig.portal.events.IPortletExecutionEventFactory;
import org.jasig.portal.portlet.PortletDispatchException;
import org.jasig.portal.portlet.container.cache.CacheControlImpl;
import org.jasig.portal.portlet.container.cache.CacheState;
import org.jasig.portal.portlet.container.cache.CachedPortletData;
import org.jasig.portal.portlet.container.cache.CachedPortletResourceData;
import org.jasig.portal.portlet.container.cache.CachingPortletOutputHandler;
import org.jasig.portal.portlet.container.cache.CachingPortletResourceOutputHandler;
import org.jasig.portal.portlet.container.cache.HeaderSettingCacheControl;
import org.jasig.portal.portlet.container.cache.IPortletCacheControlService;
import org.jasig.portal.portlet.container.cache.PortletCachingHeaderUtils;
import org.jasig.portal.portlet.container.services.AdministrativeRequestListenerController;
import org.jasig.portal.portlet.om.IPortletDefinition;
import org.jasig.portal.portlet.om.IPortletEntity;
import org.jasig.portal.portlet.om.IPortletWindow;
import org.jasig.portal.portlet.om.IPortletWindowId;
import org.jasig.portal.portlet.registry.IPortletWindowRegistry;
import org.jasig.portal.portlet.session.PortletSessionAdministrativeRequestListener;
import org.jasig.portal.security.IAuthorizationPrincipal;
import org.jasig.portal.security.IPerson;
import org.jasig.portal.security.IPersonManager;
import org.jasig.portal.services.AuthorizationService;
import org.jasig.portal.url.IPortalRequestInfo;
import org.jasig.portal.url.IUrlSyntaxProvider;
import org.jasig.portal.url.ParameterMap;
import org.jasig.portal.utils.web.PortletHttpServletRequestWrapper;
import org.jasig.portal.utils.web.PortletHttpServletResponseWrapper;
import org.jasig.portal.utils.web.PortletMimeHttpServletResponseWrapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
* Executes methods on portlets using Pluto
*
* @author Eric Dalquist
* @version $Revision$
*/
@Service
public class PortletRendererImpl implements IPortletRenderer {
    protected final Logger logger = LoggerFactory.getLogger(this.getClass());
   
    private IPersonManager personManager;
    private IPortletWindowRegistry portletWindowRegistry;
    private PortletContainer portletContainer;
    private PortletDelegationLocator portletDelegationLocator;
    private IPortletCacheControlService portletCacheControlService;
    private IPortletExecutionEventFactory portalEventFactory;
    private IUrlSyntaxProvider urlSyntaxProvider;

    @Autowired
    public void setUrlSyntaxProvider(IUrlSyntaxProvider urlSyntaxProvider) {
        this.urlSyntaxProvider = urlSyntaxProvider;
    }
    @Autowired
    public void setPortalEventFactory(IPortletExecutionEventFactory portalEventFactory) {
        this.portalEventFactory = portalEventFactory;
    }
    @Autowired
    public void setPersonManager(IPersonManager personManager) {
        this.personManager = personManager;
    }
    @Autowired
    public void setPortletWindowRegistry(IPortletWindowRegistry portletWindowRegistry) {
        this.portletWindowRegistry = portletWindowRegistry;
    }
    @Autowired
    public void setPortletContainer(PortletContainer portletContainer) {
        this.portletContainer = portletContainer;
    }
    @Autowired
    public void setPortletDelegationLocator(PortletDelegationLocator portletDelegationLocator) {
        this.portletDelegationLocator = portletDelegationLocator;
    }
    /**
     * @param portletCacheControlService the portletCacheControlService to set
     */
    @Autowired
    public void setPortletCacheControlService(
            IPortletCacheControlService portletCacheControlService) {
        this.portletCacheControlService = portletCacheControlService;
    }
   
   
    /*
     * PLT 22.1 If the content of a portlet is cached and the portlet is target of request
     * with an action-type semantic (e.g. an action or event call), the portlet container should discard the cache and
     * invoke the corresponding request handling methods of the portlet like processAction,or processEvent.
     *
     *  (non-Javadoc)
     * @see org.jasig.portal.channels.portlet.IPortletRenderer#doAction(org.jasig.portal.portlet.om.IPortletWindowId, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
     */
    @Override
    public long doAction(IPortletWindowId portletWindowId, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
        this.portletCacheControlService.purgeCachedPortletData(portletWindowId, httpServletRequest);
       
        final IPortletWindow portletWindow = this.portletWindowRegistry.getPortletWindow(httpServletRequest, portletWindowId);

        enforceConfigPermission(httpServletRequest, portletWindow);
       
        httpServletRequest = this.setupPortletRequest(httpServletRequest);
        httpServletResponse = new PortletHttpServletResponseWrapper(httpServletResponse, portletWindow);
       
        //Execute the action,
        this.logger.debug("Executing portlet action for window '{}'", portletWindow);

        final long start = System.nanoTime();
        try {
            this.portletContainer.doAction(portletWindow.getPlutoPortletWindow(), httpServletRequest, httpServletResponse);
        }
        catch (PortletException pe) {
            throw new PortletDispatchException("The portlet window '" + portletWindow + "' threw an exception while executing action.", portletWindow, pe);
        }
        catch (PortletContainerException pce) {
            throw new PortletDispatchException("The portlet container threw an exception while executing action on portlet window '" + portletWindow + "'.", portletWindow, pce);
        }
        catch (IOException ioe) {
            throw new PortletDispatchException("The portlet window '" + portletWindow + "' threw an exception while executing action.", portletWindow, ioe);
        }
       
        final long executionTime = System.nanoTime() - start;
       
        this.portalEventFactory.publishPortletActionExecutionEvent(httpServletRequest, this, portletWindowId, executionTime);
       
        return executionTime;
    }
   
    /*
     * PLT 22.1 If the content of a portlet is cached and the portlet is target of request
     * with an action-type semantic (e.g. an action or event call), the portlet container should discard the cache and
     * invoke the corresponding request handling methods of the portlet like processAction,or processEvent.
     *
     * (non-Javadoc)
     * @see org.jasig.portal.portlet.rendering.IPortletRenderer#doEvent(org.jasig.portal.portlet.om.IPortletWindowId, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, javax.portlet.Event)
     */
    @Override
    public long doEvent(IPortletWindowId portletWindowId, HttpServletRequest httpServletRequest,
            HttpServletResponse httpServletResponse, Event event) {
        this.portletCacheControlService.purgeCachedPortletData(portletWindowId, httpServletRequest);
       
        final IPortletWindow portletWindow = this.portletWindowRegistry.getPortletWindow(httpServletRequest, portletWindowId);

        enforceConfigPermission(httpServletRequest, portletWindow);
       
        httpServletRequest = this.setupPortletRequest(httpServletRequest);
        httpServletResponse = new PortletHttpServletResponseWrapper(httpServletResponse, portletWindow);
       
        //Execute the action,
        this.logger.debug("Executing portlet event for window '{}'", portletWindow);

        final long start = System.nanoTime();
        try {
            this.portletContainer.doEvent(portletWindow.getPlutoPortletWindow(), httpServletRequest, httpServletResponse, event);
        }
        catch (PortletException pe) {
            throw new PortletDispatchException("The portlet window '" + portletWindow + "' threw an exception while executing event.", portletWindow, pe);
        }
        catch (PortletContainerException pce) {
            throw new PortletDispatchException("The portlet container threw an exception while executing event on portlet window '" + portletWindow + "'.", portletWindow, pce);
        }
        catch (IOException ioe) {
            throw new PortletDispatchException("The portlet window '" + portletWindow + "' threw an exception while executing event.", portletWindow, ioe);
        }
       
        final long executionTime = System.nanoTime() - start;

        this.portalEventFactory.publishPortletEventExecutionEvent(httpServletRequest, this, portletWindowId, executionTime, event.getQName());
       
        return executionTime;
    }
   
    @Override
    public PortletRenderResult doRenderHeader(IPortletWindowId portletWindowId,
            HttpServletRequest httpServletRequest,
            HttpServletResponse httpServletResponse, PortletOutputHandler portletOutputHandler) throws IOException  {

        // enforce CONFIG mode restriction through its enforcement in doRender();
       
        return doRender(portletWindowId,
                httpServletRequest,
                httpServletResponse,
                portletOutputHandler,
                RenderPart.HEADERS);
    }
   
   
    /**
     * Interacts with the {@link IPortletCacheControlService} to determine if the markup should come from cache or not.
     * If cached data doesn't exist or is expired, this delegates to {@link #doRender(IPortletWindowId, HttpServletRequest,
            HttpServletResponse, PortletOutputHandler, RenderPart)}.
     * @throws IOException
     */
    @Override
    public PortletRenderResult doRenderMarkup(IPortletWindowId portletWindowId, HttpServletRequest httpServletRequest,
            HttpServletResponse httpServletResponse, PortletOutputHandler portletOutputHandler) throws IOException {

        // enforce CONFIG mode access restriction through its enforcement in the doRender() impl.
       
        return doRender(portletWindowId,
                httpServletRequest,
                httpServletResponse,
                portletOutputHandler,
                RenderPart.MARKUP);
    }
   
    /**
     * Describes the part of the render request and defines the part specific behaviors
     */
    protected enum RenderPart {
        HEADERS(PortletRequest.RENDER_HEADERS) {
            @Override
            public CacheState<CachedPortletData<PortletRenderResult>, PortletRenderResult> getCacheState(
                    IPortletCacheControlService portletCacheControlService, HttpServletRequest request,
                    IPortletWindowId portletWindowId) {
                return portletCacheControlService.getPortletRenderHeaderState(request, portletWindowId);
            }

            @Override
            public void cachePortletOutput(IPortletCacheControlService portletCacheControlService,
                    IPortletWindowId portletWindowId, HttpServletRequest request,
                    CacheState<CachedPortletData<PortletRenderResult>, PortletRenderResult> cacheState, CachedPortletData<PortletRenderResult> cachedPortletData) {

                portletCacheControlService.cachePortletRenderHeaderOutput(portletWindowId,
                        request,
                        cacheState,
                        cachedPortletData);
            }

            @Override
            public void publishRenderExecutionEvent(IPortletExecutionEventFactory portalEventFactory, PortletRendererImpl source,
                    HttpServletRequest request, IPortletWindowId portletWindowId, long executionTime,
                    boolean targeted, boolean cached) {
               
                portalEventFactory.publishPortletRenderHeaderExecutionEvent(request,
                        source,
                        portletWindowId,
                        executionTime,
                        targeted,
                        cached);
            }
        },
        MARKUP(PortletRequest.RENDER_MARKUP) {
            @Override
            public CacheState<CachedPortletData<PortletRenderResult>, PortletRenderResult> getCacheState(
                    IPortletCacheControlService portletCacheControlService, HttpServletRequest request,
                    IPortletWindowId portletWindowId) {
                return portletCacheControlService.getPortletRenderState(request, portletWindowId);
            }

            @Override
            public void cachePortletOutput(IPortletCacheControlService portletCacheControlService,
                    IPortletWindowId portletWindowId, HttpServletRequest request,
                    CacheState<CachedPortletData<PortletRenderResult>, PortletRenderResult> cacheState, CachedPortletData<PortletRenderResult> cachedPortletData) {

                portletCacheControlService.cachePortletRenderOutput(portletWindowId,
                        request,
                        cacheState,
                        cachedPortletData);
            }

            @Override
            public void publishRenderExecutionEvent(IPortletExecutionEventFactory portalEventFactory, PortletRendererImpl source,
                    HttpServletRequest request, IPortletWindowId portletWindowId, long executionTime,
                    boolean targeted, boolean cached) {
                portalEventFactory.publishPortletRenderExecutionEvent(request,
                        source,
                        portletWindowId,
                        executionTime,
                        targeted,
                        cached);
            }
        };
       
        private final String renderPart;
       
        private RenderPart(String renderPart) {
            this.renderPart = renderPart;
        }
       
        /**
         * Get the cache state for the request
         */
        public abstract CacheState<CachedPortletData<PortletRenderResult>, PortletRenderResult> getCacheState(
                IPortletCacheControlService portletCacheControlService, HttpServletRequest request,
                IPortletWindowId portletWindowId);
       
        /**
         * Cache the portlet output
         */
        public abstract void cachePortletOutput(IPortletCacheControlService portletCacheControlService,
                IPortletWindowId portletWindowId, HttpServletRequest request,
                CacheState<CachedPortletData<PortletRenderResult>, PortletRenderResult> cacheState, CachedPortletData<PortletRenderResult> cachedPortletData);
       
        /**
         * Public portlet event
         */
        public abstract void publishRenderExecutionEvent(IPortletExecutionEventFactory portalEventFactory,
                PortletRendererImpl source, HttpServletRequest request, IPortletWindowId portletWindowId, long executionTime,
                boolean targeted, boolean cached);

        /**
         * @return The {@link PortletRequest#RENDER_PART} name
         */
        public final String getRenderPart() {
            return renderPart;
        }

        /**
         * Determine the {@link RenderPart} from the {@link PortletRequest#RENDER_PART}
         */
        public static RenderPart getRenderPart(String renderPart) {
            if (PortletRequest.RENDER_HEADERS.equals(renderPart)) {
                return HEADERS;
            }
           
            if (PortletRequest.RENDER_MARKUP.equals(renderPart)) {
                return MARKUP;
            }

            throw new IllegalArgumentException("Unknown " + PortletRequest.RENDER_PART + " specified: " + renderPart);
        }
    }
       
    protected PortletRenderResult doRender(IPortletWindowId portletWindowId, HttpServletRequest httpServletRequest,
            HttpServletResponse httpServletResponse, PortletOutputHandler portletOutputHandler, RenderPart renderPart)
            throws IOException {
       
        final CacheState<CachedPortletData<PortletRenderResult>, PortletRenderResult> cacheState = renderPart
                .getCacheState(this.portletCacheControlService, httpServletRequest, portletWindowId);

        final IPortletWindow portletWindow = this.portletWindowRegistry.getPortletWindow(httpServletRequest, portletWindowId);

        enforceConfigPermission(httpServletRequest, portletWindow);

        /*
         * If the portlet is rendering in EXCLUSIVE WindowState ignore the provided PortletOutputHandler and
         * write directly to the response.
         *
         * THIS IS VERY BAD AND SHOULD BE DEPRECATED ALONG WITH EXCLUSIVE WINDOW STATE
         */
        if (EXCLUSIVE.equals(portletWindow.getWindowState())) {
            portletOutputHandler = new ResourcePortletOutputHandler(httpServletResponse);
        }

        if (cacheState.isUseCachedData()) {
            return doRenderReplayCachedContent(portletWindow, httpServletRequest, cacheState, portletOutputHandler, renderPart, 0);
        }
       
        final int cacheSizeThreshold = this.portletCacheControlService.getCacheSizeThreshold();
        final CachingPortletOutputHandler cachingPortletOutputHandler = new CachingPortletOutputHandler(portletOutputHandler, cacheSizeThreshold);

        final CacheControl cacheControl = cacheState.getCacheControl();
       
        //Setup the request and response
        httpServletRequest = this.setupPortletRequest(httpServletRequest);
        httpServletResponse = new PortletMimeHttpServletResponseWrapper(httpServletResponse, portletWindow, portletOutputHandler, cacheControl);
       
        httpServletRequest.setAttribute(ATTRIBUTE__PORTLET_CACHE_CONTROL, cacheControl);
        httpServletRequest.setAttribute(ATTRIBUTE__PORTLET_OUTPUT_HANDLER, cachingPortletOutputHandler);
       
        logger.debug("Rendering portlet {} for window {}", renderPart.name(), portletWindow);

        final long renderStartTime = System.nanoTime();
        try {
            httpServletRequest.setAttribute(PortletRequest.RENDER_PART, renderPart.getRenderPart());
            this.portletContainer.doRender(portletWindow.getPlutoPortletWindow(), httpServletRequest, httpServletResponse);    
        }
        catch (PortletException pe) {
            throw new PortletDispatchException("The portlet window '" + portletWindow + "' threw an exception while executing renderMarkup.", portletWindow, pe);
        }
        catch (PortletContainerException pce) {
            throw new PortletDispatchException("The portlet container threw an exception while executing renderMarkup on portlet window '" + portletWindow + "'.", portletWindow, pce);
        }
        catch (IOException ioe) {
            throw new PortletDispatchException("The portlet window '" + portletWindow + "' threw an exception while executing renderMarkup.", portletWindow, ioe);
        }
       
        final long executionTime = System.nanoTime() - renderStartTime;
       
        //See if the portlet signaled to use the cached content
        final boolean useCachedContent = cacheControl.useCachedContent();
        if (useCachedContent) {
            final CachedPortletData<PortletRenderResult> cachedPortletData = cacheState.getCachedPortletData();
            if (cachedPortletData == null) {
                throw new PortletDispatchException(
                        "The portlet window '" + portletWindow + "' indicated via CacheControl#useCachedContent " +
                        "that the portal should render cached content, however there is no cached content to return. " +
                        "This is a portlet bug.",
                        portletWindow);
            }
           
            //Update the expiration time and re-store in the cache
            cachedPortletData.updateExpirationTime(cacheControl.getExpirationTime());
           
            renderPart.cachePortletOutput(portletCacheControlService, portletWindowId, httpServletRequest, cacheState, cachedPortletData);
           
            return doRenderReplayCachedContent(portletWindow, httpServletRequest, cacheState, portletOutputHandler, renderPart, executionTime);
        }
       
        publishRenderEvent(portletWindow, httpServletRequest, renderPart, executionTime, false);

        //Build the render result
        final PortletRenderResult portletRenderResult = constructPortletRenderResult(httpServletRequest, executionTime);

        //Check if the portlet's output should be cached
        if (cacheState != null) {
            boolean shouldCache = this.portletCacheControlService.shouldOutputBeCached(cacheControl);

            if (shouldCache) {
                final CachedPortletData<PortletRenderResult> cachedPortletData = cachingPortletOutputHandler
                        .getCachedPortletData(portletRenderResult, cacheControl);

                if (cachedPortletData != null) {
                    renderPart.cachePortletOutput(portletCacheControlService,
                            portletWindowId,
                            httpServletRequest,
                            cacheState,
                            cachedPortletData);
                }
            }
        }
       
        return portletRenderResult;
    }
   
    /**
     * Replay the cached content inside the {@link CachedPortletData} as the response to a doRender.
     */
    protected PortletRenderResult doRenderReplayCachedContent(IPortletWindow portletWindow,
            HttpServletRequest httpServletRequest, CacheState<CachedPortletData<PortletRenderResult>, PortletRenderResult> cacheState,
            PortletOutputHandler portletOutputHandler, RenderPart renderPart, long baseExecutionTime) throws IOException {

        enforceConfigPermission(httpServletRequest, portletWindow);

        logger.debug("Replaying cached content for Render {} request to {}", renderPart, portletWindow);

        final long renderStartTime = System.nanoTime();
       
        final CachedPortletData<PortletRenderResult> cachedPortletData = cacheState.getCachedPortletData();
        cachedPortletData.replay(portletOutputHandler);
       
        final long executionTime = baseExecutionTime + (System.nanoTime() - renderStartTime);

        publishRenderEvent(portletWindow, httpServletRequest, renderPart, executionTime, true);
       
        final PortletRenderResult portletResult = cachedPortletData.getPortletResult();
        return new PortletRenderResult(portletResult, executionTime);
    }

    /**
     * Publish the portlet render event
     */
    protected void publishRenderEvent(IPortletWindow portletWindow, HttpServletRequest httpServletRequest,
            RenderPart renderPart, long executionTime, boolean cached) {

        final IPortletWindowId portletWindowId = portletWindow.getPortletWindowId();

        //Determine if the portlet was targeted
        final IPortalRequestInfo portalRequestInfo = this.urlSyntaxProvider.getPortalRequestInfo(httpServletRequest);
        final boolean targeted = portletWindowId.equals(portalRequestInfo.getTargetedPortletWindowId());

        renderPart.publishRenderExecutionEvent(this.portalEventFactory,
                this,
                httpServletRequest,
                portletWindowId,
                executionTime,
                targeted,
                cached);
    }
   
    /**
     * Construct a {@link PortletRenderResult} from information in the {@link HttpServletRequest}.
     * The second argument is how long the render action took.
     *
     * @param httpServletRequest
     * @param renderTime
     * @return an appropriate {@link PortletRenderResult}, never null
     */
    protected PortletRenderResult constructPortletRenderResult(HttpServletRequest httpServletRequest, long renderTime) {
         final String title = (String)httpServletRequest.getAttribute(IPortletRenderer.ATTRIBUTE__PORTLET_TITLE);
         final String newItemCountString = (String)httpServletRequest.getAttribute(IPortletRenderer.ATTRIBUTE__PORTLET_NEW_ITEM_COUNT);
         final int newItemCount;
         if (newItemCountString != null && StringUtils.isNumeric(newItemCountString)) {
             newItemCount = Integer.parseInt(newItemCountString);
         } else {
             newItemCount = 0;
         }
         final String link = (String)httpServletRequest.getAttribute(IPortletRenderer.ATTRIBUTE__PORTLET_LINK);
        
         return new PortletRenderResult(title, link, newItemCount, renderTime);
    }
   
    @Override
    public long doServeResource(IPortletWindowId portletWindowId, HttpServletRequest httpServletRequest,
            HttpServletResponse httpServletResponse, PortletResourceOutputHandler portletOutputHandler) throws IOException {
       
        final CacheState<CachedPortletResourceData<Long>, Long> cacheState = this.portletCacheControlService
                .getPortletResourceState(httpServletRequest, portletWindowId);

        final IPortletWindow portletWindow = this.portletWindowRegistry.getPortletWindow(httpServletRequest, portletWindowId);

        enforceConfigPermission(httpServletRequest, portletWindow);

        if (cacheState.isUseBrowserData()) {
            logger.trace("doServeResource-Reusing browser data");
            return doResourceReplayBrowserContent(portletWindow, httpServletRequest, cacheState, portletOutputHandler);
        }
       
        if (cacheState.isUseCachedData()) {
            logger.trace("doServeResource-Reusing cached data");
            return doResourceReplayCachedContent(portletWindow, httpServletRequest, cacheState, portletOutputHandler, 0);
        }
       
        final int cacheSizeThreshold = this.portletCacheControlService.getCacheSizeThreshold();
        final CachingPortletResourceOutputHandler cachingPortletOutputHandler = new CachingPortletResourceOutputHandler(portletOutputHandler, cacheSizeThreshold);

        CacheControl cacheControl = cacheState.getCacheControl();
        //Wrap the cache control so it immediately sets the caching related response headers
        cacheControl = new HeaderSettingCacheControl(cacheControl, cachingPortletOutputHandler);
       
        //Setup the request and response
        httpServletRequest = this.setupPortletRequest(httpServletRequest);
        httpServletResponse = new PortletResourceHttpServletResponseWrapper(httpServletResponse, portletWindow, portletOutputHandler, cacheControl);
       
        httpServletRequest.setAttribute(ATTRIBUTE__PORTLET_CACHE_CONTROL, cacheControl);
        httpServletRequest.setAttribute(ATTRIBUTE__PORTLET_OUTPUT_HANDLER, cachingPortletOutputHandler);
       
        this.logger.debug("Executing resource request for window {}", portletWindow);

        final long start = System.nanoTime();
        try {
            this.portletContainer.doServeResource(portletWindow.getPlutoPortletWindow(), httpServletRequest, httpServletResponse);
        }
        catch (PortletException pe) {
            throw new PortletDispatchException("The portlet window '" + portletWindow + "' threw an exception while executing serveResource.", portletWindow, pe);
        }
        catch (PortletContainerException pce) {
            throw new PortletDispatchException("The portlet container threw an exception while executing serveResource on portlet window '" + portletWindow + "'.", portletWindow, pce);
        }
        catch (IOException ioe) {
            throw new PortletDispatchException("The portlet window '" + portletWindow + "' threw an exception while executing serveResource.", portletWindow, ioe);
        }
        final long executionTime = System.nanoTime() - start;
       
        //See if the portlet signaled to use the cached content
        final boolean useCachedContent = cacheControl.useCachedContent();
        if (useCachedContent) {
            final CachedPortletResourceData<Long> cachedPortletResourceData = cacheState.getCachedPortletData();
           
            if (cachedPortletResourceData != null) {
                //Update the expiration time and re-store in the cache
                final CachedPortletData<Long> cachedPortletData = cachedPortletResourceData.getCachedPortletData();
                cachedPortletData.updateExpirationTime(cacheControl.getExpirationTime());
                this.portletCacheControlService.cachePortletResourceOutput(portletWindowId, httpServletRequest, cacheState, cachedPortletResourceData);
            }
           
            if (cacheState.isBrowserSetEtag()) {
                logger.trace("doServeResource-useCachedContent, Reusing browser data");
                //Browser-side content matches, send a 304
                return doResourceReplayBrowserContent(portletWindow, httpServletRequest, cacheState, portletOutputHandler);
            }
            logger.trace("doServeResource-useCachedContent, Reusing cached data");

            return doResourceReplayCachedContent(portletWindow, httpServletRequest, cacheState, cachingPortletOutputHandler, executionTime);
        }
       
        publishResourceEvent(portletWindow, httpServletRequest, executionTime, false, false);
       
        if (cacheState != null) {
            boolean shouldCache = this.portletCacheControlService.shouldOutputBeCached(cacheControl);

            if (shouldCache) {
                final CachedPortletResourceData<Long> cachedPortletResourceData = cachingPortletOutputHandler
                        .getCachedPortletResourceData(executionTime, cacheControl);

                if (cachedPortletResourceData != null) {
                    this.portletCacheControlService.cachePortletResourceOutput(portletWindowId,
                            httpServletRequest,
                            cacheState,
                            cachedPortletResourceData);
                }
            }
        }
       
        return executionTime;
    }
   
    protected long doResourceReplayBrowserContent(IPortletWindow portletWindow, HttpServletRequest httpServletRequest,
            CacheState<CachedPortletResourceData<Long>, Long> cacheState,
            PortletResourceOutputHandler portletOutputHandler) {

        enforceConfigPermission(httpServletRequest, portletWindow);
       
        logger.debug("Sending 304 for resource request to {}", portletWindow);

        if (portletOutputHandler.isCommitted()) {
            throw new IllegalStateException("Attempting to send 304 but response is already committed");
        }

        final long start = System.nanoTime();
   
        portletOutputHandler.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
       
        final CachedPortletResourceData<Long> cachedPortletResourceData = cacheState.getCachedPortletData();
        if (cachedPortletResourceData != null) {
            //Freshen up the various caching related headers
            final CachedPortletData<Long> cachedPortletData = cachedPortletResourceData.getCachedPortletData();
            PortletCachingHeaderUtils.setCachingHeaders(cachedPortletData, portletOutputHandler);
        }
       
        final long executionTime = System.nanoTime() - start;
       
        publishResourceEvent(portletWindow, httpServletRequest, executionTime, true, false);
       
        return executionTime;
    }
   
    /**
     * Replay the cached content inside the {@link CachedPortletData} as the response to a doResource.
     */
    protected Long doResourceReplayCachedContent(IPortletWindow portletWindow,
            HttpServletRequest httpServletRequest, CacheState<CachedPortletResourceData<Long>, Long> cacheState,
            PortletResourceOutputHandler portletOutputHandler, long baseExecutionTime) throws IOException {

        enforceConfigPermission(httpServletRequest, portletWindow);
       
        logger.debug("Replaying cached content for resource request to {}", portletWindow);

        final long renderStartTime = System.nanoTime();
       
        final CachedPortletResourceData<Long> cachedPortletResourceData = cacheState.getCachedPortletData();
        if (cachedPortletResourceData == null) {
            throw new PortletDispatchException(
                    "The portlet window '" + portletWindow + "' indicated via CacheControl#useCachedContent " +
                    "that the portal should render cached content, however there is no cached content to return. " +
                    "This is a portlet bug.",
                    portletWindow);
        }
       
        cachedPortletResourceData.replay(portletOutputHandler);
       
        final long executionTime = baseExecutionTime + (System.nanoTime() - renderStartTime);

        publishResourceEvent(portletWindow, httpServletRequest, executionTime, false, true);
       
        return executionTime;
    }

    /**
     * Publish the portlet resource event
     */
    protected void publishResourceEvent(IPortletWindow portletWindow, HttpServletRequest httpServletRequest,
            long executionTime, boolean usedBrowserCache, boolean usedPortalCache) {

        final IPortletWindowId portletWindowId = portletWindow.getPortletWindowId();

        this.portalEventFactory.publishPortletResourceExecutionEvent(httpServletRequest, this, portletWindowId, executionTime, usedBrowserCache, usedPortalCache);
    }
   
    /*
     * (non-Javadoc)
     * @see org.jasig.portal.portlet.rendering.IPortletRenderer#doReset(org.jasig.portal.portlet.om.IPortletWindowId, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
     */
    @Override
    public void doReset(IPortletWindowId portletWindowId, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {

        // Don't enforce config mode restriction because this is going to snap the portlet window back into view mode.

        final IPortletWindow portletWindow = this.portletWindowRegistry.getPortletWindow(httpServletRequest, portletWindowId);
        if(portletWindow != null) {
            portletWindow.setPortletMode(PortletMode.VIEW);
            portletWindow.setRenderParameters(new ParameterMap());
            portletWindow.setExpirationCache(null);

            httpServletRequest = this.setupPortletRequest(httpServletRequest);
            httpServletResponse = new PortletHttpServletResponseWrapper(httpServletResponse, portletWindow);

            httpServletRequest.setAttribute(AdministrativeRequestListenerController.DEFAULT_LISTENER_KEY_ATTRIBUTE, "sessionActionListener");
            httpServletRequest.setAttribute(PortletSessionAdministrativeRequestListener.ACTION, PortletSessionAdministrativeRequestListener.SessionAction.CLEAR);
            httpServletRequest.setAttribute(PortletSessionAdministrativeRequestListener.SCOPE, PortletSession.PORTLET_SCOPE);
           
            //TODO modify PortletContainer.doAdmin to create a specific "admin" req/res object and context so we don't have to fake it with a render req
            //These are required for a render request to be created and admin requests use a render request under the hood
            final String characterEncoding = httpServletResponse.getCharacterEncoding();
            httpServletRequest.setAttribute(ATTRIBUTE__PORTLET_OUTPUT_HANDLER, new RenderPortletOutputHandler(characterEncoding));
            httpServletRequest.setAttribute(ATTRIBUTE__PORTLET_CACHE_CONTROL, new CacheControlImpl());

            try {
                this.portletContainer.doAdmin(portletWindow.getPlutoPortletWindow(), httpServletRequest, httpServletResponse);
            }
            catch (PortletException pe) {
                throw new PortletDispatchException("The portlet window '" + portletWindow + "' threw an exception while executing admin command to clear session.", portletWindow, pe);
            }
            catch (PortletContainerException pce) {
                throw new PortletDispatchException("The portlet container threw an exception while executing admin command to clear session on portlet window '" + portletWindow + "'.", portletWindow, pce);
            }
            catch (IOException ioe) {
                throw new PortletDispatchException("The portlet window '" + portletWindow + "' threw an exception while executing admin command to clear session.", portletWindow, ioe);
            }
        } else {
            logger.debug("ignoring doReset as portletWindowRegistry#getPortletWindow returned a null result for portletWindowId {}", portletWindowId);
        }
    }

    protected HttpServletRequest setupPortletRequest(HttpServletRequest httpServletRequest) {
        final PortletHttpServletRequestWrapper portletHttpServletRequestWrapper = new PortletHttpServletRequestWrapper(httpServletRequest);
        portletHttpServletRequestWrapper.setAttribute(PortletDelegationLocator.PORTLET_DELECATION_LOCATOR_ATTR, this.portletDelegationLocator);
       
        return portletHttpServletRequestWrapper;
    }

    /**
     * Enforces config mode access control.  If requesting user does not have CONFIG permission,
     * and the PortletWindow specifies config mode, throws AuthorizationException.  Otherwise does nothing.
     *
     * @param httpServletRequest the non-null current HttpServletRequest (for determining requesting user)
     * @param portletWindow a non-null portlet window that might be in config mode
     * @throws org.jasig.portal.AuthorizationException if the user is not permitted to access config mode yet
     * portlet window specifies config mode
     * @throws java.lang.IllegalArgumentException if the request or window are null
     * @since uPortal 4.0.13.1, 4.0.14, 4.1.
     */
    protected void enforceConfigPermission(final HttpServletRequest httpServletRequest,
                                       final IPortletWindow portletWindow) {

        Validate.notNull(httpServletRequest, "Servlet request must not be null to determine remote user.");
        Validate.notNull(portletWindow, "Portlet window must not be null to determine its mode.");

        final PortletMode portletMode = portletWindow.getPortletMode();
        if (portletMode != null) {
            if (IPortletRenderer.CONFIG.equals(portletMode)) {
                final IPerson person = this.personManager.getPerson(httpServletRequest);
               
                final EntityIdentifier ei = person.getEntityIdentifier();
                final AuthorizationService authorizationService = AuthorizationService.instance();
                final IAuthorizationPrincipal ap = authorizationService.newPrincipal(ei.getKey(), ei.getType());
               
                final IPortletEntity portletEntity = portletWindow.getPortletEntity();
                final IPortletDefinition portletDefinition = portletEntity.getPortletDefinition();
               
                if (!ap.canConfigure(portletDefinition.getPortletDefinitionId().getStringId())) {

                    logger.error("User {} attempted to use portlet {} in {} but lacks permission to use that mode.  " +
                            "THIS MAY BE AN ATTEMPT TO EXPLOIT A HISTORICAL SECURITY FLAW.  " +
                            "You should probably figure out who this user is and why they are trying to access " +
                            "unauthorized portlet modes.",
                            person.getUserName(), portletDefinition.getFName(), portletMode);

                    throw new AuthorizationException(person.getUserName() + " does not have permission to render '" +
                            portletDefinition.getFName() + "' in " + portletMode + " PortletMode.");
                }
            }
        }
    }
   
}
TOP

Related Classes of org.jasig.portal.portlet.rendering.PortletRendererImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.