Merge changes I7afb6192,I7bc54c84,I8e60aa0f
* changes: Automatically load/unload/reload plugins Dynamically load plugins in new private injectors Allow SSH commands to be registered dynamically
This commit is contained in:
@@ -46,11 +46,12 @@ import com.google.gerrit.server.git.ReceiveCommitsExecutorModule;
|
||||
import com.google.gerrit.server.git.WorkQueue;
|
||||
import com.google.gerrit.server.mail.SignedTokenEmailTokenVerifier;
|
||||
import com.google.gerrit.server.mail.SmtpEmailSender;
|
||||
import com.google.gerrit.server.plugins.PluginGuiceEnvironment;
|
||||
import com.google.gerrit.server.plugins.PluginModule;
|
||||
import com.google.gerrit.server.schema.SchemaVersionCheck;
|
||||
import com.google.gerrit.server.ssh.NoSshModule;
|
||||
import com.google.gerrit.sshd.SshModule;
|
||||
import com.google.gerrit.sshd.commands.MasterCommandModule;
|
||||
import com.google.gerrit.sshd.commands.MasterPluginsModule;
|
||||
import com.google.gerrit.sshd.commands.SlaveCommandModule;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Module;
|
||||
@@ -141,6 +142,8 @@ public class Daemon extends SiteProgram {
|
||||
dbInjector = createDbInjector(MULTI_USER);
|
||||
cfgInjector = createCfgInjector();
|
||||
sysInjector = createSysInjector();
|
||||
sysInjector.getInstance(PluginGuiceEnvironment.class)
|
||||
.setCfgInjector(cfgInjector);
|
||||
manager.add(dbInjector, cfgInjector, sysInjector);
|
||||
|
||||
if (sshd) {
|
||||
@@ -209,6 +212,7 @@ public class Daemon extends SiteProgram {
|
||||
modules.add(new SmtpEmailSender.Module());
|
||||
modules.add(new SignedTokenEmailTokenVerifier.Module());
|
||||
modules.add(new PushReplication.Module());
|
||||
modules.add(new PluginModule());
|
||||
if (httpd) {
|
||||
modules.add(new CanonicalWebUrlModule() {
|
||||
@Override
|
||||
@@ -232,6 +236,8 @@ public class Daemon extends SiteProgram {
|
||||
|
||||
private void initSshd() {
|
||||
sshInjector = createSshInjector();
|
||||
sysInjector.getInstance(PluginGuiceEnvironment.class)
|
||||
.setSshInjector(sshInjector);
|
||||
manager.add(sshInjector);
|
||||
}
|
||||
|
||||
@@ -243,7 +249,6 @@ public class Daemon extends SiteProgram {
|
||||
modules.add(new SlaveCommandModule());
|
||||
} else {
|
||||
modules.add(new MasterCommandModule());
|
||||
modules.add(cfgInjector.getInstance(MasterPluginsModule.class));
|
||||
}
|
||||
} else {
|
||||
modules.add(new NoSshModule());
|
||||
|
@@ -1,146 +0,0 @@
|
||||
// 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.common;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.gerrit.server.config.SitePaths;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Module;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
@Singleton
|
||||
public class PluginLoader {
|
||||
private static final Logger log = LoggerFactory.getLogger(PluginLoader.class);
|
||||
|
||||
private final File pluginsDir;
|
||||
private Map<String, Plugin> pluginCache;
|
||||
|
||||
@Inject
|
||||
public PluginLoader(SitePaths sitePaths) {
|
||||
pluginsDir = sitePaths.plugins_dir;
|
||||
}
|
||||
|
||||
private synchronized void initialize() {
|
||||
if (pluginCache != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
pluginCache = new HashMap<String, Plugin>();
|
||||
loadPlugins();
|
||||
}
|
||||
|
||||
public Plugin get(String pluginName) {
|
||||
initialize();
|
||||
return pluginCache.get(pluginName);
|
||||
}
|
||||
|
||||
public Collection<Plugin> getPlugins() {
|
||||
initialize();
|
||||
return pluginCache.values();
|
||||
}
|
||||
|
||||
private void loadPlugins() {
|
||||
Collection<File> pluginJars;
|
||||
try {
|
||||
pluginJars = getPluginFiles();
|
||||
} catch (IOException e) {
|
||||
log.error("Cannot scan Gerrit plugins directory looking for jar files", e);
|
||||
return;
|
||||
}
|
||||
|
||||
for (File jarFile : pluginJars) {
|
||||
Plugin plugin;
|
||||
try {
|
||||
plugin = loadPlugin(jarFile);
|
||||
pluginCache.put(plugin.name, plugin);
|
||||
} catch (IOException e) {
|
||||
log.error("Cannot access plugin jar " + jarFile, e);
|
||||
} catch (ClassNotFoundException e) {
|
||||
log.error("Cannot load plugin class module from " + jarFile, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Plugin loadPlugin(File jarFile) throws IOException,
|
||||
ClassNotFoundException {
|
||||
Manifest jarManifest = new JarFile(jarFile).getManifest();
|
||||
ClassLoader parentLoader = PluginLoader.class.getClassLoader();
|
||||
ClassLoader jarClassLoader =
|
||||
new URLClassLoader(getPluginURLs(jarFile), parentLoader);
|
||||
|
||||
Attributes attrs = jarManifest.getMainAttributes();
|
||||
String pluginName = attrs.getValue("Gerrit-Plugin");
|
||||
if (Strings.isNullOrEmpty(pluginName)) {
|
||||
throw new IOException("No Gerrit-Plugin attribute in manifest");
|
||||
}
|
||||
|
||||
String moduleName = attrs.getValue("Gerrit-SshModule");
|
||||
if (Strings.isNullOrEmpty(moduleName)) {
|
||||
throw new IOException("No Gerrit-SshModule attribute in manifest");
|
||||
}
|
||||
|
||||
Class<?> moduleClass = Class.forName(moduleName, false, jarClassLoader);
|
||||
if (!Module.class.isAssignableFrom(moduleClass)) {
|
||||
throw new ClassNotFoundException(String.format(
|
||||
"Gerrit-SshModule %s is not a Guice Module",
|
||||
moduleClass.getName()));
|
||||
}
|
||||
|
||||
return new Plugin(pluginName, (Class<? extends Module>) moduleClass);
|
||||
}
|
||||
|
||||
private URL[] getPluginURLs(File jarFile) throws MalformedURLException {
|
||||
return new URL[] {jarFile.toURI().toURL()};
|
||||
}
|
||||
|
||||
private List<File> getPluginFiles() throws IOException {
|
||||
if (pluginsDir == null || !pluginsDir.exists()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
File[] plugins = pluginsDir.listFiles(new FileFilter() {
|
||||
@Override
|
||||
public boolean accept(File pathname) {
|
||||
return pathname.isFile() && pathname.getName().endsWith(".jar");
|
||||
}
|
||||
});
|
||||
if (plugins == null) {
|
||||
log.error("Cannot list " + pluginsDir.getAbsolutePath());
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return Arrays.asList(plugins);
|
||||
}
|
||||
}
|
@@ -0,0 +1,102 @@
|
||||
// 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.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.config.GerritServerConfig;
|
||||
import com.google.gerrit.server.config.SitePath;
|
||||
import com.google.gerrit.server.config.SitePaths;
|
||||
import com.google.gerrit.server.config.TrackingFooters;
|
||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||
import com.google.gwtorm.server.SchemaFactory;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provides;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import org.eclipse.jgit.lib.Config;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Copies critical objects from the {@code dbInjector} into a plugin.
|
||||
* <p>
|
||||
* Most explicit bindings are copied automatically from the cfgInjector and
|
||||
* sysInjector to be made available to a plugin's private world. This module is
|
||||
* necessary to get things bound in the dbInjector that are not otherwise easily
|
||||
* available, but that a plugin author might expect to exist.
|
||||
*/
|
||||
@Singleton
|
||||
class CopyConfigModule extends AbstractModule {
|
||||
@Inject
|
||||
@SitePath
|
||||
private File sitePath;
|
||||
|
||||
@Provides
|
||||
@SitePath
|
||||
File getSitePath() {
|
||||
return sitePath;
|
||||
}
|
||||
|
||||
@Inject
|
||||
private SitePaths sitePaths;
|
||||
|
||||
@Provides
|
||||
SitePaths getSitePaths() {
|
||||
return sitePaths;
|
||||
}
|
||||
|
||||
@Inject
|
||||
private TrackingFooters trackingFooters;
|
||||
|
||||
@Provides
|
||||
TrackingFooters getTrackingFooters() {
|
||||
return trackingFooters;
|
||||
}
|
||||
|
||||
@Inject
|
||||
@GerritServerConfig
|
||||
private Config gerritServerConfig;
|
||||
|
||||
@Provides
|
||||
@GerritServerConfig
|
||||
Config getGerritServerConfig() {
|
||||
return gerritServerConfig;
|
||||
}
|
||||
|
||||
@Inject
|
||||
private SchemaFactory<ReviewDb> schemaFactory;
|
||||
|
||||
@Provides
|
||||
SchemaFactory<ReviewDb> getSchemaFactory() {
|
||||
return schemaFactory;
|
||||
}
|
||||
|
||||
@Inject
|
||||
private GitRepositoryManager gitRepositoryManager;
|
||||
|
||||
@Provides
|
||||
GitRepositoryManager getGitRepositoryManager() {
|
||||
return gitRepositoryManager;
|
||||
}
|
||||
|
||||
@Inject
|
||||
CopyConfigModule() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
}
|
||||
}
|
@@ -0,0 +1,128 @@
|
||||
// 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.lifecycle.LifecycleListener;
|
||||
import com.google.gerrit.lifecycle.LifecycleManager;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Module;
|
||||
|
||||
import org.eclipse.jgit.storage.file.FileSnapshot;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class Plugin {
|
||||
private final String name;
|
||||
private final FileSnapshot snapshot;
|
||||
private Class<? extends Module> sysModule;
|
||||
private Class<? extends Module> sshModule;
|
||||
|
||||
private Injector sysInjector;
|
||||
private Injector sshInjector;
|
||||
private LifecycleManager manager;
|
||||
|
||||
public Plugin(String name,
|
||||
FileSnapshot snapshot,
|
||||
@Nullable Class<? extends Module> sysModule,
|
||||
@Nullable Class<? extends Module> sshModule) {
|
||||
this.name = name;
|
||||
this.snapshot = snapshot;
|
||||
this.sysModule = sysModule;
|
||||
this.sshModule = sshModule;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
boolean isModified(File jar) {
|
||||
return snapshot.lastModified() != jar.lastModified();
|
||||
}
|
||||
|
||||
public void start(PluginGuiceEnvironment env) throws Exception {
|
||||
Injector root = newRootInjector(env);
|
||||
manager = new LifecycleManager();
|
||||
|
||||
if (sysModule != null) {
|
||||
sysInjector = root.createChildInjector(root.getInstance(sysModule));
|
||||
manager.add(sysInjector);
|
||||
} else {
|
||||
sysInjector = root;
|
||||
}
|
||||
|
||||
if (sshModule != null && env.hasSshModule()) {
|
||||
sshInjector = sysInjector.createChildInjector(
|
||||
env.getSshModule(),
|
||||
sysInjector.getInstance(sshModule));
|
||||
manager.add(sshInjector);
|
||||
}
|
||||
|
||||
manager.start();
|
||||
env.onStartPlugin(this);
|
||||
}
|
||||
|
||||
private Injector newRootInjector(PluginGuiceEnvironment env) {
|
||||
return Guice.createInjector(
|
||||
env.getSysModule(),
|
||||
new AbstractModule() {
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(String.class)
|
||||
.annotatedWith(PluginName.class)
|
||||
.toInstance(name);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
if (manager != null) {
|
||||
manager.stop();
|
||||
manager = null;
|
||||
sysInjector = null;
|
||||
sshInjector = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Injector getSshInjector() {
|
||||
return sshInjector;
|
||||
}
|
||||
|
||||
public void add(final RegistrationHandle handle) {
|
||||
add(new LifecycleListener() {
|
||||
@Override
|
||||
public void start() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
handle.remove();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void add(LifecycleListener listener) {
|
||||
manager.add(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Plugin [" + name + "]";
|
||||
}
|
||||
}
|
@@ -0,0 +1,140 @@
|
||||
// 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.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.gerrit.lifecycle.LifecycleListener;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Binding;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Key;
|
||||
import com.google.inject.Module;
|
||||
import com.google.inject.Singleton;
|
||||
import com.google.inject.TypeLiteral;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* Tracks Guice bindings that should be exposed to loaded plugins.
|
||||
* <p>
|
||||
* This is an internal implementation detail of how the main server is able to
|
||||
* export its explicit Guice bindings to tightly coupled plugins, giving them
|
||||
* access to singletons and request scoped resources just like any core code.
|
||||
*/
|
||||
@Singleton
|
||||
public class PluginGuiceEnvironment {
|
||||
private final Injector sysInjector;
|
||||
private final CopyConfigModule copyConfigModule;
|
||||
private final List<StartPluginListener> listeners;
|
||||
private Module sysModule;
|
||||
private Module sshModule;
|
||||
|
||||
@Inject
|
||||
PluginGuiceEnvironment(Injector sysInjector, CopyConfigModule ccm) {
|
||||
this.sysInjector = sysInjector;
|
||||
this.copyConfigModule = ccm;
|
||||
this.listeners = new CopyOnWriteArrayList<StartPluginListener>();
|
||||
this.listeners.addAll(getListeners(sysInjector));
|
||||
}
|
||||
|
||||
Module getSysModule() {
|
||||
return sysModule;
|
||||
}
|
||||
|
||||
public void setCfgInjector(Injector cfgInjector) {
|
||||
final Module cm = copy(cfgInjector);
|
||||
final Module sm = copy(sysInjector);
|
||||
sysModule = new AbstractModule() {
|
||||
@Override
|
||||
protected void configure() {
|
||||
install(copyConfigModule);
|
||||
install(cm);
|
||||
install(sm);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void setSshInjector(Injector sshInjector) {
|
||||
sshModule = copy(sshInjector);
|
||||
listeners.addAll(getListeners(sshInjector));
|
||||
}
|
||||
|
||||
boolean hasSshModule() {
|
||||
return sshModule != null;
|
||||
}
|
||||
|
||||
Module getSshModule() {
|
||||
return sshModule;
|
||||
}
|
||||
|
||||
void onStartPlugin(Plugin plugin) {
|
||||
for (StartPluginListener l : listeners) {
|
||||
l.onStartPlugin(plugin);
|
||||
}
|
||||
}
|
||||
|
||||
private static List<StartPluginListener> getListeners(Injector src) {
|
||||
List<Binding<StartPluginListener>> bindings =
|
||||
src.findBindingsByType(new TypeLiteral<StartPluginListener>() {});
|
||||
List<StartPluginListener> found =
|
||||
Lists.newArrayListWithCapacity(bindings.size());
|
||||
for (Binding<StartPluginListener> b : bindings) {
|
||||
found.add(b.getProvider().get());
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
private static Module copy(Injector src) {
|
||||
final Map<Key<?>, Binding<?>> bindings = Maps.newLinkedHashMap();
|
||||
for (Map.Entry<Key<?>, Binding<?>> e : src.getBindings().entrySet()) {
|
||||
if (shouldCopy(e.getKey())) {
|
||||
bindings.put(e.getKey(), e.getValue());
|
||||
}
|
||||
}
|
||||
bindings.remove(Key.get(Injector.class));
|
||||
bindings.remove(Key.get(java.util.logging.Logger.class));
|
||||
|
||||
return new AbstractModule() {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected void configure() {
|
||||
for (Map.Entry<Key<?>, Binding<?>> e : bindings.entrySet()) {
|
||||
Key<Object> k = (Key<Object>) e.getKey();
|
||||
Binding<Object> b = (Binding<Object>) e.getValue();
|
||||
bind(k).toProvider(b.getProvider());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static boolean shouldCopy(Key<?> key) {
|
||||
Class<?> type = key.getTypeLiteral().getRawType();
|
||||
if (type == LifecycleListener.class) {
|
||||
return false;
|
||||
}
|
||||
if (type == StartPluginListener.class) {
|
||||
return false;
|
||||
}
|
||||
if ("org.apache.sshd.server.Command".equals(type.getName())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
@@ -0,0 +1,205 @@
|
||||
// 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.common.base.Strings;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.gerrit.lifecycle.LifecycleListener;
|
||||
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.Module;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import org.eclipse.jgit.lib.Config;
|
||||
import org.eclipse.jgit.storage.file.FileSnapshot;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
@Singleton
|
||||
public class PluginLoader implements LifecycleListener {
|
||||
private static final Logger log = LoggerFactory.getLogger(PluginLoader.class);
|
||||
|
||||
private final File pluginsDir;
|
||||
private final PluginGuiceEnvironment env;
|
||||
private final Map<String, Plugin> running;
|
||||
private final Map<String, FileSnapshot> broken;
|
||||
private final PluginScannerThread scanner;
|
||||
|
||||
@Inject
|
||||
public PluginLoader(SitePaths sitePaths,
|
||||
PluginGuiceEnvironment pe,
|
||||
@GerritServerConfig Config cfg) {
|
||||
pluginsDir = sitePaths.plugins_dir;
|
||||
env = pe;
|
||||
running = Maps.newHashMap();
|
||||
broken = Maps.newHashMap();
|
||||
scanner = new PluginScannerThread(
|
||||
this,
|
||||
ConfigUtil.getTimeUnit(cfg,
|
||||
"plugins", null, "checkFrequency",
|
||||
TimeUnit.MINUTES.toMillis(1), TimeUnit.MILLISECONDS));
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void start() {
|
||||
log.info("Loading plugins from " + pluginsDir.getAbsolutePath());
|
||||
rescan();
|
||||
scanner.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
scanner.end();
|
||||
synchronized (this) {
|
||||
for (Plugin p : running.values()) {
|
||||
p.stop();
|
||||
}
|
||||
running.clear();
|
||||
broken.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void rescan() {
|
||||
List<File> jars = scanJarsInPluginsDirectory();
|
||||
|
||||
stopRemovedPlugins(jars);
|
||||
|
||||
for (File jar : jars) {
|
||||
String name = nameOf(jar);
|
||||
FileSnapshot brokenTime = broken.get(name);
|
||||
if (brokenTime != null && !brokenTime.isModified(jar)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Plugin active = running.get(name);
|
||||
if (active != null && !active.isModified(jar)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (active != null) {
|
||||
log.warn(String.format(
|
||||
"Detected %s was replaced/overwritten."
|
||||
+ " This is not a safe way to update a plugin.",
|
||||
jar.getAbsolutePath()));
|
||||
log.info(String.format("Reloading plugin %s", name));
|
||||
active.stop();
|
||||
running.remove(name);
|
||||
}
|
||||
|
||||
FileSnapshot snapshot = FileSnapshot.save(jar);
|
||||
Plugin next;
|
||||
try {
|
||||
next = loadPlugin(name, snapshot, jar);
|
||||
next.start(env);
|
||||
} catch (Throwable err) {
|
||||
log.warn(String.format("Cannot load plugin %s", name), err);
|
||||
broken.put(name, snapshot);
|
||||
continue;
|
||||
}
|
||||
broken.remove(name);
|
||||
running.put(name, next);
|
||||
|
||||
if (active == null) {
|
||||
log.info(String.format("Loaded plugin %s", name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void stopRemovedPlugins(List<File> jars) {
|
||||
Set<String> unload = Sets.newHashSet(running.keySet());
|
||||
for (File jar : jars) {
|
||||
unload.remove(nameOf(jar));
|
||||
}
|
||||
for (String name : unload){
|
||||
log.info(String.format("Unloading plugin %s", name));
|
||||
running.remove(name).stop();
|
||||
}
|
||||
}
|
||||
|
||||
private static String nameOf(File jar) {
|
||||
String name = jar.getName();
|
||||
int ext = name.lastIndexOf('.');
|
||||
return 0 < ext ? name.substring(0, ext) : name;
|
||||
}
|
||||
|
||||
private Plugin loadPlugin(String name, FileSnapshot snapshot, File jarFile)
|
||||
throws IOException, ClassNotFoundException {
|
||||
Manifest manifest = new JarFile(jarFile).getManifest();
|
||||
|
||||
Attributes main = manifest.getMainAttributes();
|
||||
String sysName = main.getValue("Gerrit-Module");
|
||||
String sshName = main.getValue("Gerrit-SshModule");
|
||||
|
||||
URL[] urls = {jarFile.toURI().toURL()};
|
||||
ClassLoader parentLoader = PluginLoader.class.getClassLoader();
|
||||
ClassLoader pluginLoader = new URLClassLoader(urls, parentLoader);
|
||||
|
||||
Class<? extends Module> sysModule = load(sysName, pluginLoader);
|
||||
Class<? extends Module> sshModule = load(sshName, pluginLoader);
|
||||
return new Plugin(name, snapshot, sysModule, sshModule);
|
||||
}
|
||||
|
||||
private Class<? extends Module> load(String name, ClassLoader pluginLoader)
|
||||
throws ClassNotFoundException {
|
||||
if (Strings.isNullOrEmpty(name)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<? extends Module> clazz =
|
||||
(Class<? extends Module>) Class.forName(name, false, pluginLoader);
|
||||
if (!Module.class.isAssignableFrom(clazz)) {
|
||||
throw new ClassCastException(String.format(
|
||||
"Class %s does not implement %s",
|
||||
name, Module.class.getName()));
|
||||
}
|
||||
return clazz;
|
||||
}
|
||||
|
||||
private List<File> scanJarsInPluginsDirectory() {
|
||||
if (pluginsDir == null || !pluginsDir.exists()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
File[] matches = pluginsDir.listFiles(new FileFilter() {
|
||||
@Override
|
||||
public boolean accept(File pathname) {
|
||||
return pathname.getName().endsWith(".jar") && pathname.isFile();
|
||||
}
|
||||
});
|
||||
if (matches == null) {
|
||||
log.error("Cannot list " + pluginsDir.getAbsolutePath());
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return Arrays.asList(matches);
|
||||
}
|
||||
}
|
@@ -12,21 +12,16 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.common;
|
||||
package com.google.gerrit.server.plugins;
|
||||
|
||||
import com.google.inject.Module;
|
||||
|
||||
public class Plugin {
|
||||
public final String name;
|
||||
public final Class<? extends Module> sshModule;
|
||||
|
||||
public Plugin(String name, Class<? extends Module> sshModule) {
|
||||
this.name = name;
|
||||
this.sshModule = sshModule;
|
||||
}
|
||||
import com.google.gerrit.lifecycle.LifecycleModule;
|
||||
|
||||
public class PluginModule extends LifecycleModule {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Plugin [" + name + "; SshModule=" + sshModule.getName() + "]";
|
||||
protected void configure() {
|
||||
bind(PluginGuiceEnvironment.class);
|
||||
bind(PluginLoader.class);
|
||||
bind(CopyConfigModule.class);
|
||||
listener().to(PluginLoader.class);
|
||||
}
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
// 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 static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
import com.google.inject.BindingAnnotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target({ElementType.PARAMETER, ElementType.FIELD})
|
||||
@Retention(RUNTIME)
|
||||
@BindingAnnotation
|
||||
public @interface PluginName {
|
||||
}
|
@@ -0,0 +1,52 @@
|
||||
// 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 java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
class PluginScannerThread extends Thread {
|
||||
private final CountDownLatch done = new CountDownLatch(1);
|
||||
private final PluginLoader loader;
|
||||
private final long checkFrequencyMillis;
|
||||
|
||||
PluginScannerThread(PluginLoader loader, long checkFrequencyMillis) {
|
||||
this.loader = loader;
|
||||
this.checkFrequencyMillis = checkFrequencyMillis;
|
||||
setDaemon(true);
|
||||
setName("PluginScanner");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
for (;;) {
|
||||
try {
|
||||
if (done.await(checkFrequencyMillis, TimeUnit.MILLISECONDS)) {
|
||||
return;
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
loader.rescan();
|
||||
}
|
||||
}
|
||||
|
||||
void end() {
|
||||
done.countDown();
|
||||
try {
|
||||
join();
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
// 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;
|
||||
|
||||
/** Handle for registered information. */
|
||||
public interface RegistrationHandle {
|
||||
/** Delete this registration. */
|
||||
public void remove();
|
||||
}
|
@@ -0,0 +1,20 @@
|
||||
// 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;
|
||||
|
||||
/** Broadcasts event indicating a plugin was loaded. */
|
||||
public interface StartPluginListener {
|
||||
public void onStartPlugin(Plugin plugin);
|
||||
}
|
@@ -14,6 +14,8 @@
|
||||
|
||||
package com.google.gerrit.sshd;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.gerrit.server.plugins.RegistrationHandle;
|
||||
import com.google.inject.Binding;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Injector;
|
||||
@@ -23,11 +25,8 @@ import com.google.inject.TypeLiteral;
|
||||
import org.apache.sshd.server.Command;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* Creates DispatchCommand using commands registered by {@link CommandModule}.
|
||||
@@ -42,7 +41,7 @@ public class DispatchCommandProvider implements Provider<DispatchCommand> {
|
||||
private final String dispatcherName;
|
||||
private final CommandName parent;
|
||||
|
||||
private volatile Map<String, Provider<Command>> map;
|
||||
private volatile ConcurrentMap<String, Provider<Command>> map;
|
||||
|
||||
public DispatchCommandProvider(final CommandName cn) {
|
||||
this(Commands.nameOf(cn), cn);
|
||||
@@ -59,7 +58,21 @@ public class DispatchCommandProvider implements Provider<DispatchCommand> {
|
||||
return factory.create(dispatcherName, getMap());
|
||||
}
|
||||
|
||||
private Map<String, Provider<Command>> getMap() {
|
||||
public RegistrationHandle register(final CommandName name,
|
||||
final Provider<Command> cmd) {
|
||||
final ConcurrentMap<String, Provider<Command>> m = getMap();
|
||||
if (m.putIfAbsent(name.value(), cmd) != null) {
|
||||
throw new IllegalArgumentException(name.value() + " exists");
|
||||
}
|
||||
return new RegistrationHandle() {
|
||||
@Override
|
||||
public void remove() {
|
||||
m.remove(name.value(), cmd);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private ConcurrentMap<String, Provider<Command>> getMap() {
|
||||
if (map == null) {
|
||||
synchronized (this) {
|
||||
if (map == null) {
|
||||
@@ -71,10 +84,8 @@ public class DispatchCommandProvider implements Provider<DispatchCommand> {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<String, Provider<Command>> createMap() {
|
||||
final Map<String, Provider<Command>> m =
|
||||
new TreeMap<String, Provider<Command>>();
|
||||
|
||||
private ConcurrentMap<String, Provider<Command>> createMap() {
|
||||
ConcurrentMap<String, Provider<Command>> m = Maps.newConcurrentMap();
|
||||
for (final Binding<?> b : allCommands()) {
|
||||
final Annotation annotation = b.getKey().getAnnotation();
|
||||
if (annotation instanceof CommandName) {
|
||||
@@ -84,9 +95,7 @@ public class DispatchCommandProvider implements Provider<DispatchCommand> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Collections.unmodifiableMap(
|
||||
new LinkedHashMap<String, Provider<Command>>(m));
|
||||
return m;
|
||||
}
|
||||
|
||||
private static final TypeLiteral<Command> type =
|
||||
|
@@ -31,6 +31,7 @@ import com.google.gerrit.server.config.FactoryModule;
|
||||
import com.google.gerrit.server.config.GerritRequestModule;
|
||||
import com.google.gerrit.server.git.QueueProvider;
|
||||
import com.google.gerrit.server.git.WorkQueue;
|
||||
import com.google.gerrit.server.plugins.StartPluginListener;
|
||||
import com.google.gerrit.server.project.ProjectControl;
|
||||
import com.google.gerrit.server.ssh.SshInfo;
|
||||
import com.google.gerrit.server.util.RequestScopePropagator;
|
||||
@@ -46,6 +47,7 @@ import com.google.gerrit.sshd.commands.DefaultCommandModule;
|
||||
import com.google.gerrit.sshd.commands.QueryShell;
|
||||
import com.google.gerrit.util.cli.CmdLineParser;
|
||||
import com.google.gerrit.util.cli.OptionHandlerUtil;
|
||||
import com.google.inject.internal.UniqueAnnotations;
|
||||
import com.google.inject.servlet.RequestScoped;
|
||||
|
||||
import org.apache.sshd.common.KeyPairProvider;
|
||||
@@ -91,6 +93,9 @@ public class SshModule extends FactoryModule {
|
||||
install(new LifecycleModule() {
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(StartPluginListener.class)
|
||||
.annotatedWith(UniqueAnnotations.create())
|
||||
.to(SshPluginStarterCallback.class);
|
||||
listener().to(SshLog.class);
|
||||
listener().to(SshDaemon.class);
|
||||
}
|
||||
|
@@ -0,0 +1,57 @@
|
||||
// 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.sshd;
|
||||
|
||||
import com.google.gerrit.server.plugins.Plugin;
|
||||
import com.google.gerrit.server.plugins.StartPluginListener;
|
||||
import com.google.inject.Key;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import org.apache.sshd.server.Command;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@Singleton
|
||||
class SshPluginStarterCallback implements StartPluginListener {
|
||||
private static final Logger log = LoggerFactory
|
||||
.getLogger(SshPluginStarterCallback.class);
|
||||
|
||||
private final DispatchCommandProvider root;
|
||||
|
||||
@Inject
|
||||
SshPluginStarterCallback(
|
||||
@CommandName(Commands.ROOT) DispatchCommandProvider root) {
|
||||
this.root = root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartPlugin(Plugin plugin) {
|
||||
if (plugin.getSshInjector() != null) {
|
||||
Key<Command> key = Commands.key(plugin.getName());
|
||||
Provider<Command> cmd;
|
||||
try {
|
||||
cmd = plugin.getSshInjector().getProvider(key);
|
||||
} catch (RuntimeException err) {
|
||||
log.warn(String.format("Plugin %s does not define command",
|
||||
plugin.getName()), err);
|
||||
return;
|
||||
}
|
||||
plugin.add(root.register(Commands.named(plugin.getName()), cmd));
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,57 +0,0 @@
|
||||
// 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.sshd.commands;
|
||||
|
||||
import com.google.gerrit.common.Plugin;
|
||||
import com.google.gerrit.common.PluginLoader;
|
||||
import com.google.gerrit.sshd.CommandModule;
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class MasterPluginsModule extends CommandModule {
|
||||
private static final Logger log =
|
||||
LoggerFactory.getLogger(MasterPluginsModule.class);
|
||||
|
||||
private PluginLoader pluginLoader;
|
||||
|
||||
@Inject
|
||||
MasterPluginsModule(PluginLoader loader) {
|
||||
pluginLoader = loader;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
Collection<Plugin> plugins = pluginLoader.getPlugins();
|
||||
for (Plugin p : plugins) {
|
||||
if (PluginCommandModule.class.isAssignableFrom(p.sshModule)) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<PluginCommandModule> c = (Class<PluginCommandModule>) p.sshModule;
|
||||
try {
|
||||
PluginCommandModule module = c.newInstance();
|
||||
module.initSshModule(p.name);
|
||||
install(module);
|
||||
} catch (InstantiationException e) {
|
||||
log.warn("Initialization of plugin module '" + p.name + "' failed");
|
||||
} catch (IllegalAccessException e) {
|
||||
log.warn("Initialization of plugin module '" + p.name + "' failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -14,6 +14,8 @@
|
||||
|
||||
package com.google.gerrit.sshd.commands;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.gerrit.server.plugins.PluginName;
|
||||
import com.google.gerrit.sshd.CommandName;
|
||||
import com.google.gerrit.sshd.Commands;
|
||||
import com.google.gerrit.sshd.DispatchCommandProvider;
|
||||
@@ -22,20 +24,24 @@ import com.google.inject.binder.LinkedBindingBuilder;
|
||||
|
||||
import org.apache.sshd.server.Command;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
public abstract class PluginCommandModule extends AbstractModule {
|
||||
private CommandName command;
|
||||
|
||||
public void initSshModule(String pluginName) {
|
||||
command = Commands.named(pluginName);
|
||||
@Inject
|
||||
void setPluginName(@PluginName String name) {
|
||||
this.command = Commands.named(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void configure() {
|
||||
Preconditions.checkState(command != null, "@PluginName must be provided");
|
||||
bind(Commands.key(command)).toProvider(new DispatchCommandProvider(command));
|
||||
configureCmds();
|
||||
configureCommands();
|
||||
}
|
||||
|
||||
protected abstract void configureCmds();
|
||||
protected abstract void configureCommands();
|
||||
|
||||
protected LinkedBindingBuilder<Command> command(String subCmd) {
|
||||
return bind(Commands.key(command, subCmd));
|
||||
|
@@ -37,13 +37,14 @@ import com.google.gerrit.server.git.ReceiveCommitsExecutorModule;
|
||||
import com.google.gerrit.server.git.WorkQueue;
|
||||
import com.google.gerrit.server.mail.SignedTokenEmailTokenVerifier;
|
||||
import com.google.gerrit.server.mail.SmtpEmailSender;
|
||||
import com.google.gerrit.server.plugins.PluginGuiceEnvironment;
|
||||
import com.google.gerrit.server.plugins.PluginModule;
|
||||
import com.google.gerrit.server.schema.DataSourceProvider;
|
||||
import com.google.gerrit.server.schema.DatabaseModule;
|
||||
import com.google.gerrit.server.schema.SchemaModule;
|
||||
import com.google.gerrit.server.schema.SchemaVersionCheck;
|
||||
import com.google.gerrit.sshd.SshModule;
|
||||
import com.google.gerrit.sshd.commands.MasterCommandModule;
|
||||
import com.google.gerrit.sshd.commands.MasterPluginsModule;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.CreationException;
|
||||
import com.google.inject.Guice;
|
||||
@@ -113,6 +114,10 @@ public class WebAppInitializer extends GuiceServletContextListener {
|
||||
sshInjector = createSshInjector();
|
||||
webInjector = createWebInjector();
|
||||
|
||||
PluginGuiceEnvironment env = sysInjector.getInstance(PluginGuiceEnvironment.class);
|
||||
env.setCfgInjector(cfgInjector);
|
||||
env.setSshInjector(sshInjector);
|
||||
|
||||
// Push the Provider<HttpServletRequest> down into the canonical
|
||||
// URL provider. Its optional for that provider, but since we can
|
||||
// supply one we should do so, in case the administrator has not
|
||||
@@ -198,6 +203,7 @@ public class WebAppInitializer extends GuiceServletContextListener {
|
||||
modules.add(new SmtpEmailSender.Module());
|
||||
modules.add(new SignedTokenEmailTokenVerifier.Module());
|
||||
modules.add(new PushReplication.Module());
|
||||
modules.add(new PluginModule());
|
||||
modules.add(new CanonicalWebUrlModule() {
|
||||
@Override
|
||||
protected Class<? extends Provider<String>> provider() {
|
||||
@@ -212,7 +218,6 @@ public class WebAppInitializer extends GuiceServletContextListener {
|
||||
final List<Module> modules = new ArrayList<Module>();
|
||||
modules.add(new SshModule());
|
||||
modules.add(new MasterCommandModule());
|
||||
modules.add(cfgInjector.getInstance(MasterPluginsModule.class));
|
||||
return sysInjector.createChildInjector(modules);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user