Package org.jasig.portal.concurrency.caching

Source Code of org.jasig.portal.concurrency.caching.RequestCacheAspect

/**
* 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.concurrency.caching;

import java.io.Serializable;
import java.lang.annotation.AnnotationFormatError;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import javax.management.InstanceAlreadyExistsException;
import javax.management.MBeanRegistrationException;
import javax.management.MalformedObjectNameException;
import javax.management.NotCompliantMBeanException;
import javax.management.ObjectName;
import javax.servlet.http.HttpServletRequest;

import net.sf.ehcache.hibernate.management.impl.EhcacheHibernateMbeanNames;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.jasig.portal.url.IPortalRequestUtils;
import org.jasig.portal.utils.ConcurrentMapUtils;
import org.jasig.portal.utils.cache.CacheKey;
import org.jasig.portal.utils.web.PortalWebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jmx.export.MBeanExportOperations;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;


/**
* Aspect that caches the results of a method invocation in the current {@link RequestAttributes}
*
* @author Eric Dalquist
* @version $Revision$
*/
@Aspect
@Component("requestCacheAspect")
public class RequestCacheAspect implements InitializingBean {
    private static final String CACHE_MAP = RequestCacheAspect.class.getName() + ".CACHE_MAP";
    private static final Object NULL_PLACEHOLDER = new Object();
   
    protected final Logger logger = LoggerFactory.getLogger(getClass());
   
    private final ConcurrentMap<String, CacheStatistics> methodStats = new ConcurrentHashMap<String, CacheStatistics>();
    private final CacheStatistics overallStats = new CacheStatistics();

    private IPortalRequestUtils portalRequestUtils;
    private MBeanExportOperations mBeanExportOperations;
   
    @Autowired
    public void setPortalRequestUtils(IPortalRequestUtils portalRequestUtils) {
        this.portalRequestUtils = portalRequestUtils;
    }
    @Autowired(required=false)
    public void setmBeanExportOperations(MBeanExportOperations mBeanExportOperations) {
        this.mBeanExportOperations = mBeanExportOperations;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        if (this.mBeanExportOperations != null) {
            final ObjectName name = new ObjectName("uPortal:section=Cache,RequestCache=RequestCache,name=OverallStatistics");
            registerMbean(this.overallStats, name);
        }
    }
   
    @Pointcut(value="execution(public * *(..))")
    public void anyPublicMethod() { }
   
    @Around("anyPublicMethod() && @annotation(requestCache)")
    public Object cacheRequest(ProceedingJoinPoint pjp, RequestCache requestCache) throws Throwable {
        final long start = System.nanoTime();
       
        final CacheKey cacheKey = createCacheKey(pjp, requestCache);
       
        final HttpServletRequest currentPortalRequest;
        try {
            currentPortalRequest = this.portalRequestUtils.getCurrentPortalRequest();
        }
        catch (IllegalStateException e) {
            logger.trace("No current portal request, will not cache result of: {}", cacheKey);
            //No current request, simply proceed
            return pjp.proceed();
        }
       
        final CacheStatistics cacheStatistics = this.getCacheStatistics(pjp, requestCache);
       
        //Check in the cache for a result
        final ConcurrentMap<CacheKey, Object> cache = PortalWebUtils.getMapRequestAttribute(currentPortalRequest, CACHE_MAP);
        Object result = cache.get(cacheKey);
       
        //Return null if placeholder was cached
        if (requestCache.cacheNull() && result == NULL_PLACEHOLDER) {
            final long time = System.nanoTime() - start;
            cacheStatistics.recordHit(time);
            overallStats.recordHit(time);
            logger.debug("Found cached null for invocation of: {}", cacheKey);
            return null;
        }
        //Rethrow if exception was cached
        if (requestCache.cacheException() && result instanceof ExceptionHolder) {
            final long time = System.nanoTime() - start;
            cacheStatistics.recordHit(time);
            overallStats.recordHit(time);
            logger.debug("Found cached exception for invocation of: {}", cacheKey);
            throw ((ExceptionHolder)result).getThrowable();
        }
        //Return cached result
        if (result != null) {
            final long time = System.nanoTime() - start;
            cacheStatistics.recordHit(time);
            overallStats.recordHit(time);
            logger.debug("Found cached result for invocation of: {}", cacheKey);
            return result;
        }
       
        try {
            //Execute the annotated emthod
            result = pjp.proceed();
            final long time = System.nanoTime() - start;
            cacheStatistics.recordMissAndLoad(time);
            overallStats.recordMissAndLoad(time);

            if (result != null) {
                //Cache the not-null result
                cache.put(cacheKey, result);
                logger.debug("Cached result for invocation of: {}", cacheKey);
            }
            else if (requestCache.cacheNull()) {
                //If caching nulls cache the placeholder
                cache.put(cacheKey, NULL_PLACEHOLDER);
                logger.debug("Cached null for invocation of: {}", cacheKey);
            }
           
            return result;
        }
        catch (Throwable t) {
            final long time = System.nanoTime() - start;
            cacheStatistics.recordMissAndException(time);
            overallStats.recordMissAndException(time);
            if (requestCache.cacheException()) {
                //If caching exceptions wrapp the exception and cache it
                cache.put(cacheKey, new ExceptionHolder(t));
                logger.debug("Cached exception for invocation of: {}", cacheKey);
            }
            throw t;
        }
    }
   
    protected void registerMbean(Object object, ObjectName name) throws InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException {
        this.mBeanExportOperations.registerManagedResource(object, name);
    }
   
    protected final CacheStatistics getCacheStatistics(ProceedingJoinPoint pjp, RequestCache requestCache) {
        final Signature signature = pjp.getSignature();
        final String signatureString = signature.toString();
       
        CacheStatistics cacheStatistics = this.methodStats.get(signatureString);
        if (cacheStatistics == null) {
            final CacheStatistics newStats = new CacheStatistics();
            cacheStatistics = ConcurrentMapUtils.putIfAbsent(this.methodStats, signatureString, newStats);
           
            if (this.mBeanExportOperations != null && cacheStatistics == newStats) {
                final String nameString = "uPortal:section=Cache,RequestCache=RequestCache,name=" + EhcacheHibernateMbeanNames.mbeanSafe(signatureString);
                try {
                    final ObjectName name = new ObjectName(nameString);
                    registerMbean(cacheStatistics, name);
                }
                catch (MalformedObjectNameException e) {
                    logger.warn("Failed to create ObjectName {} the corresponding CacheStatistics will not be registered with JMX", nameString, e);
                }
                catch (NullPointerException e) {
                    logger.warn("Failed to create ObjectName {} the corresponding CacheStatistics will not be registered with JMX", nameString, e);
                }
                catch (InstanceAlreadyExistsException e) {
                    logger.warn("ObjectName {} is already registered, the corresponding CacheStatistics will not be registered with JMX", nameString, e);
                }
                catch (MBeanRegistrationException e) {
                    logger.warn("Failed to register ObjectName {} the corresponding CacheStatistics will not be registered with JMX", nameString, e);
                }
                catch (NotCompliantMBeanException e) {
                    logger.warn("Failed to register ObjectName {} the corresponding CacheStatistics will not be registered with JMX", nameString, e);
                }
            }
        }
       
        return cacheStatistics;
    }
   
    protected CacheKey createCacheKey(ProceedingJoinPoint pjp, RequestCache requestCache) {
        final Signature signature = pjp.getSignature();
        final Class<?> declaringType = signature.getDeclaringType();
        final String signatureLongString = signature.toLongString();
       
        final boolean[] keyMask = requestCache.keyMask();
        final Object[] args = pjp.getArgs();
       
        final Object[] keyArgs;
        if (keyMask.length == 0) {
            keyArgs = args;
        }
        else if (keyMask.length != args.length) {
            throw new AnnotationFormatError("RequestCache.keyMask has an invalid length on: " + signature.toLongString());
        }
        else {
            keyArgs = new Object[args.length];
            for (int i = 0; i < args.length; i++) {
                if (keyMask[i]) {
                    keyArgs[i] = args[i];
                }
            }
        }
       
        return CacheKey.build(signatureLongString, declaringType, keyArgs);
    }
   
    private static class ExceptionHolder implements Serializable {
        private static final long serialVersionUID = 1L;
       
        private final Throwable t;
       
        public ExceptionHolder(Throwable t) {
            this.t = t;
        }
       
        public Throwable getThrowable() {
            return this.t;
        }
    }
}
TOP

Related Classes of org.jasig.portal.concurrency.caching.RequestCacheAspect

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.