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.jcs.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;
030import java.util.Collection;
031import java.util.HashMap;
032import java.util.HashSet;
033import java.util.Iterator;
034import java.util.LinkedList;
035import java.util.Map;
036import java.util.Set;
037
038import static org.apache.commons.jcs.jcache.Asserts.assertNotNull;
039
040// kind of transactional view for a Cache<K, V>, to use with EntryProcessor
041public class TempStateCacheView<K, V> implements Cache<K, V>
042{
043    private final JCSCache<K, V> cache;
044    private final Map<K, V> put = new HashMap<K, V>();
045    private final Collection<K> remove = new LinkedList<K>();
046    private boolean removeAll = false;
047    private boolean clear = false;
048
049    public TempStateCacheView(final JCSCache<K, V> entries)
050    {
051        this.cache = entries;
052    }
053
054    public V get(final K key)
055    {
056        if (ignoreKey(key))
057        {
058            return null;
059        }
060
061        final V v = put.get(key);
062        if (v != null)
063        {
064            return v;
065        }
066
067        // for an EntryProcessor we already incremented stats - to enhance
068        // surely
069        if (cache.getConfiguration(CompleteConfiguration.class).isStatisticsEnabled())
070        {
071            final Statistics statistics = cache.getStatistics();
072            if (cache.containsKey(key))
073            {
074                statistics.increaseHits(-1);
075            }
076            else
077            {
078                statistics.increaseMisses(-1);
079            }
080        }
081        return cache.get(key);
082    }
083
084    private boolean ignoreKey(final K key)
085    {
086        return removeAll || clear || remove.contains(key);
087    }
088
089    public Map<K, V> getAll(final Set<? extends K> keys)
090    {
091        final Map<K, V> v = new HashMap<K, V>(keys.size());
092        final Set<K> missing = new HashSet<K>();
093        for (final K k : keys)
094        {
095            final V value = put.get(k);
096            if (value != null)
097            {
098                v.put(k, value);
099            }
100            else if (!ignoreKey(k))
101            {
102                missing.add(k);
103            }
104        }
105        if (!missing.isEmpty())
106        {
107            v.putAll(cache.getAll(missing));
108        }
109        return v;
110    }
111
112    public boolean containsKey(final K key)
113    {
114        return !ignoreKey(key) && (put.containsKey(key) || cache.containsKey(key));
115    }
116
117    public void loadAll(final Set<? extends K> keys, final boolean replaceExistingValues, final CompletionListener completionListener)
118    {
119        cache.loadAll(keys, replaceExistingValues, completionListener);
120    }
121
122    public void put(final K key, final V value)
123    {
124        assertNotNull(key, "key");
125        assertNotNull(value, "value");
126        put.put(key, value);
127        remove.remove(key);
128    }
129
130    public V getAndPut(final K key, final V value)
131    {
132        final V v = get(key);
133        put(key, value);
134        return v;
135    }
136
137    public void putAll(final Map<? extends K, ? extends V> map)
138    {
139        put.putAll(map);
140        for (final K k : map.keySet())
141        {
142            remove.remove(k);
143        }
144    }
145
146    public boolean putIfAbsent(final K key, final V value)
147    {
148        if (!put.containsKey(key))
149        {
150            put.put(key, value);
151            remove.remove(key);
152            return true;
153        }
154        return false;
155    }
156
157    public boolean remove(final K key)
158    {
159        final boolean noop = put.containsKey(key);
160        put.remove(key);
161        if (!ignoreKey(key))
162        {
163            if (!noop)
164            {
165                remove.add(key);
166            }
167            return true;
168        }
169        return false;
170    }
171
172    public boolean remove(final K key, final V oldValue)
173    {
174        put.remove(key);
175        if (!ignoreKey(key) && oldValue.equals(cache.get(key)))
176        {
177            remove.add(key);
178            return true;
179        }
180        return false;
181    }
182
183    public V getAndRemove(final K key)
184    {
185        final V v = get(key);
186        remove.add(key);
187        put.remove(key);
188        return v;
189    }
190
191    public boolean replace(final K key, final V oldValue, final V newValue)
192    {
193        if (oldValue.equals(get(key)))
194        {
195            put(key, newValue);
196            return true;
197        }
198        return false;
199    }
200
201    public boolean replace(final K key, final V value)
202    {
203        if (containsKey(key))
204        {
205            remove(key);
206            return true;
207        }
208        return false;
209    }
210
211    public V getAndReplace(final K key, final V value)
212    {
213        if (containsKey(key))
214        {
215            final V oldValue = get(key);
216            put(key, value);
217            return oldValue;
218        }
219        return null;
220    }
221
222    public void removeAll(final Set<? extends K> keys)
223    {
224        remove.addAll(keys);
225        for (final K k : keys)
226        {
227            put.remove(k);
228        }
229    }
230
231    @Override
232    public void removeAll()
233    {
234        removeAll = true;
235        put.clear();
236        remove.clear();
237    }
238
239    @Override
240    public void clear()
241    {
242        clear = true;
243        put.clear();
244        remove.clear();
245    }
246
247    public <C extends Configuration<K, V>> C getConfiguration(final Class<C> clazz)
248    {
249        return cache.getConfiguration(clazz);
250    }
251
252    public <T> T invoke(final K key, final EntryProcessor<K, V, T> entryProcessor, final Object... arguments) throws EntryProcessorException
253    {
254        return cache.invoke(key, entryProcessor, arguments);
255    }
256
257    public <T> Map<K, EntryProcessorResult<T>> invokeAll(Set<? extends K> keys, final EntryProcessor<K, V, T> entryProcessor,
258            final Object... arguments)
259    {
260        return cache.invokeAll(keys, entryProcessor, arguments);
261    }
262
263    @Override
264    public String getName()
265    {
266        return cache.getName();
267    }
268
269    @Override
270    public CacheManager getCacheManager()
271    {
272        return cache.getCacheManager();
273    }
274
275    @Override
276    public void close()
277    {
278        cache.close();
279    }
280
281    @Override
282    public boolean isClosed()
283    {
284        return cache.isClosed();
285    }
286
287    @Override
288    public <T> T unwrap(final Class<T> clazz)
289    {
290        return cache.unwrap(clazz);
291    }
292
293    public void registerCacheEntryListener(final CacheEntryListenerConfiguration<K, V> cacheEntryListenerConfiguration)
294    {
295        cache.registerCacheEntryListener(cacheEntryListenerConfiguration);
296    }
297
298    public void deregisterCacheEntryListener(final CacheEntryListenerConfiguration<K, V> cacheEntryListenerConfiguration)
299    {
300        cache.deregisterCacheEntryListener(cacheEntryListenerConfiguration);
301    }
302
303    @Override
304    public Iterator<Entry<K, V>> iterator()
305    {
306        return cache.iterator();
307    }
308
309    public void merge()
310    {
311        if (removeAll)
312        {
313            cache.removeAll();
314        }
315        if (clear)
316        {
317            cache.clear();
318        }
319
320        for (final Map.Entry<K, V> entry : put.entrySet())
321        {
322            cache.put(entry.getKey(), entry.getValue());
323        }
324        put.clear();
325        for (final K entry : remove)
326        {
327            cache.remove(entry);
328        }
329        remove.clear();
330    }
331}