Home | History | Annotate | Download | only in guice
      1 /**
      2  * Copyright (C) 2009 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 
     17 package com.googlecode.guice;
     18 
     19 import static com.google.inject.Asserts.assertContains;
     20 import static java.lang.annotation.RetentionPolicy.RUNTIME;
     21 
     22 import com.google.inject.AbstractModule;
     23 import com.google.inject.Binding;
     24 import com.google.inject.CreationException;
     25 import com.google.inject.Guice;
     26 import com.google.inject.Injector;
     27 import com.google.inject.Key;
     28 import com.google.inject.Scope;
     29 import com.google.inject.Scopes;
     30 import com.google.inject.Stage;
     31 import com.google.inject.TypeLiteral;
     32 import com.google.inject.name.Names;
     33 import com.google.inject.spi.Dependency;
     34 import com.google.inject.spi.HasDependencies;
     35 import com.google.inject.util.Providers;
     36 
     37 import junit.framework.TestCase;
     38 
     39 import java.lang.annotation.Annotation;
     40 import java.lang.annotation.Retention;
     41 import java.util.Set;
     42 
     43 import javax.inject.Inject;
     44 import javax.inject.Named;
     45 import javax.inject.Provider;
     46 import javax.inject.Qualifier;
     47 import javax.inject.Singleton;
     48 
     49 public class Jsr330Test extends TestCase {
     50 
     51   private final B b = new B();
     52   private final C c = new C();
     53   private final D d = new D();
     54   private final E e = new E();
     55 
     56   @Override protected void setUp() throws Exception {
     57     J.nextInstanceId = 0;
     58     K.nextInstanceId = 0;
     59   }
     60 
     61   public void testInject() {
     62     Injector injector = Guice.createInjector(new AbstractModule() {
     63       protected void configure() {
     64         bind(B.class).toInstance(b);
     65         bind(C.class).toInstance(c);
     66         bind(D.class).toInstance(d);
     67         bind(E.class).toInstance(e);
     68         bind(A.class);
     69       }
     70     });
     71 
     72     A a = injector.getInstance(A.class);
     73     assertSame(b, a.b);
     74     assertSame(c, a.c);
     75     assertSame(d, a.d);
     76     assertSame(e, a.e);
     77   }
     78 
     79   public void testQualifiedInject() {
     80     Injector injector = Guice.createInjector(new AbstractModule() {
     81       protected void configure() {
     82         bind(B.class).annotatedWith(Names.named("jodie")).toInstance(b);
     83         bind(C.class).annotatedWith(Red.class).toInstance(c);
     84         bind(D.class).annotatedWith(RED).toInstance(d);
     85         bind(E.class).annotatedWith(Names.named("jesse")).toInstance(e);
     86         bind(F.class);
     87       }
     88     });
     89 
     90     F f = injector.getInstance(F.class);
     91     assertSame(b, f.b);
     92     assertSame(c, f.c);
     93     assertSame(d, f.d);
     94     assertSame(e, f.e);
     95   }
     96 
     97   public void testProviderInject() {
     98     Injector injector = Guice.createInjector(new AbstractModule() {
     99       protected void configure() {
    100         bind(B.class).annotatedWith(Names.named("jodie")).toInstance(b);
    101         bind(C.class).toInstance(c);
    102         bind(D.class).annotatedWith(RED).toInstance(d);
    103         bind(E.class).toInstance(e);
    104         bind(G.class);
    105       }
    106     });
    107 
    108     G g = injector.getInstance(G.class);
    109     assertSame(b, g.bProvider.get());
    110     assertSame(c, g.cProvider.get());
    111     assertSame(d, g.dProvider.get());
    112     assertSame(e, g.eProvider.get());
    113   }
    114 
    115   public void testScopeAnnotation() {
    116     final TestScope scope = new TestScope();
    117 
    118     Injector injector = Guice.createInjector(new AbstractModule() {
    119       protected void configure() {
    120         bind(B.class).in(scope);
    121         bind(C.class).in(TestScoped.class);
    122         bindScope(TestScoped.class, scope);
    123       }
    124     });
    125 
    126     B b = injector.getInstance(B.class);
    127     assertSame(b, injector.getInstance(B.class));
    128     assertSame(b, injector.getInstance(B.class));
    129 
    130     C c = injector.getInstance(C.class);
    131     assertSame(c, injector.getInstance(C.class));
    132     assertSame(c, injector.getInstance(C.class));
    133 
    134     H h = injector.getInstance(H.class);
    135     assertSame(h, injector.getInstance(H.class));
    136     assertSame(h, injector.getInstance(H.class));
    137 
    138     scope.reset();
    139 
    140     assertNotSame(b, injector.getInstance(B.class));
    141     assertNotSame(c, injector.getInstance(C.class));
    142     assertNotSame(h, injector.getInstance(H.class));
    143   }
    144 
    145   public void testSingleton() {
    146     Injector injector = Guice.createInjector(new AbstractModule() {
    147       protected void configure() {
    148         bind(B.class).in(Singleton.class);
    149       }
    150     });
    151 
    152     B b = injector.getInstance(B.class);
    153     assertSame(b, injector.getInstance(B.class));
    154     assertSame(b, injector.getInstance(B.class));
    155 
    156     J j = injector.getInstance(J.class);
    157     assertSame(j, injector.getInstance(J.class));
    158     assertSame(j, injector.getInstance(J.class));
    159   }
    160 
    161   public void testEagerSingleton() {
    162     Guice.createInjector(Stage.PRODUCTION, new AbstractModule() {
    163       protected void configure() {
    164         bind(J.class);
    165         bind(K.class).in(Singleton.class);
    166       }
    167     });
    168 
    169     assertEquals(1, J.nextInstanceId);
    170     assertEquals(1, K.nextInstanceId);
    171   }
    172 
    173   public void testScopesIsSingleton() {
    174     Injector injector = Guice.createInjector(new AbstractModule() {
    175       protected void configure() {
    176         bind(J.class);
    177         bind(K.class).in(Singleton.class);
    178       }
    179     });
    180 
    181     assertTrue(Scopes.isSingleton(injector.getBinding(J.class)));
    182     assertTrue(Scopes.isSingleton(injector.getBinding(K.class)));
    183   }
    184 
    185   public void testInjectingFinalFieldsIsForbidden() {
    186     try {
    187       Guice.createInjector(new AbstractModule() {
    188         protected void configure() {
    189           bind(L.class);
    190         }
    191       });
    192       fail();
    193     } catch (CreationException expected) {
    194       assertContains(expected.getMessage(),
    195           "1) Injected field " + L.class.getName() + ".b cannot be final.");
    196     }
    197   }
    198 
    199   public void testInjectingAbstractMethodsIsForbidden() {
    200     try {
    201       Guice.createInjector(new AbstractModule() {
    202         protected void configure() {
    203           bind(M.class);
    204         }
    205       });
    206       fail();
    207     } catch (CreationException expected) {
    208       assertContains(expected.getMessage(),
    209           "1) Injected method " + AbstractM.class.getName() + ".setB() cannot be abstract.");
    210     }
    211   }
    212 
    213   public void testInjectingMethodsWithTypeParametersIsForbidden() {
    214     try {
    215       Guice.createInjector(new AbstractModule() {
    216         protected void configure() {
    217           bind(N.class);
    218         }
    219       });
    220       fail();
    221     } catch (CreationException expected) {
    222       assertContains(expected.getMessage(), "1) Injected method " + N.class.getName()
    223           + ".setB() cannot declare type parameters of its own.");
    224     }
    225   }
    226 
    227   public void testInjectingMethodsWithNonVoidReturnTypes() {
    228     Guice.createInjector(new AbstractModule() {
    229       protected void configure() {
    230         bind(P.class);
    231       }
    232     });
    233   }
    234 
    235   /**
    236    * This test verifies that we can compile bindings to provider instances
    237    * whose compile-time type implements javax.inject.Provider but not
    238    * com.google.inject.Provider. For binary compatibility, we don't (and won't)
    239    * support binding to instances of javax.inject.Provider.
    240    */
    241   public void testBindProviderClass() {
    242     Injector injector = Guice.createInjector(new AbstractModule() {
    243       protected void configure() {
    244         bind(B.class).toProvider(BProvider.class);
    245         bind(B.class).annotatedWith(Names.named("1")).toProvider(BProvider.class);
    246         bind(B.class).annotatedWith(Names.named("2")).toProvider(Key.get(BProvider.class));
    247         bind(B.class).annotatedWith(Names.named("3")).toProvider(TypeLiteral.get(BProvider.class));
    248       }
    249     });
    250 
    251     injector.getInstance(Key.get(B.class));
    252     injector.getInstance(Key.get(B.class, Names.named("1")));
    253     injector.getInstance(Key.get(B.class, Names.named("2")));
    254     injector.getInstance(Key.get(B.class, Names.named("3")));
    255   }
    256 
    257   public void testGuicify330Provider() {
    258     Provider<String> jsr330Provider = new Provider<String>() {
    259       public String get() {
    260         return "A";
    261       }
    262 
    263       @Override public String toString() {
    264         return "jsr330Provider";
    265       }
    266     };
    267 
    268     com.google.inject.Provider<String> guicified = Providers.guicify(jsr330Provider);
    269     assertEquals("guicified(jsr330Provider)", guicified.toString());
    270     assertEquals("A", guicified.get());
    271 
    272     // when you guicify the Guice-friendly, it's a no-op
    273     assertSame(guicified, Providers.guicify(guicified));
    274 
    275     assertFalse(guicified instanceof HasDependencies);
    276   }
    277 
    278   public void testGuicifyWithDependencies() {
    279     Provider<String> jsr330Provider = new Provider<String>() {
    280       @Inject double d;
    281       int i;
    282       @Inject void injectMe(int i) {
    283         this.i = i;
    284       }
    285 
    286       public String get() {
    287         return  d + "-" + i;
    288       }
    289     };
    290 
    291     final com.google.inject.Provider<String> guicified =
    292         Providers.guicify(jsr330Provider);
    293     assertTrue(guicified instanceof HasDependencies);
    294     Set<Dependency<?>> actual = ((HasDependencies)guicified).getDependencies();
    295     validateDependencies(actual, jsr330Provider.getClass());
    296 
    297     Injector injector = Guice.createInjector(new AbstractModule() {
    298       @Override
    299       protected void configure() {
    300         bind(String.class).toProvider(guicified);
    301         bind(int.class).toInstance(1);
    302         bind(double.class).toInstance(2.0d);
    303       }
    304     });
    305 
    306     Binding<String> binding = injector.getBinding(String.class);
    307     assertEquals("2.0-1", binding.getProvider().get());
    308     validateDependencies(actual, jsr330Provider.getClass());
    309   }
    310 
    311   private void validateDependencies(Set<Dependency<?>> actual, Class<?> owner) {
    312     assertEquals(actual.toString(), 2, actual.size());
    313     Dependency<?> dDep = null;
    314     Dependency<?> iDep = null;
    315     for(Dependency<?> dep : actual) {
    316       if(dep.getKey().equals(Key.get(Double.class))) {
    317         dDep = dep;
    318       } else if(dep.getKey().equals(Key.get(Integer.class))) {
    319         iDep = dep;
    320       }
    321     }
    322     assertNotNull(dDep);
    323     assertNotNull(iDep);
    324     assertEquals(TypeLiteral.get(owner), dDep.getInjectionPoint().getDeclaringType());
    325     assertEquals("d", dDep.getInjectionPoint().getMember().getName());
    326     assertEquals(-1, dDep.getParameterIndex());
    327 
    328     assertEquals(TypeLiteral.get(owner), iDep.getInjectionPoint().getDeclaringType());
    329     assertEquals("injectMe", iDep.getInjectionPoint().getMember().getName());
    330     assertEquals(0, iDep.getParameterIndex());
    331   }
    332 
    333   static class A {
    334     final B b;
    335     @Inject C c;
    336     D d;
    337     E e;
    338 
    339     @Inject A(B b) {
    340       this.b = b;
    341     }
    342 
    343     @Inject void injectD(D d, E e) {
    344       this.d = d;
    345       this.e = e;
    346     }
    347   }
    348 
    349   static class B {}
    350   static class C {}
    351   static class D {}
    352   static class E {}
    353 
    354   static class F {
    355     final B b;
    356     @Inject @Red C c;
    357     D d;
    358     E e;
    359 
    360     @Inject F(@Named("jodie") B b) {
    361       this.b = b;
    362     }
    363 
    364     @Inject void injectD(@Red D d, @Named("jesse") E e) {
    365       this.d = d;
    366       this.e = e;
    367     }
    368   }
    369 
    370   @Qualifier @Retention(RUNTIME)
    371   @interface Red {}
    372 
    373   public static final Red RED = new Red() {
    374     public Class<? extends Annotation> annotationType() {
    375       return Red.class;
    376     }
    377 
    378     @Override public boolean equals(Object obj) {
    379       return obj instanceof Red;
    380     }
    381 
    382     @Override public int hashCode() {
    383       return 0;
    384     }
    385   };
    386 
    387   static class G {
    388     final Provider<B> bProvider;
    389     @Inject Provider<C> cProvider;
    390     Provider<D> dProvider;
    391     Provider<E> eProvider;
    392 
    393     @Inject G(@Named("jodie") Provider<B> bProvider) {
    394       this.bProvider = bProvider;
    395     }
    396 
    397     @Inject void injectD(@Red Provider<D> dProvider, Provider<E> eProvider) {
    398       this.dProvider = dProvider;
    399       this.eProvider = eProvider;
    400     }
    401   }
    402 
    403   @javax.inject.Scope @Retention(RUNTIME)
    404   @interface TestScoped {}
    405 
    406   static class TestScope implements Scope {
    407     private int now = 0;
    408 
    409     public <T> com.google.inject.Provider<T> scope(Key<T> key,
    410         final com.google.inject.Provider<T> unscoped) {
    411       return new com.google.inject.Provider<T>() {
    412         private T value;
    413         private int snapshotTime = -1;
    414 
    415         public T get() {
    416           if (snapshotTime != now) {
    417             value = unscoped.get();
    418             snapshotTime = now;
    419           }
    420           return value;
    421         }
    422       };
    423     }
    424 
    425     public void reset() {
    426       now++;
    427     }
    428   }
    429 
    430   @TestScoped
    431   static class H {}
    432 
    433   @Singleton
    434   static class J {
    435     static int nextInstanceId = 0;
    436     int instanceId = nextInstanceId++;
    437   }
    438 
    439   static class K {
    440     static int nextInstanceId = 0;
    441     int instanceId = nextInstanceId++;
    442   }
    443 
    444   static class L {
    445     @SuppressWarnings("InjectJavaxInjectOnFinalField")
    446     @Inject
    447     final B b = null;
    448   }
    449 
    450   static abstract class AbstractM {
    451     @SuppressWarnings("InjectJavaxInjectOnAbstractMethod")
    452     @Inject
    453     abstract void setB(B b);
    454   }
    455 
    456   static class M extends AbstractM {
    457     void setB(B b) {}
    458   }
    459 
    460   static class N {
    461     @Inject <T> void setB(B b) {}
    462   }
    463 
    464   static class P {
    465     @Inject B setB(B b) {
    466       return b;
    467     }
    468   }
    469 
    470   static class BProvider implements Provider<B> {
    471     public B get() {
    472       return new B();
    473     }
    474   }
    475 }
    476