Export ServerInformation to extensions and plugins

Plugins can take this value by injection and learn the current
server state during their own LifecycleListener. This enables a
plugin to determine if it is loading as part of server startup, or
because it was dynamically installed or reloaded by an administrator.

Change-Id: Iac57e039ed9f9f3ecaf2f384c5a3c6a66223f5e1
This commit is contained in:
Shawn O. Pearce
2012-05-11 15:44:47 -07:00
parent 521380a28d
commit d7f89fb05f
6 changed files with 111 additions and 11 deletions

View File

@@ -0,0 +1,42 @@
// Copyright (C) 2012 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.extensions.systemstatus;
/** Exports current server information to an extension. */
public interface ServerInformation {
/** Current state of the server. */
public enum State {
/**
* The server is starting up, and network connections are not yet being
* accepted. Plugins or extensions starting during this time are starting
* for the first time in this process.
*/
STARTUP,
/**
* The server is running and handling requests. Plugins starting during this
* state may be reloading, or being installed into a running system.
*/
RUNNING,
/**
* The server is attempting a graceful halt of operations and will exit (or
* be killed by the operating system) soon.
*/
SHUTDOWN;
}
State getState();
}

View File

@@ -19,6 +19,7 @@ import com.google.common.collect.Lists;
import com.google.gerrit.extensions.annotations.PluginName; import com.google.gerrit.extensions.annotations.PluginName;
import com.google.gerrit.extensions.registration.RegistrationHandle; import com.google.gerrit.extensions.registration.RegistrationHandle;
import com.google.gerrit.extensions.registration.ReloadableRegistrationHandle; import com.google.gerrit.extensions.registration.ReloadableRegistrationHandle;
import com.google.gerrit.extensions.systemstatus.ServerInformation;
import com.google.gerrit.lifecycle.LifecycleListener; import com.google.gerrit.lifecycle.LifecycleListener;
import com.google.gerrit.lifecycle.LifecycleManager; import com.google.gerrit.lifecycle.LifecycleManager;
import com.google.inject.AbstractModule; import com.google.inject.AbstractModule;
@@ -165,9 +166,16 @@ public class Plugin {
} }
private Injector newRootInjector(PluginGuiceEnvironment env) { private Injector newRootInjector(PluginGuiceEnvironment env) {
return Guice.createInjector( List<Module> modules = Lists.newArrayListWithCapacity(4);
env.getSysModule(), modules.add(env.getSysModule());
new AbstractModule() { final ServerInformation srvInfo = env.getServerInformation();
modules.add(new AbstractModule() {
@Override
protected void configure() {
bind(ServerInformation.class).toInstance(srvInfo);
}
});
modules.add(new AbstractModule() {
@Override @Override
protected void configure() { protected void configure() {
bind(String.class) bind(String.class)
@@ -175,6 +183,7 @@ public class Plugin {
.toInstance(name); .toInstance(name);
} }
}); });
return Guice.createInjector(modules);
} }
public void stop() { public void stop() {

View File

@@ -24,6 +24,7 @@ import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.extensions.registration.PrivateInternals_DynamicMapImpl; import com.google.gerrit.extensions.registration.PrivateInternals_DynamicMapImpl;
import com.google.gerrit.extensions.registration.RegistrationHandle; import com.google.gerrit.extensions.registration.RegistrationHandle;
import com.google.gerrit.extensions.registration.ReloadableRegistrationHandle; import com.google.gerrit.extensions.registration.ReloadableRegistrationHandle;
import com.google.gerrit.extensions.systemstatus.ServerInformation;
import com.google.gerrit.lifecycle.LifecycleListener; import com.google.gerrit.lifecycle.LifecycleListener;
import com.google.inject.AbstractModule; import com.google.inject.AbstractModule;
import com.google.inject.Binding; import com.google.inject.Binding;
@@ -56,6 +57,7 @@ import javax.inject.Inject;
@Singleton @Singleton
public class PluginGuiceEnvironment { public class PluginGuiceEnvironment {
private final Injector sysInjector; private final Injector sysInjector;
private final ServerInformation srvInfo;
private final CopyConfigModule copyConfigModule; private final CopyConfigModule copyConfigModule;
private final List<StartPluginListener> onStart; private final List<StartPluginListener> onStart;
private final List<ReloadPluginListener> onReload; private final List<ReloadPluginListener> onReload;
@@ -76,8 +78,12 @@ public class PluginGuiceEnvironment {
private Map<TypeLiteral<?>, DynamicMap<?>> httpMaps; private Map<TypeLiteral<?>, DynamicMap<?>> httpMaps;
@Inject @Inject
PluginGuiceEnvironment(Injector sysInjector, CopyConfigModule ccm) { PluginGuiceEnvironment(
Injector sysInjector,
ServerInformation srvInfo,
CopyConfigModule ccm) {
this.sysInjector = sysInjector; this.sysInjector = sysInjector;
this.srvInfo = srvInfo;
this.copyConfigModule = ccm; this.copyConfigModule = ccm;
onStart = new CopyOnWriteArrayList<StartPluginListener>(); onStart = new CopyOnWriteArrayList<StartPluginListener>();
@@ -90,6 +96,10 @@ public class PluginGuiceEnvironment {
sysMaps = dynamicMapsOf(sysInjector); sysMaps = dynamicMapsOf(sysInjector);
} }
ServerInformation getServerInformation() {
return srvInfo;
}
boolean hasDynamicSet(TypeLiteral<?> type) { boolean hasDynamicSet(TypeLiteral<?> type) {
return sysSets.containsKey(type) return sysSets.containsKey(type)
|| (sshSets != null && sshSets.containsKey(type)) || (sshSets != null && sshSets.containsKey(type))

View File

@@ -17,6 +17,7 @@ package com.google.gerrit.server.plugins;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import com.google.gerrit.extensions.systemstatus.ServerInformation;
import com.google.gerrit.lifecycle.LifecycleListener; import com.google.gerrit.lifecycle.LifecycleListener;
import com.google.gerrit.server.config.ConfigUtil; import com.google.gerrit.server.config.ConfigUtil;
import com.google.gerrit.server.config.GerritServerConfig; import com.google.gerrit.server.config.GerritServerConfig;
@@ -59,6 +60,7 @@ public class PluginLoader implements LifecycleListener {
private final File pluginsDir; private final File pluginsDir;
private final File tmpDir; private final File tmpDir;
private final PluginGuiceEnvironment env; private final PluginGuiceEnvironment env;
private final ServerInformationImpl srvInfoImpl;
private final ConcurrentMap<String, Plugin> running; private final ConcurrentMap<String, Plugin> running;
private final Map<String, FileSnapshot> broken; private final Map<String, FileSnapshot> broken;
private final ReferenceQueue<ClassLoader> cleanupQueue; private final ReferenceQueue<ClassLoader> cleanupQueue;
@@ -68,10 +70,12 @@ public class PluginLoader implements LifecycleListener {
@Inject @Inject
public PluginLoader(SitePaths sitePaths, public PluginLoader(SitePaths sitePaths,
PluginGuiceEnvironment pe, PluginGuiceEnvironment pe,
ServerInformationImpl sii,
@GerritServerConfig Config cfg) { @GerritServerConfig Config cfg) {
pluginsDir = sitePaths.plugins_dir; pluginsDir = sitePaths.plugins_dir;
tmpDir = sitePaths.tmp_dir; tmpDir = sitePaths.tmp_dir;
env = pe; env = pe;
srvInfoImpl = sii;
running = Maps.newConcurrentMap(); running = Maps.newConcurrentMap();
broken = Maps.newHashMap(); broken = Maps.newHashMap();
cleanupQueue = new ReferenceQueue<ClassLoader>(); cleanupQueue = new ReferenceQueue<ClassLoader>();
@@ -184,7 +188,9 @@ public class PluginLoader implements LifecycleListener {
@Override @Override
public synchronized void start() { public synchronized void start() {
log.info("Loading plugins from " + pluginsDir.getAbsolutePath()); log.info("Loading plugins from " + pluginsDir.getAbsolutePath());
srvInfoImpl.state = ServerInformation.State.STARTUP;
rescan(false); rescan(false);
srvInfoImpl.state = ServerInformation.State.RUNNING;
if (scanner != null) { if (scanner != null) {
scanner.start(); scanner.start();
} }
@@ -195,6 +201,7 @@ public class PluginLoader implements LifecycleListener {
if (scanner != null) { if (scanner != null) {
scanner.end(); scanner.end();
} }
srvInfoImpl.state = ServerInformation.State.SHUTDOWN;
synchronized (this) { synchronized (this) {
boolean clean = !running.isEmpty(); boolean clean = !running.isEmpty();
for (Plugin p : running.values()) { for (Plugin p : running.values()) {

View File

@@ -14,11 +14,15 @@
package com.google.gerrit.server.plugins; package com.google.gerrit.server.plugins;
import com.google.gerrit.extensions.systemstatus.ServerInformation;
import com.google.gerrit.lifecycle.LifecycleModule; import com.google.gerrit.lifecycle.LifecycleModule;
public class PluginModule extends LifecycleModule { public class PluginModule extends LifecycleModule {
@Override @Override
protected void configure() { protected void configure() {
bind(ServerInformationImpl.class);
bind(ServerInformation.class).to(ServerInformationImpl.class);
bind(PluginGuiceEnvironment.class); bind(PluginGuiceEnvironment.class);
bind(PluginLoader.class); bind(PluginLoader.class);
bind(CopyConfigModule.class); bind(CopyConfigModule.class);

View File

@@ -0,0 +1,28 @@
// Copyright (C) 2012 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.plugins;
import com.google.gerrit.extensions.systemstatus.ServerInformation;
import com.google.inject.Singleton;
@Singleton
class ServerInformationImpl implements ServerInformation {
volatile State state = State.STARTUP;
@Override
public State getState() {
return state;
}
}