Created
July 7, 2014 18:13
-
-
Save gissuebot/d263b77fa21f76485bcd to your computer and use it in GitHub Desktop.
Migrated attachment for Guice issue 354, comment 1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Index: src/com/google/inject/spi/CachedValue.java | |
=================================================================== | |
--- src/com/google/inject/spi/CachedValue.java Mon Apr 06 10:36:06 BST 2009 | |
+++ src/com/google/inject/spi/CachedValue.java Mon Apr 06 10:36:06 BST 2009 | |
@@ -0,0 +1,33 @@ | |
+/** | |
+ * | |
+ * Licensed to the Apache Software Foundation (ASF) under one or more | |
+ * contributor license agreements. See the NOTICE file distributed with | |
+ * this work for additional information regarding copyright ownership. | |
+ * The ASF licenses this file to You 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.spi; | |
+ | |
+/** | |
+ * An interface to expose a cached value in a provider such as for a Scope. | |
+ * A Scope may choose to implement this method so that it can expose the current value in the scope | |
+ * | |
+ * @version $Revision: 1.1 $ | |
+ */ | |
+public interface CachedValue<T> { | |
+ /** | |
+ * Returns the currently cached value in a scope if its available or supported or null if there is | |
+ * no cached value yet or caching is not supported. | |
+ */ | |
+ T getCachedValue(); | |
+} | |
Index: src/com/google/inject/InternalFactoryToProviderAdapter.java | |
=================================================================== | |
--- src/com/google/inject/InternalFactoryToProviderAdapter.java (revision 925) | |
+++ src/com/google/inject/InternalFactoryToProviderAdapter.java Mon Apr 06 10:50:44 BST 2009 | |
@@ -22,12 +22,13 @@ | |
import com.google.inject.internal.InternalFactory; | |
import static com.google.inject.internal.Preconditions.checkNotNull; | |
import com.google.inject.internal.SourceProvider; | |
+import com.google.inject.spi.CachedValue; | |
import com.google.inject.spi.Dependency; | |
/** | |
* @author crazybob@google.com (Bob Lee) | |
*/ | |
-class InternalFactoryToProviderAdapter<T> implements InternalFactory<T> { | |
+class InternalFactoryToProviderAdapter<T> implements InternalFactory<T>, CachedValue<T> { | |
private final Initializable<Provider<? extends T>> initializable; | |
private final Object source; | |
@@ -54,4 +55,19 @@ | |
@Override public String toString() { | |
return initializable.toString(); | |
} | |
+ | |
+ public T getCachedValue() { | |
+ try { | |
+ Provider<? extends T> provider = initializable.get(new Errors(this)); | |
+ if (provider instanceof CachedValue) { | |
+ @SuppressWarnings("unchecked") | |
+ CachedValue<T> cachedValue = (CachedValue<T>) provider; | |
+ return cachedValue.getCachedValue(); | |
-} | |
+ } | |
+ } | |
+ catch (ErrorsException e) { | |
+ // ignore | |
+ } | |
+ return null; | |
+ } | |
+} | |
Index: src/com/google/inject/InjectorImpl.java | |
=================================================================== | |
--- src/com/google/inject/InjectorImpl.java (revision 925) | |
+++ src/com/google/inject/InjectorImpl.java Mon Apr 06 10:50:44 BST 2009 | |
@@ -37,6 +37,8 @@ | |
import com.google.inject.internal.SourceProvider; | |
import com.google.inject.internal.ToStringBuilder; | |
import com.google.inject.spi.BindingTargetVisitor; | |
+import com.google.inject.spi.CachedValue; | |
+import com.google.inject.spi.CachingProvider; | |
import com.google.inject.spi.ConvertedConstantBinding; | |
import com.google.inject.spi.Dependency; | |
import com.google.inject.spi.InjectionPoint; | |
@@ -748,7 +750,7 @@ | |
final InternalFactory<? extends T> factory = getInternalFactory(key, errors); | |
final Dependency<T> dependency = Dependency.get(key); | |
- return new Provider<T>() { | |
+ return new CachingProvider<T>() { | |
public T get() { | |
final Errors errors = new Errors(dependency); | |
try { | |
@@ -772,6 +774,15 @@ | |
@Override public String toString() { | |
return factory.toString(); | |
} | |
+ | |
+ public T getCachedValue() { | |
+ if (factory instanceof CachedValue) { | |
+ @SuppressWarnings("unchecked") | |
+ CachedValue<T> cachedValue = (CachedValue<T>) factory; | |
+ return cachedValue.getCachedValue(); | |
+ } | |
+ return null; | |
+ } | |
}; | |
} | |
Index: src/com/google/inject/ConstructorBindingImpl.java | |
=================================================================== | |
--- src/com/google/inject/ConstructorBindingImpl.java (revision 925) | |
+++ src/com/google/inject/ConstructorBindingImpl.java Mon Apr 06 10:36:06 BST 2009 | |
@@ -25,6 +25,7 @@ | |
import com.google.inject.internal.Scoping; | |
import com.google.inject.internal.ToStringBuilder; | |
import com.google.inject.spi.BindingTargetVisitor; | |
+import com.google.inject.spi.CachedValue; | |
import com.google.inject.spi.ConstructorBinding; | |
import com.google.inject.spi.Dependency; | |
import com.google.inject.spi.InjectableType; | |
@@ -87,7 +88,7 @@ | |
.toString(); | |
} | |
- private static class Factory<T> implements InternalFactory<T> { | |
+ private static class Factory<T> implements InternalFactory<T>, CachedValue<T> { | |
private ConstructorInjector<T> constructorInjector; | |
@SuppressWarnings("unchecked") | |
@@ -99,5 +100,9 @@ | |
// client needs), but it should be OK in practice thanks to the wonders of erasure. | |
return (T) constructorInjector.construct(errors, context, dependency.getKey().getRawType()); | |
} | |
+ | |
+ public T getCachedValue() { | |
+ return constructorInjector.getCachedValue(); | |
- } | |
-} | |
+ } | |
+ } | |
+} | |
Index: src/com/google/inject/spi/InjectableType.java | |
=================================================================== | |
--- src/com/google/inject/spi/InjectableType.java (revision 925) | |
+++ src/com/google/inject/spi/InjectableType.java Thu Apr 02 12:07:54 BST 2009 | |
@@ -220,6 +220,23 @@ | |
*/ | |
void register(InjectionListener<? super I> listener); | |
+ /** | |
+ * Registers a post injection listener for type {@code I}. Guice will notify the listener after | |
+ * injecting an instance of {@code I} and invoking all of the other injection listeners | |
+ * registered via the {@link #register(InjectionListener)}. | |
+ * | |
+ * This hook is intended for things like @PostConstruct from JSR 250 or | |
+ * for InitializingBean from Spring so that you can be notified when all the mutations on the | |
+ * injectee have completed. | |
+ * | |
+ * The order in which Guice will invoke these listeners is | |
+ * unspecified - other than they are all invoked after the listeners registered via the | |
+ * {@link #register(InjectionListener)} method. | |
+ * | |
+ * @param listener for post injections of instances of type {@code I} | |
+ */ | |
+ void registerPostInjectListener(InjectionListener<? super I> listener); | |
+ | |
/*if[AOP]*/ | |
/** | |
* Binds method interceptor[s] to methods matched in type {@code I} and its supertypes. A | |
Index: src/com/google/inject/EncounterImpl.java | |
=================================================================== | |
--- src/com/google/inject/EncounterImpl.java (revision 925) | |
+++ src/com/google/inject/EncounterImpl.java Thu Apr 02 11:38:26 BST 2009 | |
@@ -18,6 +18,7 @@ | |
import com.google.inject.internal.Errors; | |
import com.google.inject.internal.ImmutableList; | |
+import com.google.inject.internal.Iterables; | |
import com.google.inject.internal.Lists; | |
import static com.google.inject.internal.Preconditions.checkState; | |
import com.google.inject.matcher.Matcher; | |
@@ -37,6 +38,7 @@ | |
private final Errors errors; | |
private final Lookups lookups; | |
private List<InjectionListener<? super T>> injectionListeners; // lazy | |
+ private List<InjectionListener<? super T>> postInjectListeners; // lazy | |
private List<MethodAspect> aspects; // lazy | |
private boolean valid = true; | |
@@ -62,10 +64,20 @@ | |
} | |
public ImmutableList<InjectionListener<? super T>> getInjectionListeners() { | |
- return injectionListeners == null | |
+ if (injectionListeners != null) { | |
+ if (postInjectListeners == null) { | |
+ return ImmutableList.copyOf(injectionListeners); | |
+ } | |
+ else { | |
+ return ImmutableList.copyOf(Iterables.concat(injectionListeners, postInjectListeners)); | |
+ } | |
+ } | |
+ else { | |
+ return postInjectListeners == null | |
- ? ImmutableList.<InjectionListener<? super T>>of() | |
+ ? ImmutableList.<InjectionListener<? super T>>of() | |
- : ImmutableList.copyOf(injectionListeners); | |
+ : ImmutableList.copyOf(postInjectListeners); | |
- } | |
+ } | |
+ } | |
@SuppressWarnings("unchecked") // an InjectionListener<? super T> is an InjectionListener<T> | |
public void register(InjectionListener<? super T> injectionListener) { | |
@@ -78,6 +90,17 @@ | |
injectionListeners.add(injectionListener); | |
} | |
+ @SuppressWarnings("unchecked") // an InjectionListener<? super T> is an InjectionListener<T> | |
+ public void registerPostInjectListener(InjectionListener<? super T> injectionListener) { | |
+ checkState(valid, "Encounters may not be used after hear() returns."); | |
+ | |
+ if (postInjectListeners == null) { | |
+ postInjectListeners = Lists.newArrayList(); | |
+ } | |
+ | |
+ postInjectListeners.add(injectionListener); | |
+ } | |
+ | |
public void bindInterceptor(Matcher<? super Method> methodMatcher, | |
MethodInterceptor... interceptors) { | |
checkState(valid, "Encounters may not be used after hear() returns."); | |
Index: src/com/google/inject/ConstructorInjector.java | |
=================================================================== | |
--- src/com/google/inject/ConstructorInjector.java (revision 925) | |
+++ src/com/google/inject/ConstructorInjector.java Mon Apr 06 10:42:33 BST 2009 | |
@@ -23,6 +23,7 @@ | |
import com.google.inject.internal.InternalContext; | |
import com.google.inject.spi.InjectableType; | |
import com.google.inject.spi.InjectionListener; | |
+import com.google.inject.spi.CachedValue; | |
import java.lang.reflect.InvocationTargetException; | |
/** | |
@@ -31,7 +32,7 @@ | |
* | |
* @author crazybob@google.com (Bob Lee) | |
*/ | |
-class ConstructorInjector<T> { | |
+class ConstructorInjector<T> implements CachedValue<T> { | |
private final ImmutableList<SingleParameterInjector<?>> parameterInjectors; | |
private final ConstructionProxy<T> constructionProxy; | |
@@ -116,4 +117,9 @@ | |
public InjectableType<T> getInjectableType() { | |
return injectableType; | |
} | |
+ | |
+ public T getCachedValue() { | |
+ // TODO | |
+ return null; //constructionProxy.getCachedValue(); | |
-} | |
+ } | |
+} | |
Index: test/com/google/inject/InjectableTypeListenerTest.java | |
=================================================================== | |
--- test/com/google/inject/InjectableTypeListenerTest.java (revision 925) | |
+++ test/com/google/inject/InjectableTypeListenerTest.java Thu Apr 02 12:10:10 BST 2009 | |
@@ -509,4 +509,75 @@ | |
return "beep"; | |
} | |
} | |
+ | |
+ public void testPostInjectListenersAreProcessedInOrder() throws Exception { | |
+ final AtomicInteger counter = new AtomicInteger(0); | |
+ final List<Exception> failures = Lists.newArrayList(); | |
+ | |
+ Injector injector = Guice.createInjector(new AbstractModule() { | |
+ protected void configure() { | |
+ | |
+ // this test is a little naughty as its also testing that the later you register a listener | |
+ // the later you will be invoked when the javadoc explicitly gives no ordering | |
+ // guarrentees - but I couldn't think of an easier way to test | |
+ // that the post inject listeners are definitely invoked after the other listeners | |
+ | |
+ bindListener(any(), new InjectableType.Listener() { | |
+ public <I> void hear(InjectableType<I> injectableType, Encounter<I> encounter) { | |
+ encounter.registerPostInjectListener(new OrderedListener(counter, failures, 3)); | |
-} | |
+ } | |
+ }); | |
+ | |
+ bindListener(any(), new InjectableType.Listener() { | |
+ public <I> void hear(InjectableType<I> injectableType, Encounter<I> encounter) { | |
+ encounter.register(new OrderedListener(counter, failures, 1)); | |
+ } | |
+ }); | |
+ | |
+ bindListener(any(), new InjectableType.Listener() { | |
+ public <I> void hear(InjectableType<I> injectableType, Encounter<I> encounter) { | |
+ encounter.registerPostInjectListener(new OrderedListener(counter, failures, 4)); | |
+ } | |
+ }); | |
+ | |
+ bindListener(any(), new InjectableType.Listener() { | |
+ public <I> void hear(InjectableType<I> injectableType, Encounter<I> encounter) { | |
+ encounter.register(new OrderedListener(counter, failures, 2)); | |
+ } | |
+ }); | |
+ | |
+ bind(C.class); | |
+ } | |
+ }); | |
+ | |
+ injector.getInstance(C.class); | |
+ | |
+ if (!failures.isEmpty()) { | |
+ throw failures.get(0); | |
+ } | |
+ assertEquals("Listener counter", 4, counter.get()); | |
+ } | |
+ | |
+ static class OrderedListener<I> implements InjectionListener<I> { | |
+ final List<Exception> failures; | |
+ final AtomicInteger counter; | |
+ final int expectedValue; | |
+ | |
+ OrderedListener(AtomicInteger counter, List<Exception> failures, int expectedValue) { | |
+ this.counter = counter; | |
+ this.failures = failures; | |
+ this.expectedValue = expectedValue; | |
+ } | |
+ | |
+ public void afterInjection(I injectee) { | |
+ int actualValue = counter.incrementAndGet(); | |
+ try { | |
+ assertEquals("Listener invocation counter", expectedValue, actualValue); | |
+ } | |
+ catch (Exception e) { | |
+ failures.add(e); | |
+ } | |
+ } | |
+ } | |
+ | |
+} | |
Index: test/com/google/inject/ScopesTest.java | |
=================================================================== | |
--- test/com/google/inject/ScopesTest.java (revision 925) | |
+++ test/com/google/inject/ScopesTest.java Mon Apr 06 10:57:41 BST 2009 | |
@@ -18,6 +18,7 @@ | |
import static com.google.inject.Asserts.assertContains; | |
import com.google.inject.internal.Maps; | |
+import com.google.inject.spi.CachingProvider; | |
import java.io.IOException; | |
import java.lang.annotation.ElementType; | |
import java.lang.annotation.Retention; | |
@@ -28,6 +29,7 @@ | |
import java.util.Iterator; | |
import java.util.List; | |
import java.util.Map; | |
+import java.util.Set; | |
import junit.framework.TestCase; | |
/** | |
@@ -137,6 +139,44 @@ | |
} | |
} | |
+ | |
+ public void testIterateThroughSingletonValues() { | |
+ Injector injector = Guice.createInjector(singletonsModule); | |
+ | |
+ Binding<BoundAsSingleton> binding1 = injector.getBinding(BoundAsSingleton.class); | |
+ Provider<BoundAsSingleton> provider1 = binding1.getProvider(); | |
+ provider1.get(); | |
+ | |
+ boolean foundBoundAsSingleton = false; | |
+ boolean foundEagerSingleton = false; | |
+ | |
+ | |
+ // now lets iterate through all the objects | |
+ Set<Map.Entry<Key<?>,Binding<?>>> entries = injector.getBindings().entrySet(); | |
+ for (Map.Entry<Key<?>, Binding<?>> entry : entries) { | |
+ Key<?> key = entry.getKey(); | |
+ Binding<?> binding = entry.getValue(); | |
+ Provider<?> provider = binding.getProvider(); | |
+ if (provider instanceof CachingProvider<?>) { | |
+ CachingProvider<?> cachingProvider = (CachingProvider<?>) provider; | |
+ Object value = cachingProvider.getCachedValue(); | |
+ if (value != null) { | |
+ if (value instanceof BoundAsSingleton) { | |
+ foundBoundAsSingleton = true; | |
+ } | |
+ else if (value instanceof EagerSingleton) { | |
+ foundEagerSingleton = true; | |
+ } | |
+ //System.out.println("Key: " + key + " value " + value); | |
+ } | |
+ } | |
+ } | |
+ | |
+ assertTrue("Should have found bound singleton instance", foundBoundAsSingleton); | |
+ assertTrue("Should have found eager singleton instance", foundEagerSingleton); | |
+ } | |
+ | |
+ | |
@Singleton | |
interface A {} | |
static class AImpl implements A {} | |
Index: src/com/google/inject/spi/CachingProvider.java | |
=================================================================== | |
--- src/com/google/inject/spi/CachingProvider.java Mon Apr 06 10:36:06 BST 2009 | |
+++ src/com/google/inject/spi/CachingProvider.java Mon Apr 06 10:36:06 BST 2009 | |
@@ -0,0 +1,31 @@ | |
+/** | |
+ * | |
+ * Licensed to the Apache Software Foundation (ASF) under one or more | |
+ * contributor license agreements. See the NOTICE file distributed with | |
+ * this work for additional information regarding copyright ownership. | |
+ * The ASF licenses this file to You 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.spi; | |
+ | |
+import com.google.inject.Provider; | |
+ | |
+/** | |
+ * A Scope may choose to implement this method so that it can expose the current value in the scope. | |
+ * This interface combines the {@link Provider} and {@link CachedValue} together so that it is | |
+ * easier to create an inner class inside a Scope implementation. | |
+ * | |
+ * @version $Revision: 1.1 $ | |
+ */ | |
+public interface CachingProvider<T> extends Provider<T>, CachedValue<T> { | |
+} | |
Index: src/com/google/inject/Scopes.java | |
=================================================================== | |
--- src/com/google/inject/Scopes.java (revision 925) | |
+++ src/com/google/inject/Scopes.java Mon Apr 06 10:28:02 BST 2009 | |
@@ -19,6 +19,7 @@ | |
import com.google.inject.internal.Errors; | |
import com.google.inject.internal.InternalFactory; | |
import com.google.inject.internal.Scoping; | |
+import com.google.inject.spi.CachingProvider; | |
import java.lang.annotation.Annotation; | |
/** | |
@@ -35,7 +36,7 @@ | |
*/ | |
public static final Scope SINGLETON = new Scope() { | |
public <T> Provider<T> scope(Key<T> key, final Provider<T> creator) { | |
- return new Provider<T>() { | |
+ return new CachingProvider<T>() { | |
private volatile T instance; | |
@@ -58,6 +59,10 @@ | |
return instance; | |
} | |
+ public T getCachedValue() { | |
+ return instance; | |
+ } | |
+ | |
public String toString() { | |
return String.format("%s[%s]", creator, SINGLETON); | |
} | |
Index: servlet/src/com/google/inject/servlet/ServletScopes.java | |
=================================================================== | |
--- servlet/src/com/google/inject/servlet/ServletScopes.java (revision 925) | |
+++ servlet/src/com/google/inject/servlet/ServletScopes.java Mon Apr 06 10:28:02 BST 2009 | |
@@ -22,6 +22,7 @@ | |
import com.google.inject.Scope; | |
import com.google.inject.Scopes; | |
import com.google.inject.Singleton; | |
+import com.google.inject.spi.CachingProvider; | |
import com.google.inject.spi.DefaultBindingScopingVisitor; | |
import java.lang.annotation.Annotation; | |
import java.util.concurrent.atomic.AtomicBoolean; | |
@@ -43,7 +44,7 @@ | |
public static final Scope REQUEST = new Scope() { | |
public <T> Provider<T> scope(Key<T> key, final Provider<T> creator) { | |
final String name = key.toString(); | |
- return new Provider<T>() { | |
+ return new CachingProvider<T>() { | |
public T get() { | |
HttpServletRequest request = GuiceFilter.getRequest(); | |
synchronized (request) { | |
@@ -60,6 +61,15 @@ | |
public String toString() { | |
return String.format("%s[%s]", creator, REQUEST); | |
} | |
+ | |
+ public T getCachedValue() { | |
+ HttpServletRequest request = GuiceFilter.getRequest(); | |
+ synchronized (request) { | |
+ @SuppressWarnings("unchecked") | |
+ T t = (T) request.getAttribute(name); | |
+ return t; | |
+ } | |
+ } | |
}; | |
} | |
@@ -74,7 +84,7 @@ | |
public static final Scope SESSION = new Scope() { | |
public <T> Provider<T> scope(Key<T> key, final Provider<T> creator) { | |
final String name = key.toString(); | |
- return new Provider<T>() { | |
+ return new CachingProvider<T>() { | |
public T get() { | |
HttpSession session = GuiceFilter.getRequest().getSession(); | |
synchronized (session) { | |
@@ -87,9 +97,19 @@ | |
return t; | |
} | |
} | |
+ | |
public String toString() { | |
return String.format("%s[%s]", creator, SESSION); | |
} | |
+ | |
+ public T getCachedValue() { | |
+ HttpSession session = GuiceFilter.getRequest().getSession(); | |
+ synchronized (session) { | |
+ @SuppressWarnings("unchecked") | |
+ T t = (T) session.getAttribute(name); | |
+ return t; | |
+ } | |
+ } | |
}; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment