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