mirror of
https://github.com/google/guice.git
synced 2024-04-21 12:32:36 +00:00
* Sort imports * Convert tabs to spaces * Fix the ant no_aop build Revision created by MOE tool push_codebase. MOE_MIGRATION=2532 git-svn-id: https://google-guice.googlecode.com/svn/trunk@1572 d779f126-a31b-0410-b53b-1d3aecad763e
562 lines
19 KiB
Java
562 lines
19 KiB
Java
/**
|
|
* Copyright (C) 2008 Google Inc.
|
|
*
|
|
* Licensed 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;
|
|
|
|
import static com.google.inject.Asserts.assertContains;
|
|
import static com.google.inject.name.Names.named;
|
|
|
|
import com.google.common.collect.ImmutableSet;
|
|
import com.google.inject.name.Named;
|
|
import com.google.inject.name.Names;
|
|
import com.google.inject.spi.Dependency;
|
|
import com.google.inject.spi.ExposedBinding;
|
|
import com.google.inject.spi.PrivateElements;
|
|
import com.google.inject.util.Types;
|
|
|
|
import junit.framework.TestCase;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Collection;
|
|
import java.util.List;
|
|
|
|
/**
|
|
* @author jessewilson@google.com (Jesse Wilson)
|
|
*/
|
|
public class PrivateModuleTest extends TestCase {
|
|
|
|
public void testBasicUsage() {
|
|
Injector injector = Guice.createInjector(new AbstractModule() {
|
|
protected void configure() {
|
|
bind(String.class).annotatedWith(named("a")).toInstance("public");
|
|
|
|
install(new PrivateModule() {
|
|
public void configure() {
|
|
bind(String.class).annotatedWith(named("b")).toInstance("i");
|
|
|
|
bind(AB.class).annotatedWith(named("one")).to(AB.class);
|
|
expose(AB.class).annotatedWith(named("one"));
|
|
}
|
|
});
|
|
|
|
install(new PrivateModule() {
|
|
public void configure() {
|
|
bind(String.class).annotatedWith(named("b")).toInstance("ii");
|
|
|
|
bind(AB.class).annotatedWith(named("two")).to(AB.class);
|
|
expose(AB.class).annotatedWith(named("two"));
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
AB ab1 = injector.getInstance(Key.get(AB.class, named("one")));
|
|
assertEquals("public", ab1.a);
|
|
assertEquals("i", ab1.b);
|
|
|
|
AB ab2 = injector.getInstance(Key.get(AB.class, named("two")));
|
|
assertEquals("public", ab2.a);
|
|
assertEquals("ii", ab2.b);
|
|
}
|
|
|
|
public void testWithoutPrivateModules() {
|
|
Injector injector = Guice.createInjector(new AbstractModule() {
|
|
protected void configure() {
|
|
PrivateBinder bindA = binder().newPrivateBinder();
|
|
bindA.bind(String.class).annotatedWith(named("a")).toInstance("i");
|
|
bindA.expose(String.class).annotatedWith(named("a"));
|
|
bindA.bind(String.class).annotatedWith(named("c")).toInstance("private to A");
|
|
|
|
PrivateBinder bindB = binder().newPrivateBinder();
|
|
bindB.bind(String.class).annotatedWith(named("b")).toInstance("ii");
|
|
bindB.expose(String.class).annotatedWith(named("b"));
|
|
bindB.bind(String.class).annotatedWith(named("c")).toInstance("private to B");
|
|
}
|
|
});
|
|
|
|
assertEquals("i", injector.getInstance(Key.get(String.class, named("a"))));
|
|
assertEquals("ii", injector.getInstance(Key.get(String.class, named("b"))));
|
|
}
|
|
|
|
public void testMisplacedExposedAnnotation() {
|
|
try {
|
|
Guice.createInjector(new AbstractModule() {
|
|
protected void configure() {}
|
|
|
|
@Provides @Exposed
|
|
String provideString() {
|
|
return "i";
|
|
}
|
|
});
|
|
fail();
|
|
} catch (CreationException expected) {
|
|
assertContains(expected.getMessage(), "Cannot expose java.lang.String on a standard binder. ",
|
|
"Exposed bindings are only applicable to private binders.",
|
|
" at " + PrivateModuleTest.class.getName(), "provideString(PrivateModuleTest.java:");
|
|
}
|
|
}
|
|
|
|
public void testMisplacedExposeStatement() {
|
|
try {
|
|
Guice.createInjector(new AbstractModule() {
|
|
protected void configure() {
|
|
((PrivateBinder) binder()).expose(String.class).annotatedWith(named("a"));
|
|
}
|
|
});
|
|
fail();
|
|
} catch (CreationException expected) {
|
|
assertContains(expected.getMessage(), "Cannot expose java.lang.String on a standard binder. ",
|
|
"Exposed bindings are only applicable to private binders.",
|
|
" at " + PrivateModuleTest.class.getName(), "configure(PrivateModuleTest.java:");
|
|
}
|
|
}
|
|
|
|
public void testPrivateModulesAndProvidesMethods() {
|
|
Injector injector = Guice.createInjector(new AbstractModule() {
|
|
protected void configure() {
|
|
install(new PrivateModule() {
|
|
public void configure() {
|
|
expose(String.class).annotatedWith(named("a"));
|
|
}
|
|
|
|
@Provides @Named("a") String providePublicA() {
|
|
return "i";
|
|
}
|
|
|
|
@Provides @Named("b") String providePrivateB() {
|
|
return "private";
|
|
}
|
|
});
|
|
|
|
install(new PrivateModule() {
|
|
public void configure() {}
|
|
|
|
@Provides @Named("c") String providePrivateC() {
|
|
return "private";
|
|
}
|
|
|
|
@Provides @Exposed @Named("d") String providePublicD() {
|
|
return "ii";
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
assertEquals("i", injector.getInstance(Key.get(String.class, named("a"))));
|
|
|
|
try {
|
|
injector.getInstance(Key.get(String.class, named("b")));
|
|
fail();
|
|
} catch(ConfigurationException expected) {
|
|
}
|
|
|
|
try {
|
|
injector.getInstance(Key.get(String.class, named("c")));
|
|
fail();
|
|
} catch(ConfigurationException expected) {
|
|
}
|
|
|
|
assertEquals("ii", injector.getInstance(Key.get(String.class, named("d"))));
|
|
}
|
|
|
|
public void testCannotBindAKeyExportedByASibling() {
|
|
try {
|
|
Guice.createInjector(new AbstractModule() {
|
|
protected void configure() {
|
|
install(new PrivateModule() {
|
|
public void configure() {
|
|
bind(String.class).toInstance("public");
|
|
expose(String.class);
|
|
}
|
|
});
|
|
|
|
install(new PrivateModule() {
|
|
public void configure() {
|
|
bind(String.class).toInstance("private");
|
|
}
|
|
});
|
|
}
|
|
});
|
|
fail();
|
|
} catch (CreationException expected) {
|
|
assertContains(expected.getMessage(),
|
|
"A binding to java.lang.String was already configured at ",
|
|
getClass().getName(), ".configure(PrivateModuleTest.java:",
|
|
" at " + getClass().getName(), ".configure(PrivateModuleTest.java:");
|
|
}
|
|
}
|
|
|
|
public void testExposeButNoBind() {
|
|
try {
|
|
Guice.createInjector(new AbstractModule() {
|
|
protected void configure() {
|
|
bind(String.class).annotatedWith(named("a")).toInstance("a");
|
|
bind(String.class).annotatedWith(named("b")).toInstance("b");
|
|
|
|
install(new PrivateModule() {
|
|
public void configure() {
|
|
expose(AB.class);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
fail("AB was exposed but not bound");
|
|
} catch (CreationException expected) {
|
|
assertContains(expected.getMessage(),
|
|
"Could not expose() " + AB.class.getName() + ", it must be explicitly bound",
|
|
".configure(PrivateModuleTest.java:");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Ensure that when we've got errors in different private modules, Guice presents all errors
|
|
* in a unified message.
|
|
*/
|
|
public void testMessagesFromPrivateModulesAreNicelyIntegrated() {
|
|
try {
|
|
Guice.createInjector(
|
|
new PrivateModule() {
|
|
public void configure() {
|
|
bind(C.class);
|
|
}
|
|
},
|
|
new PrivateModule() {
|
|
public void configure() {
|
|
bind(AB.class);
|
|
}
|
|
}
|
|
);
|
|
fail();
|
|
} catch (CreationException expected) {
|
|
assertContains(expected.getMessage(),
|
|
"1) No implementation for " + C.class.getName() + " was bound.",
|
|
"at " + getClass().getName(), ".configure(PrivateModuleTest.java:",
|
|
"2) No implementation for " + String.class.getName(), "Named(value=a) was bound.",
|
|
"for field at " + AB.class.getName() + ".a(PrivateModuleTest.java:",
|
|
"3) No implementation for " + String.class.getName(), "Named(value=b) was bound.",
|
|
"for field at " + AB.class.getName() + ".b(PrivateModuleTest.java:",
|
|
"3 errors");
|
|
}
|
|
}
|
|
|
|
public void testNestedPrivateInjectors() {
|
|
Injector injector = Guice.createInjector(new PrivateModule() {
|
|
public void configure() {
|
|
expose(String.class);
|
|
|
|
install(new PrivateModule() {
|
|
public void configure() {
|
|
bind(String.class).toInstance("nested");
|
|
expose(String.class);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
assertEquals("nested", injector.getInstance(String.class));
|
|
}
|
|
|
|
public void testInstallingRegularModulesFromPrivateModules() {
|
|
Injector injector = Guice.createInjector(new PrivateModule() {
|
|
public void configure() {
|
|
expose(String.class);
|
|
|
|
install(new AbstractModule() {
|
|
protected void configure() {
|
|
bind(String.class).toInstance("nested");
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
assertEquals("nested", injector.getInstance(String.class));
|
|
}
|
|
|
|
public void testNestedPrivateModulesWithSomeKeysUnexposed() {
|
|
Injector injector = Guice.createInjector(new PrivateModule() {
|
|
public void configure() {
|
|
bind(String.class).annotatedWith(named("bound outer, exposed outer")).toInstance("boeo");
|
|
expose(String.class).annotatedWith(named("bound outer, exposed outer"));
|
|
bind(String.class).annotatedWith(named("bound outer, exposed none")).toInstance("boen");
|
|
expose(String.class).annotatedWith(named("bound inner, exposed both"));
|
|
|
|
install(new PrivateModule() {
|
|
public void configure() {
|
|
bind(String.class).annotatedWith(named("bound inner, exposed both")).toInstance("bieb");
|
|
expose(String.class).annotatedWith(named("bound inner, exposed both"));
|
|
bind(String.class).annotatedWith(named("bound inner, exposed none")).toInstance("bien");
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
assertEquals("boeo",
|
|
injector.getInstance(Key.get(String.class, named("bound outer, exposed outer"))));
|
|
assertEquals("bieb",
|
|
injector.getInstance(Key.get(String.class, named("bound inner, exposed both"))));
|
|
|
|
try {
|
|
injector.getInstance(Key.get(String.class, named("bound outer, exposed none")));
|
|
fail();
|
|
} catch (ConfigurationException expected) {
|
|
}
|
|
|
|
try {
|
|
injector.getInstance(Key.get(String.class, named("bound inner, exposed none")));
|
|
fail();
|
|
} catch (ConfigurationException expected) {
|
|
}
|
|
}
|
|
|
|
public void testDependenciesBetweenPrivateAndPublic() {
|
|
Injector injector = Guice.createInjector(
|
|
new PrivateModule() {
|
|
protected void configure() {}
|
|
|
|
@Provides @Exposed @Named("a") String provideA() {
|
|
return "A";
|
|
}
|
|
|
|
@Provides @Exposed @Named("abc") String provideAbc(@Named("ab") String ab) {
|
|
return ab + "C";
|
|
}
|
|
},
|
|
new AbstractModule() {
|
|
protected void configure() {}
|
|
|
|
@Provides @Named("ab") String provideAb(@Named("a") String a) {
|
|
return a + "B";
|
|
}
|
|
|
|
@Provides @Named("abcd") String provideAbcd(@Named("abc") String abc) {
|
|
return abc + "D";
|
|
}
|
|
}
|
|
);
|
|
|
|
assertEquals("ABCD", injector.getInstance(Key.get(String.class, named("abcd"))));
|
|
}
|
|
|
|
public void testDependenciesBetweenPrivateAndPublicWithPublicEagerSingleton() {
|
|
Injector injector = Guice.createInjector(
|
|
new PrivateModule() {
|
|
protected void configure() {}
|
|
|
|
@Provides @Exposed @Named("a") String provideA() {
|
|
return "A";
|
|
}
|
|
|
|
@Provides @Exposed @Named("abc") String provideAbc(@Named("ab") String ab) {
|
|
return ab + "C";
|
|
}
|
|
},
|
|
new AbstractModule() {
|
|
protected void configure() {
|
|
bind(String.class).annotatedWith(named("abcde")).toProvider(new Provider<String>() {
|
|
@Inject @Named("abcd") String abcd;
|
|
|
|
public String get() {
|
|
return abcd + "E";
|
|
}
|
|
}).asEagerSingleton();
|
|
}
|
|
|
|
@Provides @Named("ab") String provideAb(@Named("a") String a) {
|
|
return a + "B";
|
|
}
|
|
|
|
@Provides @Named("abcd") String provideAbcd(@Named("abc") String abc) {
|
|
return abc + "D";
|
|
}
|
|
}
|
|
);
|
|
|
|
assertEquals("ABCDE", injector.getInstance(Key.get(String.class, named("abcde"))));
|
|
}
|
|
|
|
public void testDependenciesBetweenPrivateAndPublicWithPrivateEagerSingleton() {
|
|
Injector injector = Guice.createInjector(
|
|
new AbstractModule() {
|
|
protected void configure() {}
|
|
|
|
@Provides @Named("ab") String provideAb(@Named("a") String a) {
|
|
return a + "B";
|
|
}
|
|
|
|
@Provides @Named("abcd") String provideAbcd(@Named("abc") String abc) {
|
|
return abc + "D";
|
|
}
|
|
},
|
|
new PrivateModule() {
|
|
protected void configure() {
|
|
bind(String.class).annotatedWith(named("abcde")).toProvider(new Provider<String>() {
|
|
@Inject @Named("abcd") String abcd;
|
|
|
|
public String get() {
|
|
return abcd + "E";
|
|
}
|
|
}).asEagerSingleton();
|
|
expose(String.class).annotatedWith(named("abcde"));
|
|
}
|
|
|
|
@Provides @Exposed @Named("a") String provideA() {
|
|
return "A";
|
|
}
|
|
|
|
@Provides @Exposed @Named("abc") String provideAbc(@Named("ab") String ab) {
|
|
return ab + "C";
|
|
}
|
|
}
|
|
);
|
|
|
|
assertEquals("ABCDE", injector.getInstance(Key.get(String.class, named("abcde"))));
|
|
}
|
|
|
|
static class AB {
|
|
@Inject @Named("a") String a;
|
|
@Inject @Named("b") String b;
|
|
}
|
|
|
|
interface C {}
|
|
|
|
public void testSpiAccess() {
|
|
Injector injector = Guice.createInjector(new PrivateModule() {
|
|
public void configure() {
|
|
bind(String.class).annotatedWith(named("a")).toInstance("private");
|
|
bind(String.class).annotatedWith(named("b")).toInstance("exposed");
|
|
expose(String.class).annotatedWith(named("b"));
|
|
}
|
|
});
|
|
|
|
ExposedBinding<?> binding
|
|
= (ExposedBinding<?>) injector.getBinding(Key.get(String.class, Names.named("b")));
|
|
assertEquals(ImmutableSet.<Dependency<?>>of(Dependency.get(Key.get(Injector.class))),
|
|
binding.getDependencies());
|
|
PrivateElements privateElements = binding.getPrivateElements();
|
|
assertEquals(ImmutableSet.<Key<?>>of(Key.get(String.class, named("b"))),
|
|
privateElements.getExposedKeys());
|
|
assertContains(privateElements.getExposedSource(Key.get(String.class, named("b"))).toString(),
|
|
PrivateModuleTest.class.getName(), ".configure(PrivateModuleTest.java:");
|
|
Injector privateInjector = privateElements.getInjector();
|
|
assertEquals("private", privateInjector.getInstance(Key.get(String.class, Names.named("a"))));
|
|
}
|
|
|
|
public void testParentBindsSomethingInPrivate() {
|
|
try {
|
|
Guice.createInjector(new FailingModule());
|
|
fail();
|
|
} catch(CreationException expected) {
|
|
assertEquals(1, expected.getErrorMessages().size());
|
|
assertContains(expected.toString(),
|
|
"Unable to create binding for java.util.List.",
|
|
"It was already configured on one or more child injectors or private modules",
|
|
"bound at " + FailingPrivateModule.class.getName() + ".configure(",
|
|
"bound at " + SecondFailingPrivateModule.class.getName() + ".configure(",
|
|
"If it was in a PrivateModule, did you forget to expose the binding?",
|
|
"at " + FailingModule.class.getName() + ".configure(");
|
|
}
|
|
}
|
|
|
|
public void testParentBindingToPrivateLinkedJitBinding() {
|
|
Injector injector = Guice.createInjector(new ManyPrivateModules());
|
|
try {
|
|
injector.getBinding(Key.get(Types.providerOf(List.class)));
|
|
fail();
|
|
} catch(ConfigurationException expected) {
|
|
assertEquals(1, expected.getErrorMessages().size());
|
|
assertContains(expected.toString(),
|
|
"Unable to create binding for com.google.inject.Provider<java.util.List>.",
|
|
"It was already configured on one or more child injectors or private modules",
|
|
"bound at " + FailingPrivateModule.class.getName() + ".configure(",
|
|
"bound at " + SecondFailingPrivateModule.class.getName() + ".configure(",
|
|
"If it was in a PrivateModule, did you forget to expose the binding?",
|
|
"while locating com.google.inject.Provider<java.util.List>");
|
|
}
|
|
}
|
|
|
|
public void testParentBindingToPrivateJitBinding() {
|
|
Injector injector = Guice.createInjector(new ManyPrivateModules());
|
|
try {
|
|
injector.getBinding(PrivateFoo.class);
|
|
fail();
|
|
} catch(ConfigurationException expected) {
|
|
assertEquals(1, expected.getErrorMessages().size());
|
|
assertContains(expected.toString(),
|
|
"Unable to create binding for " + PrivateFoo.class.getName(),
|
|
"It was already configured on one or more child injectors or private modules",
|
|
"(bound by a just-in-time binding)",
|
|
"If it was in a PrivateModule, did you forget to expose the binding?",
|
|
"while locating " + PrivateFoo.class.getName());
|
|
}
|
|
}
|
|
|
|
private static class FailingModule extends AbstractModule {
|
|
@Override
|
|
protected void configure() {
|
|
bind(Collection.class).to(List.class);
|
|
install(new ManyPrivateModules());
|
|
}
|
|
}
|
|
|
|
private static class ManyPrivateModules extends AbstractModule {
|
|
@Override
|
|
protected void configure() {
|
|
// make sure duplicate sources are collapsed
|
|
install(new FailingPrivateModule());
|
|
install(new FailingPrivateModule());
|
|
// but additional sources are listed
|
|
install(new SecondFailingPrivateModule());
|
|
}
|
|
}
|
|
|
|
private static class FailingPrivateModule extends PrivateModule {
|
|
@Override
|
|
protected void configure() {
|
|
bind(List.class).toInstance(new ArrayList());
|
|
|
|
// Add the Provider<List> binding, created just-in-time,
|
|
// to make sure our linked JIT bindings have the correct source.
|
|
getProvider(Key.get(Types.providerOf(List.class)));
|
|
|
|
// Request a JIT binding for PrivateFoo, which can only
|
|
// be created in the private module because it depends
|
|
// on List.
|
|
getProvider(PrivateFoo.class);
|
|
}
|
|
}
|
|
|
|
/** A second class, so we can see another name in the source list. */
|
|
private static class SecondFailingPrivateModule extends PrivateModule {
|
|
@Override
|
|
protected void configure() {
|
|
bind(List.class).toInstance(new ArrayList());
|
|
|
|
// Add the Provider<List> binding, created just-in-time,
|
|
// to make sure our linked JIT bindings have the correct source.
|
|
getProvider(Key.get(Types.providerOf(List.class)));
|
|
|
|
// Request a JIT binding for PrivateFoo, which can only
|
|
// be created in the private module because it depends
|
|
// on List.
|
|
getProvider(PrivateFoo.class);
|
|
}
|
|
}
|
|
|
|
private static class PrivateFoo {
|
|
@Inject List list;
|
|
}
|
|
}
|