001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.commons.jcs3.jcache;
020
021import javax.cache.Cache;
022import javax.cache.CacheManager;
023import javax.cache.configuration.CacheEntryListenerConfiguration;
024import javax.cache.configuration.CompleteConfiguration;
025import javax.cache.configuration.Configuration;
026import javax.cache.integration.CompletionListener;
027import javax.cache.processor.EntryProcessor;
028import javax.cache.processor.EntryProcessorException;
029import javax.cache.processor.EntryProcessorResult;
030
031import static org.apache.commons.jcs3.jcache.Asserts.assertNotNull;
032
033import java.util.Collection;
034import java.util.HashMap;
035import java.util.HashSet;
036import java.util.Iterator;
037import java.util.LinkedList;
038import java.util.Map;
039import java.util.Set;
040
041// kind of transactional view for a Cache<K, V>, to use with EntryProcessor
042public class TempStateCacheView<K, V> implements Cache<K, V>
043{
044    private final JCSCache<K, V> cache;
045    private final Map<K, V> put = new HashMap<>();
046    private final Collection<K> remove = new LinkedList<>();
047    private boolean removeAll;
048    private boolean clear;
049
050    public TempStateCacheView(final JCSCache<K, V> entries)
051    {
052        this.cache = entries;
053    }
054
055    @Override
056    public V get(final K key)
057    {
058        if (ignoreKey(key))
059        {
060            return null;
061        }
062
063        final V v = put.get(key);
064        if (v != null)
065        {
066            return v;
067        }
068
069        // for an EntryProcessor we already incremented stats - to enhance
070        // surely
071        if (cache.getConfiguration(CompleteConfiguration.class).isStatisticsEnabled())
072        {
073            final Statistics statistics = cache.getStatistics();
074            if (cache.containsKey(key))
075            {
076                statistics.increaseHits(-1);
077            }
078            else
079            {
080                statistics.increaseMisses(-1);
081            }
082        }
083        return cache.get(key);
084    }
085
086    private boolean ignoreKey(final K key)
087    {
088        return removeAll || clear || remove.contains(key);
089    }
090
091    @Override
092    public Map<K, V> getAll(final Set<? extends K> keys)
093    {
094        final Map<K, V> v = new HashMap<>(keys.size());
095        final Set<K> missing = new HashSet<>();
096        for (final K k : keys)
097        {
098            final V value = put.get(k);
099            if (value != null)
100            {
101                v.put(k, value);
102            }
103            else if (!ignoreKey(k))
104            {
105                missing.add(k);
106            }
107        }
108        if (!missing.isEmpty())
109        {
110            v.putAll(cache.getAll(missing));
111        }
112        return v;
113    }
114
115    @Override
116    public boolean containsKey(final K key)
117    {
118        return !ignoreKey(key) && (put.containsKey(key) || cache.containsKey(key));
119    }
120
121    @Override
122    public void loadAll(final Set<? extends K> keys, final boolean replaceExistingValues, final CompletionListener completionListener)
123    {
124        cache.loadAll(keys, replaceExistingValues, completionListener);
125    }
126
127    @Override
128    public void put(final K key, final V value)
129    {
130        assertNotNull(key, "key");
131        assertNotNull(value, "value");
132        put.put(key, value);
133        remove.remove(key);
134    }
135
136    @Override
137    public V getAndPut(final K key, final V value)
138    {
139        final V v = get(key);
140        put(key, value);
141        return v;
142    }
143
144    @Override
145    public void putAll(final Map<? extends K, ? extends V> map)
146    {
147        put.putAll(map);
148        for (final K k : map.keySet())
149        {
150            remove.remove(k);
151        }
152    }
153
154    @Override
155    public boolean putIfAbsent(final K key, final V value)
156    {
157        if (!put.containsKey(key))
158        {
159            put.put(key, value);
160            remove.remove(key);
161            return true;
162        }
163        return false;
164    }
165
166    @Override
167    public boolean remove(final K key)
168    {
169        final boolean noop = put.containsKey(key);
170        put.remove(key);
171        if (!ignoreKey(key))
172        {
173            if (!noop)
174            {
175                remove.add(key);
176            }
177            return true;
178        }
179        return false;
180    }
181
182    @Override
183    public boolean remove(final K key, final V oldValue)
184    {
185        put.remove(key);
186        if (!ignoreKey(key) && oldValue.equals(cache.get(key)))
187        {
188            remove.add(key);
189            return true;
190        }
191        return false;
192    }
193
194    @Override
195    public V getAndRemove(final K key)
196    {
197        final V v = get(key);
198        remove.add(key);
199        put.remove(key);
200        return v;
201    }
202
203    @Override
204    public boolean replace(final K key, final V oldValue, final V newValue)
205    {
206        if (oldValue.equals(get(key)))
207        {
208            put(key, newValue);
209            return true;
210        }
211        return false;
212    }
213
214    @Override
215    public boolean replace(final K key, final V value)
216    {
217        if (containsKey(key))
218        {
219            remove(key);
220            return true;
221        }
222        return false;
223    }
224
225    @Override
226    public V getAndReplace(final K key, final V value)
227    {
228        if (containsKey(key))
229        {
230            final V oldValue = get(key);
231            put(key, value);
232            return oldValue;
233        }
234        return null;
235    }
236
237    @Override
238    public void removeAll(final Set<? extends K> keys)
239    {
240        remove.addAll(keys);
241        for (final K k : keys)
242        {
243            put.remove(k);
244        }
245    }
246
247    @Override
248    public void removeAll()
249    {
250        removeAll = true;
251        put.clear();
252        remove.clear();
253    }
254
255    @Override
256    public void clear()
257    {
258        clear = true;
259        put.clear();
260        remove.clear();
261    }
262
263    @Override
264    public <C extends Configuration<K, V>> C getConfiguration(final Class<C> clazz)
265    {
266        return cache.getConfiguration(clazz);
267    }
268
269    @Override
270    public <T> T invoke(final K key, final EntryProcessor<K, V, T> entryProcessor, final Object... arguments) throws EntryProcessorException
271    {
272        return cache.invoke(key, entryProcessor, arguments);
273    }
274
275    @Override
276    public <T> Map<K, EntryProcessorResult<T>> invokeAll(final Set<? extends K> keys, final EntryProcessor<K, V, T> entryProcessor,
277            final Object... arguments)
278    {
279        return cache.invokeAll(keys, entryProcessor, arguments);
280    }
281
282    @Override
283    public String getName()
284    {
285        return cache.getName();
286    }
287
288    @Override
289    public CacheManager getCacheManager()
290    {
291        return cache.getCacheManager();
292    }
293
294    @Override
295    public void close()
296    {
297        cache.close();
298    }
299
300    @Override
301    public boolean isClosed()
302    {
303        return cache.isClosed();
304    }
305
306    @Override
307    public <T> T unwrap(final Class<T> clazz)
308    {
309        return cache.unwrap(clazz);
310    }
311
312    @Override
313    public void registerCacheEntryListener(final CacheEntryListenerConfiguration<K, V> cacheEntryListenerConfiguration)
314    {
315        cache.registerCacheEntryListener(cacheEntryListenerConfiguration);
316    }
317
318    @Override
319    public void deregisterCacheEntryListener(final CacheEntryListenerConfiguration<K, V> cacheEntryListenerConfiguration)
320    {
321        cache.deregisterCacheEntryListener(cacheEntryListenerConfiguration);
322    }
323
324    @Override
325    public Iterator<Entry<K, V>> iterator()
326    {
327        return cache.iterator();
328    }
329
330    public void merge()
331    {
332        if (removeAll)
333        {
334            cache.removeAll();
335        }
336        if (clear)
337        {
338            cache.clear();
339        }
340
341        for (final Map.Entry<K, V> entry : put.entrySet())
342        {
343            cache.put(entry.getKey(), entry.getValue());
344        }
345        put.clear();
346        for (final K entry : remove)
347        {
348            cache.remove(entry);
349        }
350        remove.clear();
351    }
352}