diff options
Diffstat (limited to 'gerrit-ehcache/src/main/java/com/google/gerrit/ehcache/EhcachePoolImpl.java')
-rw-r--r-- | gerrit-ehcache/src/main/java/com/google/gerrit/ehcache/EhcachePoolImpl.java | 271 |
1 files changed, 271 insertions, 0 deletions
diff --git a/gerrit-ehcache/src/main/java/com/google/gerrit/ehcache/EhcachePoolImpl.java b/gerrit-ehcache/src/main/java/com/google/gerrit/ehcache/EhcachePoolImpl.java new file mode 100644 index 0000000000..c25c381c60 --- /dev/null +++ b/gerrit-ehcache/src/main/java/com/google/gerrit/ehcache/EhcachePoolImpl.java @@ -0,0 +1,271 @@ +// 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.ehcache; + +import static java.util.concurrent.TimeUnit.MINUTES; +import static java.util.concurrent.TimeUnit.SECONDS; + +import com.google.gerrit.lifecycle.LifecycleListener; +import com.google.gerrit.lifecycle.LifecycleModule; +import com.google.gerrit.server.cache.CacheModule; +import com.google.gerrit.server.cache.CachePool; +import com.google.gerrit.server.cache.CacheProvider; +import com.google.gerrit.server.cache.EntryCreator; +import com.google.gerrit.server.cache.EvictionPolicy; +import com.google.gerrit.server.cache.ProxyCache; +import com.google.gerrit.server.config.ConfigUtil; +import com.google.gerrit.server.config.GerritServerConfig; +import com.google.gerrit.server.config.SitePaths; +import com.google.inject.Inject; +import com.google.inject.Singleton; + +import net.sf.ehcache.CacheManager; +import net.sf.ehcache.Ehcache; +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.eclipse.jgit.lib.Config; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +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 EhcachePoolImpl implements CachePool { + private static final Logger log = + LoggerFactory.getLogger(EhcachePoolImpl.class); + + public static class Module extends LifecycleModule { + @Override + protected void configure() { + bind(CachePool.class).to(EhcachePoolImpl.class); + bind(EhcachePoolImpl.class); + listener().to(EhcachePoolImpl.Lifecycle.class); + } + } + + public static class Lifecycle implements LifecycleListener { + private final EhcachePoolImpl cachePool; + + @Inject + Lifecycle(final EhcachePoolImpl cachePool) { + this.cachePool = cachePool; + } + + @Override + public void start() { + cachePool.start(); + } + + @Override + public void stop() { + cachePool.stop(); + } + } + + private final Config config; + private final SitePaths site; + + private final Object lock = new Object(); + private final Map<String, CacheProvider<?, ?>> caches; + private CacheManager manager; + + @Inject + EhcachePoolImpl(@GerritServerConfig final Config cfg, final SitePaths site) { + this.config = cfg; + this.site = site; + this.caches = new HashMap<String, CacheProvider<?, ?>>(); + } + + private void start() { + synchronized (lock) { + if (manager != null) { + throw new IllegalStateException("Cache pool has already been started"); + } + + try { + System.setProperty("net.sf.ehcache.skipUpdateCheck", "" + true); + } catch (SecurityException e) { + // Ignore it, the system is just going to ping some external page + // using a background thread and there's not much we can do about + // it now. + } + + manager = new CacheManager(new Factory().toConfiguration()); + for (CacheProvider<?, ?> p : caches.values()) { + Ehcache eh = manager.getEhcache(p.getName()); + EntryCreator<?, ?> c = p.getEntryCreator(); + if (c != null) { + p.bind(new PopulatingCache(eh, c)); + } else { + p.bind(new SimpleCache(eh)); + } + } + } + } + + private 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; + } + } + + public <K, V> ProxyCache<K, V> 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 ProxyCache<K, V>(); + } + } + + 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; + } + + File loc = site.resolve(config.getString("cache", null, "directory")); + if (loc == null) { + } else 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 CacheConfiguration newConfiguration() { + 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); + } + return c; + } + + private void configureDefaultCache() { + mgr.setDefaultCacheConfiguration(newConfiguration()); + } + + private CacheConfiguration newCache(final String name) { + CacheConfiguration c = newConfiguration(); + c.setName(name); + return c; + } + } +} |