Package org.jasig.portal.portlet.container.cache

Source Code of org.jasig.portal.portlet.container.cache.PortletCacheControlServiceImpl

/**
* 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.container.cache;

import java.io.Serializable;
import java.util.Locale;
import java.util.Map;

import javax.portlet.CacheControl;
import javax.portlet.MimeResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;
import net.sf.ehcache.config.CacheConfiguration;

import org.apache.pluto.container.om.portlet.PortletDefinition;
import org.jasig.portal.portlet.om.IPortletDefinitionId;
import org.jasig.portal.portlet.om.IPortletEntity;
import org.jasig.portal.portlet.om.IPortletEntityId;
import org.jasig.portal.portlet.om.IPortletWindow;
import org.jasig.portal.portlet.om.IPortletWindowId;
import org.jasig.portal.portlet.registry.IPortletDefinitionRegistry;
import org.jasig.portal.portlet.registry.IPortletWindowRegistry;
import org.jasig.portal.portlet.rendering.PortletRenderResult;
import org.jasig.portal.url.IPortalRequestInfo;
import org.jasig.portal.url.IUrlSyntaxProvider;
import org.jasig.portal.utils.cache.TaggedCacheEntryPurger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.support.RequestContextUtils;


/**
* Default implementation of {@link IPortletCacheControlService}.
* {@link CacheControl}s are stored in a {@link Map} stored as a {@link HttpServletRequest} attribute.
*
* @author Nicholas Blair
* @version $Id$
*/
@Service
public class PortletCacheControlServiceImpl implements IPortletCacheControlService {
    private static final String IF_NONE_MATCH = "If-None-Match";
    private static final String IF_MODIFIED_SINCE = "If-Modified-Since";
   
  protected final Logger logger = LoggerFactory.getLogger(this.getClass());

  private TaggedCacheEntryPurger taggedCacheEntryPurger;
 
  private IPortletWindowRegistry portletWindowRegistry;
  private IPortletDefinitionRegistry portletDefinitionRegistry;
  private IUrlSyntaxProvider urlSyntaxProvider;
 
    private Ehcache privateScopePortletRenderHeaderOutputCache;
    private Ehcache publicScopePortletRenderHeaderOutputCache;
   
    private Ehcache privateScopePortletRenderOutputCache;
    private Ehcache publicScopePortletRenderOutputCache;
   
    private Ehcache privateScopePortletResourceOutputCache;
    private Ehcache publicScopePortletResourceOutputCache;
   
    // default to 100 KB
    private int cacheSizeThreshold = 102400;
   
    @Autowired
    public void setTaggedCacheEntryPurger(TaggedCacheEntryPurger taggedCacheEntryPurger) {
        this.taggedCacheEntryPurger = taggedCacheEntryPurger;
    }
   
    @Autowired
    @Qualifier("org.jasig.portal.portlet.container.cache.PortletCacheControlServiceImpl.privateScopePortletRenderHeaderOutputCache")
    public void setPrivateScopePortletRenderHeaderOutputCache(Ehcache privateScopePortletRenderHeaderOutputCache) {
        this.privateScopePortletRenderHeaderOutputCache = privateScopePortletRenderHeaderOutputCache;
    }

    @Autowired
    @Qualifier("org.jasig.portal.portlet.container.cache.PortletCacheControlServiceImpl.publicScopePortletRenderHeaderOutputCache")
    public void setPublicScopePortletRenderHeaderOutputCache(Ehcache publicScopePortletRenderHeaderOutputCache) {
        this.publicScopePortletRenderHeaderOutputCache = publicScopePortletRenderHeaderOutputCache;
    }
   
   
    @Autowired
    @Qualifier("org.jasig.portal.portlet.container.cache.PortletCacheControlServiceImpl.privateScopePortletRenderOutputCache")
    public void setPrivateScopePortletRenderOutputCache(Ehcache privateScopePortletRenderOutputCache) {
        this.privateScopePortletRenderOutputCache = privateScopePortletRenderOutputCache;
    }

    @Autowired
    @Qualifier("org.jasig.portal.portlet.container.cache.PortletCacheControlServiceImpl.publicScopePortletRenderOutputCache")
    public void setPublicScopePortletRenderOutputCache(Ehcache publicScopePortletRenderOutputCache) {
        this.publicScopePortletRenderOutputCache = publicScopePortletRenderOutputCache;
    }

    @Autowired
    @Qualifier("org.jasig.portal.portlet.container.cache.PortletCacheControlServiceImpl.privateScopePortletResourceOutputCache")
    public void setPrivateScopePortletResourceOutputCache(Ehcache privateScopePortletResourceOutputCache) {
        this.privateScopePortletResourceOutputCache = privateScopePortletResourceOutputCache;
    }

    @Autowired
    @Qualifier("org.jasig.portal.portlet.container.cache.PortletCacheControlServiceImpl.publicScopePortletResourceOutputCache")
    public void setPublicScopePortletResourceOutputCache(Ehcache publicScopePortletResourceOutputCache) {
        this.publicScopePortletResourceOutputCache = publicScopePortletResourceOutputCache;
    }
   
  /**
   * @param cacheSizeThreshold the cacheSizeThreshold to set in bytes
   */
    @Value("${org.jasig.portal.portlet.container.cache.PortletCacheControlServiceImpl.cacheSizeThreshold:102400}")
  public void setCacheSizeThreshold(int cacheSizeThreshold) {
    this.cacheSizeThreshold = cacheSizeThreshold;
  }
   
  @Override
  public int getCacheSizeThreshold() {
    return cacheSizeThreshold;
  }
  @Autowired
  public void setPortletWindowRegistry(
      IPortletWindowRegistry portletWindowRegistry) {
    this.portletWindowRegistry = portletWindowRegistry;
  }
  @Autowired
  public void setPortletDefinitionRegistry(
      IPortletDefinitionRegistry portletDefinitionRegistry) {
    this.portletDefinitionRegistry = portletDefinitionRegistry;
  }
  @Autowired
  public void setUrlSyntaxProvider(IUrlSyntaxProvider urlSyntaxProvider) {
        this.urlSyntaxProvider = urlSyntaxProvider;
    }

    @Override
    public CacheState<CachedPortletData<PortletRenderResult>, PortletRenderResult> getPortletRenderHeaderState(
            HttpServletRequest request, IPortletWindowId portletWindowId) {

        final IPortletWindow portletWindow = this.portletWindowRegistry.getPortletWindow(request, portletWindowId);
        if (portletWindow == null) {
            logger.warn("portletWindowRegistry returned null for {}, returning default cacheControl and no cached portlet data", portletWindowId);
            return new CacheState<CachedPortletData<PortletRenderResult>, PortletRenderResult>();
        }
       
        //Generate the public render-header cache key
        final IPortalRequestInfo portalRequestInfo = this.urlSyntaxProvider.getPortalRequestInfo(request);
        final Locale locale = RequestContextUtils.getLocale(request);
        final PublicPortletCacheKey publicCacheKey = PublicPortletCacheKey.createPublicPortletRenderHeaderCacheKey(portletWindow, portalRequestInfo, locale);
       
        return this.<CachedPortletData<PortletRenderResult>, PortletRenderResult> getPortletState(request,
                portletWindow,
                publicCacheKey,
                this.publicScopePortletRenderHeaderOutputCache,
                this.privateScopePortletRenderHeaderOutputCache,
                false);
    }
   
    @Override
    public CacheState<CachedPortletData<PortletRenderResult>, PortletRenderResult> getPortletRenderState(
            HttpServletRequest request, IPortletWindowId portletWindowId) {

        final IPortletWindow portletWindow = this.portletWindowRegistry.getPortletWindow(request, portletWindowId);
        if (portletWindow == null) {
            logger.warn("portletWindowRegistry returned null for {}, returning default cacheControl and no cached portlet data", portletWindowId);
            return new CacheState<CachedPortletData<PortletRenderResult>, PortletRenderResult>();
        }
       
        //Generate the public render cache key
        final IPortalRequestInfo portalRequestInfo = this.urlSyntaxProvider.getPortalRequestInfo(request);
        final Locale locale = RequestContextUtils.getLocale(request);
        final PublicPortletCacheKey publicCacheKey = PublicPortletCacheKey.createPublicPortletRenderCacheKey(portletWindow, portalRequestInfo, locale);
       
        return this.<CachedPortletData<PortletRenderResult>, PortletRenderResult> getPortletState(request,
                portletWindow,
                publicCacheKey,
                this.publicScopePortletRenderOutputCache,
                this.privateScopePortletRenderOutputCache,
                false);
    }
   
    @Override
    public CacheState<CachedPortletResourceData<Long>, Long> getPortletResourceState(HttpServletRequest request,
            IPortletWindowId portletWindowId) {

        final IPortletWindow portletWindow = this.portletWindowRegistry.getPortletWindow(request, portletWindowId);
        if (portletWindow == null) {
            logger.warn("portletWindowRegistry returned null for {}, returning default cacheControl and no cached portlet data", portletWindowId);
            return new CacheState<CachedPortletResourceData<Long>, Long>();
        }
       
        //Generate the public resource cache key
        final IPortalRequestInfo portalRequestInfo = this.urlSyntaxProvider.getPortalRequestInfo(request);
        final Locale locale = RequestContextUtils.getLocale(request);
        final PublicPortletCacheKey publicCacheKey = PublicPortletCacheKey.createPublicPortletResourceCacheKey(portletWindow, portalRequestInfo, locale);
       
        return this.<CachedPortletResourceData<Long>, Long> getPortletState(request,
                portletWindow,
                publicCacheKey,
                this.publicScopePortletResourceOutputCache,
                this.privateScopePortletResourceOutputCache,
                true);
    }
   
    private <D extends CachedPortletResultHolder<T>, T extends Serializable> CacheState<D, T> getPortletState(
            HttpServletRequest request,
            IPortletWindow portletWindow,
            PublicPortletCacheKey publicCacheKey,
            Ehcache publicOutputCache,
            Ehcache privateOutputCache,
            boolean useHttpHeaders) {
       
        //See if there is any cached data for the portlet header request
        final CacheState<D, T> cacheState = this.<D, T> getPortletCacheState(request,
                portletWindow,
                publicCacheKey,
                publicOutputCache,
                privateOutputCache);
   
        String etagHeader = null;
        final D cachedPortletData = cacheState.getCachedPortletData();
        if (cachedPortletData != null) {
            if (useHttpHeaders) {
                //Browser headers being used, check ETag and Last Modified
               
                etagHeader = request.getHeader(IF_NONE_MATCH);
                if (etagHeader != null && etagHeader.equals(cachedPortletData.getEtag())) {
                    //ETag is valid, mark the browser data as matching
                    cacheState.setBrowserDataMatches(true);
                }
                else {
                    long ifModifiedSince = request.getDateHeader(IF_MODIFIED_SINCE);
                    if (ifModifiedSince >= 0 && cachedPortletData.getTimeStored() <= ifModifiedSince) {
                        //Cached content hasn't been modified since header date, mark the browser data as matching
                        cacheState.setBrowserDataMatches(true);
                    }
                }
            }
           
            final long expirationTime = cachedPortletData.getExpirationTime();
            if (expirationTime == -1 || expirationTime > System.currentTimeMillis()) {
                //Cached data exists, see if it can be used with no additional work
                //Cached data is not expired, check if browser data should be used
                cacheState.setUseCachedData(true);
               
                //Copy browser-data-matching flag to the user-browser-data flag
                cacheState.setUseBrowserData(cacheState.isBrowserDataMatches());
           
                //No browser side data to be used, return the cached data for replay
                return cacheState;
            }
        }
       
        //Build CacheControl structure
        final CacheControl cacheControl = cacheState.getCacheControl();
       
        //Get the portlet descriptor
        final IPortletEntity entity = portletWindow.getPortletEntity();
        final IPortletDefinitionId definitionId = entity.getPortletDefinitionId();
        final PortletDefinition portletDescriptor = this.portletDefinitionRegistry.getParentPortletDescriptor(definitionId);
       
        //Set the default scope
        final String cacheScopeValue = portletDescriptor.getCacheScope();
        if (MimeResponse.PUBLIC_SCOPE.equalsIgnoreCase(cacheScopeValue)) {
            cacheControl.setPublicScope(true);
        }
       
        //Set the default expiration time
        cacheControl.setExpirationTime(portletDescriptor.getExpirationCache());
       
        // Use the request etag if it exists (implies useHttpHeaders==true)
        if (etagHeader != null) {
            cacheControl.setETag(etagHeader);
            cacheState.setBrowserSetEtag(true);
        }
        // No browser-set etag, use the cached etag value if there is cached data
        else if (cachedPortletData != null) {
            logger.debug("setting cacheControl.eTag from cached data to {}", cachedPortletData.getEtag());
            cacheControl.setETag(cachedPortletData.getEtag());
        }
       
        return cacheState;
    }
   
    /**
     * Get the cached portlet data looking in both the public and then private caches returning the first found
     *
     * @param request The current request
     * @param portletWindow The window to get data for
     * @param publicCacheKey The public cache key
     * @param publicOutputCache The public cache
     * @param privateOutputCache The private cache
     */
    @SuppressWarnings("unchecked")
    protected <D extends CachedPortletResultHolder<T>, T extends Serializable> CacheState<D, T> getPortletCacheState(HttpServletRequest request, IPortletWindow portletWindow,
            PublicPortletCacheKey publicCacheKey, Ehcache publicOutputCache, Ehcache privateOutputCache) {
       
        final CacheState<D, T> cacheState = new CacheState<D, T>();
        cacheState.setPublicPortletCacheKey(publicCacheKey);
       
        final IPortletWindowId portletWindowId = portletWindow.getPortletWindowId();

        //Check for publicly cached data
        D cachedPortletData = (D)this.getCachedPortletData(publicCacheKey, publicOutputCache, portletWindow);
        if (cachedPortletData != null) {
            cacheState.setCachedPortletData(cachedPortletData);
            return cacheState;
        }
       
        //Generate private cache key
        final HttpSession session = request.getSession();
        final String sessionId = session.getId();
        final IPortletEntityId entityId = portletWindow.getPortletEntityId();
        final PrivatePortletCacheKey privateCacheKey = new PrivatePortletCacheKey(sessionId, portletWindowId, entityId, publicCacheKey);
        cacheState.setPrivatePortletCacheKey(privateCacheKey);
       
        //Check for privately cached data
        cachedPortletData = (D)this.getCachedPortletData(privateCacheKey, privateOutputCache, portletWindow);
        if (cachedPortletData != null) {
            cacheState.setCachedPortletData(cachedPortletData);
            return cacheState;
        }

        return cacheState;
    }

    /**
     * Get the cached portlet data for the key, cache and window. If there is {@link CachedPortletData}
     * in the cache it will only be returned if {@link CachedPortletData#isExpired()} is false or
     * {@link CachedPortletData#getEtag()} is not null.
     *
     * @param cacheKey The cache key
     * @param outputCache The cache
     * @param portletWindow The portlet window the lookup is for
     * @return The cache data for the portlet window
     */
    @SuppressWarnings("unchecked")
    protected <T extends Serializable> CachedPortletResultHolder<T> getCachedPortletData(Serializable cacheKey, Ehcache outputCache,
            IPortletWindow portletWindow) {

        final Element publicCacheElement = outputCache.get(cacheKey);
        if (publicCacheElement == null) {
            logger.debug("No cached output for key {}", cacheKey);
            return null;
        }

        final CachedPortletResultHolder<T> cachedPortletData = (CachedPortletResultHolder<T>) publicCacheElement.getObjectValue();
        if (publicCacheElement.isExpired() && cachedPortletData.getEtag() == null) {
            logger.debug("Cached output for key {} is expired", cacheKey);
            outputCache.remove(cacheKey);
            return null;
        }

        logger.debug("Returning cached output with key {} for {}", cacheKey, portletWindow);
        return (CachedPortletResultHolder<T>) publicCacheElement.getObjectValue();
    }
   
    @Override
    public boolean shouldOutputBeCached(CacheControl cacheControl) {
        if (cacheControl.getExpirationTime() != 0) {
            return true;
        }
        else {
            return false;
        }
    }
 
   
  @Override
    public void cachePortletRenderHeaderOutput(IPortletWindowId portletWindowId, HttpServletRequest httpRequest,
            CacheState<CachedPortletData<PortletRenderResult>, PortletRenderResult> cacheState, CachedPortletData<PortletRenderResult> cachedPortletData) {
     
        cachePortletOutput(portletWindowId,
                httpRequest,
                cacheState,
                cachedPortletData,
                this.publicScopePortletRenderHeaderOutputCache,
                this.privateScopePortletRenderHeaderOutputCache);
    }

    @Override
    public void cachePortletRenderOutput(IPortletWindowId portletWindowId, HttpServletRequest httpRequest,
            CacheState<CachedPortletData<PortletRenderResult>, PortletRenderResult> cacheState, CachedPortletData<PortletRenderResult> cachedPortletData) {

        cachePortletOutput(portletWindowId,
                httpRequest,
                cacheState,
                cachedPortletData,
                this.publicScopePortletRenderOutputCache,
                this.privateScopePortletRenderOutputCache);
  }

   
  @Override
    public void cachePortletResourceOutput(IPortletWindowId portletWindowId, HttpServletRequest httpRequest,
            CacheState<CachedPortletResourceData<Long>, Long> cacheState,
            CachedPortletResourceData<Long> cachedPortletResourceData) {
     
        cachePortletOutput(portletWindowId,
                httpRequest,
                cacheState,
                cachedPortletResourceData,
                this.publicScopePortletResourceOutputCache,
                this.privateScopePortletResourceOutputCache);
  }

    private <D extends CachedPortletResultHolder<T>, T extends Serializable> void cachePortletOutput(IPortletWindowId portletWindowId, HttpServletRequest httpRequest,
            CacheState<D, T> cacheState, D cachedPortletData, Ehcache publicOutputCache, Ehcache privateOutputCache) {
       
        final IPortletWindow portletWindow = this.portletWindowRegistry.getPortletWindow(httpRequest, portletWindowId);
        final CacheControl cacheControl = cacheState.getCacheControl();
       
        if (cacheControl.isPublicScope()) {
            final PublicPortletCacheKey publicCacheKey = cacheState.getPublicPortletCacheKey();
            this.cacheElement(publicOutputCache, publicCacheKey, cachedPortletData, cacheControl);
            logger.debug("Cached public data under key {} for {}", publicCacheKey, portletWindow);
        }
        else {
            PrivatePortletCacheKey privateCacheKey = cacheState.getPrivatePortletCacheKey();
           
            //Private key can be null if getPortletState found publicly cached data but the portlet's response is now privately scoped
            if (privateCacheKey == null) {
                final HttpSession session = httpRequest.getSession();
                final String sessionId = session.getId();
                final IPortletEntityId entityId = portletWindow.getPortletEntityId();
                final PublicPortletCacheKey publicCacheKey = cacheState.getPublicPortletCacheKey();
                privateCacheKey = new PrivatePortletCacheKey(sessionId, portletWindowId, entityId, publicCacheKey);
            }
           
            this.cacheElement(privateOutputCache, privateCacheKey, cachedPortletData, cacheControl);
            logger.debug("Cached private data under key {} for {}", privateCacheKey, portletWindow);
        }
    }
 
  /**
   * Construct an appropriate Cache {@link Element} for the cacheKey and data.
   * The element's ttl will be set depending on whether expiration or validation method is indicated from the CacheControl and the cache's configuration.
   */
  protected void cacheElement(Ehcache cache, Serializable cacheKey, CachedPortletResultHolder<?> data, CacheControl cacheControl) {
    // using validation method, ignore expirationTime and defer to cache configuration
        if (cacheControl.getETag() != null) {
            final Element element = new Element(cacheKey, data);
            cache.put(element);
            return;
    }
   
    // using expiration method, -1 for CacheControl#expirationTime means "forever" (e.g. ignore and defer to cache configuration)
    final int expirationTime = cacheControl.getExpirationTime();
    if(expirationTime == -1) {
      final Element element = new Element(cacheKey, data);
      cache.put(element);
            return;
    }

    // using expiration method with a positive expiration, set that value as the element's TTL if it is lower than the configured cache TTL
    final CacheConfiguration cacheConfiguration = cache.getCacheConfiguration();
    final Element element = new Element(cacheKey, data);
        final long cacheTTL = cacheConfiguration.getTimeToLiveSeconds();
        if (expirationTime < cacheTTL) {
            element.setTimeToLive(expirationTime);
        }
    cache.put(element);
  }
 
  @Override
  public boolean purgeCachedPortletData(IPortletWindowId portletWindowId,
      HttpServletRequest httpRequest) {
    final IPortletWindow portletWindow = this.portletWindowRegistry.getPortletWindow(httpRequest, portletWindowId);
        final IPortletEntity entity = portletWindow.getPortletEntity();
        final IPortletDefinitionId definitionId = entity.getPortletDefinitionId();
        final HttpSession session = httpRequest.getSession();
       
        int purgeCount = 0;
       
        //Remove all publicly cached data
        purgeCount += this.taggedCacheEntryPurger.purgeCacheEntries(PublicPortletCacheKey.createTag(definitionId));
       
        //Remove all privately cached data
        purgeCount += this.taggedCacheEntryPurger.purgeCacheEntries(PrivatePortletCacheKey.createTag(session.getId(), portletWindowId));
       
        logger.debug("Purging all cached data for {} removed {} keys", portletWindow, purgeCount);
       
        return purgeCount != 0;
  }
}
TOP

Related Classes of org.jasig.portal.portlet.container.cache.PortletCacheControlServiceImpl

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.