Line data Source code
1 : // Copyright (C) 2012 The Android Open Source Project
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 :
15 : package com.google.gerrit.server.plugins;
16 :
17 : import static com.google.gerrit.extensions.registration.PrivateInternals_DynamicTypes.dynamicItemsOf;
18 : import static com.google.gerrit.extensions.registration.PrivateInternals_DynamicTypes.dynamicMapsOf;
19 : import static com.google.gerrit.extensions.registration.PrivateInternals_DynamicTypes.dynamicSetsOf;
20 : import static java.util.Objects.requireNonNull;
21 :
22 : import com.google.common.collect.LinkedListMultimap;
23 : import com.google.common.collect.ListMultimap;
24 : import com.google.common.collect.Lists;
25 : import com.google.gerrit.common.Nullable;
26 : import com.google.gerrit.common.UsedAt;
27 : import com.google.gerrit.extensions.annotations.RootRelative;
28 : import com.google.gerrit.extensions.events.LifecycleListener;
29 : import com.google.gerrit.extensions.registration.DynamicItem;
30 : import com.google.gerrit.extensions.registration.DynamicMap;
31 : import com.google.gerrit.extensions.registration.DynamicSet;
32 : import com.google.gerrit.extensions.registration.PrivateInternals_DynamicMapImpl;
33 : import com.google.gerrit.extensions.registration.PrivateInternals_DynamicTypes;
34 : import com.google.gerrit.extensions.registration.RegistrationHandle;
35 : import com.google.gerrit.extensions.registration.ReloadableRegistrationHandle;
36 : import com.google.gerrit.extensions.systemstatus.ServerInformation;
37 : import com.google.gerrit.extensions.webui.WebUiPlugin;
38 : import com.google.gerrit.index.IndexCollection;
39 : import com.google.gerrit.metrics.MetricMaker;
40 : import com.google.gerrit.server.util.PluginRequestContext;
41 : import com.google.gerrit.server.util.RequestContext;
42 : import com.google.gerrit.server.util.ThreadLocalRequestContext;
43 : import com.google.inject.AbstractModule;
44 : import com.google.inject.Binding;
45 : import com.google.inject.Guice;
46 : import com.google.inject.Inject;
47 : import com.google.inject.Injector;
48 : import com.google.inject.Key;
49 : import com.google.inject.Module;
50 : import com.google.inject.Provider;
51 : import com.google.inject.Singleton;
52 : import com.google.inject.TypeLiteral;
53 : import com.google.inject.internal.UniqueAnnotations;
54 : import java.lang.annotation.Annotation;
55 : import java.lang.reflect.ParameterizedType;
56 : import java.util.Collections;
57 : import java.util.HashMap;
58 : import java.util.HashSet;
59 : import java.util.Iterator;
60 : import java.util.LinkedHashMap;
61 : import java.util.List;
62 : import java.util.Map;
63 : import java.util.Set;
64 : import java.util.concurrent.CopyOnWriteArrayList;
65 : import javax.servlet.http.HttpServletRequest;
66 : import javax.servlet.http.HttpServletResponse;
67 :
68 : /**
69 : * Tracks Guice bindings that should be exposed to loaded plugins.
70 : *
71 : * <p>This is an internal implementation detail of how the main server is able to export its
72 : * explicit Guice bindings to tightly coupled plugins, giving them access to singletons and request
73 : * scoped resources just like any core code.
74 : */
75 : @Singleton
76 : public class PluginGuiceEnvironment {
77 : private final Injector sysInjector;
78 : private final ServerInformation srvInfo;
79 : private final ThreadLocalRequestContext local;
80 : private final CopyConfigModule copyConfigModule;
81 : private final Set<Key<?>> copyConfigKeys;
82 : private final List<StartPluginListener> onStart;
83 : private final List<StopPluginListener> onStop;
84 : private final List<ReloadPluginListener> onReload;
85 : private final MetricMaker serverMetrics;
86 :
87 : private Module sysModule;
88 : private Module sshModule;
89 : private Module httpModule;
90 :
91 : private Provider<ModuleGenerator> sshGen;
92 : private Provider<ModuleGenerator> httpGen;
93 :
94 : private Map<TypeLiteral<?>, DynamicItem<?>> sysItems;
95 : private Map<TypeLiteral<?>, DynamicItem<?>> sshItems;
96 : private Map<TypeLiteral<?>, DynamicItem<?>> httpItems;
97 :
98 : private Map<TypeLiteral<?>, DynamicSet<?>> sysSets;
99 : private Map<TypeLiteral<?>, DynamicSet<?>> sshSets;
100 : private Map<TypeLiteral<?>, DynamicSet<?>> httpSets;
101 :
102 : private Map<TypeLiteral<?>, DynamicMap<?>> sysMaps;
103 : private Map<TypeLiteral<?>, DynamicMap<?>> sshMaps;
104 : private Map<TypeLiteral<?>, DynamicMap<?>> httpMaps;
105 :
106 : @Inject
107 : PluginGuiceEnvironment(
108 : Injector sysInjector,
109 : ThreadLocalRequestContext local,
110 : ServerInformation srvInfo,
111 : CopyConfigModule ccm,
112 149 : MetricMaker serverMetrics) {
113 149 : this.sysInjector = sysInjector;
114 149 : this.srvInfo = srvInfo;
115 149 : this.local = local;
116 149 : this.copyConfigModule = ccm;
117 149 : this.copyConfigKeys = Guice.createInjector(ccm).getAllBindings().keySet();
118 149 : this.serverMetrics = serverMetrics;
119 :
120 149 : onStart = new CopyOnWriteArrayList<>();
121 149 : onStart.addAll(listeners(sysInjector, StartPluginListener.class));
122 :
123 149 : onStop = new CopyOnWriteArrayList<>();
124 149 : onStop.addAll(listeners(sysInjector, StopPluginListener.class));
125 :
126 149 : onReload = new CopyOnWriteArrayList<>();
127 149 : onReload.addAll(listeners(sysInjector, ReloadPluginListener.class));
128 :
129 149 : sysItems = dynamicItemsOf(sysInjector);
130 149 : sysSets = dynamicSetsOf(sysInjector);
131 149 : sysMaps = dynamicMapsOf(sysInjector);
132 149 : }
133 :
134 : ServerInformation getServerInformation() {
135 0 : return srvInfo;
136 : }
137 :
138 : MetricMaker getServerMetrics() {
139 13 : return serverMetrics;
140 : }
141 :
142 : boolean hasDynamicItem(TypeLiteral<?> type) {
143 0 : return sysItems.containsKey(type)
144 0 : || (sshItems != null && sshItems.containsKey(type))
145 0 : || (httpItems != null && httpItems.containsKey(type));
146 : }
147 :
148 : boolean hasDynamicSet(TypeLiteral<?> type) {
149 0 : return sysSets.containsKey(type)
150 0 : || (sshSets != null && sshSets.containsKey(type))
151 0 : || (httpSets != null && httpSets.containsKey(type));
152 : }
153 :
154 : boolean hasDynamicMap(TypeLiteral<?> type) {
155 0 : return sysMaps.containsKey(type)
156 0 : || (sshMaps != null && sshMaps.containsKey(type))
157 0 : || (httpMaps != null && httpMaps.containsKey(type));
158 : }
159 :
160 : public Module getSysModule() {
161 12 : return sysModule;
162 : }
163 :
164 : public void setDbCfgInjector(Injector dbInjector, Injector cfgInjector) {
165 138 : final Module db = copy(dbInjector);
166 138 : final Module cm = copy(cfgInjector);
167 138 : final Module sm = copy(sysInjector);
168 138 : sysModule =
169 138 : new AbstractModule() {
170 : @Override
171 : protected void configure() {
172 12 : install(copyConfigModule);
173 12 : install(db);
174 12 : install(cm);
175 12 : install(sm);
176 12 : }
177 : };
178 138 : }
179 :
180 : public void setSshInjector(Injector injector) {
181 17 : sshModule = copy(injector);
182 17 : sshGen = injector.getProvider(ModuleGenerator.class);
183 17 : sshItems = dynamicItemsOf(injector);
184 17 : sshSets = dynamicSetsOf(injector);
185 17 : sshMaps = dynamicMapsOf(injector);
186 17 : onStart.addAll(listeners(injector, StartPluginListener.class));
187 17 : onStop.addAll(listeners(injector, StopPluginListener.class));
188 17 : onReload.addAll(listeners(injector, ReloadPluginListener.class));
189 17 : }
190 :
191 : boolean hasSshModule() {
192 13 : return sshModule != null;
193 : }
194 :
195 : Module getSshModule() {
196 2 : return sshModule;
197 : }
198 :
199 : ModuleGenerator newSshModuleGenerator() {
200 0 : return sshGen.get();
201 : }
202 :
203 : public void setHttpInjector(Injector injector) {
204 99 : httpModule = copy(injector);
205 99 : httpGen = injector.getProvider(ModuleGenerator.class);
206 99 : httpItems = dynamicItemsOf(injector);
207 99 : httpSets = httpDynamicSetsOf(injector);
208 99 : httpMaps = dynamicMapsOf(injector);
209 99 : onStart.addAll(listeners(injector, StartPluginListener.class));
210 99 : onStop.addAll(listeners(injector, StopPluginListener.class));
211 99 : onReload.addAll(listeners(injector, ReloadPluginListener.class));
212 99 : }
213 :
214 : private Map<TypeLiteral<?>, DynamicSet<?>> httpDynamicSetsOf(Injector i) {
215 : // Copy binding of DynamicSet<WebUiPlugin> from sysInjector to HTTP.
216 : // This supports older plugins that bound a plugin in the HttpModule.
217 99 : TypeLiteral<WebUiPlugin> key = TypeLiteral.get(WebUiPlugin.class);
218 99 : DynamicSet<?> web = sysSets.get(key);
219 99 : requireNonNull(web, "DynamicSet<WebUiPlugin> exists in sysInjector");
220 :
221 99 : Map<TypeLiteral<?>, DynamicSet<?>> m = new HashMap<>(dynamicSetsOf(i));
222 99 : m.put(key, web);
223 99 : return Collections.unmodifiableMap(m);
224 : }
225 :
226 : boolean hasHttpModule() {
227 13 : return httpModule != null;
228 : }
229 :
230 : @UsedAt(UsedAt.Project.GOOGLE)
231 : public Module getHttpModule() {
232 9 : return httpModule;
233 : }
234 :
235 : ModuleGenerator newHttpModuleGenerator() {
236 0 : return httpGen.get();
237 : }
238 :
239 : public RequestContext enter(Plugin plugin) {
240 14 : return local.setContext(new PluginRequestContext(plugin.getPluginUser()));
241 : }
242 :
243 : public void exit(RequestContext old) {
244 14 : local.setContext(old);
245 14 : }
246 :
247 : public void onStartPlugin(Plugin plugin) {
248 14 : RequestContext oldContext = enter(plugin);
249 : try {
250 14 : attachItem(sysItems, plugin.getSysInjector(), plugin);
251 14 : attachItem(sshItems, plugin.getSshInjector(), plugin);
252 14 : attachItem(httpItems, plugin.getHttpInjector(), plugin);
253 :
254 14 : attachSet(sysSets, plugin.getSysInjector(), plugin);
255 14 : attachSet(sshSets, plugin.getSshInjector(), plugin);
256 14 : attachSet(httpSets, plugin.getHttpInjector(), plugin);
257 :
258 14 : attachMap(sysMaps, plugin.getSysInjector(), plugin);
259 14 : attachMap(sshMaps, plugin.getSshInjector(), plugin);
260 14 : attachMap(httpMaps, plugin.getHttpInjector(), plugin);
261 : } finally {
262 14 : exit(oldContext);
263 : }
264 :
265 14 : for (StartPluginListener l : onStart) {
266 11 : l.onStartPlugin(plugin);
267 11 : }
268 14 : }
269 :
270 : public void onStopPlugin(Plugin plugin) {
271 14 : for (StopPluginListener l : onStop) {
272 9 : l.onStopPlugin(plugin);
273 9 : }
274 14 : }
275 :
276 : private void attachItem(
277 : Map<TypeLiteral<?>, DynamicItem<?>> items, @Nullable Injector src, Plugin plugin) {
278 : for (RegistrationHandle h :
279 14 : PrivateInternals_DynamicTypes.attachItems(src, plugin.getName(), items)) {
280 0 : plugin.add(h);
281 0 : }
282 14 : }
283 :
284 : private void attachSet(
285 : Map<TypeLiteral<?>, DynamicSet<?>> sets, @Nullable Injector src, Plugin plugin) {
286 : for (RegistrationHandle h :
287 14 : PrivateInternals_DynamicTypes.attachSets(src, plugin.getName(), sets)) {
288 9 : plugin.add(h);
289 9 : }
290 14 : }
291 :
292 : private void attachMap(
293 : Map<TypeLiteral<?>, DynamicMap<?>> maps, @Nullable Injector src, Plugin plugin) {
294 : for (RegistrationHandle h :
295 14 : PrivateInternals_DynamicTypes.attachMaps(src, plugin.getName(), maps)) {
296 9 : plugin.add(h);
297 9 : }
298 14 : }
299 :
300 : void onReloadPlugin(Plugin oldPlugin, Plugin newPlugin) {
301 : // Index all old registrations by the raw type. These may be replaced
302 : // during the reattach calls below. Any that are not replaced will be
303 : // removed when the old plugin does its stop routine.
304 1 : ListMultimap<TypeLiteral<?>, ReloadableRegistrationHandle<?>> old = LinkedListMultimap.create();
305 1 : for (ReloadableRegistrationHandle<?> h : oldPlugin.getReloadableHandles()) {
306 1 : old.put(h.getKey().getTypeLiteral(), h);
307 1 : }
308 :
309 1 : RequestContext oldContext = enter(newPlugin);
310 : try {
311 1 : reattachMap(old, sysMaps, newPlugin.getSysInjector(), newPlugin);
312 1 : reattachMap(old, sshMaps, newPlugin.getSshInjector(), newPlugin);
313 1 : reattachMap(old, httpMaps, newPlugin.getHttpInjector(), newPlugin);
314 :
315 1 : reattachSet(old, sysSets, newPlugin.getSysInjector(), newPlugin);
316 1 : reattachSet(old, sshSets, newPlugin.getSshInjector(), newPlugin);
317 1 : reattachSet(old, httpSets, newPlugin.getHttpInjector(), newPlugin);
318 :
319 1 : reattachItem(old, sysItems, newPlugin.getSysInjector(), newPlugin);
320 1 : reattachItem(old, sshItems, newPlugin.getSshInjector(), newPlugin);
321 1 : reattachItem(old, httpItems, newPlugin.getHttpInjector(), newPlugin);
322 : } finally {
323 1 : exit(oldContext);
324 : }
325 :
326 1 : for (ReloadPluginListener l : onReload) {
327 1 : l.onReloadPlugin(oldPlugin, newPlugin);
328 1 : }
329 1 : }
330 :
331 : private void reattachMap(
332 : ListMultimap<TypeLiteral<?>, ReloadableRegistrationHandle<?>> oldHandles,
333 : Map<TypeLiteral<?>, DynamicMap<?>> maps,
334 : @Nullable Injector src,
335 : Plugin newPlugin) {
336 1 : if (src == null || maps == null || maps.isEmpty()) {
337 1 : return;
338 : }
339 :
340 1 : for (Map.Entry<TypeLiteral<?>, DynamicMap<?>> e : maps.entrySet()) {
341 : @SuppressWarnings("unchecked")
342 1 : TypeLiteral<Object> type = (TypeLiteral<Object>) e.getKey();
343 :
344 : @SuppressWarnings("unchecked")
345 1 : PrivateInternals_DynamicMapImpl<Object> map =
346 1 : (PrivateInternals_DynamicMapImpl<Object>) e.getValue();
347 :
348 1 : Map<Annotation, ReloadableRegistrationHandle<?>> am = new HashMap<>();
349 1 : for (ReloadableRegistrationHandle<?> h : oldHandles.get(type)) {
350 0 : Annotation a = h.getKey().getAnnotation();
351 0 : if (a != null && !UNIQUE_ANNOTATION.isInstance(a)) {
352 0 : am.put(a, h);
353 : }
354 0 : }
355 :
356 1 : for (Binding<?> binding : bindings(src, e.getKey())) {
357 : @SuppressWarnings("unchecked")
358 0 : Binding<Object> b = (Binding<Object>) binding;
359 0 : Key<Object> key = b.getKey();
360 0 : if (key.getAnnotation() == null) {
361 0 : continue;
362 : }
363 :
364 : @SuppressWarnings("unchecked")
365 0 : ReloadableRegistrationHandle<Object> h =
366 0 : (ReloadableRegistrationHandle<Object>) am.remove(key.getAnnotation());
367 0 : if (h != null) {
368 0 : replace(newPlugin, h, b);
369 0 : oldHandles.remove(type, h);
370 : } else {
371 0 : newPlugin.add(map.put(newPlugin.getName(), b.getKey(), b.getProvider()));
372 : }
373 0 : }
374 1 : }
375 1 : }
376 :
377 : /** Type used to declare unique annotations. Guice hides this, so extract it. */
378 149 : private static final Class<?> UNIQUE_ANNOTATION = UniqueAnnotations.create().annotationType();
379 :
380 : private void reattachSet(
381 : ListMultimap<TypeLiteral<?>, ReloadableRegistrationHandle<?>> oldHandles,
382 : Map<TypeLiteral<?>, DynamicSet<?>> sets,
383 : @Nullable Injector src,
384 : Plugin newPlugin) {
385 1 : if (src == null || sets == null || sets.isEmpty()) {
386 1 : return;
387 : }
388 :
389 1 : for (Map.Entry<TypeLiteral<?>, DynamicSet<?>> e : sets.entrySet()) {
390 : @SuppressWarnings("unchecked")
391 1 : TypeLiteral<Object> type = (TypeLiteral<Object>) e.getKey();
392 :
393 : @SuppressWarnings("unchecked")
394 1 : DynamicSet<Object> set = (DynamicSet<Object>) e.getValue();
395 :
396 : // Index all old handles that match this DynamicSet<T> keyed by
397 : // annotations. Ignore the unique annotations, thereby favoring
398 : // the @Named annotations or some other non-unique naming.
399 1 : Map<Annotation, ReloadableRegistrationHandle<?>> am = new HashMap<>();
400 1 : List<ReloadableRegistrationHandle<?>> old = oldHandles.get(type);
401 1 : Iterator<ReloadableRegistrationHandle<?>> oi = old.iterator();
402 1 : while (oi.hasNext()) {
403 1 : ReloadableRegistrationHandle<?> h = oi.next();
404 1 : Annotation a = h.getKey().getAnnotation();
405 1 : if (a != null && !UNIQUE_ANNOTATION.isInstance(a)) {
406 0 : am.put(a, h);
407 0 : oi.remove();
408 : }
409 1 : }
410 :
411 : // Replace old handles with new bindings, favoring cases where there
412 : // is an exact match on an @Named annotation. If there is no match
413 : // pick any handle and replace it. We generally expect only one
414 : // handle of each DynamicSet type when using unique annotations, but
415 : // possibly multiple ones if @Named was used. Plugin authors that want
416 : // atomic replacement across reloads should use @Named annotations with
417 : // stable names that do not change across plugin versions to ensure the
418 : // handles are swapped correctly.
419 1 : oi = old.iterator();
420 1 : for (Binding<?> binding : bindings(src, type)) {
421 : @SuppressWarnings("unchecked")
422 1 : Binding<Object> b = (Binding<Object>) binding;
423 1 : Key<Object> key = b.getKey();
424 1 : if (key.getAnnotation() == null) {
425 0 : continue;
426 : }
427 :
428 : @SuppressWarnings("unchecked")
429 1 : ReloadableRegistrationHandle<Object> h1 =
430 1 : (ReloadableRegistrationHandle<Object>) am.remove(key.getAnnotation());
431 1 : if (h1 != null) {
432 0 : replace(newPlugin, h1, b);
433 1 : } else if (oi.hasNext()) {
434 : @SuppressWarnings("unchecked")
435 1 : ReloadableRegistrationHandle<Object> h2 =
436 1 : (ReloadableRegistrationHandle<Object>) oi.next();
437 1 : oi.remove();
438 1 : replace(newPlugin, h2, b);
439 1 : } else {
440 0 : newPlugin.add(set.add(newPlugin.getName(), b.getKey(), b.getProvider()));
441 : }
442 1 : }
443 1 : }
444 1 : }
445 :
446 : private void reattachItem(
447 : ListMultimap<TypeLiteral<?>, ReloadableRegistrationHandle<?>> oldHandles,
448 : Map<TypeLiteral<?>, DynamicItem<?>> items,
449 : @Nullable Injector src,
450 : Plugin newPlugin) {
451 1 : if (src == null || items == null || items.isEmpty()) {
452 1 : return;
453 : }
454 :
455 1 : for (Map.Entry<TypeLiteral<?>, DynamicItem<?>> e : items.entrySet()) {
456 : @SuppressWarnings("unchecked")
457 1 : TypeLiteral<Object> type = (TypeLiteral<Object>) e.getKey();
458 :
459 : @SuppressWarnings("unchecked")
460 1 : DynamicItem<Object> item = (DynamicItem<Object>) e.getValue();
461 :
462 1 : Iterator<ReloadableRegistrationHandle<?>> oi = oldHandles.get(type).iterator();
463 :
464 1 : for (Binding<?> binding : bindings(src, type)) {
465 : @SuppressWarnings("unchecked")
466 0 : Binding<Object> b = (Binding<Object>) binding;
467 0 : if (oi.hasNext()) {
468 : @SuppressWarnings("unchecked")
469 0 : ReloadableRegistrationHandle<Object> h = (ReloadableRegistrationHandle<Object>) oi.next();
470 0 : oi.remove();
471 0 : replace(newPlugin, h, b);
472 0 : } else {
473 0 : newPlugin.add(item.set(b.getKey(), b.getProvider(), newPlugin.getName()));
474 : }
475 0 : }
476 1 : }
477 1 : }
478 :
479 : private static <T> void replace(
480 : Plugin newPlugin, ReloadableRegistrationHandle<T> h, Binding<T> b) {
481 1 : RegistrationHandle n = h.replace(b.getKey(), b.getProvider());
482 1 : if (n != null) {
483 1 : newPlugin.add(n);
484 : }
485 1 : }
486 :
487 : static <T> List<T> listeners(Injector src, Class<T> type) {
488 149 : List<Binding<T>> bindings = bindings(src, TypeLiteral.get(type));
489 149 : int cnt = bindings != null ? bindings.size() : 0;
490 149 : List<T> found = Lists.newArrayListWithCapacity(cnt);
491 149 : if (bindings != null) {
492 149 : for (Binding<T> b : bindings) {
493 149 : found.add(b.getProvider().get());
494 149 : }
495 : }
496 149 : return found;
497 : }
498 :
499 : private static <T> List<Binding<T>> bindings(Injector src, TypeLiteral<T> type) {
500 149 : return src.findBindingsByType(type);
501 : }
502 :
503 : private Module copy(Injector src) {
504 138 : Set<TypeLiteral<?>> dynamicTypes = new HashSet<>();
505 138 : Set<TypeLiteral<?>> dynamicItemTypes = new HashSet<>();
506 138 : for (Map.Entry<Key<?>, Binding<?>> e : src.getBindings().entrySet()) {
507 138 : TypeLiteral<?> type = e.getKey().getTypeLiteral();
508 138 : if (type.getRawType() == DynamicItem.class) {
509 138 : ParameterizedType t = (ParameterizedType) type.getType();
510 138 : dynamicItemTypes.add(TypeLiteral.get(t.getActualTypeArguments()[0]));
511 138 : } else if (type.getRawType() == DynamicSet.class || type.getRawType() == DynamicMap.class) {
512 138 : ParameterizedType t = (ParameterizedType) type.getType();
513 138 : dynamicTypes.add(TypeLiteral.get(t.getActualTypeArguments()[0]));
514 : }
515 138 : }
516 :
517 138 : final Map<Key<?>, Binding<?>> bindings = new LinkedHashMap<>();
518 138 : for (Map.Entry<Key<?>, Binding<?>> e : src.getBindings().entrySet()) {
519 138 : if (dynamicTypes.contains(e.getKey().getTypeLiteral())
520 138 : && e.getKey().getAnnotation() != null) {
521 : // A type used in DynamicSet or DynamicMap that has an annotation
522 : // must be picked up by the set/map itself. A type used in either
523 : // but without an annotation may be magic glue implementing F and
524 : // using DynamicSet<F> or DynamicMap<F> internally. That should be
525 : // exported to plugins.
526 138 : continue;
527 138 : } else if (dynamicItemTypes.contains(e.getKey().getTypeLiteral())) {
528 138 : continue;
529 138 : } else if (shouldCopy(e.getKey())) {
530 138 : bindings.put(e.getKey(), e.getValue());
531 : }
532 138 : }
533 138 : bindings.remove(Key.get(Injector.class));
534 138 : bindings.remove(Key.get(java.util.logging.Logger.class));
535 :
536 : @Nullable
537 138 : final Binding<HttpServletRequest> requestBinding =
538 138 : src.getExistingBinding(Key.get(HttpServletRequest.class));
539 :
540 : @Nullable
541 138 : final Binding<HttpServletResponse> responseBinding =
542 138 : src.getExistingBinding(Key.get(HttpServletResponse.class));
543 :
544 138 : return new AbstractModule() {
545 : @SuppressWarnings("unchecked")
546 : @Override
547 : protected void configure() {
548 12 : for (Map.Entry<Key<?>, Binding<?>> e : bindings.entrySet()) {
549 12 : Key<Object> k = (Key<Object>) e.getKey();
550 12 : Binding<Object> b = (Binding<Object>) e.getValue();
551 12 : bind(k).toProvider(b.getProvider());
552 12 : }
553 :
554 12 : if (requestBinding != null) {
555 1 : bind(HttpServletRequest.class)
556 1 : .annotatedWith(RootRelative.class)
557 1 : .toProvider(requestBinding.getProvider());
558 : }
559 12 : if (responseBinding != null) {
560 1 : bind(HttpServletResponse.class)
561 1 : .annotatedWith(RootRelative.class)
562 1 : .toProvider(responseBinding.getProvider());
563 : }
564 12 : }
565 : };
566 : }
567 :
568 : private boolean shouldCopy(Key<?> key) {
569 138 : if (copyConfigKeys.contains(key)) {
570 138 : return false;
571 : }
572 138 : Class<?> type = key.getTypeLiteral().getRawType();
573 138 : if (LifecycleListener.class.isAssignableFrom(type)
574 : // This is needed for secondary index to work from plugin listeners
575 138 : && !IndexCollection.class.isAssignableFrom(type)) {
576 138 : return false;
577 : }
578 138 : if (StartPluginListener.class.isAssignableFrom(type)) {
579 103 : return false;
580 : }
581 138 : if (StopPluginListener.class.isAssignableFrom(type)) {
582 99 : return false;
583 : }
584 138 : if (MetricMaker.class.isAssignableFrom(type)) {
585 138 : return false;
586 : }
587 :
588 138 : if (type.getName().startsWith("com.google.inject.")) {
589 99 : return false;
590 : }
591 :
592 138 : if (is("org.apache.sshd.server.command.Command", type)) {
593 17 : return false;
594 : }
595 :
596 138 : if (is("javax.servlet.Filter", type)) {
597 99 : return false;
598 : }
599 138 : if (is("javax.servlet.ServletContext", type)) {
600 99 : return false;
601 : }
602 138 : if (is("javax.servlet.ServletRequest", type)) {
603 99 : return false;
604 : }
605 138 : if (is("javax.servlet.ServletResponse", type)) {
606 99 : return false;
607 : }
608 138 : if (is("javax.servlet.http.HttpServlet", type)) {
609 99 : return false;
610 : }
611 138 : if (is("javax.servlet.http.HttpServletRequest", type)) {
612 0 : return false;
613 : }
614 138 : if (is("javax.servlet.http.HttpServletResponse", type)) {
615 0 : return false;
616 : }
617 138 : if (is("javax.servlet.http.HttpSession", type)) {
618 99 : return false;
619 : }
620 138 : if (Map.class.isAssignableFrom(type)
621 99 : && key.getAnnotationType() != null
622 : && "com.google.inject.servlet.RequestParameters"
623 99 : .equals(key.getAnnotationType().getName())) {
624 99 : return false;
625 : }
626 138 : if (type.getName().startsWith("com.google.gerrit.httpd.GitOverHttpServlet$")) {
627 99 : return false;
628 : }
629 138 : return true;
630 : }
631 :
632 : static boolean is(String name, Class<?> type) {
633 139 : while (type != null) {
634 139 : if (name.equals(type.getName())) {
635 104 : return true;
636 : }
637 :
638 139 : Class<?>[] interfaces = type.getInterfaces();
639 139 : if (interfaces != null) {
640 139 : for (Class<?> i : interfaces) {
641 139 : if (is(name, i)) {
642 100 : return true;
643 : }
644 : }
645 : }
646 :
647 139 : type = type.getSuperclass();
648 139 : }
649 138 : return false;
650 : }
651 : }
|