package org.cache2k.jcache.provider; /* * #%L * cache2k JCache provider * %% * Copyright (C) 2000 - 2017 headissue GmbH, Munich * %% * 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. * #L% */ import org.cache2k.Cache; import org.cache2k.CacheEntry; import org.cache2k.processor.EntryProcessor; import org.cache2k.integration.CacheWriterException; import org.cache2k.processor.EntryProcessingResult; import org.cache2k.CacheOperationCompletionListener; import org.cache2k.processor.MutableCacheEntry; import org.cache2k.core.EntryAction; import org.cache2k.core.InternalCache; import org.cache2k.jcache.provider.event.EventHandling; import javax.cache.CacheManager; import javax.cache.configuration.CacheEntryListenerConfiguration; import javax.cache.configuration.CompleteConfiguration; import javax.cache.configuration.Configuration; import javax.cache.configuration.MutableConfiguration; import javax.cache.integration.CacheLoaderException; import javax.cache.integration.CompletionListener; import javax.cache.processor.EntryProcessorException; import javax.cache.processor.EntryProcessorResult; import javax.cache.processor.MutableEntry; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; import java.util.concurrent.atomic.AtomicLong; /** * @author Jens Wilke; created: 2015-03-28 */ public class JCacheAdapter<K, V> implements javax.cache.Cache<K, V> { JCacheManagerAdapter manager; Cache<K, V> cache; InternalCache<K, V> cacheImpl; boolean storeByValue; boolean loaderConfigured = false; boolean readThrough = false; boolean statisticsEnabled = false; boolean configurationEnabled = false; boolean flushJmxStatistics = false; Class<K> keyType; Class<V> valueType; AtomicLong iterationHitCorrectionCounter = new AtomicLong(); EventHandling<K,V> eventHandling; public JCacheAdapter(JCacheManagerAdapter _manager, Cache<K, V> _cache) { manager = _manager; cache = _cache; cacheImpl = (InternalCache<K, V>) _cache; } @Override public V get(K k) { checkClosed(); if (readThrough) { return cache.get(k); } return cache.peek(k); } @Override public Map<K, V> getAll(Set<? extends K> _keys) { checkClosed(); if (readThrough) { return cache.getAll(_keys); } return cache.peekAll(_keys); } @Override public boolean containsKey(K key) { checkClosed(); return cache.containsKey(key); } @Override public void loadAll(Set<? extends K> keys, boolean replaceExistingValues, final CompletionListener completionListener) { checkClosed(); if (!loaderConfigured) { if (completionListener != null) { completionListener.onCompletion(); } return; } CacheOperationCompletionListener l = null; if (completionListener != null) { l = new CacheOperationCompletionListener() { @Override public void onCompleted() { completionListener.onCompletion(); } @Override public void onException(Throwable _exception) { if (_exception instanceof Exception) { completionListener.onException((Exception) _exception); } else { completionListener.onException(new CacheLoaderException(_exception)); } } }; } if (replaceExistingValues) { cache.reloadAll(keys, l); } else { cache.loadAll(keys, l); } } @Override public void put(K k, V v) { checkClosed(); checkNullValue(v); try { cache.put(k, v); } catch (CacheWriterException ex) { throw new javax.cache.integration.CacheWriterException(ex); } catch (EntryAction.ListenerException ex) { throw new javax.cache.event.CacheEntryListenerException(ex); } } @Override public V getAndPut(K key, V _value) { checkClosed(); checkNullValue(_value); try { return cache.peekAndPut(key, _value); } catch (CacheWriterException ex) { throw new javax.cache.integration.CacheWriterException(ex); } catch (EntryAction.ListenerException ex) { throw new javax.cache.event.CacheEntryListenerException(ex); } } private static void checkNullValue(Object _value) { if (_value == null) { throw new NullPointerException("null value not supported"); } } void checkNullKey(K key) { if (key == null) { throw new NullPointerException("null key not supported"); } } @Override public void putAll(Map<? extends K, ? extends V> map) { checkClosed(); if (map == null) { throw new NullPointerException("null map parameter"); } if (map.containsKey(null)) { throw new NullPointerException("null key not allowed"); } for (Map.Entry<? extends K, ? extends V> e : map.entrySet()) { V v = e.getValue(); checkNullValue(e.getValue()); } try { cache.putAll(map); } catch (EntryAction.ListenerException ex) { throw new javax.cache.event.CacheEntryListenerException(ex); } catch (CacheWriterException ex) { throw new javax.cache.integration.CacheWriterException(ex); } } @Override public boolean putIfAbsent(K key, V _value) { checkClosed(); checkNullValue(_value); try { return cache.putIfAbsent(key, _value); } catch (EntryAction.ListenerException ex) { throw new javax.cache.event.CacheEntryListenerException(ex); } catch (CacheWriterException ex) { throw new javax.cache.integration.CacheWriterException(ex); } } @Override public boolean remove(K key) { checkClosed(); try { return cacheImpl.containsAndRemove(key); } catch (EntryAction.ListenerException ex) { throw new javax.cache.event.CacheEntryListenerException(ex); } catch (CacheWriterException ex) { throw new javax.cache.integration.CacheWriterException(ex); } } @Override public boolean remove(K key, V _oldValue) { checkClosed(); checkNullValue(_oldValue); try { return cache.removeIfEquals(key, _oldValue); } catch (EntryAction.ListenerException ex) { throw new javax.cache.event.CacheEntryListenerException(ex); } catch (CacheWriterException ex) { throw new javax.cache.integration.CacheWriterException(ex); } } @Override public V getAndRemove(K key) { checkClosed(); try { return cache.peekAndRemove(key); } catch (EntryAction.ListenerException ex) { throw new javax.cache.event.CacheEntryListenerException(ex); } catch (CacheWriterException ex) { throw new javax.cache.integration.CacheWriterException(ex); } } @Override public boolean replace(K key, V _oldValue, V _newValue) { checkClosed(); checkNullValue(_oldValue); checkNullValue(_newValue); try { return cache.replaceIfEquals(key, _oldValue, _newValue); } catch (EntryAction.ListenerException ex) { throw new javax.cache.event.CacheEntryListenerException(ex); } catch (CacheWriterException ex) { throw new javax.cache.integration.CacheWriterException(ex); } } @Override public boolean replace(K key, V _value) { checkClosed(); checkNullValue(_value); try { return cache.replace(key, _value); } catch (EntryAction.ListenerException ex) { throw new javax.cache.event.CacheEntryListenerException(ex); } catch (CacheWriterException ex) { throw new javax.cache.integration.CacheWriterException(ex); } } @Override public V getAndReplace(K key, V _value) { checkClosed(); checkNullValue(_value); try { return cache.peekAndReplace(key, _value); } catch (EntryAction.ListenerException ex) { throw new javax.cache.event.CacheEntryListenerException(ex); } catch (CacheWriterException ex) { throw new javax.cache.integration.CacheWriterException(ex); } } @Override public void removeAll(Set<? extends K> keys) { checkClosed(); try { cache.removeAll(keys); } catch (EntryAction.ListenerException ex) { throw new javax.cache.event.CacheEntryListenerException(ex); } catch (CacheWriterException ex) { throw new javax.cache.integration.CacheWriterException(ex); } } @Override public void removeAll() { checkClosed(); try { cache.removeAll(); } catch (EntryAction.ListenerException ex) { throw new javax.cache.event.CacheEntryListenerException(ex); } catch (CacheWriterException ex) { throw new javax.cache.integration.CacheWriterException(ex); } } @Override public void clear() { cache.clear(); } @SuppressWarnings("unchecked") @Override public <C extends Configuration<K, V>> C getConfiguration(Class<C> _class) { if (CompleteConfiguration.class.isAssignableFrom(_class)) { MutableConfiguration<K, V> cfg = new MutableConfiguration<K, V>(); cfg.setTypes(keyType, valueType); cfg.setStatisticsEnabled(statisticsEnabled); cfg.setManagementEnabled(configurationEnabled); cfg.setStoreByValue(storeByValue); Collection<CacheEntryListenerConfiguration<K,V>> _listenerConfigurations = eventHandling.getAllListenerConfigurations(); for (CacheEntryListenerConfiguration<K,V> _listenerConfig : _listenerConfigurations) { cfg.addCacheEntryListenerConfiguration(_listenerConfig); } return (C) cfg; } return (C) new Configuration<K, V>() { @Override public Class<K> getKeyType() { return keyType; } @Override public Class<V> getValueType() { return valueType; } @Override public boolean isStoreByValue() { return storeByValue; } }; } @Override public <T> T invoke(K key, javax.cache.processor.EntryProcessor<K,V,T> entryProcessor, Object... arguments) throws EntryProcessorException { checkClosed(); checkNullKey(key); Map<K, EntryProcessorResult<T>> m = invokeAll(Collections.singleton(key), entryProcessor, arguments); return !m.isEmpty() ? m.values().iterator().next().get() : null; } @Override public <T> Map<K, EntryProcessorResult<T>> invokeAll(Set<? extends K> keys, final javax.cache.processor.EntryProcessor<K,V,T> entryProcessor, final Object... arguments) { checkClosed(); if (entryProcessor == null) { throw new NullPointerException("processor is null"); } EntryProcessor<K, V, T> p = new EntryProcessor<K, V, T>() { @Override public T process(final MutableCacheEntry<K, V> e) throws Exception { MutableEntryAdapter<K, V> me = new MutableEntryAdapter<K, V>(e); T _result = entryProcessor.process(me, arguments); return _result; } }; Map<K, EntryProcessingResult<T>> _result = cache.invokeAll(keys, p); Map<K, EntryProcessorResult<T>> _mappedResult = new HashMap<K, EntryProcessorResult<T>>(); for (Map.Entry<K, EntryProcessingResult<T>> e : _result.entrySet()) { final EntryProcessingResult<T> pr = e.getValue(); EntryProcessorResult<T> epr = new EntryProcessorResult<T>() { @Override public T get() throws EntryProcessorException { Throwable t = pr.getException(); if (t != null) { if (t instanceof EntryProcessorException) { throw (EntryProcessorException) t; } throw new EntryProcessorException(t); } return pr.getResult(); } }; _mappedResult.put(e.getKey(), epr); } return _mappedResult; } @Override public String getName() { return cache.getName(); } @Override public CacheManager getCacheManager() { return manager; } @Override public void close() { cache.close(); } @Override public boolean isClosed() { return cache.isClosed(); } @Override public <T> T unwrap(Class<T> clazz) { if (Cache.class.equals(clazz)) { return (T) cache; } throw new IllegalArgumentException("requested class unknown"); } @Override public void registerCacheEntryListener(CacheEntryListenerConfiguration<K, V> cfg) { eventHandling.registerListener(cfg); } @Override public void deregisterCacheEntryListener(CacheEntryListenerConfiguration<K, V> cfg) { if (cfg == null) { throw new NullPointerException(); } eventHandling.deregisterListener(cfg); } /** * Iterate with the help of cache2k key iterator. */ @Override public Iterator<Entry<K,V>> iterator() { checkClosed(); final Iterator<K> _keyIterator = cache.keys().iterator(); return new Iterator<Entry<K, V>>() { CacheEntry<K, V> entry; @Override public boolean hasNext() { while(_keyIterator.hasNext()) { entry = cache.getEntry(_keyIterator.next()); if (entry.getException() == null) { return true; } } entry = null; return false; } @Override public Entry<K, V> next() { if (entry == null && !hasNext()) { throw new NoSuchElementException(); } return new Entry<K, V>() { @Override public K getKey() { return entry.getKey(); } @Override public V getValue() { return entry.getValue(); } @Override public <T> T unwrap(Class<T> _class) { if (CacheEntry.class.equals(_class)) { return (T) entry; } return null; } }; } @Override public void remove() { if (entry == null) { throw new IllegalStateException("hasNext() / next() not called or end of iteration reached"); } cache.remove(entry.getKey()); } }; } private void checkClosed() { if (cache.isClosed()) { throw new IllegalStateException("cache is closed"); } } private class MutableEntryAdapter<K, V> implements MutableEntry<K, V> { private final MutableCacheEntry<K, V> entry; MutableEntryAdapter(MutableCacheEntry<K, V> e) { entry = e; } @Override public boolean exists() { return entry.exists(); } @Override public void remove() { entry.remove(); } @Override public void setValue(V value) { checkNullValue(value); entry.setValue(value); } @Override public K getKey() { return entry.getKey(); } @Override public V getValue() { if (!readThrough && !exists()) { return null; } return entry.getValue(); } @Override public <T> T unwrap(Class<T> clazz) { return null; } } }