| /* Copyright 2012-2013 SpringSource. |
| * |
| * 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. |
| */ |
| package grails.plugin.cache; |
| |
| import java.io.Serializable; |
| import java.util.Collection; |
| import java.util.concurrent.ConcurrentMap; |
| |
| import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap; |
| import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap.Builder; |
| |
| import org.springframework.cache.Cache; |
| import org.springframework.cache.support.SimpleValueWrapper; |
| import org.springframework.util.Assert; |
| |
| /** |
| * @author Jakob Drangmeister |
| */ |
| public class GrailsConcurrentLinkedMapCache implements GrailsCache { |
| |
| private static final Object NULL_HOLDER = new NullHolder(); |
| private String name; |
| private long capacity; |
| private final ConcurrentLinkedHashMap<Object, Object> store; |
| private final boolean allowNullValues; |
| |
| /** |
| * Create a new GrailsConcurrentLinkedMapCache with the specified name |
| * and capacity |
| * @param name the name of the cache |
| * @param capaccity of the map |
| * @param allow null values (default is true) |
| */ |
| public GrailsConcurrentLinkedMapCache(String name, long capacity, boolean allowNullValues) { |
| this.name = name; |
| this.capacity = capacity; |
| this.allowNullValues = allowNullValues; |
| this.store = new ConcurrentLinkedHashMap.Builder<Object, Object>() |
| .maximumWeightedCapacity(capacity) |
| .build(); |
| } |
| |
| /** |
| * Create a new GrailsConcurrentLinkedMapCache with the specified name |
| * and capacity |
| * @param name the name of the cache |
| * @param capaccity of the map |
| */ |
| public GrailsConcurrentLinkedMapCache(String name, long capacity) { |
| this(name, capacity, true); |
| } |
| |
| public final long getCapacity() { |
| return this.store.capacity(); |
| } |
| |
| @Override |
| public final String getName() { |
| return this.name; |
| } |
| |
| @Override |
| public final ConcurrentMap<Object, Object> getNativeCache() { |
| return this.store; |
| } |
| |
| public final int getSize() { |
| return this.store.size(); |
| } |
| |
| public final boolean isAllowNullValues() { |
| return this.allowNullValues; |
| } |
| |
| @Override |
| public GrailsValueWrapper get(Object key) { |
| Object value = getNativeCache().get(key); |
| return value == null ? null : new GrailsValueWrapper(fromStoreValue(value), null); |
| } |
| |
| @SuppressWarnings("unchecked") |
| public Collection<Object> getAllKeys() { |
| return getNativeCache().keySet(); |
| } |
| |
| public Collection<Object> getHottestKeys() { |
| return this.store.descendingKeySet(); |
| } |
| |
| @Override |
| public void put(Object key, Object value) { |
| this.store.put(key, toStoreValue(value)); |
| } |
| |
| public ValueWrapper putIfAbsent(Object key, Object value) { |
| Object existing = this.store.putIfAbsent(key, value); |
| return toWrapper(existing); |
| } |
| |
| @Override |
| public void evict(Object key) { |
| this.store.remove(key); |
| } |
| |
| @Override |
| public void clear() { |
| this.store.clear(); |
| } |
| |
| /** |
| * Convert the given value from the internal store to a user value |
| * returned from the get method (adapting {@code null}). |
| * @param storeValue the store value |
| * @return the value to return to the user |
| */ |
| protected Object fromStoreValue(Object storeValue) { |
| if (this.allowNullValues && storeValue == NULL_HOLDER) { |
| return null; |
| } |
| return storeValue; |
| } |
| |
| /** |
| * Convert the given user value, as passed into the put method, |
| * to a value in the internal store (adapting {@code null}). |
| * @param userValue the given user value |
| * @return the value to store |
| */ |
| protected Object toStoreValue(Object userValue) { |
| if (this.allowNullValues && userValue == null) { |
| return NULL_HOLDER; |
| } |
| return userValue; |
| } |
| |
| private ValueWrapper toWrapper(Object value) { |
| return (value != null ? new SimpleValueWrapper(fromStoreValue(value)) : null); |
| } |
| |
| @SuppressWarnings("serial") |
| |
| private static class NullHolder implements Serializable { |
| } |
| } |