de.escidoc.core.aa.business.cache.RequestAttributesCache.java Source code

Java tutorial

Introduction

Here is the source code for de.escidoc.core.aa.business.cache.RequestAttributesCache.java

Source

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at license/ESCIDOC.LICENSE
 * or http://www.escidoc.de/license.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at license/ESCIDOC.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2006-2008 Fachinformationszentrum Karlsruhe Gesellschaft
 * fuer wissenschaftlich-technische Information mbH and Max-Planck-
 * Gesellschaft zur Foerderung der Wissenschaft e.V.  
 * All rights reserved.  Use is subject to license terms.
 */
package de.escidoc.core.aa.business.cache;

import com.sun.xacml.EvaluationCtx;
import de.escidoc.core.common.util.configuration.EscidocConfiguration;
import de.escidoc.core.common.util.string.StringUtility;
import org.apache.commons.collections.map.LRUMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Collections;
import java.util.Map;

/**
 * Class to cache objects retrieved from the system for the XACML engine.<br> The objects are store in a {@link Map}
 * that is synchronized by {@link Collections}.synchronizedMap({@link Map}).
 *
 * @author Roland Werner (Accenture)
 */
public final class RequestAttributesCache {

    /**
     * The logger.
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(RequestAttributesCache.class);

    /**
     * Fall back value if reading property {@link <code>EscidocConfiguration.AA_CACHE_USERS_SIZE</code>} fails.
     */
    private static final int USERS_CACHE_SIZE_FALL_BACK = 50;

    /**
     * Fall back value if reading property {@link <code>EscidocConfiguration.AA_CACHE_ATTRIBUTES_SIZE</code>} fails.
     */
    private static final int INTERNAL_CACHE_SIZE_FALL_BACK = 50;

    /**
     * The cache is implemented as a synchronized LRUMap (least-recently-used map), so it can only grow to a certain
     * size.
     */
    private static Map<EvaluationCtx, Map<Object, Object>> attributesCache;

    /**
     * This cache size should be set to the number of expected concurrent users. It is fetched from the properties. If
     * this fails, a fall back value is used.
     */
    private static int usersCacheSize = USERS_CACHE_SIZE_FALL_BACK;

    /**
     * This cache size should be set to the number of system objects that should be cached for a request at one point of
     * time. It is fetched from the properties. If this fails, a fall back value is used.
     */
    private static int internalCacheSize;

    static {
        initCaches();
    }

    /**
     * Private constructor to prevent class from being instantiated.
     */
    private RequestAttributesCache() {
    }

    /**
     * Initializes the caches.<br/>The cache sizes are fetched from the eSciDoc Configuration. If this fails, the
     * default values are used as fallback an an error is logged.
     */
    private static void initCaches() {

        try {
            usersCacheSize = Integer.parseInt(
                    EscidocConfiguration.getInstance().get(EscidocConfiguration.ESCIDOC_CORE_AA_CACHE_USERS_SIZE));
        } catch (final Exception e) {
            if (LOGGER.isWarnEnabled()) {
                LOGGER.warn("Error on parsing user cache size.");
            }
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Error on parsing user cache size.", e);
            }
            usersCacheSize = USERS_CACHE_SIZE_FALL_BACK;
        }
        try {
            internalCacheSize = Integer.parseInt(EscidocConfiguration.getInstance()
                    .get(EscidocConfiguration.ESCIDOC_CORE_AA_CACHE_ATTRIBUTES_SIZE));
        } catch (final Exception e) {
            if (LOGGER.isWarnEnabled()) {
                LOGGER.warn("Error on parsing internal cache size.");
            }
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Error on parsing internal cache size.", e);
            }
            internalCacheSize = INTERNAL_CACHE_SIZE_FALL_BACK;
        }

        createAttributeCache();
    }

    /**
     * Creates the attribute cache.<br/>The cache is implemented as a synchronized LRUMap (least-recently-used map), so
     * it can only grow to a certain size. The size is taken from the static field <code>usersCacheSize</code> that has
     * to be initialized before calling this method (this is not checked).
     */
    @SuppressWarnings("unchecked")
    private static void createAttributeCache() {

        attributesCache = Collections.synchronizedMap(new LRUMap(usersCacheSize));
    }

    /**
     * Creates the internal map holding the system objects (attributes) that shall be cached for a request.<br/>
     *
     * @return Returns a synchronized LRU map. The map size is taken from the static field
     *         <code>internalCacheSize</code> that has to be initialized before calling this method (this is not
     *         checked).
     */
    @SuppressWarnings("unchecked")
    private static Map<Object, Object> createInternalMap() {

        return Collections.synchronizedMap(new LRUMap(internalCacheSize));
    }

    /**
     * Stores the provided object using the provided key in the internal cache for this EvaluationCtx.
     * <p/>
     * Realised as a outer LRUMap that uses context as key and which has an inner LRUMap as value. The inner LRUMap has
     * key as key and object as value. Both LRUMaps are synchronized.
     *
     * @param context The context to use as key for the outer HashMap.
     * @param key     The key to use as key for the inner HashMap.
     * @param object  The value for the inner HashMap.
     */
    public static void put(final EvaluationCtx context, final Object key, final Object object) {

        try {
            if (key == null || context == null) {
                return;
            }
            Map<Object, Object> internalMap = attributesCache.get(context);
            if (internalMap == null) {
                internalMap = createInternalMap();
                attributesCache.put(context, internalMap);
            }
            internalMap.put(key, object);
        } catch (final RuntimeException e) {
            LOGGER.error(StringUtility.format("Runtime exception during put.", context, key, object), e);
            createAttributeCache();
        }
    }

    /**
     * Gets the object for the provided key, given that we are still in the same EvaluationCtx.
     * <p/>
     * Realisation see method put.
     *
     * @param context The context to use as key for the outer HashMap.
     * @param key     The key to use as key for the inner HashMap.
     * @return The value of the inner HashMap.
     */
    public static Object get(final EvaluationCtx context, final Object key) {

        try {
            if (key == null || context == null) {
                return null;
            }
            final Map<Object, Object> internalMap = attributesCache.get(context);
            if (internalMap == null) {
                return null;
            }
            return internalMap.get(key);
        } catch (final RuntimeException e) {
            LOGGER.error(StringUtility.format("Runtime exception during get.", context, key), e);
            createAttributeCache();
            return null;
        }
    }

}