All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.javakaffee.web.msm.NodeAvailabilityCache Maven / Gradle / Ivy

There is a newer version: 2.3.2
Show newest version
/*
 * Copyright 2009 Martin Grotzke
 *
 * Licensed 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 de.javakaffee.web.msm;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import javax.annotation.Nonnull;

import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;

import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.SuppressWarnings;

/**
 * An LRUCache that supports a maximum number of cache entries and a time to
 * live for them. The TTL is measured from insertion time to access time.
 *
 * @author Martin Grotzke
 * @version $Id$
 * @param 
 *            the type of the key
 */
public class NodeAvailabilityCache {

    private static final Log LOG = LogFactory.getLog( NodeAvailabilityCache.class );

    private final long _ttl;
    private final ConcurrentHashMap> _map;
    private final CacheLoader _cacheLoader;

    /**
     * Create a new LRUCache with a maximum number of cache entries and a
     * specified time to live for cache entries. The TTL is measured from
     * insertion time to access time.
     *
     * @param size
     *            the maximum number of cached items
     * @param ttlInMillis
     *            the time to live in milli seconds. Specify -1 for no limit
     * @param cacheLoader
     *            the cache loader to use
     */
    public NodeAvailabilityCache( final int size, final long ttlInMillis, final CacheLoader cacheLoader ) {
        _ttl = ttlInMillis;
        _map = new ConcurrentHashMap>( size / 2 );
        _cacheLoader = cacheLoader;
    }

    /**
     * If the specified key is not already associated with a value or if it's
     * associated with a different value, associate it with the given value.
     * This is equivalent to
     *
     * 
     *  if (map.get(key) == null || !map.get(key).equals(value))
     *    return map.put(key, value);
     * else
     *    return map.get(key);
     * 
     * 
* * except that the action is performed atomically. * * @param key * the key to associate the value with. * @param available * the value to associate with the provided key. * @return the previous value associated with the specified key, or null if * there was no mapping for the key */ @CheckForNull @SuppressWarnings( "NP_BOOLEAN_RETURN_NULL" ) public Boolean setNodeAvailable( final K key, final boolean available ) { final ManagedItem item = _map.get( key ); final Boolean availableObj = Boolean.valueOf( available ); if ( item == null || item._value != availableObj ) { final ManagedItem previous = _map.put( key, new ManagedItem( availableObj, System.currentTimeMillis() ) ); return previous != null ? previous._value : null; } else { return item._value; } } /** * Determines, if the node is available. If it's not cached, it's loaded * from the cache loader. * * @param key * the key to check * @return true if the node is marked as available. */ public boolean isNodeAvailable( @Nonnull final K key ) { final ManagedItem item = _map.get( key ); if ( item == null ) { return updateIsNodeAvailable( key ); } else if ( isExpired( item ) ) { _map.remove( key ); return updateIsNodeAvailable( key ); } else { return item._value; } } private boolean isExpired( final ManagedItem item ) { return _ttl > -1 && System.currentTimeMillis() - item._insertionTime > _ttl; } private boolean updateIsNodeAvailable( final K key ) { final Boolean result = Boolean.valueOf( _cacheLoader.isNodeAvailable( key ) ); if ( LOG.isDebugEnabled() ) { LOG.debug( "CacheLoader returned node availability '" + result + "' for node '" + key + "'." ); } _map.put( key, new ManagedItem( result, System.currentTimeMillis() ) ); return result; } /** * All known keys. * * @return a list of all keys, never null. */ public List getKeys() { return new ArrayList( _map.keySet() ); } /** * A set of nodes that are stored as unavailable. * * @return a set of unavailable nodes, never null. */ public Set getUnavailableNodes() { final Set result = new HashSet(); for ( final Map.Entry> entry : _map.entrySet() ) { if ( !entry.getValue()._value.booleanValue() && !isExpired( entry.getValue() ) ) { result.add( entry.getKey() ); } } return result; } /** * Stores a value with the timestamp this value was added to the cache. * * @param * the type of the value */ private static final class ManagedItem { private final T _value; private final long _insertionTime; private ManagedItem( final T value, final long accessTime ) { _value = value; _insertionTime = accessTime; } } /** * The cache loader interface. * * @param * the type of the key. */ static interface CacheLoader { boolean isNodeAvailable( K key ); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy