1 /**
2  * Copyright (C) 2010 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.google.inject.name;
18 
19 import static com.google.inject.Asserts.assertContains;
20 
21 import com.google.inject.AbstractModule;
22 import com.google.inject.ConfigurationException;
23 import com.google.inject.CreationException;
24 import com.google.inject.Guice;
25 import com.google.inject.Inject;
26 import com.google.inject.Injector;
27 import com.google.inject.Key;
28 import com.google.inject.Module;
29 import com.google.inject.Provides;
30 
31 import junit.framework.TestCase;
32 
33 import java.io.Serializable;
34 import java.lang.annotation.Annotation;
35 import java.util.Properties;
36 
37 /**
38  * Tests that {@code javax.inject.Named} and {@code com.google.inject.name.Named} are completely
39  * interchangeable: bindings for one can be used to inject the other.
40  *
41  * @author cgdecker@gmail.com (Colin Decker)
42  */
43 public class NamedEquivalanceTest extends TestCase {
44 
45   private static final Module GUICE_BINDING_MODULE = moduleWithAnnotation(Names.named("foo"));
46   private static final Module JSR330_BINDING_MODULE = moduleWithAnnotation(new JsrNamed("foo"));
47   private static final Module GUICE_PROVIDER_METHOD_MODULE = getGuiceBindingProviderMethodModule();
48   private static final Module JSR330_PROVIDER_METHOD_MODULE = getJsr330BindingProviderMethodModule();
49 
testKeysCreatedWithDifferentTypesAreEqual()50   public void testKeysCreatedWithDifferentTypesAreEqual() {
51     assertEquals(keyForAnnotation(new GuiceNamed("foo")), keyForAnnotation(new JsrNamed("foo")));
52     assertEquals(keyForAnnotation(Names.named("foo")), keyForAnnotation(new GuiceNamed("foo")));
53     assertEquals(keyForAnnotation(Names.named("foo")), keyForAnnotation(new JsrNamed("foo")));
54 
55     assertEquals(keyForAnnotationType(com.google.inject.name.Named.class),
56         keyForAnnotationType(javax.inject.Named.class));
57   }
58 
keyForAnnotation(Annotation annotation)59   private static Key<String> keyForAnnotation(Annotation annotation) {
60     return Key.get(String.class, annotation);
61   }
62 
keyForAnnotationType(Class<? extends Annotation> annotationType)63   private static Key<String> keyForAnnotationType(Class<? extends Annotation> annotationType) {
64     return Key.get(String.class, annotationType);
65   }
66 
testBindingWithNamesCanInjectBothTypes()67   public void testBindingWithNamesCanInjectBothTypes() {
68     assertInjectionsSucceed(GUICE_BINDING_MODULE);
69   }
70 
testBindingWithJsr330AnnotationCanInjectBothTypes()71   public void testBindingWithJsr330AnnotationCanInjectBothTypes() {
72     assertInjectionsSucceed(JSR330_BINDING_MODULE);
73   }
74 
testBindingWithGuiceNamedAnnotatedProviderMethodCanInjectBothTypes()75   public void testBindingWithGuiceNamedAnnotatedProviderMethodCanInjectBothTypes() {
76     assertInjectionsSucceed(GUICE_PROVIDER_METHOD_MODULE);
77   }
78 
testBindingWithJsr330NamedAnnotatedProviderMethodCanInjectBothTypes()79   public void testBindingWithJsr330NamedAnnotatedProviderMethodCanInjectBothTypes() {
80     assertInjectionsSucceed(JSR330_PROVIDER_METHOD_MODULE);
81   }
82 
testBindingDifferentTypesWithSameValueIsIgnored()83   public void testBindingDifferentTypesWithSameValueIsIgnored() {
84     assertDuplicateBinding(GUICE_BINDING_MODULE, JSR330_BINDING_MODULE, false);
85     assertDuplicateBinding(JSR330_BINDING_MODULE, GUICE_BINDING_MODULE, false);
86   }
87 
testBindingDifferentTypesWithSameValueIsAnErrorWithProviderMethods()88   public void testBindingDifferentTypesWithSameValueIsAnErrorWithProviderMethods() {
89     assertDuplicateBinding(GUICE_PROVIDER_METHOD_MODULE, JSR330_PROVIDER_METHOD_MODULE, true);
90     assertDuplicateBinding(JSR330_PROVIDER_METHOD_MODULE, GUICE_PROVIDER_METHOD_MODULE, true);
91   }
92 
testBindingDifferentTypesWithSameValueIsAnErrorMixed()93   public void testBindingDifferentTypesWithSameValueIsAnErrorMixed() {
94     assertDuplicateBinding(GUICE_BINDING_MODULE, JSR330_PROVIDER_METHOD_MODULE, true);
95     assertDuplicateBinding(JSR330_BINDING_MODULE, GUICE_PROVIDER_METHOD_MODULE, true);
96   }
97 
testMissingBindingForGuiceNamedUsesSameTypeInErrorMessage()98   public void testMissingBindingForGuiceNamedUsesSameTypeInErrorMessage() {
99     assertMissingBindingErrorMessageUsesType(GuiceNamedClient.class);
100   }
101 
testMissingBindingForJsr330NamedUsesSameTypeInErrorMessage()102   public void testMissingBindingForJsr330NamedUsesSameTypeInErrorMessage() {
103     assertMissingBindingErrorMessageUsesType(Jsr330NamedClient.class);
104   }
105 
testBindPropertiesWorksWithJsr330()106   public void testBindPropertiesWorksWithJsr330() {
107     assertInjectionsSucceed(new AbstractModule() {
108       @Override protected void configure() {
109         Properties properties = new Properties();
110         properties.put("foo", "bar");
111         Names.bindProperties(binder(), properties);
112       }
113     });
114   }
115 
assertMissingBindingErrorMessageUsesType(Class<?> clientType)116   private static void assertMissingBindingErrorMessageUsesType(Class<?> clientType) {
117     try {
118       Guice.createInjector().getInstance(clientType);
119       fail("should have thrown ConfigurationException");
120     } catch (ConfigurationException e) {
121       assertContains(e.getMessage(),
122           "No implementation for java.lang.String annotated with @com.google.inject.name.Named(value=foo) was bound.");
123     }
124   }
125 
assertDuplicateBinding(Module a, Module b, boolean fails)126   private static void assertDuplicateBinding(Module a, Module b, boolean fails) {
127     try {
128       Guice.createInjector(a, b);
129       if(fails) {
130         fail("should have thrown CreationException");
131       }
132     } catch (CreationException e) {
133       if(fails) {
134         assertContains(e.getMessage(),
135             "A binding to java.lang.String annotated with @com.google.inject.name.Named(value=foo) was already configured");
136       } else {
137         throw e;
138       }
139     }
140   }
141 
moduleWithAnnotation(final Annotation annotation)142   private static Module moduleWithAnnotation(final Annotation annotation) {
143     return new AbstractModule() {
144       @Override protected void configure() {
145         bindConstant().annotatedWith(annotation).to("bar");
146       }
147     };
148   }
149 
150   private static void assertInjectionsSucceed(Module module) {
151     Injector injector = Guice.createInjector(module);
152     assertInjected(injector.getInstance(GuiceNamedClient.class), injector
153         .getInstance(Jsr330NamedClient.class));
154   }
155 
156   private static void assertInjected(GuiceNamedClient guiceClient, Jsr330NamedClient jsr330Client) {
157     assertEquals("bar", guiceClient.foo);
158     assertEquals("bar", jsr330Client.foo);
159   }
160 
161   private static Module getJsr330BindingProviderMethodModule() {
162     return new AbstractModule() {
163       @Override protected void configure() {}
164       @SuppressWarnings("unused") @Provides @javax.inject.Named("foo") String provideFoo() {
165         return "bar";
166       }
167     };
168   }
169 
170   private static Module getGuiceBindingProviderMethodModule() {
171     return new AbstractModule() {
172       @Override protected void configure() {}
173       @SuppressWarnings("unused") @Provides @Named("foo") String provideFoo() {
174         return "bar";
175       }
176     };
177   }
178 
179   private static class GuiceNamedClient {
180     @Inject @Named("foo") String foo;
181   }
182 
183   private static class Jsr330NamedClient {
184     @Inject @javax.inject.Named("foo") String foo;
185   }
186 
187   private static class JsrNamed implements javax.inject.Named, Serializable {
188     private final String value;
189 
190     public JsrNamed(String value) {
191       this.value = value;
192     }
193 
194     public String value() {
195       return this.value;
196     }
197 
198     public int hashCode() {
199       // This is specified in java.lang.Annotation.
200       return (127 * "value".hashCode()) ^ value.hashCode();
201     }
202 
203     public boolean equals(Object o) {
204       if (!(o instanceof javax.inject.Named)) {
205         return false;
206       }
207 
208       javax.inject.Named other = (javax.inject.Named) o;
209       return value.equals(other.value());
210     }
211 
212     public String toString() {
213       return "@" + javax.inject.Named.class.getName() + "(value=" + value + ")";
214     }
215 
216     public Class<? extends Annotation> annotationType() {
217       return javax.inject.Named.class;
218     }
219 
220     private static final long serialVersionUID = 0;
221   }
222 
223   private static class GuiceNamed implements com.google.inject.name.Named, Serializable {
224     private final String value;
225 
226     public GuiceNamed(String value) {
227       this.value = value;
228     }
229 
230     public String value() {
231       return this.value;
232     }
233 
234     public int hashCode() {
235       // This is specified in java.lang.Annotation.
236       return (127 * "value".hashCode()) ^ value.hashCode();
237     }
238 
239     public boolean equals(Object o) {
240       if (!(o instanceof com.google.inject.name.Named)) {
241         return false;
242       }
243 
244       com.google.inject.name.Named other = (com.google.inject.name.Named) o;
245       return value.equals(other.value());
246     }
247 
248     public String toString() {
249       return "@" + com.google.inject.name.Named.class.getName() + "(value=" + value + ")";
250     }
251 
252     public Class<? extends Annotation> annotationType() {
253       return com.google.inject.name.Named.class;
254     }
255 
256     private static final long serialVersionUID = 0;
257   }
258 }
259