TempStateCacheView.java
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.apache.commons.jcs3.jcache;
import javax.cache.Cache;
import javax.cache.CacheManager;
import javax.cache.configuration.CacheEntryListenerConfiguration;
import javax.cache.configuration.CompleteConfiguration;
import javax.cache.configuration.Configuration;
import javax.cache.integration.CompletionListener;
import javax.cache.processor.EntryProcessor;
import javax.cache.processor.EntryProcessorException;
import javax.cache.processor.EntryProcessorResult;
import static org.apache.commons.jcs3.jcache.Asserts.assertNotNull;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
// kind of transactional view for a Cache<K, V>, to use with EntryProcessor
public class TempStateCacheView<K, V> implements Cache<K, V>
{
private final JCSCache<K, V> cache;
private final Map<K, V> put = new HashMap<>();
private final Collection<K> remove = new LinkedList<>();
private boolean removeAll;
private boolean clear;
public TempStateCacheView(final JCSCache<K, V> entries)
{
this.cache = entries;
}
@Override
public V get(final K key)
{
if (ignoreKey(key))
{
return null;
}
final V v = put.get(key);
if (v != null)
{
return v;
}
// for an EntryProcessor we already incremented stats - to enhance
// surely
if (cache.getConfiguration(CompleteConfiguration.class).isStatisticsEnabled())
{
final Statistics statistics = cache.getStatistics();
if (cache.containsKey(key))
{
statistics.increaseHits(-1);
}
else
{
statistics.increaseMisses(-1);
}
}
return cache.get(key);
}
private boolean ignoreKey(final K key)
{
return removeAll || clear || remove.contains(key);
}
@Override
public Map<K, V> getAll(final Set<? extends K> keys)
{
final Map<K, V> v = new HashMap<>(keys.size());
final Set<K> missing = new HashSet<>();
for (final K k : keys)
{
final V value = put.get(k);
if (value != null)
{
v.put(k, value);
}
else if (!ignoreKey(k))
{
missing.add(k);
}
}
if (!missing.isEmpty())
{
v.putAll(cache.getAll(missing));
}
return v;
}
@Override
public boolean containsKey(final K key)
{
return !ignoreKey(key) && (put.containsKey(key) || cache.containsKey(key));
}
@Override
public void loadAll(final Set<? extends K> keys, final boolean replaceExistingValues, final CompletionListener completionListener)
{
cache.loadAll(keys, replaceExistingValues, completionListener);
}
@Override
public void put(final K key, final V value)
{
assertNotNull(key, "key");
assertNotNull(value, "value");
put.put(key, value);
remove.remove(key);
}
@Override
public V getAndPut(final K key, final V value)
{
final V v = get(key);
put(key, value);
return v;
}
@Override
public void putAll(final Map<? extends K, ? extends V> map)
{
put.putAll(map);
for (final K k : map.keySet())
{
remove.remove(k);
}
}
@Override
public boolean putIfAbsent(final K key, final V value)
{
if (!put.containsKey(key))
{
put.put(key, value);
remove.remove(key);
return true;
}
return false;
}
@Override
public boolean remove(final K key)
{
final boolean noop = put.containsKey(key);
put.remove(key);
if (!ignoreKey(key))
{
if (!noop)
{
remove.add(key);
}
return true;
}
return false;
}
@Override
public boolean remove(final K key, final V oldValue)
{
put.remove(key);
if (!ignoreKey(key) && oldValue.equals(cache.get(key)))
{
remove.add(key);
return true;
}
return false;
}
@Override
public V getAndRemove(final K key)
{
final V v = get(key);
remove.add(key);
put.remove(key);
return v;
}
@Override
public boolean replace(final K key, final V oldValue, final V newValue)
{
if (oldValue.equals(get(key)))
{
put(key, newValue);
return true;
}
return false;
}
@Override
public boolean replace(final K key, final V value)
{
if (containsKey(key))
{
remove(key);
return true;
}
return false;
}
@Override
public V getAndReplace(final K key, final V value)
{
if (containsKey(key))
{
final V oldValue = get(key);
put(key, value);
return oldValue;
}
return null;
}
@Override
public void removeAll(final Set<? extends K> keys)
{
remove.addAll(keys);
for (final K k : keys)
{
put.remove(k);
}
}
@Override
public void removeAll()
{
removeAll = true;
put.clear();
remove.clear();
}
@Override
public void clear()
{
clear = true;
put.clear();
remove.clear();
}
@Override
public <C extends Configuration<K, V>> C getConfiguration(final Class<C> clazz)
{
return cache.getConfiguration(clazz);
}
@Override
public <T> T invoke(final K key, final EntryProcessor<K, V, T> entryProcessor, final Object... arguments) throws EntryProcessorException
{
return cache.invoke(key, entryProcessor, arguments);
}
@Override
public <T> Map<K, EntryProcessorResult<T>> invokeAll(final Set<? extends K> keys, final EntryProcessor<K, V, T> entryProcessor,
final Object... arguments)
{
return cache.invokeAll(keys, entryProcessor, arguments);
}
@Override
public String getName()
{
return cache.getName();
}
@Override
public CacheManager getCacheManager()
{
return cache.getCacheManager();
}
@Override
public void close()
{
cache.close();
}
@Override
public boolean isClosed()
{
return cache.isClosed();
}
@Override
public <T> T unwrap(final Class<T> clazz)
{
return cache.unwrap(clazz);
}
@Override
public void registerCacheEntryListener(final CacheEntryListenerConfiguration<K, V> cacheEntryListenerConfiguration)
{
cache.registerCacheEntryListener(cacheEntryListenerConfiguration);
}
@Override
public void deregisterCacheEntryListener(final CacheEntryListenerConfiguration<K, V> cacheEntryListenerConfiguration)
{
cache.deregisterCacheEntryListener(cacheEntryListenerConfiguration);
}
@Override
public Iterator<Entry<K, V>> iterator()
{
return cache.iterator();
}
public void merge()
{
if (removeAll)
{
cache.removeAll();
}
if (clear)
{
cache.clear();
}
for (final Map.Entry<K, V> entry : put.entrySet())
{
cache.put(entry.getKey(), entry.getValue());
}
put.clear();
for (final K entry : remove)
{
cache.remove(entry);
}
remove.clear();
}
}