Skip to content

Instantly share code, notes, and snippets.

@gissuebot
Created July 7, 2014 18:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gissuebot/d263b77fa21f76485bcd to your computer and use it in GitHub Desktop.
Save gissuebot/d263b77fa21f76485bcd to your computer and use it in GitHub Desktop.
Migrated attachment for Guice issue 354, comment 1
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