diff --git a/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/H2CacheFactory.java b/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/H2CacheFactory.java index 85a051b68f..cb209e2b4c 100644 --- a/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/H2CacheFactory.java +++ b/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/H2CacheFactory.java @@ -14,20 +14,22 @@ package com.google.gerrit.server.cache.h2; -import com.google.common.base.Preconditions; import com.google.common.cache.Cache; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.collect.Lists; import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.google.gerrit.extensions.events.LifecycleListener; +import com.google.gerrit.extensions.registration.DynamicMap; import com.google.gerrit.server.cache.CacheBinding; import com.google.gerrit.server.cache.PersistentCacheFactory; import com.google.gerrit.server.cache.h2.H2CacheImpl.SqlStore; import com.google.gerrit.server.cache.h2.H2CacheImpl.ValueHolder; import com.google.gerrit.server.config.GerritServerConfig; import com.google.gerrit.server.config.SitePaths; +import com.google.gerrit.server.plugins.Plugin; import com.google.inject.Inject; +import com.google.inject.Provider; import com.google.inject.Singleton; import com.google.inject.TypeLiteral; @@ -37,6 +39,7 @@ import org.slf4j.LoggerFactory; import java.io.File; import java.util.List; +import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -48,17 +51,18 @@ class H2CacheFactory implements PersistentCacheFactory, LifecycleListener { private final DefaultCacheFactory defaultFactory; private final Config config; - private final File cacheDir; + private final File cacheDir; private final List> caches; + private final DynamicMap> cacheMap; private final ExecutorService executor; private final ScheduledExecutorService cleanup; - private volatile boolean started; @Inject H2CacheFactory( DefaultCacheFactory defaultCacheFactory, @GerritServerConfig Config cfg, - SitePaths site) { + SitePaths site, + DynamicMap> cacheMap) { defaultFactory = defaultCacheFactory; config = cfg; @@ -79,6 +83,7 @@ class H2CacheFactory implements PersistentCacheFactory, LifecycleListener { } caches = Lists.newLinkedList(); + this.cacheMap = cacheMap; if (cacheDir != null) { executor = Executors.newFixedThreadPool( @@ -100,7 +105,6 @@ class H2CacheFactory implements PersistentCacheFactory, LifecycleListener { @Override public void start() { - started = true; if (executor != null) { for (final H2CacheImpl cache : caches) { executor.execute(new Runnable() { @@ -141,15 +145,16 @@ class H2CacheFactory implements PersistentCacheFactory, LifecycleListener { log.warn("Interrupted waiting for disk cache to shutdown"); } } - for (H2CacheImpl cache : caches) { - cache.stop(); + synchronized (caches) { + for (H2CacheImpl cache : caches) { + cache.stop(); + } } } @SuppressWarnings({"unchecked", "cast"}) @Override public Cache build(CacheBinding def) { - Preconditions.checkState(!started, "cache must be built before start"); long limit = config.getLong("cache", def.name(), "diskLimit", 128 << 20); if (cacheDir == null || limit <= 0) { @@ -160,7 +165,9 @@ class H2CacheFactory implements PersistentCacheFactory, LifecycleListener { H2CacheImpl cache = new H2CacheImpl( executor, store, def.keyType(), (Cache>) defaultFactory.create(def, true).build()); - caches.add(cache); + synchronized (caches) { + caches.add(cache); + } return cache; } @@ -169,7 +176,6 @@ class H2CacheFactory implements PersistentCacheFactory, LifecycleListener { public LoadingCache build( CacheBinding def, CacheLoader loader) { - Preconditions.checkState(!started, "cache must be built before start"); long limit = config.getLong("cache", def.name(), "diskLimit", 128 << 20); if (cacheDir == null || limit <= 0) { @@ -187,6 +193,19 @@ class H2CacheFactory implements PersistentCacheFactory, LifecycleListener { return cache; } + @Override + public void onStop(Plugin plugin) { + synchronized (caches) { + for (Map.Entry>> entry : + cacheMap.byPlugin(plugin.getName()).entrySet()) { + Cache cache = entry.getValue().get(); + if (caches.remove(cache)) { + ((H2CacheImpl) cache).stop(); + } + } + } + } + private SqlStore newSqlStore( String name, TypeLiteral keyType, diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/cache/PersistentCacheFactory.java b/gerrit-server/src/main/java/com/google/gerrit/server/cache/PersistentCacheFactory.java index 983e956be5..0769b2a5d6 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/cache/PersistentCacheFactory.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/cache/PersistentCacheFactory.java @@ -17,6 +17,7 @@ package com.google.gerrit.server.cache; import com.google.common.cache.Cache; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; +import com.google.gerrit.server.plugins.Plugin; public interface PersistentCacheFactory { Cache build(CacheBinding def); @@ -24,4 +25,6 @@ public interface PersistentCacheFactory { LoadingCache build( CacheBinding def, CacheLoader loader); + + void onStop(Plugin plugin); } diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java index 732da4b2aa..9a1ca5e308 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java @@ -31,6 +31,7 @@ import com.google.gerrit.extensions.events.LifecycleListener; import com.google.gerrit.extensions.systemstatus.ServerInformation; import com.google.gerrit.extensions.webui.JavaScriptPlugin; import com.google.gerrit.server.PluginUser; +import com.google.gerrit.server.cache.PersistentCacheFactory; import com.google.gerrit.server.config.CanonicalWebUrl; import com.google.gerrit.server.config.ConfigUtil; import com.google.gerrit.server.config.GerritServerConfig; @@ -90,6 +91,7 @@ public class PluginLoader implements LifecycleListener { private final Provider cleaner; private final PluginScannerThread scanner; private final Provider urlProvider; + private final PersistentCacheFactory persistentCacheFactory; private final boolean remoteAdmin; @Inject @@ -99,7 +101,8 @@ public class PluginLoader implements LifecycleListener { PluginUser.Factory puf, Provider pct, @GerritServerConfig Config cfg, - @CanonicalWebUrl Provider provider) { + @CanonicalWebUrl Provider provider, + PersistentCacheFactory cacheFactory) { pluginsDir = sitePaths.plugins_dir; dataDir = sitePaths.data_dir; tmpDir = sitePaths.tmp_dir; @@ -113,6 +116,7 @@ public class PluginLoader implements LifecycleListener { cleanupHandles = Maps.newConcurrentMap(); cleaner = pct; urlProvider = provider; + persistentCacheFactory = cacheFactory; remoteAdmin = cfg.getBoolean("plugins", null, "allowRemoteAdmin", false); @@ -228,6 +232,7 @@ public class PluginLoader implements LifecycleListener { } synchronized private void unloadPlugin(Plugin plugin) { + persistentCacheFactory.onStop(plugin); String name = plugin.getName(); log.info(String.format("Unloading plugin %s", name)); plugin.stop(env);