Rewrite our build as modular maven components

This refactoring splits the code up into different components, with
their own per-component CLASSPATH.  By moving all of our classes
into isolated components we can better isolate the classpaths and
try to avoid unexpected dependency problems.  It also allows us to
more clearly define which components are used by the GWT UI and
thus must be compiled under GWT, and which components are run on
the server and can therefore use more of the J2SE API.

Change-Id: I833cc22bacc5655d1c9099ed7c2b0e0a5b08855a
Signed-off-by: Shawn O. Pearce <sop@google.com>
This commit is contained in:
Shawn O. Pearce
2009-11-07 12:55:26 -08:00
parent 2464ac82b7
commit 44671f5c69
719 changed files with 9467 additions and 2417 deletions

View File

@@ -0,0 +1,43 @@
// 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 java.util.concurrent.TimeUnit;
/**
* A fast in-memory and/or on-disk based cache.
*
* @type <K> type of key used to lookup entries in the cache.
* @type <V> type of value stored within each cache entry.
*/
public interface Cache<K, V> {
/** Get the element from the cache, or null if not stored in the cache. */
public V get(K key);
/** Put one element into the cache, replacing any existing value. */
public void put(K key, V value);
/** Remove any existing value from the cache, no-op if not present. */
public void remove(K key);
/**
* Get the time an element will survive in the cache.
*
* @param unit desired units of the return value.
* @return time an item can live before being purged.
*/
public long getTimeToLive(TimeUnit unit);
}

View File

@@ -0,0 +1,101 @@
// 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 com.google.inject.AbstractModule;
import com.google.inject.Key;
import com.google.inject.TypeLiteral;
import com.google.inject.name.Names;
import java.io.Serializable;
/**
* Miniature DSL to support binding {@link Cache} instances in Guice.
*/
public abstract class CacheModule extends AbstractModule {
/**
* Declare an unnamed in-memory cache.
*
* @param <K> type of key used to lookup entries.
* @param <V> type of value stored by the cache.
* @param type type literal for the cache, this literal will be used to match
* injection sites.
* @return binding to describe the cache. Caller must set at least the name on
* the returned binding.
*/
protected <K, V> UnnamedCacheBinding core(final TypeLiteral<Cache<K, V>> type) {
return core(Key.get(type));
}
/**
* Declare a named in-memory cache.
*
* @param <K> type of key used to lookup entries.
* @param <V> type of value stored by the cache.
* @param type type literal for the cache, this literal will be used to match
* injection sites. Injection sites are matched by this type literal
* and with {@code @Named} annotations.
* @return binding to describe the cache.
*/
protected <K, V> NamedCacheBinding core(final TypeLiteral<Cache<K, V>> type,
final String name) {
return core(Key.get(type, Names.named(name))).name(name);
}
private <K, V> UnnamedCacheBinding core(final Key<Cache<K, V>> key) {
final boolean disk = false;
final CacheProvider<K, V> b = new CacheProvider<K, V>(disk);
bind(key).toProvider(b);
return b;
}
/**
* Declare an unnamed in-memory/on-disk cache.
*
* @param <K> type of key used to find entries, must be {@link Serializable}.
* @param <V> type of value stored by the cache, must be {@link Serializable}.
* @param type type literal for the cache, this literal will be used to match
* injection sites. Injection sites are matched by this type literal
* and with {@code @Named} annotations.
* @return binding to describe the cache. Caller must set at least the name on
* the returned binding.
*/
protected <K extends Serializable, V extends Serializable> UnnamedCacheBinding disk(
final TypeLiteral<Cache<K, V>> type) {
return disk(Key.get(type));
}
/**
* Declare a named in-memory/on-disk cache.
*
* @param <K> type of key used to find entries, must be {@link Serializable}.
* @param <V> type of value stored by the cache, must be {@link Serializable}.
* @param type type literal for the cache, this literal will be used to match
* injection sites. Injection sites are matched by this type literal
* and with {@code @Named} annotations.
* @return binding to describe the cache.
*/
protected <K extends Serializable, V extends Serializable> NamedCacheBinding disk(
final TypeLiteral<Cache<K, V>> type, final String name) {
return disk(Key.get(type, Names.named(name))).name(name);
}
private <K, V> UnnamedCacheBinding disk(final Key<Cache<K, V>> key) {
final boolean disk = true;
final CacheProvider<K, V> b = new CacheProvider<K, V>(disk);
bind(key).toProvider(b);
return b;
}
}

View File

@@ -0,0 +1,231 @@
// 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 java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;
import com.google.gerrit.server.config.ConfigUtil;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.SitePath;
import com.google.inject.Inject;
import com.google.inject.ProvisionException;
import com.google.inject.Singleton;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.config.CacheConfiguration;
import net.sf.ehcache.config.Configuration;
import net.sf.ehcache.config.DiskStoreConfiguration;
import net.sf.ehcache.store.MemoryStoreEvictionPolicy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.eclipse.jgit.lib.Config;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
/** Pool of all declared caches created by {@link CacheModule}s. */
@Singleton
public class CachePool {
private static final Logger log = LoggerFactory.getLogger(CachePool.class);
private final Config config;
private final File sitePath;
private final Object lock = new Object();
private final Map<String, CacheProvider<?, ?>> caches;
private CacheManager manager;
@Inject
CachePool(@GerritServerConfig final Config cfg, @SitePath final File sitePath) {
this.config = cfg;
this.sitePath = sitePath;
this.caches = new HashMap<String, CacheProvider<?, ?>>();
}
/** Start the cache pool. The pool must be started before any access occurs. */
public void start() {
synchronized (lock) {
if (manager != null) {
throw new IllegalStateException("Cache pool has already been started");
}
manager = new CacheManager(new Factory().toConfiguration());
for (CacheProvider<?, ?> p : caches.values()) {
p.bind(manager.getEhcache(p.getName()));
}
}
}
/** Stop the cache pool. The pool should be stopped before terminating. */
public void stop() {
synchronized (lock) {
if (manager != null) {
manager.shutdown();
}
}
}
/** <i>Discouraged</i> Get the underlying cache descriptions, for statistics. */
public CacheManager getCacheManager() {
synchronized (lock) {
return manager;
}
}
<K, V> ProxyEhcache register(final CacheProvider<K, V> provider) {
synchronized (lock) {
if (manager != null) {
throw new IllegalStateException("Cache pool has already been started");
}
final String n = provider.getName();
if (caches.containsKey(n) && caches.get(n) != provider) {
throw new IllegalStateException("Cache \"" + n + "\" already defined");
}
caches.put(n, provider);
return new ProxyEhcache(n);
}
}
private class Factory {
private static final int MB = 1024 * 1024;
private final Configuration mgr = new Configuration();
Configuration toConfiguration() {
configureDiskStore();
configureDefaultCache();
for (CacheProvider<?, ?> p : caches.values()) {
final String name = p.getName();
final CacheConfiguration c = newCache(name);
c.setMemoryStoreEvictionPolicyFromObject(toPolicy(p.evictionPolicy()));
c.setMaxElementsInMemory(getInt(name, "memorylimit", p.memoryLimit()));
c.setTimeToIdleSeconds(0);
c.setTimeToLiveSeconds(getSeconds(name, "maxage", p.maxAge()));
c.setEternal(c.getTimeToLiveSeconds() == 0);
if (p.disk() && mgr.getDiskStoreConfiguration() != null) {
c.setMaxElementsOnDisk(getInt(name, "disklimit", p.diskLimit()));
int v = c.getDiskSpoolBufferSizeMB() * MB;
v = getInt(name, "diskbuffer", v) / MB;
c.setDiskSpoolBufferSizeMB(Math.max(1, v));
c.setOverflowToDisk(c.getMaxElementsOnDisk() > 0);
c.setDiskPersistent(c.getMaxElementsOnDisk() > 0);
}
mgr.addCache(c);
}
return mgr;
}
private MemoryStoreEvictionPolicy toPolicy(final EvictionPolicy policy) {
switch (policy) {
case LFU:
return MemoryStoreEvictionPolicy.LFU;
case LRU:
return MemoryStoreEvictionPolicy.LRU;
default:
throw new IllegalArgumentException("Unsupported " + policy);
}
}
private int getInt(String n, String s, int d) {
return config.getInt("cache", n, s, d);
}
private long getSeconds(String n, String s, long d) {
d = MINUTES.convert(d, SECONDS);
long m = ConfigUtil.getTimeUnit(config, "cache", n, s, d, MINUTES);
return SECONDS.convert(m, MINUTES);
}
private void configureDiskStore() {
boolean needDisk = false;
for (CacheProvider<?, ?> p : caches.values()) {
if (p.disk()) {
needDisk = true;
break;
}
}
if (!needDisk) {
return;
}
String path = config.getString("cache", null, "directory");
if (path == null || path.length() == 0) {
return;
}
File loc = new File(path);
if (!loc.isAbsolute()) {
loc = new File(sitePath, path);
}
if (loc.exists() || loc.mkdirs()) {
if (loc.canWrite()) {
final DiskStoreConfiguration c = new DiskStoreConfiguration();
c.setPath(loc.getAbsolutePath());
mgr.addDiskStore(c);
log.info("Enabling disk cache " + loc.getAbsolutePath());
} else {
log.warn("Can't write to disk cache: " + loc.getAbsolutePath());
}
} else {
log.warn("Can't create disk cache: " + loc.getAbsolutePath());
}
}
private void configureDefaultCache() {
final CacheConfiguration c = new CacheConfiguration();
c.setMaxElementsInMemory(1024);
c.setMemoryStoreEvictionPolicyFromObject(MemoryStoreEvictionPolicy.LFU);
c.setTimeToIdleSeconds(0);
c.setTimeToLiveSeconds(0 /* infinite */);
c.setEternal(true);
if (mgr.getDiskStoreConfiguration() != null) {
c.setMaxElementsOnDisk(16384);
c.setOverflowToDisk(false);
c.setDiskPersistent(false);
c.setDiskSpoolBufferSizeMB(5);
c.setDiskExpiryThreadIntervalSeconds(60 * 60);
}
mgr.setDefaultCacheConfiguration(c);
}
private CacheConfiguration newCache(final String name) {
try {
final CacheConfiguration c;
c = mgr.getDefaultCacheConfiguration().clone();
c.setName(name);
return c;
} catch (CloneNotSupportedException e) {
throw new ProvisionException("Cannot configure cache " + name, e);
}
}
}
}

View File

@@ -0,0 +1,129 @@
// 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.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.ProvisionException;
import net.sf.ehcache.Ehcache;
import java.util.concurrent.TimeUnit;
final class CacheProvider<K, V> implements Provider<Cache<K, V>>,
NamedCacheBinding, UnnamedCacheBinding {
private final boolean disk;
private int memoryLimit;
private int diskLimit;
private long maxAge;
private EvictionPolicy evictionPolicy;
private String cacheName;
private ProxyEhcache cache;
CacheProvider(final boolean disk) {
this.disk = disk;
memoryLimit(1024);
maxAge(90, DAYS);
evictionPolicy(LFU);
if (disk) {
diskLimit(16384);
}
}
@Inject
void setCachePool(final CachePool pool) {
this.cache = pool.register(this);
}
void bind(final Ehcache ehcache) {
cache.bind(ehcache);
}
String getName() {
if (cacheName == null) {
throw new ProvisionException("Cache has no name");
}
return cacheName;
}
boolean disk() {
return disk;
}
int memoryLimit() {
return memoryLimit;
}
int diskLimit() {
return diskLimit;
}
long maxAge() {
return maxAge;
}
EvictionPolicy evictionPolicy() {
return evictionPolicy;
}
public NamedCacheBinding name(final String name) {
if (cacheName != null) {
throw new IllegalStateException("Cache name already set");
}
cacheName = name;
return this;
}
public NamedCacheBinding memoryLimit(final int objects) {
memoryLimit = objects;
return this;
}
public NamedCacheBinding diskLimit(final int objects) {
if (!disk) {
// TODO This should really be a compile time type error, but I'm
// too lazy to create the mess of permutations required to setup
// type safe returns for bindings in our little DSL.
//
throw new IllegalStateException("Cache is not disk based");
}
diskLimit = objects;
return this;
}
public NamedCacheBinding maxAge(final long duration, final TimeUnit unit) {
maxAge = SECONDS.convert(duration, unit);
return this;
}
@Override
public NamedCacheBinding evictionPolicy(final EvictionPolicy policy) {
evictionPolicy = policy;
return this;
}
public Cache<K, V> get() {
if (cache == null) {
throw new ProvisionException("Cache \"" + cacheName + "\" not available");
}
return new SimpleCache<K, V>(cache);
}
}

View File

@@ -0,0 +1,24 @@
// 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;
/** How entries should be evicted from the cache. */
public enum EvictionPolicy {
/** Least recently used is evicted first. */
LRU,
/** Least frequently used is evicted first. */
LFU;
}

View File

@@ -0,0 +1,32 @@
// 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 java.util.concurrent.TimeUnit;
/** Configure a cache declared within a {@link CacheModule} instance. */
public interface NamedCacheBinding {
/** Set the number of objects to cache in memory. */
public NamedCacheBinding memoryLimit(int objects);
/** Set the number of objects to cache in memory. */
public NamedCacheBinding diskLimit(int objects);
/** Set the time an element lives before being expired. */
public NamedCacheBinding maxAge(long duration, TimeUnit durationUnits);
/** Set the eviction policy for elements when the cache is full. */
public NamedCacheBinding evictionPolicy(EvictionPolicy policy);
}

View File

@@ -0,0 +1,393 @@
// Copyright (C) 2008 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 net.sf.ehcache.CacheException;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;
import net.sf.ehcache.Statistics;
import net.sf.ehcache.Status;
import net.sf.ehcache.bootstrap.BootstrapCacheLoader;
import net.sf.ehcache.config.CacheConfiguration;
import net.sf.ehcache.event.RegisteredEventListeners;
import net.sf.ehcache.exceptionhandler.CacheExceptionHandler;
import net.sf.ehcache.extension.CacheExtension;
import net.sf.ehcache.loader.CacheLoader;
import net.sf.ehcache.statistics.CacheUsageListener;
import net.sf.ehcache.statistics.LiveCacheStatistics;
import net.sf.ehcache.statistics.sampled.SampledCacheStatistics;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/** Proxy around a cache which has not yet been created. */
final class ProxyEhcache implements Ehcache {
private final String cacheName;
private volatile Ehcache self;
ProxyEhcache(final String cacheName) {
this.cacheName = cacheName;
}
void bind(final Ehcache self) {
this.self = self;
}
private Ehcache self() {
return self;
}
@Override
public Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
@Override
public String getName() {
return cacheName;
}
@Override
public void setName(String name) {
throw new UnsupportedOperationException();
}
//
// Everything else delegates through self.
//
public void bootstrap() {
self().bootstrap();
}
public long calculateInMemorySize() throws IllegalStateException,
CacheException {
return self().calculateInMemorySize();
}
public void clearStatistics() {
self().clearStatistics();
}
public void dispose() throws IllegalStateException {
self().dispose();
}
public void evictExpiredElements() {
self().evictExpiredElements();
}
public void flush() throws IllegalStateException, CacheException {
self().flush();
}
public Element get(Object key) throws IllegalStateException, CacheException {
return self().get(key);
}
public Element get(Serializable key) throws IllegalStateException,
CacheException {
return self().get(key);
}
@SuppressWarnings("unchecked")
public Map getAllWithLoader(Collection keys, Object loaderArgument)
throws CacheException {
return self().getAllWithLoader(keys, loaderArgument);
}
public float getAverageGetTime() {
return self().getAverageGetTime();
}
public BootstrapCacheLoader getBootstrapCacheLoader() {
return self().getBootstrapCacheLoader();
}
public CacheConfiguration getCacheConfiguration() {
if (self == null) {
// In Ehcache 1.7, BlockingCache wants to ask us if we are
// clustered using Terracotta. Unfortunately it is too early
// to know for certain as the caches have not actually been
// created or configured.
//
return new CacheConfiguration();
}
return self().getCacheConfiguration();
}
public RegisteredEventListeners getCacheEventNotificationService() {
return self().getCacheEventNotificationService();
}
public CacheExceptionHandler getCacheExceptionHandler() {
return self().getCacheExceptionHandler();
}
public CacheManager getCacheManager() {
return self().getCacheManager();
}
public int getDiskStoreSize() throws IllegalStateException {
return self().getDiskStoreSize();
}
public String getGuid() {
return self().getGuid();
}
@SuppressWarnings("unchecked")
public List getKeys() throws IllegalStateException, CacheException {
return self().getKeys();
}
@SuppressWarnings("unchecked")
public List getKeysNoDuplicateCheck() throws IllegalStateException {
return self().getKeysNoDuplicateCheck();
}
@SuppressWarnings("unchecked")
public List getKeysWithExpiryCheck() throws IllegalStateException,
CacheException {
return self().getKeysWithExpiryCheck();
}
public long getMemoryStoreSize() throws IllegalStateException {
return self().getMemoryStoreSize();
}
public Element getQuiet(Object key) throws IllegalStateException,
CacheException {
return self().getQuiet(key);
}
public Element getQuiet(Serializable key) throws IllegalStateException,
CacheException {
return self().getQuiet(key);
}
public List<CacheExtension> getRegisteredCacheExtensions() {
return self().getRegisteredCacheExtensions();
}
public List<CacheLoader> getRegisteredCacheLoaders() {
return self().getRegisteredCacheLoaders();
}
public int getSize() throws IllegalStateException, CacheException {
return self().getSize();
}
public Statistics getStatistics() throws IllegalStateException {
return self().getStatistics();
}
public int getStatisticsAccuracy() {
return self().getStatisticsAccuracy();
}
public Status getStatus() {
return self().getStatus();
}
public Element getWithLoader(Object key, CacheLoader loader,
Object loaderArgument) throws CacheException {
return self().getWithLoader(key, loader, loaderArgument);
}
public void initialise() {
self().initialise();
}
public boolean isDisabled() {
return self().isDisabled();
}
public boolean isElementInMemory(Object key) {
return self().isElementInMemory(key);
}
public boolean isElementInMemory(Serializable key) {
return self().isElementInMemory(key);
}
public boolean isElementOnDisk(Object key) {
return self().isElementOnDisk(key);
}
public boolean isElementOnDisk(Serializable key) {
return self().isElementOnDisk(key);
}
public boolean isExpired(Element element) throws IllegalStateException,
NullPointerException {
return self().isExpired(element);
}
public boolean isKeyInCache(Object key) {
return self().isKeyInCache(key);
}
public boolean isValueInCache(Object value) {
return self().isValueInCache(value);
}
public void load(Object key) throws CacheException {
self().load(key);
}
@SuppressWarnings("unchecked")
public void loadAll(Collection keys, Object argument) throws CacheException {
self().loadAll(keys, argument);
}
public void put(Element element, boolean doNotNotifyCacheReplicators)
throws IllegalArgumentException, IllegalStateException, CacheException {
self().put(element, doNotNotifyCacheReplicators);
}
public void put(Element element) throws IllegalArgumentException,
IllegalStateException, CacheException {
self().put(element);
}
public void putQuiet(Element element) throws IllegalArgumentException,
IllegalStateException, CacheException {
self().putQuiet(element);
}
public void registerCacheExtension(CacheExtension cacheExtension) {
self().registerCacheExtension(cacheExtension);
}
public void registerCacheLoader(CacheLoader cacheLoader) {
self().registerCacheLoader(cacheLoader);
}
public boolean remove(Object key, boolean doNotNotifyCacheReplicators)
throws IllegalStateException {
return self().remove(key, doNotNotifyCacheReplicators);
}
public boolean remove(Object key) throws IllegalStateException {
return self().remove(key);
}
public boolean remove(Serializable key, boolean doNotNotifyCacheReplicators)
throws IllegalStateException {
return self().remove(key, doNotNotifyCacheReplicators);
}
public boolean remove(Serializable key) throws IllegalStateException {
return self().remove(key);
}
public void removeAll() throws IllegalStateException, CacheException {
self().removeAll();
}
public void removeAll(boolean doNotNotifyCacheReplicators)
throws IllegalStateException, CacheException {
self().removeAll(doNotNotifyCacheReplicators);
}
public boolean removeQuiet(Object key) throws IllegalStateException {
return self().removeQuiet(key);
}
public boolean removeQuiet(Serializable key) throws IllegalStateException {
return self().removeQuiet(key);
}
public void setBootstrapCacheLoader(BootstrapCacheLoader bootstrapCacheLoader)
throws CacheException {
self().setBootstrapCacheLoader(bootstrapCacheLoader);
}
public void setCacheExceptionHandler(
CacheExceptionHandler cacheExceptionHandler) {
self().setCacheExceptionHandler(cacheExceptionHandler);
}
public void setCacheManager(CacheManager cacheManager) {
self().setCacheManager(cacheManager);
}
public void setDisabled(boolean disabled) {
self().setDisabled(disabled);
}
public void setDiskStorePath(String diskStorePath) throws CacheException {
self().setDiskStorePath(diskStorePath);
}
public void setStatisticsAccuracy(int statisticsAccuracy) {
self().setStatisticsAccuracy(statisticsAccuracy);
}
public void unregisterCacheExtension(CacheExtension cacheExtension) {
self().unregisterCacheExtension(cacheExtension);
}
public void unregisterCacheLoader(CacheLoader cacheLoader) {
self().unregisterCacheLoader(cacheLoader);
}
public Object getInternalContext() {
return self.getInternalContext();
}
public LiveCacheStatistics getLiveCacheStatistics() throws IllegalStateException {
return self.getLiveCacheStatistics();
}
public SampledCacheStatistics getSampledCacheStatistics() {
return self.getSampledCacheStatistics();
}
public int getSizeBasedOnAccuracy(int statisticsAccuracy) throws IllegalArgumentException,
IllegalStateException, CacheException {
return self.getSizeBasedOnAccuracy(statisticsAccuracy);
}
public boolean isSampledStatisticsEnabled() {
return self.isSampledStatisticsEnabled();
}
public boolean isStatisticsEnabled() {
return self.isStatisticsEnabled();
}
public void registerCacheUsageListener(CacheUsageListener cacheUsageListener)
throws IllegalStateException {
self.registerCacheUsageListener(cacheUsageListener);
}
public void removeCacheUsageListener(CacheUsageListener cacheUsageListener)
throws IllegalStateException {
self.removeCacheUsageListener(cacheUsageListener);
}
public void setSampledStatisticsEnabled(boolean enableStatistics) {
self.setSampledStatisticsEnabled(enableStatistics);
}
public void setStatisticsEnabled(boolean enableStatistics) {
self.setStatisticsEnabled(enableStatistics);
}
}

View File

@@ -0,0 +1,138 @@
// Copyright (C) 2008 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 java.util.concurrent.TimeUnit.SECONDS;
import net.sf.ehcache.CacheException;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;
import net.sf.ehcache.constructs.blocking.CacheEntryFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.TimeUnit;
/**
* A decorator for {@link Cache} which automatically constructs missing entries.
* <p>
* On a cache miss {@link #createEntry(Object)} is invoked, allowing the
* application specific subclass to compute the entry and return it for caching.
* During a miss the cache takes a lock related to the missing key, ensuring
* that at most one thread performs the creation work, and other threads wait
* for the result. Concurrent creations are possible if two different keys miss
* and hash to different locks in the internal lock table.
*
* @param <K> type of key used to name cache entries.
* @param <V> type of value stored within a cache entry.
*/
public abstract class SelfPopulatingCache<K, V> implements Cache<K, V> {
private static final Logger log =
LoggerFactory.getLogger(SelfPopulatingCache.class);
private final net.sf.ehcache.constructs.blocking.SelfPopulatingCache self;
/**
* Create a new cache which uses another cache to store entries.
*
* @param backingStore cache which will store the entries for this cache.
*/
@SuppressWarnings("unchecked")
public SelfPopulatingCache(final Cache<K, V> backingStore) {
final Ehcache s = ((SimpleCache) backingStore).getEhcache();
final CacheEntryFactory f = new CacheEntryFactory() {
@SuppressWarnings("unchecked")
@Override
public Object createEntry(Object key) throws Exception {
return SelfPopulatingCache.this.createEntry((K) key);
}
};
self = new net.sf.ehcache.constructs.blocking.SelfPopulatingCache(s, f);
}
/**
* Invoked on a cache miss, to compute the cache entry.
*
* @param key entry whose content needs to be obtained.
* @return new cache content. The caller will automatically put this object
* into the cache.
* @throws Exception the cache content cannot be computed. No entry will be
* stored in the cache, and {@link #missing(Object)} will be invoked
* instead. Future requests for the same key will retry this method.
*/
protected abstract V createEntry(K key) throws Exception;
/** Invoked when {@link #createEntry(Object)} fails, by default return null. */
protected V missing(K key) {
return null;
}
/**
* Get the element from the cache, or {@link #missing(Object)} if not found.
* <p>
* The {@link #missing(Object)} method is only invoked if:
* <ul>
* <li>{@code key == null}, in which case the application should return a
* suitable return value that callers can accept, or throw a RuntimeException.
* <li>{@code createEntry(key)} threw an exception, in which case the entry
* was not stored in the cache. An entry was recorded in the application log,
* but a return value is still required.
* <li>The cache has been shutdown, and access is forbidden.
* </ul>
*
* @param key key to locate.
* @return either the cached entry, or {@code missing(key)} if not found.
*/
@SuppressWarnings("unchecked")
public V get(final K key) {
if (key == null) {
return missing(key);
}
final Element m;
try {
m = self.get(key);
} catch (IllegalStateException err) {
log.error("Cannot lookup " + key + " in \"" + self.getName() + "\"", err);
return missing(key);
} catch (CacheException err) {
log.error("Cannot lookup " + key + " in \"" + self.getName() + "\"", err);
return missing(key);
}
return m != null ? (V) m.getObjectValue() : missing(key);
}
public void remove(final K key) {
if (key != null) {
self.remove(key);
}
}
public void put(K key, V value) {
self.put(new Element(key, value));
}
@Override
public long getTimeToLive(final TimeUnit unit) {
final long maxAge = self.getCacheConfiguration().getTimeToLiveSeconds();
return unit.convert(maxAge, SECONDS);
}
@Override
public String toString() {
return "Cache[" + self.getName() + "]";
}
}

View File

@@ -0,0 +1,85 @@
// 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 java.util.concurrent.TimeUnit.SECONDS;
import net.sf.ehcache.CacheException;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.TimeUnit;
/**
* A fast in-memory and/or on-disk based cache.
*
* @type <K> type of key used to lookup entries in the cache.
* @type <V> type of value stored within each cache entry.
*/
final class SimpleCache<K, V> implements Cache<K, V> {
private static final Logger log = LoggerFactory.getLogger(SimpleCache.class);
private final Ehcache self;
SimpleCache(final Ehcache self) {
this.self = self;
}
Ehcache getEhcache() {
return self;
}
@SuppressWarnings("unchecked")
public V get(final K key) {
if (key == null) {
return null;
}
final Element m;
try {
m = self.get(key);
} catch (IllegalStateException err) {
log.error("Cannot lookup " + key + " in \"" + self.getName() + "\"", err);
return null;
} catch (CacheException err) {
log.error("Cannot lookup " + key + " in \"" + self.getName() + "\"", err);
return null;
}
return m != null ? (V) m.getObjectValue() : null;
}
public void put(final K key, final V value) {
self.put(new Element(key, value));
}
public void remove(final K key) {
if (key != null) {
self.remove(key);
}
}
@Override
public long getTimeToLive(final TimeUnit unit) {
final long maxAge = self.getCacheConfiguration().getTimeToLiveSeconds();
return unit.convert(maxAge, SECONDS);
}
@Override
public String toString() {
return "Cache[" + self.getName() + "]";
}
}

View File

@@ -0,0 +1,22 @@
// 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;
/** Configure a cache declared within a {@link CacheModule} instance. */
public interface UnnamedCacheBinding {
/** Set the name of the cache. */
public NamedCacheBinding name(String cacheName);
}