Author: limpbizkit
Date: Tue Mar 31 15:37:26 2009
New Revision: 922
Added:
trunk/src/com/google/inject/DeferredLookups.java
trunk/src/com/google/inject/Lookups.java
Modified:
trunk/src/com/google/inject/ConstructorInjectorStore.java
trunk/src/com/google/inject/EncounterImpl.java
trunk/src/com/google/inject/InjectorBuilder.java
trunk/src/com/google/inject/InjectorImpl.java
trunk/src/com/google/inject/InjectorShell.java
trunk/src/com/google/inject/MembersInjectorStore.java
trunk/src/com/google/inject/spi/Elements.java
trunk/src/com/google/inject/spi/MembersInjectorLookup.java
trunk/src/com/google/inject/spi/ProviderLookup.java
trunk/test/com/google/inject/InjectableTypeListenerTest.java
Log:
Support deferred lookups of providers and members injectors from the
Encounter.
The behaviour is slightly different depending on when the
InjectableTypeListener.hear() method is invoked...
A) If it's invoked before the Injector is created, then getProvider()
always returns immediately. The provider may not be used until the injector
has been created.
B) If it's invoked after the Injector has been created, then getProvider()
builds the provider immediately. This includes the work to build the
binding (recursively) and this method may fail with a
ConfigurationException.
It would be possible to change B) to behave more like A), but the utility
of doing so is quite limited. One case that still needs test coverage is
when the listener calls getProvider(B.class) while hearing about B. For
now, this will probably fail. That's probably okay for now.
Modified: trunk/src/com/google/inject/ConstructorInjectorStore.java
==============================================================================
--- trunk/src/com/google/inject/ConstructorInjectorStore.java (original)
+++ trunk/src/com/google/inject/ConstructorInjectorStore.java Tue Mar 31
15:37:26 2009
@@ -79,7 +79,7 @@
ImmutableSet<InjectionPoint> injectableMembers =
membersInjector.getInjectionPoints();
ProxyFactory<T> proxyFactory = new ProxyFactory<T>(injectionPoint,
injector.methodAspects);
- EncounterImpl<T> encounter = new EncounterImpl<T>();
+ EncounterImpl<T> encounter = new EncounterImpl<T>(errors,
injector.lookups);
InjectableType<T> injectableType = new InjectableType<T>(
injectionPoint, type, injectableMembers,
proxyFactory.getInterceptors());
Added: trunk/src/com/google/inject/DeferredLookups.java
==============================================================================
--- (empty file)
+++ trunk/src/com/google/inject/DeferredLookups.java Tue Mar 31 15:37:26
2009
@@ -0,0 +1,59 @@
+/**
+ * Copyright (C) 2009 Google Inc.
+ *
+ * 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.inject;
+
+import com.google.inject.internal.Errors;
+import com.google.inject.internal.Lists;
+import com.google.inject.spi.Element;
+import com.google.inject.spi.MembersInjectorLookup;
+import com.google.inject.spi.ProviderLookup;
+import java.util.List;
+
+/**
+ * Returns providers and members injectors that haven't yet been
initialized. As a part of injector
+ * creation it's necessary to {...@link #initialize initialize} these lookups.
+ *
+ * @author [email protected] (Jesse Wilson)
+ */
+public class DeferredLookups implements Lookups {
+ private final InjectorImpl injector;
+ private final List<Element> lookups = Lists.newArrayList();
+
+ public DeferredLookups(InjectorImpl injector) {
+ this.injector = injector;
+ }
+
+ /**
+ * Initialize the specified lookups, either immediately or when the
injector is created.
+ */
+ public void initialize(Errors errors) {
+ injector.lookups = injector;
+ new LookupProcessor(errors).process(injector, lookups);
+ }
+
+ public <T> Provider<T> getProvider(Key<T> key) {
+ ProviderLookup<T> lookup = new ProviderLookup<T>(key, key);
+ lookups.add(lookup);
+ return lookup.getProvider();
+ }
+
+ public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> type) {
+ MembersInjectorLookup<T> lookup = new MembersInjectorLookup<T>(type,
type);
+ lookups.add(lookup);
+ return lookup.getMembersInjector();
+ }
+}
Modified: trunk/src/com/google/inject/EncounterImpl.java
==============================================================================
--- trunk/src/com/google/inject/EncounterImpl.java (original)
+++ trunk/src/com/google/inject/EncounterImpl.java Tue Mar 31 15:37:26 2009
@@ -16,25 +16,34 @@
package com.google.inject;
-import com.google.inject.spi.InjectableType;
-import com.google.inject.spi.InjectionListener;
-import com.google.inject.spi.Message;
+import com.google.inject.internal.Errors;
import com.google.inject.internal.ImmutableList;
import com.google.inject.internal.Lists;
import com.google.inject.matcher.Matcher;
import com.google.inject.matcher.Matchers;
-import java.util.List;
+import com.google.inject.spi.InjectableType;
+import com.google.inject.spi.InjectionListener;
+import com.google.inject.spi.Message;
import java.lang.reflect.Method;
+import java.util.List;
import org.aopalliance.intercept.MethodInterceptor;
/**
* @author [email protected] (Jesse Wilson)
*/
-class EncounterImpl<T> implements InjectableType.Encounter<T> {
+public final class EncounterImpl<T> implements InjectableType.Encounter<T>
{
+
+ private final Errors errors;
+ private final Lookups lookups;
private List<InjectionListener<? super T>> injectionListeners; // lazy
private List<MethodAspect> aspects; // lazy
- boolean hasAddedAspects() {
+ public EncounterImpl(Errors errors, Lookups lookups) {
+ this.errors = errors;
+ this.lookups = lookups;
+ }
+
+ public boolean hasAddedAspects() {
return aspects != null;
}
@@ -46,7 +55,7 @@
return aspects;
}
- ImmutableList<InjectionListener<? super T>> getInjectionListeners() {
+ public ImmutableList<InjectionListener<? super T>>
getInjectionListeners() {
return injectionListeners == null
? ImmutableList.<InjectionListener<? super T>>of()
: ImmutableList.copyOf(injectionListeners);
@@ -58,7 +67,7 @@
injectionListeners = Lists.newArrayList();
}
- injectionListeners.add((InjectionListener<T>) injectionListener);
+ injectionListeners.add(injectionListener);
}
public void bindInterceptor(Matcher<? super Method> methodMatcher,
@@ -72,30 +81,30 @@
}
public void addError(String message, Object... arguments) {
- throw new UnsupportedOperationException("TODO");
+ errors.addMessage(message, arguments);
}
public void addError(Throwable t) {
- throw new UnsupportedOperationException("TODO");
+ errors.errorInUserCode(t, "An exception was caught and reported.
Message: %s", t.getMessage());
}
public void addError(Message message) {
- throw new UnsupportedOperationException("TODO");
+ errors.addMessage(message);
}
public <T> Provider<T> getProvider(Key<T> key) {
- throw new UnsupportedOperationException("TODO");
+ return lookups.getProvider(key);
}
public <T> Provider<T> getProvider(Class<T> type) {
- throw new UnsupportedOperationException("TODO");
+ return getProvider(Key.get(type));
}
public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T>
typeLiteral) {
- throw new UnsupportedOperationException("TODO");
+ return lookups.getMembersInjector(typeLiteral);
}
public <T> MembersInjector<T> getMembersInjector(Class<T> type) {
- throw new UnsupportedOperationException("TODO");
+ return getMembersInjector(TypeLiteral.get(type));
}
-}
+}
\ No newline at end of file
Modified: trunk/src/com/google/inject/InjectorBuilder.java
==============================================================================
--- trunk/src/com/google/inject/InjectorBuilder.java (original)
+++ trunk/src/com/google/inject/InjectorBuilder.java Tue Mar 31 15:37:26
2009
@@ -138,6 +138,9 @@
stopwatch.resetAndLog("Instance member validation");
new LookupProcessor(errors).process(shells);
+ for (InjectorShell shell : shells) {
+ ((DeferredLookups) shell.getInjector().lookups).initialize(errors);
+ }
stopwatch.resetAndLog("Provider verification");
for (InjectorShell shell : shells) {
Modified: trunk/src/com/google/inject/InjectorImpl.java
==============================================================================
--- trunk/src/com/google/inject/InjectorImpl.java (original)
+++ trunk/src/com/google/inject/InjectorImpl.java Tue Mar 31 15:37:26 2009
@@ -60,7 +60,7 @@
* @author [email protected] (Bob Lee)
* @see InjectorBuilder
*/
-class InjectorImpl implements Injector {
+class InjectorImpl implements Injector, Lookups {
final State state;
final InjectorImpl parent;
final BindingsMultimap bindingsMultimap = new BindingsMultimap();
@@ -69,6 +69,8 @@
/** Just-in-time binding cache. Guarded by state.lock() */
final Map<Key<?>, BindingImpl<?>> jitBindings = Maps.newHashMap();
+
+ Lookups lookups = new DeferredLookups(this);
InjectorImpl(@Nullable InjectorImpl parent, State state, Initializer
initializer) {
this.parent = parent;
Modified: trunk/src/com/google/inject/InjectorShell.java
==============================================================================
--- trunk/src/com/google/inject/InjectorShell.java (original)
+++ trunk/src/com/google/inject/InjectorShell.java Tue Mar 31 15:37:26 2009
@@ -148,11 +148,11 @@
end[NO_AOP]*/
new InjectableTypeListenerBindingProcessor(errors).process(injector,
elements);
- stopwatch.resetAndLog("InjectableType listeners creation");
List<InjectableTypeListenerBinding> listenerBindings
= injector.state.getInjectableTypeListenerBindings();
injector.constructors = new ConstructorInjectorStore(injector,
listenerBindings);
injector.membersInjectorStore = new MembersInjectorStore(injector,
listenerBindings);
+ stopwatch.resetAndLog("InjectableType listeners creation");
new ScopeBindingProcessor(errors).process(injector, elements);
stopwatch.resetAndLog("Scopes creation");
Added: trunk/src/com/google/inject/Lookups.java
==============================================================================
--- (empty file)
+++ trunk/src/com/google/inject/Lookups.java Tue Mar 31 15:37:26 2009
@@ -0,0 +1,30 @@
+/**
+ * Copyright (C) 2009 Google Inc.
+ *
+ * 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.inject;
+
+/**
+ * Accessors for providers and members injectors. The returned values will
not be functional until
+ * the injector has been created.
+ *
+ * @author [email protected] (Jesse Wilson)
+ */
+interface Lookups {
+
+ <T> Provider<T> getProvider(Key<T> key);
+
+ <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> type);
+}
Modified: trunk/src/com/google/inject/MembersInjectorStore.java
==============================================================================
--- trunk/src/com/google/inject/MembersInjectorStore.java (original)
+++ trunk/src/com/google/inject/MembersInjectorStore.java Tue Mar 31
15:37:26 2009
@@ -21,15 +21,12 @@
import com.google.inject.internal.FailableCache;
import com.google.inject.internal.ImmutableList;
import com.google.inject.internal.Lists;
-import com.google.inject.matcher.Matcher;
import com.google.inject.spi.InjectableType;
import com.google.inject.spi.InjectableTypeListenerBinding;
import com.google.inject.spi.InjectionPoint;
import java.lang.reflect.Field;
-import java.lang.reflect.Method;
import java.util.List;
import java.util.Set;
-import org.aopalliance.intercept.MethodInterceptor;
/**
* Members injectors by type.
@@ -92,13 +89,7 @@
InjectableType<T> injectableType = new InjectableType<T>(null, type,
membersInjector.getInjectionPoints());
- EncounterImpl<T> encounter = new EncounterImpl<T>() {
- @Override public void bindInterceptor(Matcher<? super Method>
methodMatcher,
- MethodInterceptor... interceptors) {
- throw new UnsupportedOperationException("TODO");
- // TODO: add an error here
- }
- };
+ EncounterImpl<T> encounter = new EncounterImpl<T>(errors,
injector.lookups);
for (InjectableTypeListenerBinding typeListener :
injectableTypeListenerBindings) {
if (typeListener.getTypeMatcher().matches(type)) {
Modified: trunk/src/com/google/inject/spi/Elements.java
==============================================================================
--- trunk/src/com/google/inject/spi/Elements.java (original)
+++ trunk/src/com/google/inject/spi/Elements.java Tue Mar 31 15:37:26 2009
@@ -38,7 +38,6 @@
import com.google.inject.internal.ImmutableList;
import com.google.inject.internal.Lists;
import static com.google.inject.internal.Preconditions.checkArgument;
-import static com.google.inject.internal.Preconditions.checkState;
import com.google.inject.internal.PrivateElementsImpl;
import com.google.inject.internal.ProviderMethodsModule;
import com.google.inject.internal.Sets;
@@ -182,18 +181,7 @@
final MembersInjectorLookup<T> element
= new MembersInjectorLookup<T>(getSource(), typeLiteral);
elements.add(element);
- return new MembersInjector<T>() {
- public void injectMembers(T instance) {
- MembersInjector<T> delegate = element.getDelegate();
- checkState(delegate != null,
- "This MembersInjector cannot be used until the Injector has
been created.");
- delegate.injectMembers(instance);
- }
-
- @Override public String toString() {
- return "MembersInjector<" + typeLiteral + ">";
- }
- };
+ return element.getMembersInjector();
}
public <T> MembersInjector<T> getMembersInjector(Class<T> type) {
@@ -268,18 +256,7 @@
public <T> Provider<T> getProvider(final Key<T> key) {
final ProviderLookup<T> element = new ProviderLookup<T>(getSource(),
key);
elements.add(element);
- return new Provider<T>() {
- public T get() {
- Provider<T> delegate = element.getDelegate();
- checkState(delegate != null,
- "This Provider cannot be used until the Injector has been
created.");
- return delegate.get();
- }
-
- @Override public String toString() {
- return "Provider<" + key.getTypeLiteral() + ">";
- }
- };
+ return element.getProvider();
}
public <T> Provider<T> getProvider(Class<T> type) {
Modified: trunk/src/com/google/inject/spi/MembersInjectorLookup.java
==============================================================================
--- trunk/src/com/google/inject/spi/MembersInjectorLookup.java (original)
+++ trunk/src/com/google/inject/spi/MembersInjectorLookup.java Tue Mar 31
15:37:26 2009
@@ -38,7 +38,7 @@
private final TypeLiteral<T> type;
private MembersInjector<T> delegate;
- MembersInjectorLookup(Object source, TypeLiteral<T> type) {
+ public MembersInjectorLookup(Object source, TypeLiteral<T> type) {
this.source = checkNotNull(source, "source");
this.type = checkNotNull(type, "type");
}
@@ -79,5 +79,24 @@
*/
public MembersInjector<T> getDelegate() {
return delegate;
+ }
+
+ /**
+ * Returns the looked up members injector. The result is not valid until
this lookup has been
+ * initialized, which usually happens when the injector is created. The
members injector will
+ * throw an {...@code IllegalStateException} if you try to use it
beforehand.
+ */
+ public MembersInjector<T> getMembersInjector() {
+ return new MembersInjector<T>() {
+ public void injectMembers(T instance) {
+ checkState(delegate != null,
+ "This MembersInjector cannot be used until the Injector has
been created.");
+ delegate.injectMembers(instance);
+ }
+
+ @Override public String toString() {
+ return "MembersInjector<" + type + ">";
+ }
+ };
}
}
Modified: trunk/src/com/google/inject/spi/ProviderLookup.java
==============================================================================
--- trunk/src/com/google/inject/spi/ProviderLookup.java (original)
+++ trunk/src/com/google/inject/spi/ProviderLookup.java Tue Mar 31 15:37:26
2009
@@ -37,7 +37,7 @@
private final Key<T> key;
private Provider<T> delegate;
- ProviderLookup(Object source, Key<T> key) {
+ public ProviderLookup(Object source, Key<T> key) {
this.source = checkNotNull(source, "source");
this.key = checkNotNull(key, "key");
}
@@ -74,5 +74,24 @@
*/
public Provider<T> getDelegate() {
return delegate;
+ }
+
+ /**
+ * Returns the looked up provider. The result is not valid until this
lookup has been initialized,
+ * which usually happens when the injector is created. The provider will
throw an {...@code
+ * IllegalStateException} if you try to use it beforehand.
+ */
+ public Provider<T> getProvider() {
+ return new Provider<T>() {
+ public T get() {
+ checkState(delegate != null,
+ "This Provider cannot be used until the Injector has been
created.");
+ return delegate.get();
+ }
+
+ @Override public String toString() {
+ return "Provider<" + key.getTypeLiteral() + ">";
+ }
+ };
}
}
Modified: trunk/test/com/google/inject/InjectableTypeListenerTest.java
==============================================================================
--- trunk/test/com/google/inject/InjectableTypeListenerTest.java
(original)
+++ trunk/test/com/google/inject/InjectableTypeListenerTest.java Tue Mar
31
15:37:26 2009
@@ -16,20 +16,21 @@
package com.google.inject;
+import static com.google.inject.Asserts.assertContains;
import com.google.inject.internal.ImmutableList;
import com.google.inject.internal.ImmutableMap;
import com.google.inject.internal.Lists;
import com.google.inject.matcher.Matcher;
-import com.google.inject.matcher.Matchers;
import static com.google.inject.matcher.Matchers.any;
+import static com.google.inject.matcher.Matchers.only;
import com.google.inject.spi.InjectableType;
import com.google.inject.spi.InjectableType.Encounter;
import com.google.inject.spi.InjectionListener;
-import static com.google.inject.Asserts.assertContains;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
import junit.framework.TestCase;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
@@ -134,7 +135,7 @@
}
public void testAddingInterceptors() throws NoSuchMethodException {
- final Matcher<Object> buzz = Matchers.only(C.class.getMethod("buzz"));
+ final Matcher<Object> buzz = only(C.class.getMethod("buzz"));
Injector injector = Guice.createInjector(new AbstractModule() {
protected void configure() {
@@ -343,6 +344,69 @@
assertEquals(0, constructionCounts.getAndSet(0));
assertEquals(2, memberInjectionCounts.getAndSet(0));
}
+
+ public void testLookupsAtInjectorCreateTime() {
+ final AtomicReference<Provider<B>> bProviderReference = new
AtomicReference<Provider<B>>();
+ final AtomicReference<MembersInjector<A>> aMembersInjectorReference
+ = new AtomicReference<MembersInjector<A>>();
+
+ Guice.createInjector(new AbstractModule() {
+ protected void configure() {
+ bindListener(only(TypeLiteral.get(C.class)), new
InjectableType.Listener() {
+ public <I> void hear(InjectableType<I> injectableType,
Encounter<I> encounter) {
+ Provider<B> bProvider = encounter.getProvider(B.class);
+ try {
+ bProvider.get();
+ fail();
+ } catch (IllegalStateException expected) {
+ assertEquals("This Provider cannot be used until the
Injector has been created.",
+ expected.getMessage());
+ }
+ bProviderReference.set(bProvider);
+
+ MembersInjector<A> aMembersInjector =
encounter.getMembersInjector(A.class);
+ try {
+ aMembersInjector.injectMembers(new A());
+ fail();
+ } catch (IllegalStateException expected) {
+ assertEquals(
+ "This MembersInjector cannot be used until the Injector
has been created.",
+ expected.getMessage());
+ }
+ aMembersInjectorReference.set(aMembersInjector);
+ }
+ });
+
+ bind(C.class);
+ }
+ });
+
+ assertNotNull(bProviderReference.get().get());
+
+ A a = new A();
+ aMembersInjectorReference.get().injectMembers(a);
+ assertNotNull(a.injector);
+ }
+
+ public void testLookupsPostCreate() {
+ Injector injector = Guice.createInjector(new AbstractModule() {
+ protected void configure() {
+ bindListener(only(TypeLiteral.get(C.class)), new
InjectableType.Listener() {
+ public <I> void hear(InjectableType<I> injectableType,
Encounter<I> encounter) {
+ assertNotNull(encounter.getProvider(B.class).get());
+
+ A a = new A();
+ encounter.getMembersInjector(A.class).injectMembers(a);
+ assertNotNull(a.injector);
+ }
+ });
+ }
+ });
+
+ injector.getInstance(C.class);
+ }
+
+ // TODO: recursively accessing a lookup should fail
static class A {
@Inject Injector injector;
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"google-guice-dev" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/google-guice-dev?hl=en
-~----------~----~----~----~------~----~------~--~---