JCSCache.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 static org.apache.commons.jcs3.jcache.Asserts.assertNotNull;
- import static org.apache.commons.jcs3.jcache.serialization.Serializations.copy;
- import java.io.Closeable;
- import java.io.IOException;
- import java.util.Collections;
- import java.util.HashMap;
- import java.util.HashSet;
- import java.util.Iterator;
- import java.util.Map;
- import java.util.Properties;
- import java.util.Set;
- import java.util.concurrent.ConcurrentHashMap;
- import java.util.concurrent.ConcurrentMap;
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.Executors;
- import javax.cache.Cache;
- import javax.cache.CacheException;
- import javax.cache.CacheManager;
- import javax.cache.configuration.CacheEntryListenerConfiguration;
- import javax.cache.configuration.Configuration;
- import javax.cache.configuration.Factory;
- import javax.cache.event.EventType;
- import javax.cache.expiry.Duration;
- import javax.cache.expiry.EternalExpiryPolicy;
- import javax.cache.expiry.ExpiryPolicy;
- import javax.cache.integration.CacheLoader;
- import javax.cache.integration.CacheLoaderException;
- import javax.cache.integration.CacheWriter;
- import javax.cache.integration.CacheWriterException;
- import javax.cache.integration.CompletionListener;
- import javax.cache.processor.EntryProcessor;
- import javax.cache.processor.EntryProcessorException;
- import javax.cache.processor.EntryProcessorResult;
- import javax.management.ObjectName;
- import org.apache.commons.jcs3.engine.CacheElement;
- import org.apache.commons.jcs3.engine.ElementAttributes;
- import org.apache.commons.jcs3.engine.behavior.ICacheElement;
- import org.apache.commons.jcs3.engine.behavior.IElementAttributes;
- import org.apache.commons.jcs3.engine.behavior.IElementSerializer;
- import org.apache.commons.jcs3.jcache.jmx.JCSCacheMXBean;
- import org.apache.commons.jcs3.jcache.jmx.JCSCacheStatisticsMXBean;
- import org.apache.commons.jcs3.jcache.jmx.JMXs;
- import org.apache.commons.jcs3.jcache.proxy.ExceptionWrapperHandler;
- import org.apache.commons.jcs3.jcache.thread.DaemonThreadFactory;
- import org.apache.commons.jcs3.utils.serialization.StandardSerializer;
- // TODO: configure serializer
- public class JCSCache<K, V> implements Cache<K, V>
- {
- private final ExpiryAwareCache<K, V> delegate;
- private final JCSCachingManager manager;
- private final JCSConfiguration<K, V> config;
- private final CacheLoader<K, V> loader;
- private final CacheWriter<? super K, ? super V> writer;
- private final ExpiryPolicy expiryPolicy;
- private final ObjectName cacheConfigObjectName;
- private final ObjectName cacheStatsObjectName;
- private final String name;
- private volatile boolean closed;
- private final Map<CacheEntryListenerConfiguration<K, V>, JCSListener<K, V>> listeners = new ConcurrentHashMap<>();
- private final Statistics statistics = new Statistics();
- private final ExecutorService pool;
- private final IElementSerializer serializer; // using json/xml should work as well -> don't force Serializable
- public JCSCache(final ClassLoader classLoader, final JCSCachingManager mgr,
- final String cacheName, final JCSConfiguration<K, V> configuration,
- final Properties properties, final ExpiryAwareCache<K, V> cache)
- {
- manager = mgr;
- name = cacheName;
- delegate = cache;
- if (delegate.getElementAttributes() == null)
- {
- delegate.setElementAttributes(new ElementAttributes());
- }
- delegate.getElementAttributes().addElementEventHandler(new EvictionListener(statistics));
- config = configuration;
- final int poolSize = Integer.parseInt(property(properties, cacheName, "pool.size", "3"));
- final DaemonThreadFactory threadFactory = new DaemonThreadFactory("JCS-JCache-" + cacheName + "-");
- pool = poolSize > 0 ? Executors.newFixedThreadPool(poolSize, threadFactory) : Executors.newCachedThreadPool(threadFactory);
- try
- {
- serializer = (IElementSerializer) classLoader.loadClass(property(properties, "serializer", cacheName, StandardSerializer.class.getName())).getDeclaredConstructor().newInstance();
- }
- catch (final Exception e)
- {
- throw new IllegalArgumentException(e);
- }
- final Factory<CacheLoader<K, V>> cacheLoaderFactory = configuration.getCacheLoaderFactory();
- if (cacheLoaderFactory == null)
- {
- loader = (CacheLoader<K, V>) NoLoader.INSTANCE;
- }
- else
- {
- loader = ExceptionWrapperHandler
- .newProxy(classLoader, cacheLoaderFactory.create(), CacheLoaderException.class, CacheLoader.class);
- }
- final Factory<CacheWriter<? super K, ? super V>> cacheWriterFactory = configuration.getCacheWriterFactory();
- if (cacheWriterFactory == null)
- {
- writer = (CacheWriter<K, V>) NoWriter.INSTANCE;
- }
- else
- {
- writer = ExceptionWrapperHandler
- .newProxy(classLoader, cacheWriterFactory.create(), CacheWriterException.class, CacheWriter.class);
- }
- final Factory<ExpiryPolicy> expiryPolicyFactory = configuration.getExpiryPolicyFactory();
- if (expiryPolicyFactory == null)
- {
- expiryPolicy = new EternalExpiryPolicy();
- }
- else
- {
- expiryPolicy = expiryPolicyFactory.create();
- }
- for (final CacheEntryListenerConfiguration<K, V> listener : config.getCacheEntryListenerConfigurations())
- {
- listeners.put(listener, new JCSListener<>(listener));
- }
- delegate.init(this, listeners);
- statistics.setActive(config.isStatisticsEnabled());
- final String mgrStr = manager.getURI().toString().replaceAll(",|:|=|\n", ".");
- final String cacheStr = name.replaceAll(",|:|=|\n", ".");
- try
- {
- cacheConfigObjectName = new ObjectName("javax.cache:type=CacheConfiguration,"
- + "CacheManager=" + mgrStr + "," + "Cache=" + cacheStr);
- cacheStatsObjectName = new ObjectName("javax.cache:type=CacheStatistics,"
- + "CacheManager=" + mgrStr + "," + "Cache=" + cacheStr);
- }
- catch (final Exception e)
- {
- throw new IllegalArgumentException(e);
- }
- if (config.isManagementEnabled())
- {
- JMXs.register(cacheConfigObjectName, new JCSCacheMXBean<>(this));
- }
- if (config.isStatisticsEnabled())
- {
- JMXs.register(cacheStatsObjectName, new JCSCacheStatisticsMXBean(statistics));
- }
- }
- private static String property(final Properties properties, final String cacheName, final String name, final String defaultValue)
- {
- return properties.getProperty(cacheName + "." + name, properties.getProperty(name, defaultValue));
- }
- private void assertNotClosed()
- {
- if (isClosed())
- {
- throw new IllegalStateException("cache closed");
- }
- }
- @Override
- public V get(final K key)
- {
- assertNotClosed();
- assertNotNull(key, "key");
- final long getStart = Times.now(false);
- return doGetControllingExpiry(getStart, key, true, false, false, true);
- }
- private V doLoad(final K key, final boolean update, final long now, final boolean propagateLoadException)
- {
- V v = null;
- try
- {
- v = loader.load(key);
- }
- catch (final CacheLoaderException e)
- {
- if (propagateLoadException)
- {
- throw e;
- }
- }
- if (v != null)
- {
- final Duration duration = update ? expiryPolicy.getExpiryForUpdate() : expiryPolicy.getExpiryForCreation();
- if (isNotZero(duration))
- {
- final IElementAttributes clone = delegate.getElementAttributes().clone();
- if (ElementAttributes.class.isInstance(clone))
- {
- ElementAttributes.class.cast(clone).setCreateTime();
- }
- final ICacheElement<K, V> element = updateElement(key, v, duration, clone);
- try
- {
- delegate.update(element);
- }
- catch (final IOException e)
- {
- throw new CacheException(e);
- }
- }
- }
- return v;
- }
- private ICacheElement<K, V> updateElement(final K key, final V v, final Duration duration, final IElementAttributes attrs)
- {
- final ICacheElement<K, V> element = new CacheElement<>(name, key, v);
- if (duration != null)
- {
- attrs.setTimeFactorForMilliseconds(1);
- final boolean eternal = duration.isEternal();
- attrs.setIsEternal(eternal);
- if (!eternal)
- {
- attrs.setLastAccessTimeNow();
- }
- // MaxLife = -1 to use IdleTime excepted if jcache.ccf asked for something else
- }
- element.setElementAttributes(attrs);
- return element;
- }
- private void touch(final K key, final ICacheElement<K, V> element)
- {
- if (config.isStoreByValue())
- {
- final K copy = copy(serializer, manager.getClassLoader(), key);
- try
- {
- delegate.update(new CacheElement<>(name, copy, element.getVal(), element.getElementAttributes()));
- }
- catch (final IOException e)
- {
- throw new CacheException(e);
- }
- }
- }
- @Override
- public Map<K, V> getAll(final Set<? extends K> keys)
- {
- assertNotClosed();
- for (final K k : keys)
- {
- assertNotNull(k, "key");
- }
- final long now = Times.now(false);
- final Map<K, V> result = new HashMap<>();
- for (final K key : keys) {
- assertNotNull(key, "key");
- final ICacheElement<K, V> elt = delegate.get(key);
- V val = elt != null ? elt.getVal() : null;
- if (val == null && config.isReadThrough())
- {
- val = doLoad(key, false, now, false);
- if (val != null)
- {
- result.put(key, val);
- }
- }
- else if (elt != null)
- {
- final Duration expiryForAccess = expiryPolicy.getExpiryForAccess();
- if (isNotZero(expiryForAccess))
- {
- touch(key, elt);
- result.put(key, val);
- }
- else
- {
- forceExpires(key);
- }
- }
- }
- return result;
- }
- @Override
- public boolean containsKey(final K key)
- {
- assertNotClosed();
- assertNotNull(key, "key");
- return delegate.get(key) != null;
- }
- @Override
- public void put(final K key, final V rawValue)
- {
- assertNotClosed();
- assertNotNull(key, "key");
- assertNotNull(rawValue, "value");
- final ICacheElement<K, V> oldElt = delegate.get(key);
- final V old = oldElt != null ? oldElt.getVal() : null;
- final boolean storeByValue = config.isStoreByValue();
- final V value = storeByValue ? copy(serializer, manager.getClassLoader(), rawValue) : rawValue;
- final boolean created = old == null;
- final Duration duration = created ? expiryPolicy.getExpiryForCreation() : expiryPolicy.getExpiryForUpdate();
- if (isNotZero(duration))
- {
- final boolean statisticsEnabled = config.isStatisticsEnabled();
- final long start = Times.now(false);
- final K jcsKey = storeByValue ? copy(serializer, manager.getClassLoader(), key) : key;
- final ICacheElement<K, V> element = updateElement( // reuse it to create basic structure
- jcsKey, value, created ? null : duration,
- oldElt != null ? oldElt.getElementAttributes() : delegate.getElementAttributes().clone());
- if (created && duration != null) { // set maxLife
- final IElementAttributes copy = element.getElementAttributes();
- copy.setTimeFactorForMilliseconds(1);
- final boolean eternal = duration.isEternal();
- copy.setIsEternal(eternal);
- if (ElementAttributes.class.isInstance(copy)) {
- ElementAttributes.class.cast(copy).setCreateTime();
- }
- if (!eternal)
- {
- copy.setIsEternal(false);
- if (duration == expiryPolicy.getExpiryForAccess())
- {
- element.getElementAttributes().setIdleTime(duration.getTimeUnit().toMillis(duration.getDurationAmount()));
- }
- else
- {
- element.getElementAttributes().setMaxLife(duration.getTimeUnit().toMillis(duration.getDurationAmount()));
- }
- }
- element.setElementAttributes(copy);
- }
- writer.write(new JCSEntry<>(jcsKey, value));
- try
- {
- delegate.update(element);
- }
- catch (final IOException e)
- {
- throw new CacheException(e);
- }
- for (final JCSListener<K, V> listener : listeners.values())
- {
- if (created)
- {
- listener.onCreated(Collections.singletonList(new JCSCacheEntryEvent<>(this,
- EventType.CREATED, null, key, value)));
- }
- else
- {
- listener.onUpdated(Collections.singletonList(new JCSCacheEntryEvent<>(this,
- EventType.UPDATED, old, key, value)));
- }
- }
- if (statisticsEnabled)
- {
- statistics.increasePuts(1);
- statistics.addPutTime(System.currentTimeMillis() - start);
- }
- }
- else
- {
- if (!created)
- {
- forceExpires(key);
- }
- }
- }
- private static boolean isNotZero(final Duration duration)
- {
- return duration == null || !duration.isZero();
- }
- private void forceExpires(final K cacheKey)
- {
- final ICacheElement<K, V> elt = delegate.get(cacheKey);
- delegate.remove(cacheKey);
- for (final JCSListener<K, V> listener : listeners.values())
- {
- listener.onExpired(Collections.singletonList(new JCSCacheEntryEvent<>(this,
- EventType.REMOVED, null, cacheKey, elt.getVal())));
- }
- }
- @Override
- public V getAndPut(final K key, final V value)
- {
- assertNotClosed();
- assertNotNull(key, "key");
- assertNotNull(value, "value");
- final long getStart = Times.now(false);
- final V v = doGetControllingExpiry(getStart, key, false, false, true, false);
- put(key, value);
- return v;
- }
- @Override
- public void putAll(final Map<? extends K, ? extends V> map)
- {
- assertNotClosed();
- final TempStateCacheView<K, V> view = new TempStateCacheView<>(this);
- for (final Map.Entry<? extends K, ? extends V> e : map.entrySet())
- {
- view.put(e.getKey(), e.getValue());
- }
- view.merge();
- }
- @Override
- public boolean putIfAbsent(final K key, final V value)
- {
- if (!containsKey(key))
- {
- put(key, value);
- return true;
- }
- return false;
- }
- @Override
- public boolean remove(final K key)
- {
- assertNotClosed();
- assertNotNull(key, "key");
- final boolean statisticsEnabled = config.isStatisticsEnabled();
- final long start = Times.now(!statisticsEnabled);
- writer.delete(key);
- final ICacheElement<K, V> v = delegate.get(key);
- delegate.remove(key);
- final V value = v != null && v.getVal() != null ? v.getVal() : null;
- final boolean remove = v != null;
- for (final JCSListener<K, V> listener : listeners.values())
- {
- listener.onRemoved(Collections.singletonList(new JCSCacheEntryEvent<>(this,
- EventType.REMOVED, null, key, value)));
- }
- if (remove && statisticsEnabled)
- {
- statistics.increaseRemovals(1);
- statistics.addRemoveTime(Times.now(false) - start);
- }
- return remove;
- }
- @Override
- public boolean remove(final K key, final V oldValue)
- {
- assertNotClosed();
- assertNotNull(key, "key");
- assertNotNull(oldValue, "oldValue");
- final long getStart = Times.now(false);
- final V v = doGetControllingExpiry(getStart, key, false, false, false, false);
- if (oldValue.equals(v))
- {
- remove(key);
- return true;
- }
- if (v != null)
- {
- // weird but just for stats to be right (org.jsr107.tck.expiry.CacheExpiryTest.removeSpecifiedEntryShouldNotCallExpiryPolicyMethods())
- expiryPolicy.getExpiryForAccess();
- }
- return false;
- }
- @Override
- public V getAndRemove(final K key)
- {
- assertNotClosed();
- assertNotNull(key, "key");
- final long getStart = Times.now(false);
- final V v = doGetControllingExpiry(getStart, key, false, false, true, false);
- remove(key);
- return v;
- }
- private V doGetControllingExpiry(final long getStart, final K key, final boolean updateAcess, final boolean forceDoLoad, final boolean skipLoad,
- final boolean propagateLoadException)
- {
- final boolean statisticsEnabled = config.isStatisticsEnabled();
- final ICacheElement<K, V> elt = delegate.get(key);
- V v = elt != null ? elt.getVal() : null;
- if (v == null && (config.isReadThrough() || forceDoLoad))
- {
- if (!skipLoad)
- {
- v = doLoad(key, false, getStart, propagateLoadException);
- }
- }
- else if (statisticsEnabled)
- {
- if (v != null)
- {
- statistics.increaseHits(1);
- }
- else
- {
- statistics.increaseMisses(1);
- }
- }
- if (updateAcess && elt != null)
- {
- final Duration expiryForAccess = expiryPolicy.getExpiryForAccess();
- if (!isNotZero(expiryForAccess))
- {
- forceExpires(key);
- }
- else if (expiryForAccess != null && (!elt.getElementAttributes().getIsEternal() || !expiryForAccess.isEternal()))
- {
- try
- {
- delegate.update(updateElement(key, elt.getVal(), expiryForAccess, elt.getElementAttributes()));
- }
- catch (final IOException e)
- {
- throw new CacheException(e);
- }
- }
- }
- if (statisticsEnabled && v != null)
- {
- statistics.addGetTime(Times.now(false) - getStart);
- }
- return v;
- }
- @Override
- public boolean replace(final K key, final V oldValue, final V newValue)
- {
- assertNotClosed();
- assertNotNull(key, "key");
- assertNotNull(oldValue, "oldValue");
- assertNotNull(newValue, "newValue");
- final boolean statisticsEnabled = config.isStatisticsEnabled();
- final ICacheElement<K, V> elt = delegate.get(key);
- if (elt != null)
- {
- V value = elt.getVal();
- if (value != null && statisticsEnabled)
- {
- statistics.increaseHits(1);
- }
- if (value == null && config.isReadThrough())
- {
- value = doLoad(key, false, Times.now(false), false);
- }
- if (value != null && value.equals(oldValue))
- {
- put(key, newValue);
- return true;
- }
- if (value != null)
- {
- final Duration expiryForAccess = expiryPolicy.getExpiryForAccess();
- if (expiryForAccess != null && (!elt.getElementAttributes().getIsEternal() || !expiryForAccess.isEternal()))
- {
- try
- {
- delegate.update(updateElement(key, elt.getVal(), expiryForAccess, elt.getElementAttributes()));
- }
- catch (final IOException e)
- {
- throw new CacheException(e);
- }
- }
- }
- }
- else if (statisticsEnabled)
- {
- statistics.increaseMisses(1);
- }
- return false;
- }
- @Override
- public boolean replace(final K key, final V value)
- {
- assertNotClosed();
- assertNotNull(key, "key");
- assertNotNull(value, "value");
- final boolean statisticsEnabled = config.isStatisticsEnabled();
- if (containsKey(key))
- {
- if (statisticsEnabled)
- {
- statistics.increaseHits(1);
- }
- put(key, value);
- return true;
- }
- if (statisticsEnabled)
- {
- statistics.increaseMisses(1);
- }
- return false;
- }
- @Override
- public V getAndReplace(final K key, final V value)
- {
- assertNotClosed();
- assertNotNull(key, "key");
- assertNotNull(value, "value");
- final boolean statisticsEnabled = config.isStatisticsEnabled();
- final ICacheElement<K, V> elt = delegate.get(key);
- if (elt != null)
- {
- V oldValue = elt.getVal();
- if (oldValue == null && config.isReadThrough())
- {
- oldValue = doLoad(key, false, Times.now(false), false);
- }
- else if (statisticsEnabled)
- {
- statistics.increaseHits(1);
- }
- put(key, value);
- return oldValue;
- }
- if (statisticsEnabled)
- {
- statistics.increaseMisses(1);
- }
- return null;
- }
- @Override
- public void removeAll(final Set<? extends K> keys)
- {
- assertNotClosed();
- assertNotNull(keys, "keys");
- for (final K k : keys)
- {
- remove(k);
- }
- }
- @Override
- public void removeAll()
- {
- assertNotClosed();
- for (final K k : delegate.getKeySet())
- {
- remove(k);
- }
- }
- @Override
- public void clear()
- {
- assertNotClosed();
- try
- {
- delegate.removeAll();
- }
- catch (final IOException e)
- {
- throw new CacheException(e);
- }
- }
- @Override
- public <C2 extends Configuration<K, V>> C2 getConfiguration(final Class<C2> clazz)
- {
- assertNotClosed();
- return clazz.cast(config);
- }
- @Override
- public void loadAll(final Set<? extends K> keys, final boolean replaceExistingValues, final CompletionListener completionListener)
- {
- assertNotClosed();
- assertNotNull(keys, "keys");
- for (final K k : keys)
- {
- assertNotNull(k, "a key");
- }
- pool.submit(() -> doLoadAll(keys, replaceExistingValues, completionListener));
- }
- private void doLoadAll(final Set<? extends K> keys, final boolean replaceExistingValues, final CompletionListener completionListener)
- {
- try
- {
- final long now = Times.now(false);
- for (final K k : keys)
- {
- if (replaceExistingValues)
- {
- doLoad(k, containsKey(k), now, completionListener != null);
- continue;
- }
- if (containsKey(k))
- {
- continue;
- }
- doGetControllingExpiry(now, k, true, true, false, completionListener != null);
- }
- }
- catch (final RuntimeException e)
- {
- if (completionListener != null)
- {
- completionListener.onException(e);
- return;
- }
- }
- if (completionListener != null)
- {
- completionListener.onCompletion();
- }
- }
- @Override
- public <T> T invoke(final K key, final EntryProcessor<K, V, T> entryProcessor, final Object... arguments) throws EntryProcessorException
- {
- final TempStateCacheView<K, V> view = new TempStateCacheView<>(this);
- final T t = doInvoke(view, key, entryProcessor, arguments);
- view.merge();
- return t;
- }
- private <T> T doInvoke(final TempStateCacheView<K, V> view, final K key, final EntryProcessor<K, V, T> entryProcessor,
- final Object... arguments)
- {
- assertNotClosed();
- assertNotNull(entryProcessor, "entryProcessor");
- assertNotNull(key, "key");
- try
- {
- if (config.isStatisticsEnabled())
- {
- if (containsKey(key))
- {
- statistics.increaseHits(1);
- }
- else
- {
- statistics.increaseMisses(1);
- }
- }
- return entryProcessor.process(new JCSMutableEntry<>(view, key), arguments);
- }
- catch (final Exception ex)
- {
- return throwEntryProcessorException(ex);
- }
- }
- private static <T> T throwEntryProcessorException(final Exception ex)
- {
- if (EntryProcessorException.class.isInstance(ex))
- {
- throw EntryProcessorException.class.cast(ex);
- }
- throw new EntryProcessorException(ex);
- }
- @Override
- public <T> Map<K, EntryProcessorResult<T>> invokeAll(final Set<? extends K> keys, final EntryProcessor<K, V, T> entryProcessor,
- final Object... arguments)
- {
- assertNotClosed();
- assertNotNull(entryProcessor, "entryProcessor");
- final Map<K, EntryProcessorResult<T>> results = new HashMap<>();
- for (final K k : keys)
- {
- try
- {
- final T invoke = invoke(k, entryProcessor, arguments);
- if (invoke != null)
- {
- results.put(k, () -> invoke);
- }
- }
- catch (final Exception e)
- {
- results.put(k, () -> throwEntryProcessorException(e));
- }
- }
- return results;
- }
- @Override
- public void registerCacheEntryListener(final CacheEntryListenerConfiguration<K, V> cacheEntryListenerConfiguration)
- {
- assertNotClosed();
- if (listeners.containsKey(cacheEntryListenerConfiguration))
- {
- throw new IllegalArgumentException(cacheEntryListenerConfiguration + " already registered");
- }
- listeners.put(cacheEntryListenerConfiguration, new JCSListener<>(cacheEntryListenerConfiguration));
- config.addListener(cacheEntryListenerConfiguration);
- }
- @Override
- public void deregisterCacheEntryListener(final CacheEntryListenerConfiguration<K, V> cacheEntryListenerConfiguration)
- {
- assertNotClosed();
- listeners.remove(cacheEntryListenerConfiguration);
- config.removeListener(cacheEntryListenerConfiguration);
- }
- @Override
- public Iterator<Entry<K, V>> iterator()
- {
- assertNotClosed();
- final Iterator<K> keys = new HashSet<>(delegate.getKeySet()).iterator();
- return new Iterator<Entry<K, V>>()
- {
- private K lastKey;
- @Override
- public boolean hasNext()
- {
- return keys.hasNext();
- }
- @Override
- public Entry<K, V> next()
- {
- lastKey = keys.next();
- return new JCSEntry<>(lastKey, get(lastKey));
- }
- @Override
- public void remove()
- {
- if (isClosed() || lastKey == null)
- {
- throw new IllegalStateException(isClosed() ? "cache closed" : "call next() before remove()");
- }
- JCSCache.this.remove(lastKey);
- }
- };
- }
- @Override
- public String getName()
- {
- assertNotClosed();
- return name;
- }
- @Override
- public CacheManager getCacheManager()
- {
- assertNotClosed();
- return manager;
- }
- @Override
- public synchronized void close()
- {
- if (isClosed())
- {
- return;
- }
- for (final Runnable task : pool.shutdownNow()) {
- task.run();
- }
- manager.release(getName());
- closed = true;
- close(loader);
- close(writer);
- close(expiryPolicy);
- for (final JCSListener<K, V> listener : listeners.values())
- {
- close(listener);
- }
- listeners.clear();
- JMXs.unregister(cacheConfigObjectName);
- JMXs.unregister(cacheStatsObjectName);
- try
- {
- delegate.removeAll();
- }
- catch (final IOException e)
- {
- throw new CacheException(e);
- }
- }
- private static void close(final Object potentiallyCloseable)
- {
- if (Closeable.class.isInstance(potentiallyCloseable))
- {
- Closeable.class.cast(potentiallyCloseable);
- }
- }
- @Override
- public boolean isClosed()
- {
- return closed;
- }
- @Override
- public <T> T unwrap(final Class<T> clazz)
- {
- assertNotClosed();
- if (clazz.isInstance(this))
- {
- return clazz.cast(this);
- }
- if (clazz.isAssignableFrom(Map.class) || clazz.isAssignableFrom(ConcurrentMap.class))
- {
- return clazz.cast(delegate);
- }
- throw new IllegalArgumentException(clazz.getName() + " not supported in unwrap");
- }
- public Statistics getStatistics()
- {
- return statistics;
- }
- public void enableManagement()
- {
- config.managementEnabled();
- JMXs.register(cacheConfigObjectName, new JCSCacheMXBean<>(this));
- }
- public void disableManagement()
- {
- config.managementDisabled();
- JMXs.unregister(cacheConfigObjectName);
- }
- public void enableStatistics()
- {
- config.statisticsEnabled();
- statistics.setActive(true);
- JMXs.register(cacheStatsObjectName, new JCSCacheStatisticsMXBean(statistics));
- }
- public void disableStatistics()
- {
- config.statisticsDisabled();
- statistics.setActive(false);
- JMXs.unregister(cacheStatsObjectName);
- }
- }