Created
July 7, 2014 17:59
-
-
Save gissuebot/2fc80c4e9fd07c29f9c7 to your computer and use it in GitHub Desktop.
Migrated attachment for Guice issue 131, comment 4
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: extensions/assistedinject/src/com/google/inject/assistedinject/FactoryModule.java | |
=================================================================== | |
--- extensions/assistedinject/src/com/google/inject/assistedinject/FactoryModule.java (revision 0) | |
+++ extensions/assistedinject/src/com/google/inject/assistedinject/FactoryModule.java (revision 0) | |
@@ -0,0 +1,335 @@ | |
+package com.google.inject.assistedinject; | |
+ | |
+import com.google.common.collect.ImmutableSet; | |
+import com.google.inject.AbstractModule; | |
+import com.google.inject.Binder; | |
+import com.google.inject.BindingAnnotation; | |
+import com.google.inject.CreationException; | |
+import com.google.inject.Guice; | |
+import com.google.inject.Inject; | |
+import com.google.inject.Injector; | |
+import com.google.inject.Key; | |
+import com.google.inject.Provider; | |
+import com.google.inject.Stage; | |
+import com.google.inject.internal.Errors; | |
+import com.google.inject.spi.Element; | |
+import com.google.inject.spi.Elements; | |
+import com.google.inject.spi.ModuleWriter; | |
+ | |
+import java.lang.annotation.Annotation; | |
+import java.lang.reflect.InvocationHandler; | |
+import java.lang.reflect.Method; | |
+import java.lang.reflect.Proxy; | |
+import java.lang.reflect.Type; | |
+import java.util.ArrayList; | |
+import java.util.HashMap; | |
+import java.util.List; | |
+import java.util.Map; | |
+import java.util.Set; | |
+import java.util.logging.Logger; | |
+ | |
+/** | |
+ * Provides a mechanism to combine user-specified parameters with | |
+ * {@link Injector}-specified parameters when creating new objects. | |
+ * | |
+ * <p>To use a {@link FactoryModule}: | |
+ * | |
+ * <p>Annotate your implementation class' constructor with | |
+ * {@literal @}{@link Inject} and binding annotations on parameters as necessary: | |
+ * <pre><code>public class RealPayment implements Payment { | |
+ * {@literal @}Inject | |
+ * public RealPayment(CreditService creditService, AuthService authService, | |
+ * {@literal @}Named("startDate") Date startDate, | |
+ * {@literal @}Named("amount") Money amount) { | |
+ * ... | |
+ * } | |
+ * }</code></pre> | |
+ * | |
+ * <p>Write an interface with a method that accepts the user-specified | |
+ * parameters with the same binding annotations as in the implementation | |
+ * class' constructor: | |
+ * <pre><code>public interface PaymentFactory { | |
+ * Payment create(Date startDate, Money amount); | |
+ * }</code></pre> | |
+ * | |
+ * <p>You can name your create methods whatever you like, such as <i>create</i>, | |
+ * or <i>createPayment</i> or <i>newPayment</i>. The concrete class must | |
+ * be assignable to the return type of your create method. You can also provide | |
+ * multiple factory methods within the same factory interface. | |
+ * | |
+ * <p>In your Guice {@link com.google.inject.Module module}, bind your factory | |
+ * interface to an instance of {@link FactoryModule} and tell the | |
+ * {@link FactoryModule} to use your implementation class: | |
+ * <pre><code>install(FactoryModule.forInterface(PaymentFactory.class) { | |
+ * @Override protected void configureFactoryBindings() { | |
+ * bind(Payment.class).to(RealPayment.class); | |
+ * });</code></pre> | |
+ * | |
+ * <p>If there are other classes your factory should know about, you can | |
+ * chain together several {@code binding()} calls. | |
+ * | |
+ * <p>Now you can {@literal @}{@code Inject} your factory interface into your | |
+ * Guice-injected classes. When you invoke the create method on that factory, | |
+ * the {@link FactoryModule} will instantiate the implementation | |
+ * class using parameters from the injector and the factory method. | |
+ * | |
+ * <pre><code>public class PaymentAction { | |
+ * {@literal @}Inject private PaymentFactory paymentFactory; | |
+ * | |
+ * public void doPayment(Money amount) { | |
+ * Payment payment = paymentFactory.create(new Date(), amount); | |
+ * payment.apply(); | |
+ * } | |
+ * }</code></pre> | |
+ * | |
+ * @param <F> The factory interface | |
+ * | |
+ * @author dtm@google.com (Daniel Martin) | |
+ */ | |
+public class FactoryModule extends AbstractModule { | |
+ private final Class<?> factoryClass; | |
+ private boolean inChildMode = false; | |
+ | |
+ public FactoryModule(Class<?> factoryClass) { | |
+ this.factoryClass = factoryClass; | |
+ } | |
+ | |
+ private static class FactoryProvider<F> implements Provider<F> { | |
+ private final Class<F> factoryClass; | |
+ private final FactoryModule srcModule; | |
+ private F factory; | |
+ | |
+ FactoryProvider(Class<F> factoryClass, FactoryModule srcModule) { | |
+ this.factoryClass = factoryClass; | |
+ this.srcModule = srcModule; | |
+ } | |
+ | |
+ @SuppressWarnings("unused") | |
+ @Inject private void initFactoryProvider(Injector injector) { | |
+ List<Element> childConfig = Elements.getElements(new AbstractModule() { | |
+ @Override protected void configure() { | |
+ srcModule.doChildConfiguration(binder()); | |
+ } | |
+ }); | |
+ | |
+ final Map<Method, FactoryMethodImplementation> methodImplementations = | |
+ new HashMap<Method, FactoryMethodImplementation>(); | |
+ for (Method method : getFactoryInterfaceMethods()) { | |
+ FactoryMethodImplementation methodImplementation = | |
+ new FactoryMethodImplementation(method, childConfig, injector); | |
+ methodImplementations.put(method, methodImplementation); | |
+ } | |
+ | |
+ InvocationHandler proxyImplementation = new InvocationHandler() { | |
+ public Object invoke(Object proxy, Method method, Object[] args) | |
+ throws Throwable { | |
+ FactoryMethodImplementation methodImplementation = | |
+ methodImplementations.get(method); | |
+ if (methodImplementation == null) { | |
+ if (method.getDeclaringClass().equals(Object.class)) { | |
+ if (method.getName().equals("equals")) { | |
+ return proxy == args[0]; | |
+ } | |
+ return method.invoke(this, args); | |
+ } | |
+ throw new IllegalStateException( | |
+ "Unexpected method called: " + method); | |
+ } | |
+ return methodImplementation.invoke(args); | |
+ } | |
+ | |
+ @Override | |
+ public String toString() { | |
+ return "Proxy for " + factoryClass; | |
+ } | |
+ }; | |
+ | |
+ factory = factoryClass.cast(Proxy.newProxyInstance( | |
+ factoryClass.getClassLoader(), | |
+ new Class[] {factoryClass}, proxyImplementation)); | |
+ } | |
+ | |
+ public F get() { | |
+ return factory; | |
+ } | |
+ | |
+ /** | |
+ * Return all the methods we need our proxy to implement, including those | |
+ * defined on super interfaces. Do not return those defined on | |
+ * {@link Object}. | |
+ */ | |
+ private List<Method> getFactoryInterfaceMethods() { | |
+ List<Method> retval = new ArrayList<Method>(); | |
+ for (Method method : factoryClass.getMethods()) { | |
+ if (method.getDeclaringClass().isInterface()) { | |
+ retval.add(method); | |
+ } | |
+ } | |
+ return retval; | |
+ } | |
+ | |
+ static <F> void bind(Binder binder, Class<F> clazz, FactoryModule mod) { | |
+ binder.bind(clazz).toProvider(new FactoryProvider<F>(clazz, mod)); | |
+ } | |
+ } | |
+ | |
+ | |
+ @Override | |
+ protected final void configure() { | |
+ if (inChildMode) { | |
+ configureFactoryBindings(); | |
+ } else { | |
+ FactoryProvider.bind(binder(), factoryClass, this); | |
+ } | |
+ } | |
+ | |
+ private void doChildConfiguration(Binder binder) { | |
+ try { | |
+ inChildMode = true; | |
+ configure(binder); | |
+ } finally { | |
+ inChildMode = false; | |
+ } | |
+ } | |
+ | |
+ /** | |
+ * Configure the bindings that should be present in the factory that are not | |
+ * supplied by user parameters to factory methods. Subclasses will want to | |
+ * override this in all but the simplest cases. | |
+ */ | |
+ protected void configureFactoryBindings() { | |
+ } | |
+ | |
+ /** | |
+ * Find the {@link Key}s that a particular method's parameters take. | |
+ * | |
+ * @param method The method to inspect. | |
+ * @return A {@link List} of {@code Key}s in the same order as the method's | |
+ * parameters. | |
+ */ | |
+ private static List<Key<?>> getParameterKeys(Method method) { | |
+ List<Key<?>> retval = new ArrayList<Key<?>>(); | |
+ Annotation[][] parameterAnnotations = method.getParameterAnnotations(); | |
+ Type[] parameterTypes = method.getGenericParameterTypes(); | |
+ for(int i=0; i < parameterTypes.length; i++) { | |
+ retval.add(getKey(parameterTypes[i], parameterAnnotations[i], | |
+ "Parameter " + (i + 1) + " of method " + method)); | |
+ } | |
+ return retval; | |
+ } | |
+ | |
+ /** | |
+ * Find the {@link Key} that describes a particular method's return type. | |
+ * | |
+ * @param method The method to inspect. | |
+ * @return A {@link Key} that has the same type and binding annotations | |
+ * as {@code method}'s return type. | |
+ */ | |
+ private static Key<?> getReturnKey(Method method) { | |
+ return getKey(method.getGenericReturnType(), method.getAnnotations(), | |
+ "Return type of method " + method); | |
+ } | |
+ | |
+ /** | |
+ * Find a key for a given type and annotations. | |
+ * | |
+ * @param type The {@link Type} of the key to construct | |
+ * @param annotations An array of {@link Annotation}s to examine. It is an | |
+ * error if more than one of these is marked | |
+ * {@literal @}{@link BindingAnnotation} | |
+ * @param where Context to use for error messages | |
+ * @throws IllegalArgumentException If more than one of {@code annotations} | |
+ * is marked {@literal @}{@link BindingAnnotation} | |
+ */ | |
+ private static Key<?> getKey(Type type, Annotation[] annotations, | |
+ String where) { | |
+ Annotation bindingAnnotation = null; | |
+ for (Annotation parameterAnnotation : annotations) { | |
+ if (parameterAnnotation.annotationType() | |
+ .getAnnotation(BindingAnnotation.class) != null) { | |
+ if (bindingAnnotation != null) { | |
+ throw new IllegalArgumentException( | |
+ where + ": multiple binding annotations found"); | |
+ } | |
+ bindingAnnotation = parameterAnnotation; | |
+ } | |
+ } | |
+ if (bindingAnnotation != null) { | |
+ return Key.get(type, bindingAnnotation); | |
+ } else { | |
+ return Key.get(type); | |
+ } | |
+ } | |
+ | |
+ private static class FactoryMethodImplementation { | |
+ static final Set<Key<?>> GUICE_TYPES = | |
+ ImmutableSet.of(Key.get(Injector.class), Key.get(Logger.class), | |
+ Key.get(Stage.class)); | |
+ final List<Key<?>> parameterKeys; | |
+ final Injector parentInjector; | |
+ final Key<?> returnKey; | |
+ final List<Element> extraChildConfig; | |
+ | |
+ FactoryMethodImplementation( | |
+ Method method, List<Element> extraChildConfig, Injector parentInjector) { | |
+ parameterKeys = getParameterKeys(method); | |
+ returnKey = FactoryModule.getReturnKey(method); | |
+ this.parentInjector = parentInjector; | |
+ this.extraChildConfig = extraChildConfig; | |
+ try { | |
+ testBindings(); | |
+ } catch (CreationException ce) { | |
+ throw new RuntimeException( | |
+ Errors.format("Errors found testing bindings for " + method, | |
+ ce.getErrorMessages())); | |
+ } | |
+ } | |
+ | |
+ @SuppressWarnings("unchecked") | |
+ Object invoke(final Object[] args) { | |
+ Injector childInjector = | |
+ parentInjector.createChildInjector(new AbstractModule() { | |
+ @Override | |
+ protected void configure() { | |
+ for (int i = 0; i < args.length; i++) { | |
+ bind((Key)parameterKeys.get(i)).toInstance(args[i]); | |
+ } | |
+ new ModuleWriter().apply(binder(), extraChildConfig); | |
+ } | |
+ }); | |
+ return childInjector.getInstance(returnKey); | |
+ } | |
+ | |
+ private void testBindings() { | |
+ Guice.createInjector(Stage.TOOL, new AbstractModule() { | |
+ @Override | |
+ protected void configure() { | |
+ for (Key<?> key : parameterKeys) { | |
+ bindToThrowingProvider(key); | |
+ } | |
+ // TODO: iterate through parent's parent, etc. | |
+ for (Key<?> key : parentInjector.getBindings().keySet()) { | |
+ if (!GUICE_TYPES.contains(key)) { | |
+ bindToThrowingProvider(key); | |
+ } | |
+ } | |
+ new ModuleWriter().apply(binder(), extraChildConfig); | |
+ requireBinding(returnKey); | |
+ } | |
+ | |
+ private <T> void bindToThrowingProvider(Key<T> key) { | |
+ bind(key).toProvider(new Provider<T>() { | |
+ public T get() { | |
+ throw new RuntimeException("This is a test injector"); | |
+ } | |
+ }); | |
+ } | |
+ }); | |
+ | |
+ } | |
+ | |
+ Key<?> getReturnKey() { | |
+ return returnKey; | |
+ } | |
+ } | |
+} | |
Index: extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider.java | |
=================================================================== | |
--- extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider.java (revision 647) | |
+++ extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider.java (working copy) | |
@@ -64,8 +64,8 @@ | |
* FactoryProvider.newFactory(PaymentFactory.class, RealPayment.class));</code></pre> | |
* | |
* <p>Now you can {@literal @}{@code Inject} your factory interface into your | |
- * Guice-injected classes. When you invoke the create method on that factory, the | |
- * {@link FactoryProvider} will instantiate the implementation class using | |
+ * Guice-injected classes. When you invoke the create method on that factory, | |
+ * the {@link FactoryProvider} will instantiate the implementation class using | |
* parameters from the injector and the factory method. | |
* | |
* <pre><code>public class PaymentAction { | |
@@ -78,25 +78,24 @@ | |
* }</code></pre> | |
* | |
* @param <F> The factory interface | |
- * @param <R> The concrete class to be created. | |
* | |
* @author jmourits@google.com (Jerome Mourits) | |
* @author jessewilson@google.com (Jesse Wilson) | |
*/ | |
-public class FactoryProvider<F, R> implements Provider<F> { | |
+public class FactoryProvider<F> implements Provider<F> { | |
private Injector injector; | |
private final Class<F> factoryType; | |
- private final Class<R> implementationType; | |
+ private final Class<?> implementationType; | |
private final Map<Method, AssistedConstructor<?>> factoryMethodToConstructor; | |
- public static <X,Y> FactoryProvider<X,Y> newFactory( | |
- Class<X> factoryType, Class<Y> implementationType){ | |
- return new FactoryProvider<X, Y>(factoryType,implementationType); | |
+ public static <X> FactoryProvider<X> newFactory( | |
+ Class<X> factoryType, Class<?> implementationType){ | |
+ return new FactoryProvider<X>(factoryType,implementationType); | |
} | |
- private FactoryProvider(Class<F> factoryType, Class<R> implementationType) { | |
+ private FactoryProvider(Class<F> factoryType, Class<?> implementationType) { | |
this.factoryType = factoryType; | |
this.implementationType = implementationType; | |
this.factoryMethodToConstructor = createMethodMapping(); | |
@@ -104,7 +103,7 @@ | |
} | |
@Inject | |
- @SuppressWarnings({"unchecked", "unused"}) | |
+ @SuppressWarnings({"unused"}) | |
private void setInjectorAndCheckUnboundParametersAreInjectable( | |
Injector injector) { | |
this.injector = injector; | |
@@ -145,7 +144,6 @@ | |
return false; | |
} | |
- @SuppressWarnings("unchecked") | |
private boolean paramCanBeInjected(Parameter parameter, Injector injector) { | |
return parameter.isBound(injector); | |
} | |
@@ -204,7 +202,6 @@ | |
return result; | |
} | |
- @SuppressWarnings({"unchecked"}) | |
public F get() { | |
InvocationHandler invocationHandler = new InvocationHandler() { | |
@@ -242,7 +239,7 @@ | |
} | |
}; | |
- return (F) Proxy.newProxyInstance(factoryType.getClassLoader(), | |
- new Class[] {factoryType}, invocationHandler); | |
+ return factoryType.cast(Proxy.newProxyInstance(factoryType.getClassLoader(), | |
+ new Class[] {factoryType}, invocationHandler)); | |
} | |
} | |
Index: extensions/assistedinject/test/com/google/inject/assistedinject/FactoryModuleTest.java | |
=================================================================== | |
--- extensions/assistedinject/test/com/google/inject/assistedinject/FactoryModuleTest.java (revision 0) | |
+++ extensions/assistedinject/test/com/google/inject/assistedinject/FactoryModuleTest.java (revision 0) | |
@@ -0,0 +1,252 @@ | |
+package com.google.inject.assistedinject; | |
+ | |
+import com.google.inject.AbstractModule; | |
+import com.google.inject.CreationException; | |
+import com.google.inject.Guice; | |
+import com.google.inject.Inject; | |
+import com.google.inject.Provider; | |
+import com.google.inject.matcher.Matchers; | |
+import com.google.inject.name.Named; | |
+import com.google.inject.name.Names; | |
+ | |
+import junit.framework.TestCase; | |
+ | |
+import org.aopalliance.intercept.MethodInterceptor; | |
+import org.aopalliance.intercept.MethodInvocation; | |
+ | |
+import java.lang.annotation.Retention; | |
+import java.lang.annotation.RetentionPolicy; | |
+import java.util.concurrent.atomic.AtomicReference; | |
+ | |
+public class FactoryModuleTest extends TestCase { | |
+ @Retention(RetentionPolicy.RUNTIME) | |
+ public static @interface Traced {} | |
+ public static interface StringService { | |
+ String getString(); | |
+ } | |
+ | |
+ public static interface StringServiceFactory { | |
+ StringService create( | |
+ @Named("user") String user, @Named("server") String server); | |
+ } | |
+ | |
+ public static class RemoteStringService implements StringService { | |
+ private final String user; | |
+ private final String server; | |
+ | |
+ @Inject public RemoteStringService( | |
+ @Named("user") String user, @Named("server") String server) { | |
+ this.user = user; | |
+ this.server = server; | |
+ } | |
+ | |
+ public String getString() { | |
+ return "Service for " + user + "@" + server; | |
+ } | |
+ } | |
+ | |
+ public void testBasic() { | |
+ StringServiceFactory factory = Guice.createInjector( | |
+ new FactoryModule(StringServiceFactory.class) { | |
+ @Override | |
+ protected void configureFactoryBindings() { | |
+ bind(StringService.class).to(RemoteStringService.class); | |
+ } | |
+ }).getInstance(StringServiceFactory.class); | |
+ assertEquals("Factory-constructed remote service string", | |
+ "Service for bob@veblen", factory.create("bob", "veblen").getString()); | |
+ } | |
+ | |
+ public static class LocalStringService implements StringService { | |
+ private final String user; | |
+ | |
+ @Inject public LocalStringService(@Named("user") String user) { | |
+ this.user = user; | |
+ } | |
+ | |
+ @Traced | |
+ public String getString() { | |
+ return "Local service for " + user; | |
+ } | |
+ } | |
+ | |
+ public void testOverparametrized() { | |
+ StringServiceFactory factory = Guice.createInjector( | |
+ new FactoryModule(StringServiceFactory.class) { | |
+ @Override | |
+ protected void configureFactoryBindings() { | |
+ bind(StringService.class).to(LocalStringService.class); | |
+ } | |
+ }).getInstance(StringServiceFactory.class); | |
+ assertEquals("Factory-constructed local service string", | |
+ "Local service for bob", factory.create("bob", "veblen").getString()); | |
+ } | |
+ | |
+ static class DelegatingStringService implements StringService { | |
+ private final StringService backend; | |
+ | |
+ @Inject public DelegatingStringService( | |
+ @Named("backend") StringService backend) { | |
+ this.backend = backend; | |
+ } | |
+ | |
+ public String getString() { | |
+ return "Delegate to " + backend.getString(); | |
+ } | |
+ } | |
+ | |
+ public void testIntermediateBinding() { | |
+ StringServiceFactory factory = Guice.createInjector( | |
+ new FactoryModule(StringServiceFactory.class) { | |
+ @Override | |
+ protected void configureFactoryBindings() { | |
+ bind(StringService.class).to(DelegatingStringService.class); | |
+ bind(StringService.class).annotatedWith(Names.named("backend")) | |
+ .to(RemoteStringService.class); | |
+ } | |
+ }).getInstance(StringServiceFactory.class); | |
+ | |
+ assertEquals("Delegated service response", | |
+ "Delegate to Service for bob@veblen", | |
+ factory.create("bob", "veblen").getString()); | |
+ } | |
+ | |
+ interface DualStringServiceFactory extends StringServiceFactory { | |
+ @Named("secondary") StringService createSecondary( | |
+ @Named("user") String user, @Named("server") String server); | |
+ } | |
+ | |
+ public void testInterfaceInheritance() { | |
+ DualStringServiceFactory factory = Guice.createInjector( | |
+ new FactoryModule(DualStringServiceFactory.class) { | |
+ @Override | |
+ protected void configureFactoryBindings() { | |
+ bind(StringService.class).to(RemoteStringService.class); | |
+ bind(StringService.class).annotatedWith(Names.named("secondary")) | |
+ .to(LocalStringService.class); | |
+ } | |
+ }).getInstance(DualStringServiceFactory.class); | |
+ assertEquals("Factory-constructed remote service string", | |
+ "Service for bob@veblen", | |
+ factory.create("bob", "veblen").getString()); | |
+ assertEquals("Factory-constructed local service string", | |
+ "Local service for bob", | |
+ factory.createSecondary("bob", "veblen").getString()); | |
+ } | |
+ | |
+ interface LocalStringServiceFactory { | |
+ LocalStringService create(@Named("user") String user); | |
+ } | |
+ | |
+ public void testNonDelegating() { | |
+ LocalStringServiceFactory factory = Guice.createInjector( | |
+ new FactoryModule(LocalStringServiceFactory.class) | |
+ ).getInstance(LocalStringServiceFactory.class); | |
+ assertEquals("Factory-constructed local service string", | |
+ "Local service for bob", factory.create("bob").getString()); | |
+ } | |
+ | |
+ static class DelayedLocalStringService implements StringService { | |
+ private final Provider<String> userProvider; | |
+ | |
+ @Inject public DelayedLocalStringService( | |
+ @Named("user") Provider<String> userProvider) { | |
+ this.userProvider = userProvider; | |
+ } | |
+ | |
+ public String getString() { | |
+ return "Local service for " + userProvider.get(); | |
+ } | |
+ } | |
+ | |
+ public void testDelayed() throws Throwable { | |
+ StringServiceFactory factory = Guice.createInjector( | |
+ new FactoryModule(StringServiceFactory.class) { | |
+ @Override | |
+ protected void configureFactoryBindings() { | |
+ bind(StringService.class).to(DelayedLocalStringService.class); | |
+ } | |
+ }).getInstance(StringServiceFactory.class); | |
+ final StringService bobService = factory.create("bob", ""); | |
+ assertEquals("Factory-constructed local service string", | |
+ "Local service for bob", bobService.getString()); | |
+ final StringService joeService = factory.create("joe", ""); | |
+ assertEquals("Factory-constructed local service string", | |
+ "Local service for joe", joeService.getString()); | |
+ final AtomicReference<Throwable> thrown = new AtomicReference<Throwable>(); | |
+ Thread bobThread = new Thread() { | |
+ @Override | |
+ public void run() { | |
+ try { | |
+ assertEquals("Factory-constructed local service string", | |
+ "Local service for bob", bobService.getString()); | |
+ } catch (Throwable t) { | |
+ thrown.set(t); | |
+ } | |
+ } | |
+ }; | |
+ bobThread.run(); | |
+ bobThread.join(); | |
+ if (thrown.get() != null) { | |
+ throw thrown.get(); | |
+ } | |
+ Thread joeThread = new Thread() { | |
+ @Override | |
+ public void run() { | |
+ try { | |
+ assertEquals("Factory-constructed local service string", | |
+ "Local service for joe", joeService.getString()); | |
+ } catch (Throwable t) { | |
+ thrown.set(t); | |
+ } | |
+ } | |
+ }; | |
+ joeThread.run(); | |
+ joeThread.join(); | |
+ if (thrown.get() != null) { | |
+ throw thrown.get(); | |
+ } | |
+ } | |
+ | |
+ public void testUnderSpecifiedBindings() { | |
+ try { | |
+ Guice.createInjector(new FactoryModule(StringServiceFactory.class)); | |
+ fail("We shouldn't be able to create this injector"); | |
+ } catch (CreationException ce) { | |
+ assertTrue("The exception message should have contained " + | |
+ "'StringServiceFactory.create'; it was: " + ce.getMessage(), | |
+ ce.getMessage().contains("StringServiceFactory.create")); | |
+ } | |
+ } | |
+ | |
+ public void disabled_testPermgenSpace() { | |
+ LocalStringServiceFactory factory = Guice.createInjector( | |
+ new FactoryModule(LocalStringServiceFactory.class), | |
+ new AbstractModule() { | |
+ @Override | |
+ protected void configure() { | |
+ bindInterceptor(Matchers.subclassesOf(StringService.class), | |
+ Matchers.annotatedWith(Traced.class), | |
+ new MethodInterceptor() { | |
+ public Object invoke(MethodInvocation arg0) throws Throwable { | |
+ Class<?> serviceClass = arg0.getThis().getClass(); | |
+ String noPackageName = serviceClass.getName().replaceFirst(".*\\.", ""); | |
+ return "intercepted in class " + noPackageName + ": " + arg0.proceed(); | |
+ } | |
+ }); | |
+ } | |
+ } | |
+ ).getInstance(LocalStringServiceFactory.class); | |
+ for (int i=0; i < 5000; i++) { | |
+ LocalStringService service = factory.create("User " + i); | |
+ System.out.println(service.getString()); | |
+ } | |
+ } | |
+ | |
+ public static void main(String args[]) { | |
+ long now = System.currentTimeMillis(); | |
+ System.out.println(now); | |
+ new FactoryModuleTest().disabled_testPermgenSpace(); | |
+ System.out.println(System.currentTimeMillis() - now); | |
+ } | |
+} | |
Property changes on: extensions\assistedinject\test\com\google\inject\assistedinject\FactoryModuleTest.java | |
___________________________________________________________________ | |
Added: svn:mergeinfo | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment