| // Copyright (C) 2009 The Android Open Source Project |
| // |
| // 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 com.google.gerrit.server.cache; |
| |
| import static com.google.gerrit.server.cache.EvictionPolicy.LFU; |
| import static java.util.concurrent.TimeUnit.DAYS; |
| import static java.util.concurrent.TimeUnit.SECONDS; |
| |
| import com.google.gwtorm.protobuf.CodecFactory; |
| import com.google.gwtorm.protobuf.ProtobufCodec; |
| import com.google.inject.Inject; |
| import com.google.inject.Provider; |
| import com.google.inject.ProvisionException; |
| import com.google.inject.TypeLiteral; |
| |
| import java.lang.reflect.Constructor; |
| import java.lang.reflect.ParameterizedType; |
| import java.lang.reflect.Type; |
| import java.util.concurrent.TimeUnit; |
| |
| public final class CacheProvider<K, V> implements Provider<Cache<K, V>>, |
| NamedCacheBinding<K, V>, UnnamedCacheBinding<K, V> { |
| private final CacheModule module; |
| private int memoryLimit; |
| private int diskLimit; |
| private long maxAge; |
| private EvictionPolicy evictionPolicy; |
| private String cacheName; |
| private ProxyCache<K, V> cache; |
| private Provider<EntryCreator<K, V>> entryCreator; |
| private Class<K> keyClass; |
| private Class<V> valueClass; |
| private Provider<V> valueProvider; |
| |
| @SuppressWarnings("unchecked") |
| CacheProvider(CacheModule module, TypeLiteral<Cache<K, V>> typeLiteral) { |
| this.module = module; |
| |
| memoryLimit(1024); |
| maxAge(90, DAYS); |
| evictionPolicy(LFU); |
| |
| Type[] tmp = |
| ((ParameterizedType) typeLiteral.getType()).getActualTypeArguments(); |
| |
| keyClass = (Class<K>) tmp[0]; |
| valueClass = (Class<V>) tmp[1]; |
| |
| for (Constructor c : valueClass.getDeclaredConstructors()) { |
| if (c.getAnnotation(Inject.class) != null) { |
| valueProvider = module.getValueProvider(valueClass); |
| } |
| } |
| |
| try { |
| ProtobufCodec<K> keyCodec = CodecFactory.encoder(keyClass); |
| keyCodec.decode(new byte[0]); |
| } catch (RuntimeException err) { |
| throw new IllegalStateException("Cannot support " + keyClass |
| + " in protobuf format", err); |
| } |
| if (valueProvider == null) { |
| try { |
| ProtobufCodec<V> valueCodec = CodecFactory.encoder(valueClass); |
| valueCodec.decode(new byte[0]); |
| } catch (RuntimeException err) { |
| throw new IllegalStateException("Cannot support " + valueClass |
| + " in protobuf format", err); |
| } |
| } |
| } |
| |
| @Inject |
| void setCachePool(final CachePool pool) { |
| this.cache = pool.register(this); |
| } |
| |
| public void bind(Cache<K, V> impl) { |
| if (cache == null) { |
| throw new ProvisionException("Cache was never registered"); |
| } |
| cache.bind(impl); |
| } |
| |
| public EntryCreator<K, V> getEntryCreator() { |
| return entryCreator != null ? entryCreator.get() : null; |
| } |
| |
| public Provider<V> getValueProvider() { |
| return valueProvider; |
| } |
| |
| public String getName() { |
| if (cacheName == null) { |
| throw new ProvisionException("Cache has no name"); |
| } |
| return cacheName; |
| } |
| |
| public boolean disk() { |
| return diskLimit() > 0; |
| } |
| |
| public int memoryLimit() { |
| return memoryLimit; |
| } |
| |
| public int diskLimit() { |
| return diskLimit; |
| } |
| |
| public long maxAge() { |
| return maxAge; |
| } |
| |
| public Class<K> getKeyClass() { |
| return keyClass; |
| } |
| |
| public Class<V> getValueClass() { |
| return valueClass; |
| } |
| |
| public EvictionPolicy evictionPolicy() { |
| return evictionPolicy; |
| } |
| |
| public NamedCacheBinding<K, V> name(final String name) { |
| if (cacheName != null) { |
| throw new IllegalStateException("Cache name already set"); |
| } |
| cacheName = name; |
| return this; |
| } |
| |
| public NamedCacheBinding<K, V> memoryLimit(final int objects) { |
| memoryLimit = objects; |
| return this; |
| } |
| |
| public NamedCacheBinding<K, V> diskLimit(final int objects) { |
| diskLimit = objects; |
| return this; |
| } |
| |
| public NamedCacheBinding<K, V> maxAge(final long duration, final TimeUnit unit) { |
| maxAge = SECONDS.convert(duration, unit); |
| return this; |
| } |
| |
| @Override |
| public NamedCacheBinding<K, V> evictionPolicy(final EvictionPolicy policy) { |
| evictionPolicy = policy; |
| return this; |
| } |
| |
| public NamedCacheBinding<K, V> populateWith( |
| Class<? extends EntryCreator<K, V>> creator) { |
| entryCreator = module.getEntryCreator(this, creator); |
| return this; |
| } |
| |
| public Cache<K, V> get() { |
| if (cache == null) { |
| throw new ProvisionException("Cache \"" + cacheName + "\" not available"); |
| } |
| return cache; |
| } |
| } |