de.javakaffee.web.msm.NodeAvailabilityCache Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of memcached-session-manager Show documentation
Show all versions of memcached-session-manager Show documentation
The msm core, provides java serialization strategy.
/*
* 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 );
}
}