* stable-2.16: Error Prone: Enable and fix ClassCanBeStatic AutoRegisterModulesTest: Declare inner classes as static Fix issue with auto registering ssh commands Change-Id: I32a2f763e1977bec55a02afe0bbf32b151f5f2c0
652 lines
23 KiB
Java
652 lines
23 KiB
Java
// 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 com.google.gerrit.extensions.registration.PrivateInternals_DynamicTypes.dynamicItemsOf;
|
|
import static com.google.gerrit.extensions.registration.PrivateInternals_DynamicTypes.dynamicMapsOf;
|
|
import static com.google.gerrit.extensions.registration.PrivateInternals_DynamicTypes.dynamicSetsOf;
|
|
import static java.util.Objects.requireNonNull;
|
|
|
|
import com.google.common.collect.LinkedListMultimap;
|
|
import com.google.common.collect.ListMultimap;
|
|
import com.google.common.collect.Lists;
|
|
import com.google.gerrit.common.Nullable;
|
|
import com.google.gerrit.common.UsedAt;
|
|
import com.google.gerrit.extensions.annotations.RootRelative;
|
|
import com.google.gerrit.extensions.events.LifecycleListener;
|
|
import com.google.gerrit.extensions.registration.DynamicItem;
|
|
import com.google.gerrit.extensions.registration.DynamicMap;
|
|
import com.google.gerrit.extensions.registration.DynamicSet;
|
|
import com.google.gerrit.extensions.registration.PrivateInternals_DynamicMapImpl;
|
|
import com.google.gerrit.extensions.registration.PrivateInternals_DynamicTypes;
|
|
import com.google.gerrit.extensions.registration.RegistrationHandle;
|
|
import com.google.gerrit.extensions.registration.ReloadableRegistrationHandle;
|
|
import com.google.gerrit.extensions.systemstatus.ServerInformation;
|
|
import com.google.gerrit.extensions.webui.WebUiPlugin;
|
|
import com.google.gerrit.index.IndexCollection;
|
|
import com.google.gerrit.metrics.MetricMaker;
|
|
import com.google.gerrit.server.util.PluginRequestContext;
|
|
import com.google.gerrit.server.util.RequestContext;
|
|
import com.google.gerrit.server.util.ThreadLocalRequestContext;
|
|
import com.google.inject.AbstractModule;
|
|
import com.google.inject.Binding;
|
|
import com.google.inject.Guice;
|
|
import com.google.inject.Inject;
|
|
import com.google.inject.Injector;
|
|
import com.google.inject.Key;
|
|
import com.google.inject.Module;
|
|
import com.google.inject.Provider;
|
|
import com.google.inject.Singleton;
|
|
import com.google.inject.TypeLiteral;
|
|
import com.google.inject.internal.UniqueAnnotations;
|
|
import java.lang.annotation.Annotation;
|
|
import java.lang.reflect.ParameterizedType;
|
|
import java.util.Collections;
|
|
import java.util.HashMap;
|
|
import java.util.HashSet;
|
|
import java.util.Iterator;
|
|
import java.util.LinkedHashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
import java.util.concurrent.CopyOnWriteArrayList;
|
|
import javax.servlet.http.HttpServletRequest;
|
|
import javax.servlet.http.HttpServletResponse;
|
|
|
|
/**
|
|
* 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 ServerInformation srvInfo;
|
|
private final ThreadLocalRequestContext local;
|
|
private final CopyConfigModule copyConfigModule;
|
|
private final Set<Key<?>> copyConfigKeys;
|
|
private final List<StartPluginListener> onStart;
|
|
private final List<StopPluginListener> onStop;
|
|
private final List<ReloadPluginListener> onReload;
|
|
private final MetricMaker serverMetrics;
|
|
|
|
private Module sysModule;
|
|
private Module sshModule;
|
|
private Module httpModule;
|
|
|
|
private Provider<ModuleGenerator> sshGen;
|
|
private Provider<ModuleGenerator> httpGen;
|
|
|
|
private Map<TypeLiteral<?>, DynamicItem<?>> sysItems;
|
|
private Map<TypeLiteral<?>, DynamicItem<?>> sshItems;
|
|
private Map<TypeLiteral<?>, DynamicItem<?>> httpItems;
|
|
|
|
private Map<TypeLiteral<?>, DynamicSet<?>> sysSets;
|
|
private Map<TypeLiteral<?>, DynamicSet<?>> sshSets;
|
|
private Map<TypeLiteral<?>, DynamicSet<?>> httpSets;
|
|
|
|
private Map<TypeLiteral<?>, DynamicMap<?>> sysMaps;
|
|
private Map<TypeLiteral<?>, DynamicMap<?>> sshMaps;
|
|
private Map<TypeLiteral<?>, DynamicMap<?>> httpMaps;
|
|
|
|
@Inject
|
|
PluginGuiceEnvironment(
|
|
Injector sysInjector,
|
|
ThreadLocalRequestContext local,
|
|
ServerInformation srvInfo,
|
|
CopyConfigModule ccm,
|
|
MetricMaker serverMetrics) {
|
|
this.sysInjector = sysInjector;
|
|
this.srvInfo = srvInfo;
|
|
this.local = local;
|
|
this.copyConfigModule = ccm;
|
|
this.copyConfigKeys = Guice.createInjector(ccm).getAllBindings().keySet();
|
|
this.serverMetrics = serverMetrics;
|
|
|
|
onStart = new CopyOnWriteArrayList<>();
|
|
onStart.addAll(listeners(sysInjector, StartPluginListener.class));
|
|
|
|
onStop = new CopyOnWriteArrayList<>();
|
|
onStop.addAll(listeners(sysInjector, StopPluginListener.class));
|
|
|
|
onReload = new CopyOnWriteArrayList<>();
|
|
onReload.addAll(listeners(sysInjector, ReloadPluginListener.class));
|
|
|
|
sysItems = dynamicItemsOf(sysInjector);
|
|
sysSets = dynamicSetsOf(sysInjector);
|
|
sysMaps = dynamicMapsOf(sysInjector);
|
|
}
|
|
|
|
ServerInformation getServerInformation() {
|
|
return srvInfo;
|
|
}
|
|
|
|
MetricMaker getServerMetrics() {
|
|
return serverMetrics;
|
|
}
|
|
|
|
boolean hasDynamicItem(TypeLiteral<?> type) {
|
|
return sysItems.containsKey(type)
|
|
|| (sshItems != null && sshItems.containsKey(type))
|
|
|| (httpItems != null && httpItems.containsKey(type));
|
|
}
|
|
|
|
boolean hasDynamicSet(TypeLiteral<?> type) {
|
|
return sysSets.containsKey(type)
|
|
|| (sshSets != null && sshSets.containsKey(type))
|
|
|| (httpSets != null && httpSets.containsKey(type));
|
|
}
|
|
|
|
boolean hasDynamicMap(TypeLiteral<?> type) {
|
|
return sysMaps.containsKey(type)
|
|
|| (sshMaps != null && sshMaps.containsKey(type))
|
|
|| (httpMaps != null && httpMaps.containsKey(type));
|
|
}
|
|
|
|
public Module getSysModule() {
|
|
return sysModule;
|
|
}
|
|
|
|
public void setDbCfgInjector(Injector dbInjector, Injector cfgInjector) {
|
|
final Module db = copy(dbInjector);
|
|
final Module cm = copy(cfgInjector);
|
|
final Module sm = copy(sysInjector);
|
|
sysModule =
|
|
new AbstractModule() {
|
|
@Override
|
|
protected void configure() {
|
|
install(copyConfigModule);
|
|
install(db);
|
|
install(cm);
|
|
install(sm);
|
|
}
|
|
};
|
|
}
|
|
|
|
public void setSshInjector(Injector injector) {
|
|
sshModule = copy(injector);
|
|
sshGen = injector.getProvider(ModuleGenerator.class);
|
|
sshItems = dynamicItemsOf(injector);
|
|
sshSets = dynamicSetsOf(injector);
|
|
sshMaps = dynamicMapsOf(injector);
|
|
onStart.addAll(listeners(injector, StartPluginListener.class));
|
|
onStop.addAll(listeners(injector, StopPluginListener.class));
|
|
onReload.addAll(listeners(injector, ReloadPluginListener.class));
|
|
}
|
|
|
|
boolean hasSshModule() {
|
|
return sshModule != null;
|
|
}
|
|
|
|
Module getSshModule() {
|
|
return sshModule;
|
|
}
|
|
|
|
ModuleGenerator newSshModuleGenerator() {
|
|
return sshGen.get();
|
|
}
|
|
|
|
public void setHttpInjector(Injector injector) {
|
|
httpModule = copy(injector);
|
|
httpGen = injector.getProvider(ModuleGenerator.class);
|
|
httpItems = dynamicItemsOf(injector);
|
|
httpSets = httpDynamicSetsOf(injector);
|
|
httpMaps = dynamicMapsOf(injector);
|
|
onStart.addAll(listeners(injector, StartPluginListener.class));
|
|
onStop.addAll(listeners(injector, StopPluginListener.class));
|
|
onReload.addAll(listeners(injector, ReloadPluginListener.class));
|
|
}
|
|
|
|
private Map<TypeLiteral<?>, DynamicSet<?>> httpDynamicSetsOf(Injector i) {
|
|
// Copy binding of DynamicSet<WebUiPlugin> from sysInjector to HTTP.
|
|
// This supports older plugins that bound a plugin in the HttpModule.
|
|
TypeLiteral<WebUiPlugin> key = TypeLiteral.get(WebUiPlugin.class);
|
|
DynamicSet<?> web = sysSets.get(key);
|
|
requireNonNull(web, "DynamicSet<WebUiPlugin> exists in sysInjector");
|
|
|
|
Map<TypeLiteral<?>, DynamicSet<?>> m = new HashMap<>(dynamicSetsOf(i));
|
|
m.put(key, web);
|
|
return Collections.unmodifiableMap(m);
|
|
}
|
|
|
|
boolean hasHttpModule() {
|
|
return httpModule != null;
|
|
}
|
|
|
|
@UsedAt(UsedAt.Project.GOOGLE)
|
|
public Module getHttpModule() {
|
|
return httpModule;
|
|
}
|
|
|
|
ModuleGenerator newHttpModuleGenerator() {
|
|
return httpGen.get();
|
|
}
|
|
|
|
public RequestContext enter(Plugin plugin) {
|
|
return local.setContext(new PluginRequestContext(plugin.getPluginUser()));
|
|
}
|
|
|
|
public void exit(RequestContext old) {
|
|
local.setContext(old);
|
|
}
|
|
|
|
public void onStartPlugin(Plugin plugin) {
|
|
RequestContext oldContext = enter(plugin);
|
|
try {
|
|
attachItem(sysItems, plugin.getSysInjector(), plugin);
|
|
attachItem(sshItems, plugin.getSshInjector(), plugin);
|
|
attachItem(httpItems, plugin.getHttpInjector(), plugin);
|
|
|
|
attachSet(sysSets, plugin.getSysInjector(), plugin);
|
|
attachSet(sshSets, plugin.getSshInjector(), plugin);
|
|
attachSet(httpSets, plugin.getHttpInjector(), plugin);
|
|
|
|
attachMap(sysMaps, plugin.getSysInjector(), plugin);
|
|
attachMap(sshMaps, plugin.getSshInjector(), plugin);
|
|
attachMap(httpMaps, plugin.getHttpInjector(), plugin);
|
|
} finally {
|
|
exit(oldContext);
|
|
}
|
|
|
|
for (StartPluginListener l : onStart) {
|
|
l.onStartPlugin(plugin);
|
|
}
|
|
}
|
|
|
|
public void onStopPlugin(Plugin plugin) {
|
|
for (StopPluginListener l : onStop) {
|
|
l.onStopPlugin(plugin);
|
|
}
|
|
}
|
|
|
|
private void attachItem(
|
|
Map<TypeLiteral<?>, DynamicItem<?>> items, @Nullable Injector src, Plugin plugin) {
|
|
for (RegistrationHandle h :
|
|
PrivateInternals_DynamicTypes.attachItems(src, plugin.getName(), items)) {
|
|
plugin.add(h);
|
|
}
|
|
}
|
|
|
|
private void attachSet(
|
|
Map<TypeLiteral<?>, DynamicSet<?>> sets, @Nullable Injector src, Plugin plugin) {
|
|
for (RegistrationHandle h :
|
|
PrivateInternals_DynamicTypes.attachSets(src, plugin.getName(), sets)) {
|
|
plugin.add(h);
|
|
}
|
|
}
|
|
|
|
private void attachMap(
|
|
Map<TypeLiteral<?>, DynamicMap<?>> maps, @Nullable Injector src, Plugin plugin) {
|
|
for (RegistrationHandle h :
|
|
PrivateInternals_DynamicTypes.attachMaps(src, plugin.getName(), maps)) {
|
|
plugin.add(h);
|
|
}
|
|
}
|
|
|
|
void onReloadPlugin(Plugin oldPlugin, Plugin newPlugin) {
|
|
// Index all old registrations by the raw type. These may be replaced
|
|
// during the reattach calls below. Any that are not replaced will be
|
|
// removed when the old plugin does its stop routine.
|
|
ListMultimap<TypeLiteral<?>, ReloadableRegistrationHandle<?>> old = LinkedListMultimap.create();
|
|
for (ReloadableRegistrationHandle<?> h : oldPlugin.getReloadableHandles()) {
|
|
old.put(h.getKey().getTypeLiteral(), h);
|
|
}
|
|
|
|
RequestContext oldContext = enter(newPlugin);
|
|
try {
|
|
reattachMap(old, sysMaps, newPlugin.getSysInjector(), newPlugin);
|
|
reattachMap(old, sshMaps, newPlugin.getSshInjector(), newPlugin);
|
|
reattachMap(old, httpMaps, newPlugin.getHttpInjector(), newPlugin);
|
|
|
|
reattachSet(old, sysSets, newPlugin.getSysInjector(), newPlugin);
|
|
reattachSet(old, sshSets, newPlugin.getSshInjector(), newPlugin);
|
|
reattachSet(old, httpSets, newPlugin.getHttpInjector(), newPlugin);
|
|
|
|
reattachItem(old, sysItems, newPlugin.getSysInjector(), newPlugin);
|
|
reattachItem(old, sshItems, newPlugin.getSshInjector(), newPlugin);
|
|
reattachItem(old, httpItems, newPlugin.getHttpInjector(), newPlugin);
|
|
} finally {
|
|
exit(oldContext);
|
|
}
|
|
|
|
for (ReloadPluginListener l : onReload) {
|
|
l.onReloadPlugin(oldPlugin, newPlugin);
|
|
}
|
|
}
|
|
|
|
private void reattachMap(
|
|
ListMultimap<TypeLiteral<?>, ReloadableRegistrationHandle<?>> oldHandles,
|
|
Map<TypeLiteral<?>, DynamicMap<?>> maps,
|
|
@Nullable Injector src,
|
|
Plugin newPlugin) {
|
|
if (src == null || maps == null || maps.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
for (Map.Entry<TypeLiteral<?>, DynamicMap<?>> e : maps.entrySet()) {
|
|
@SuppressWarnings("unchecked")
|
|
TypeLiteral<Object> type = (TypeLiteral<Object>) e.getKey();
|
|
|
|
@SuppressWarnings("unchecked")
|
|
PrivateInternals_DynamicMapImpl<Object> map =
|
|
(PrivateInternals_DynamicMapImpl<Object>) e.getValue();
|
|
|
|
Map<Annotation, ReloadableRegistrationHandle<?>> am = new HashMap<>();
|
|
for (ReloadableRegistrationHandle<?> h : oldHandles.get(type)) {
|
|
Annotation a = h.getKey().getAnnotation();
|
|
if (a != null && !UNIQUE_ANNOTATION.isInstance(a)) {
|
|
am.put(a, h);
|
|
}
|
|
}
|
|
|
|
for (Binding<?> binding : bindings(src, e.getKey())) {
|
|
@SuppressWarnings("unchecked")
|
|
Binding<Object> b = (Binding<Object>) binding;
|
|
Key<Object> key = b.getKey();
|
|
if (key.getAnnotation() == null) {
|
|
continue;
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
ReloadableRegistrationHandle<Object> h =
|
|
(ReloadableRegistrationHandle<Object>) am.remove(key.getAnnotation());
|
|
if (h != null) {
|
|
replace(newPlugin, h, b);
|
|
oldHandles.remove(type, h);
|
|
} else {
|
|
newPlugin.add(map.put(newPlugin.getName(), b.getKey(), b.getProvider()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Type used to declare unique annotations. Guice hides this, so extract it. */
|
|
private static final Class<?> UNIQUE_ANNOTATION = UniqueAnnotations.create().annotationType();
|
|
|
|
private void reattachSet(
|
|
ListMultimap<TypeLiteral<?>, ReloadableRegistrationHandle<?>> oldHandles,
|
|
Map<TypeLiteral<?>, DynamicSet<?>> sets,
|
|
@Nullable Injector src,
|
|
Plugin newPlugin) {
|
|
if (src == null || sets == null || sets.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
for (Map.Entry<TypeLiteral<?>, DynamicSet<?>> e : sets.entrySet()) {
|
|
@SuppressWarnings("unchecked")
|
|
TypeLiteral<Object> type = (TypeLiteral<Object>) e.getKey();
|
|
|
|
@SuppressWarnings("unchecked")
|
|
DynamicSet<Object> set = (DynamicSet<Object>) e.getValue();
|
|
|
|
// Index all old handles that match this DynamicSet<T> keyed by
|
|
// annotations. Ignore the unique annotations, thereby favoring
|
|
// the @Named annotations or some other non-unique naming.
|
|
Map<Annotation, ReloadableRegistrationHandle<?>> am = new HashMap<>();
|
|
List<ReloadableRegistrationHandle<?>> old = oldHandles.get(type);
|
|
Iterator<ReloadableRegistrationHandle<?>> oi = old.iterator();
|
|
while (oi.hasNext()) {
|
|
ReloadableRegistrationHandle<?> h = oi.next();
|
|
Annotation a = h.getKey().getAnnotation();
|
|
if (a != null && !UNIQUE_ANNOTATION.isInstance(a)) {
|
|
am.put(a, h);
|
|
oi.remove();
|
|
}
|
|
}
|
|
|
|
// Replace old handles with new bindings, favoring cases where there
|
|
// is an exact match on an @Named annotation. If there is no match
|
|
// pick any handle and replace it. We generally expect only one
|
|
// handle of each DynamicSet type when using unique annotations, but
|
|
// possibly multiple ones if @Named was used. Plugin authors that want
|
|
// atomic replacement across reloads should use @Named annotations with
|
|
// stable names that do not change across plugin versions to ensure the
|
|
// handles are swapped correctly.
|
|
oi = old.iterator();
|
|
for (Binding<?> binding : bindings(src, type)) {
|
|
@SuppressWarnings("unchecked")
|
|
Binding<Object> b = (Binding<Object>) binding;
|
|
Key<Object> key = b.getKey();
|
|
if (key.getAnnotation() == null) {
|
|
continue;
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
ReloadableRegistrationHandle<Object> h1 =
|
|
(ReloadableRegistrationHandle<Object>) am.remove(key.getAnnotation());
|
|
if (h1 != null) {
|
|
replace(newPlugin, h1, b);
|
|
} else if (oi.hasNext()) {
|
|
@SuppressWarnings("unchecked")
|
|
ReloadableRegistrationHandle<Object> h2 =
|
|
(ReloadableRegistrationHandle<Object>) oi.next();
|
|
oi.remove();
|
|
replace(newPlugin, h2, b);
|
|
} else {
|
|
newPlugin.add(set.add(newPlugin.getName(), b.getKey(), b.getProvider()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void reattachItem(
|
|
ListMultimap<TypeLiteral<?>, ReloadableRegistrationHandle<?>> oldHandles,
|
|
Map<TypeLiteral<?>, DynamicItem<?>> items,
|
|
@Nullable Injector src,
|
|
Plugin newPlugin) {
|
|
if (src == null || items == null || items.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
for (Map.Entry<TypeLiteral<?>, DynamicItem<?>> e : items.entrySet()) {
|
|
@SuppressWarnings("unchecked")
|
|
TypeLiteral<Object> type = (TypeLiteral<Object>) e.getKey();
|
|
|
|
@SuppressWarnings("unchecked")
|
|
DynamicItem<Object> item = (DynamicItem<Object>) e.getValue();
|
|
|
|
Iterator<ReloadableRegistrationHandle<?>> oi = oldHandles.get(type).iterator();
|
|
|
|
for (Binding<?> binding : bindings(src, type)) {
|
|
@SuppressWarnings("unchecked")
|
|
Binding<Object> b = (Binding<Object>) binding;
|
|
if (oi.hasNext()) {
|
|
@SuppressWarnings("unchecked")
|
|
ReloadableRegistrationHandle<Object> h = (ReloadableRegistrationHandle<Object>) oi.next();
|
|
oi.remove();
|
|
replace(newPlugin, h, b);
|
|
} else {
|
|
newPlugin.add(item.set(b.getKey(), b.getProvider(), newPlugin.getName()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static <T> void replace(
|
|
Plugin newPlugin, ReloadableRegistrationHandle<T> h, Binding<T> b) {
|
|
RegistrationHandle n = h.replace(b.getKey(), b.getProvider());
|
|
if (n != null) {
|
|
newPlugin.add(n);
|
|
}
|
|
}
|
|
|
|
static <T> List<T> listeners(Injector src, Class<T> type) {
|
|
List<Binding<T>> bindings = bindings(src, TypeLiteral.get(type));
|
|
int cnt = bindings != null ? bindings.size() : 0;
|
|
List<T> found = Lists.newArrayListWithCapacity(cnt);
|
|
if (bindings != null) {
|
|
for (Binding<T> b : bindings) {
|
|
found.add(b.getProvider().get());
|
|
}
|
|
}
|
|
return found;
|
|
}
|
|
|
|
private static <T> List<Binding<T>> bindings(Injector src, TypeLiteral<T> type) {
|
|
return src.findBindingsByType(type);
|
|
}
|
|
|
|
private Module copy(Injector src) {
|
|
Set<TypeLiteral<?>> dynamicTypes = new HashSet<>();
|
|
Set<TypeLiteral<?>> dynamicItemTypes = new HashSet<>();
|
|
for (Map.Entry<Key<?>, Binding<?>> e : src.getBindings().entrySet()) {
|
|
TypeLiteral<?> type = e.getKey().getTypeLiteral();
|
|
if (type.getRawType() == DynamicItem.class) {
|
|
ParameterizedType t = (ParameterizedType) type.getType();
|
|
dynamicItemTypes.add(TypeLiteral.get(t.getActualTypeArguments()[0]));
|
|
} else if (type.getRawType() == DynamicSet.class || type.getRawType() == DynamicMap.class) {
|
|
ParameterizedType t = (ParameterizedType) type.getType();
|
|
dynamicTypes.add(TypeLiteral.get(t.getActualTypeArguments()[0]));
|
|
}
|
|
}
|
|
|
|
final Map<Key<?>, Binding<?>> bindings = new LinkedHashMap<>();
|
|
for (Map.Entry<Key<?>, Binding<?>> e : src.getBindings().entrySet()) {
|
|
if (dynamicTypes.contains(e.getKey().getTypeLiteral())
|
|
&& e.getKey().getAnnotation() != null) {
|
|
// A type used in DynamicSet or DynamicMap that has an annotation
|
|
// must be picked up by the set/map itself. A type used in either
|
|
// but without an annotation may be magic glue implementing F and
|
|
// using DynamicSet<F> or DynamicMap<F> internally. That should be
|
|
// exported to plugins.
|
|
continue;
|
|
} else if (dynamicItemTypes.contains(e.getKey().getTypeLiteral())) {
|
|
continue;
|
|
} else 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));
|
|
|
|
@Nullable
|
|
final Binding<HttpServletRequest> requestBinding =
|
|
src.getExistingBinding(Key.get(HttpServletRequest.class));
|
|
|
|
@Nullable
|
|
final Binding<HttpServletResponse> responseBinding =
|
|
src.getExistingBinding(Key.get(HttpServletResponse.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());
|
|
}
|
|
|
|
if (requestBinding != null) {
|
|
bind(HttpServletRequest.class)
|
|
.annotatedWith(RootRelative.class)
|
|
.toProvider(requestBinding.getProvider());
|
|
}
|
|
if (responseBinding != null) {
|
|
bind(HttpServletResponse.class)
|
|
.annotatedWith(RootRelative.class)
|
|
.toProvider(responseBinding.getProvider());
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
private boolean shouldCopy(Key<?> key) {
|
|
if (copyConfigKeys.contains(key)) {
|
|
return false;
|
|
}
|
|
Class<?> type = key.getTypeLiteral().getRawType();
|
|
if (LifecycleListener.class.isAssignableFrom(type)
|
|
// This is needed for secondary index to work from plugin listeners
|
|
&& !IndexCollection.class.isAssignableFrom(type)) {
|
|
return false;
|
|
}
|
|
if (StartPluginListener.class.isAssignableFrom(type)) {
|
|
return false;
|
|
}
|
|
if (StopPluginListener.class.isAssignableFrom(type)) {
|
|
return false;
|
|
}
|
|
if (MetricMaker.class.isAssignableFrom(type)) {
|
|
return false;
|
|
}
|
|
|
|
if (type.getName().startsWith("com.google.inject.")) {
|
|
return false;
|
|
}
|
|
|
|
if (is("org.apache.sshd.server.command.Command", type)) {
|
|
return false;
|
|
}
|
|
|
|
if (is("javax.servlet.Filter", type)) {
|
|
return false;
|
|
}
|
|
if (is("javax.servlet.ServletContext", type)) {
|
|
return false;
|
|
}
|
|
if (is("javax.servlet.ServletRequest", type)) {
|
|
return false;
|
|
}
|
|
if (is("javax.servlet.ServletResponse", type)) {
|
|
return false;
|
|
}
|
|
if (is("javax.servlet.http.HttpServlet", type)) {
|
|
return false;
|
|
}
|
|
if (is("javax.servlet.http.HttpServletRequest", type)) {
|
|
return false;
|
|
}
|
|
if (is("javax.servlet.http.HttpServletResponse", type)) {
|
|
return false;
|
|
}
|
|
if (is("javax.servlet.http.HttpSession", type)) {
|
|
return false;
|
|
}
|
|
if (Map.class.isAssignableFrom(type)
|
|
&& key.getAnnotationType() != null
|
|
&& "com.google.inject.servlet.RequestParameters"
|
|
.equals(key.getAnnotationType().getName())) {
|
|
return false;
|
|
}
|
|
if (type.getName().startsWith("com.google.gerrit.httpd.GitOverHttpServlet$")) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static boolean is(String name, Class<?> type) {
|
|
while (type != null) {
|
|
if (name.equals(type.getName())) {
|
|
return true;
|
|
}
|
|
|
|
Class<?>[] interfaces = type.getInterfaces();
|
|
if (interfaces != null) {
|
|
for (Class<?> i : interfaces) {
|
|
if (is(name, i)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
type = type.getSuperclass();
|
|
}
|
|
return false;
|
|
}
|
|
}
|