blob: ab396e499fc4a8aeb1b6a88e545746885511e0c5 [file] [log] [blame]
limpbizkit5fb9d922008-10-14 23:35:56 +00001/**
2 * Copyright (C) 2008 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.google.inject;
18
limpbizkit5fb9d922008-10-14 23:35:56 +000019import com.google.inject.internal.Errors;
20import com.google.inject.internal.ErrorsException;
limpbizkit53664a72009-02-21 00:25:27 +000021import com.google.inject.internal.Lists;
22import com.google.inject.internal.Maps;
23import static com.google.inject.internal.Preconditions.checkNotNull;
limpbizkit5fb9d922008-10-14 23:35:56 +000024import com.google.inject.spi.InjectionPoint;
25import java.util.Map;
limpbizkit5fb9d922008-10-14 23:35:56 +000026import java.util.Set;
27import java.util.concurrent.CountDownLatch;
28
29/**
limpbizkit1db23932008-10-15 00:06:43 +000030 * Manages and injects instances at injector-creation time. This is made more complicated by
31 * instances that request other instances while they're being injected. We overcome this by using
32 * {@link Initializable}, which attempts to perform injection before use.
limpbizkit5fb9d922008-10-14 23:35:56 +000033 *
34 * @author jessewilson@google.com (Jesse Wilson)
35 */
36class Initializer {
37 /** the only thread that we'll use to inject members. */
38 private final Thread creatingThread = Thread.currentThread();
39
40 /** zero means everything is injected. */
41 private final CountDownLatch ready = new CountDownLatch(1);
42
43 /** Maps instances that need injection to a source that registered them */
limpbizkit1db23932008-10-15 00:06:43 +000044 private final Map<Object, InjectableReference<?>> pendingInjection = Maps.newIdentityHashMap();
limpbizkit5fb9d922008-10-14 23:35:56 +000045
46 /**
47 * Registers an instance for member injection when that step is performed.
48 *
49 * @param instance an instance that optionally has members to be injected (each annotated with
50 * @Inject).
51 * @param source the source location that this injection was requested
52 */
limpbizkitfcbdf992008-11-26 02:37:35 +000053 public <T> Initializable<T> requestInjection(InjectorImpl injector, T instance, Object source,
limpbizkit5fb9d922008-10-14 23:35:56 +000054 Set<InjectionPoint> injectionPoints) {
55 checkNotNull(source);
limpbizkit1db23932008-10-15 00:06:43 +000056
limpbizkita843a952009-04-08 22:24:55 +000057 // short circuit if the object has no injections
58 if (instance == null
59 || (injectionPoints.isEmpty() && !injector.membersInjectorStore.hasTypeListeners())) {
limpbizkit1db23932008-10-15 00:06:43 +000060 return Initializables.of(instance);
61 }
62
limpbizkitfcbdf992008-11-26 02:37:35 +000063 InjectableReference<T> initializable = new InjectableReference<T>(injector, instance, source);
limpbizkit1db23932008-10-15 00:06:43 +000064 pendingInjection.put(instance, initializable);
65 return initializable;
limpbizkit5fb9d922008-10-14 23:35:56 +000066 }
67
68 /**
69 * Prepares member injectors for all injected instances. This prompts Guice to do static analysis
70 * on the injected instances.
71 */
72 void validateOustandingInjections(Errors errors) {
limpbizkit1db23932008-10-15 00:06:43 +000073 for (InjectableReference<?> reference : pendingInjection.values()) {
limpbizkit5fb9d922008-10-14 23:35:56 +000074 try {
limpbizkit1db23932008-10-15 00:06:43 +000075 reference.validate(errors);
limpbizkit5fb9d922008-10-14 23:35:56 +000076 } catch (ErrorsException e) {
77 errors.merge(e.getErrors());
78 }
79 }
80 }
81
82 /**
83 * Performs creation-time injections on all objects that require it. Whenever fulfilling an
limpbizkit1db23932008-10-15 00:06:43 +000084 * injection depends on another object that requires injection, we inject it first. If the two
85 * instances are codependent (directly or transitively), ordering of injection is arbitrary.
limpbizkit5fb9d922008-10-14 23:35:56 +000086 */
87 void injectAll(final Errors errors) {
88 // loop over a defensive copy since ensureInjected() mutates the set. Unfortunately, that copy
89 // is made complicated by a bug in IBM's JDK, wherein entrySet().toArray(Object[]) doesn't work
limpbizkit1db23932008-10-15 00:06:43 +000090 for (InjectableReference<?> reference : Lists.newArrayList(pendingInjection.values())) {
limpbizkit5fb9d922008-10-14 23:35:56 +000091 try {
limpbizkit1db23932008-10-15 00:06:43 +000092 reference.get(errors);
limpbizkit5fb9d922008-10-14 23:35:56 +000093 } catch (ErrorsException e) {
94 errors.merge(e.getErrors());
95 }
96 }
97
limpbizkit1db23932008-10-15 00:06:43 +000098 if (!pendingInjection.isEmpty()) {
99 throw new AssertionError("Failed to satisfy " + pendingInjection);
limpbizkit5fb9d922008-10-14 23:35:56 +0000100 }
101
102 ready.countDown();
103 }
104
105 private class InjectableReference<T> implements Initializable<T> {
limpbizkitfcbdf992008-11-26 02:37:35 +0000106 private final InjectorImpl injector;
limpbizkit5fb9d922008-10-14 23:35:56 +0000107 private final T instance;
limpbizkit1db23932008-10-15 00:06:43 +0000108 private final Object source;
limpbizkit7cef5b02009-03-29 21:16:51 +0000109 private MembersInjectorImpl<T> membersInjector;
limpbizkit5fb9d922008-10-14 23:35:56 +0000110
limpbizkitfcbdf992008-11-26 02:37:35 +0000111 public InjectableReference(InjectorImpl injector, T instance, Object source) {
112 this.injector = injector;
limpbizkit5fb9d922008-10-14 23:35:56 +0000113 this.instance = checkNotNull(instance, "instance");
limpbizkit1db23932008-10-15 00:06:43 +0000114 this.source = checkNotNull(source, "source");
115 }
116
117 public void validate(Errors errors) throws ErrorsException {
limpbizkit7cef5b02009-03-29 21:16:51 +0000118 @SuppressWarnings("unchecked") // the type of 'T' is a TypeLiteral<T>
119 TypeLiteral<T> type = TypeLiteral.get((Class<T>) instance.getClass());
120 membersInjector = injector.membersInjectorStore.get(type, errors.withSource(source));
limpbizkit5fb9d922008-10-14 23:35:56 +0000121 }
122
123 /**
limpbizkit1db23932008-10-15 00:06:43 +0000124 * Reentrant. If {@code instance} was registered for injection at injector-creation time, this
125 * method will ensure that all its members have been injected before returning.
limpbizkit5fb9d922008-10-14 23:35:56 +0000126 */
127 public T get(Errors errors) throws ErrorsException {
limpbizkit1db23932008-10-15 00:06:43 +0000128 if (ready.getCount() == 0) {
129 return instance;
130 }
131
132 // just wait for everything to be injected by another thread
133 if (Thread.currentThread() != creatingThread) {
134 try {
135 ready.await();
136 return instance;
137 } catch (InterruptedException e) {
138 // Give up, since we don't know if our injection is ready
139 throw new RuntimeException(e);
140 }
141 }
142
limpbizkit8f370d32008-10-16 16:15:48 +0000143 // toInject needs injection, do it right away. we only do this once, even if it fails
limpbizkit1db23932008-10-15 00:06:43 +0000144 if (pendingInjection.remove(instance) != null) {
limpbizkita843a952009-04-08 22:24:55 +0000145 membersInjector.injectAndNotify(instance, errors.withSource(source));
limpbizkit1db23932008-10-15 00:06:43 +0000146 }
147
limpbizkit5fb9d922008-10-14 23:35:56 +0000148 return instance;
149 }
150
151 @Override public String toString() {
152 return instance.toString();
153 }
154 }
limpbizkit5fb9d922008-10-14 23:35:56 +0000155}