key) {
/**
* Returns an type that's appropriate for use in a key.
*
- * If the raw type of {@code typeLiteral} is a {@code javax.inject.Provider}, this returns a
- * {@code com.google.inject.Provider} with the same type parameters.
+ *
If the raw type of {@code typeLiteral} is a {@code javax.inject.Provider} or {@code
+ * jakarta.inject.Provider}, this returns a {@code com.google.inject.Provider} with the same type
+ * parameters.
*
*
If the type is a primitive, the corresponding wrapper type will be returned.
*
@@ -91,11 +92,12 @@ public static TypeLiteral canonicalizeForKey(TypeLiteral typeLiteral)
throw new ConfigurationException(errors.getMessages());
}
- if (typeLiteral.getRawType() == javax.inject.Provider.class) {
+ if (typeLiteral.getRawType() == javax.inject.Provider.class
+ || typeLiteral.getRawType() == jakarta.inject.Provider.class) {
ParameterizedType parameterizedType = (ParameterizedType) type;
// the following casts are generally unsafe, but com.google.inject.Provider extends
- // javax.inject.Provider and is covariant
+ // javax.inject.Provider & jakarta.inject.Provider and is covariant
@SuppressWarnings("unchecked")
TypeLiteral guiceProviderType =
(TypeLiteral)
diff --git a/core/src/com/google/inject/spi/InjectionPoint.java b/core/src/com/google/inject/spi/InjectionPoint.java
index 84b91e406b..2f566614ef 100644
--- a/core/src/com/google/inject/spi/InjectionPoint.java
+++ b/core/src/com/google/inject/spi/InjectionPoint.java
@@ -167,7 +167,7 @@ private Dependency newDependency(Key key, boolean allowsNull, int para
/** Returns the injected constructor, field, or method. */
public Member getMember() {
- // TODO -).
+ // TODO: Don't expose the original member (which probably has setAccessible(true)).
return member;
}
@@ -300,7 +300,8 @@ public static InjectionPoint forConstructorOf(TypeLiteral> type, boolean atInj
.filter(
constructor ->
constructor.isAnnotationPresent(Inject.class)
- || constructor.isAnnotationPresent(javax.inject.Inject.class))
+ || constructor.isAnnotationPresent(javax.inject.Inject.class)
+ || constructor.isAnnotationPresent(jakarta.inject.Inject.class))
.collect(Collectors.toList());
Constructor> injectableConstructor = null;
@@ -477,20 +478,21 @@ private static boolean checkForMisplacedBindingAnnotations(Member member, Errors
abstract static class InjectableMember {
final TypeLiteral> declaringType;
final boolean optional;
- final boolean jsr330;
+ final boolean specInject;
InjectableMember previous;
InjectableMember next;
InjectableMember(TypeLiteral> declaringType, Annotation atInject) {
this.declaringType = declaringType;
- if (atInject.annotationType() == javax.inject.Inject.class) {
+ if (atInject.annotationType() == javax.inject.Inject.class
+ || atInject.annotationType() == jakarta.inject.Inject.class) {
optional = false;
- jsr330 = true;
+ specInject = true;
return;
}
- jsr330 = false;
+ specInject = false;
optional = ((Inject) atInject).optional();
}
@@ -536,6 +538,9 @@ public boolean isFinal() {
static Annotation getAtInject(AnnotatedElement member) {
Annotation a = member.getAnnotation(javax.inject.Inject.class);
+ if (a == null) {
+ a = member.getAnnotation(jakarta.inject.Inject.class);
+ }
return a == null ? member.getAnnotation(Inject.class) : a;
}
@@ -646,7 +651,7 @@ boolean removeIfOverriddenBy(
InjectableMethod possiblyOverridden = iterator.next();
if (overrides(method, possiblyOverridden.method)) {
boolean wasGuiceInject =
- !possiblyOverridden.jsr330 || possiblyOverridden.overrodeGuiceInject;
+ !possiblyOverridden.specInject || possiblyOverridden.overrodeGuiceInject;
if (injectableMethod != null) {
injectableMethod.overrodeGuiceInject = wasGuiceInject;
}
@@ -719,7 +724,7 @@ private static Set getInjectionPoints(
Annotation atInject = getAtInject(field);
if (atInject != null) {
InjectableField injectableField = new InjectableField(current, field, atInject);
- if (injectableField.jsr330 && Modifier.isFinal(field.getModifiers())) {
+ if (injectableField.specInject && Modifier.isFinal(field.getModifiers())) {
errors.cannotInjectFinalField(field);
}
injectableMembers.add(injectableField);
@@ -837,7 +842,7 @@ private static boolean isEligibleForInjection(Method method, boolean statics) {
private static boolean isValidMethod(InjectableMethod injectableMethod, Errors errors) {
boolean result = true;
- if (injectableMethod.jsr330) {
+ if (injectableMethod.specInject) {
Method method = injectableMethod.method;
if (Modifier.isAbstract(method.getModifiers())) {
errors.cannotInjectAbstractMethod(method);
diff --git a/core/src/com/google/inject/util/Providers.java b/core/src/com/google/inject/util/Providers.java
index e3a7dc136f..fd8c2c91d0 100644
--- a/core/src/com/google/inject/util/Providers.java
+++ b/core/src/com/google/inject/util/Providers.java
@@ -17,6 +17,7 @@
package com.google.inject.util;
import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.ImmutableSet.toImmutableSet;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableSet;
@@ -82,6 +83,19 @@ public int hashCode() {
}
}
+ /**
+ * Returns itself. This exists primarily to avoid ambiguous method reference compile errors when
+ * calling guicify with a Guice provider.
+ *
+ * @since vNext
+ * @deprecated Marked as deprecated as a hint to users that calling this is unnecessary, because
+ * the provider is already a guice Provider.
+ */
+ @Deprecated
+ public static Provider guicify(Provider provider) {
+ return provider;
+ }
+
/**
* Returns a Guice-friendly {@code com.google.inject.Provider} for the given JSR-330 {@code
* javax.inject.Provider}. The converse method is unnecessary, since Guice providers directly
@@ -100,21 +114,49 @@ public static Provider guicify(javax.inject.Provider provider) {
Set injectionPoints =
InjectionPoint.forInstanceMethodsAndFields(provider.getClass());
if (injectionPoints.isEmpty()) {
- return new GuicifiedProvider(delegate);
+ return new GuicifiedJSR330Provider(delegate);
} else {
Set> mutableDeps = Sets.newHashSet();
for (InjectionPoint ip : injectionPoints) {
mutableDeps.addAll(ip.getDependencies());
}
final Set> dependencies = ImmutableSet.copyOf(mutableDeps);
- return new GuicifiedProviderWithDependencies(dependencies, delegate);
+ return new GuicifiedJSR330ProviderWithDependencies(dependencies, delegate);
+ }
+ }
+
+ /**
+ * Returns a Guice-friendly {@code com.google.inject.Provider} for the given {@code
+ * jakarta.inject.Provider}. The converse method is unnecessary, since Guice providers directly
+ * implement the jakarta.inject.Provider interface.
+ *
+ * @since vNext
+ */
+ public static Provider guicify(jakarta.inject.Provider provider) {
+ if (provider instanceof Provider) {
+ return (Provider) provider;
+ }
+
+ jakarta.inject.Provider delegate = checkNotNull(provider, "provider");
+
+ // Ensure that we inject all injection points from the delegate provider.
+ Set injectionPoints =
+ InjectionPoint.forInstanceMethodsAndFields(provider.getClass());
+ if (injectionPoints.isEmpty()) {
+ return new GuicifiedJakartaProvider(delegate);
+ } else {
+ ImmutableSet> dependencies =
+ injectionPoints.stream()
+ .flatMap(ip -> ip.getDependencies().stream())
+ .collect(toImmutableSet());
+ return new GuicifiedJakartaProviderWithDependencies(dependencies, delegate);
}
}
- private static class GuicifiedProvider implements Provider {
+ private static class GuicifiedJSR330Provider implements Provider {
protected final javax.inject.Provider delegate;
- private GuicifiedProvider(javax.inject.Provider delegate) {
+ private GuicifiedJSR330Provider(javax.inject.Provider delegate) {
this.delegate = delegate;
}
@@ -130,8 +172,8 @@ public String toString() {
@Override
public boolean equals(Object obj) {
- return (obj instanceof GuicifiedProvider)
- && Objects.equal(delegate, ((GuicifiedProvider>) obj).delegate);
+ return (obj instanceof GuicifiedJSR330Provider)
+ && Objects.equal(delegate, ((GuicifiedJSR330Provider>) obj).delegate);
}
@Override
@@ -140,11 +182,40 @@ public int hashCode() {
}
}
- private static final class GuicifiedProviderWithDependencies extends GuicifiedProvider
- implements ProviderWithDependencies {
+ private static class GuicifiedJakartaProvider implements Provider {
+ protected final jakarta.inject.Provider delegate;
+
+ private GuicifiedJakartaProvider(jakarta.inject.Provider delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public T get() {
+ return delegate.get();
+ }
+
+ @Override
+ public String toString() {
+ return "guicified(" + delegate + ")";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return (obj instanceof GuicifiedJakartaProvider)
+ && Objects.equal(delegate, ((GuicifiedJakartaProvider>) obj).delegate);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(delegate);
+ }
+ }
+
+ private static final class GuicifiedJSR330ProviderWithDependencies
+ extends GuicifiedJSR330Provider implements ProviderWithDependencies {
private final Set> dependencies;
- private GuicifiedProviderWithDependencies(
+ private GuicifiedJSR330ProviderWithDependencies(
Set> dependencies, javax.inject.Provider delegate) {
super(delegate);
this.dependencies = dependencies;
@@ -161,4 +232,26 @@ public Set> getDependencies() {
return dependencies;
}
}
+
+ private static final class GuicifiedJakartaProviderWithDependencies
+ extends GuicifiedJakartaProvider implements ProviderWithDependencies {
+ private final Set> dependencies;
+
+ private GuicifiedJakartaProviderWithDependencies(
+ Set> dependencies, jakarta.inject.Provider delegate) {
+ super(delegate);
+ this.dependencies = dependencies;
+ }
+
+ @SuppressWarnings("unused")
+ @Inject
+ void initialize(Injector injector) {
+ injector.injectMembers(delegate);
+ }
+
+ @Override
+ public Set> getDependencies() {
+ return dependencies;
+ }
+ }
}
diff --git a/core/src/com/google/inject/util/Types.java b/core/src/com/google/inject/util/Types.java
index 32714cc7dc..54934a056d 100644
--- a/core/src/com/google/inject/util/Types.java
+++ b/core/src/com/google/inject/util/Types.java
@@ -144,4 +144,15 @@ public static ParameterizedType providerOf(Type providedType) {
public static Type javaxProviderOf(Type type) {
return Types.newParameterizedType(javax.inject.Provider.class, type);
}
+
+ /**
+ * Returns a type modelling a {@link jakarta.inject.Provider} that provides elements of type
+ * {@code elementType}.
+ *
+ * @return a {@link java.io.Serializable serializable} parameterized type.
+ * @since vNext
+ */
+ public static Type jakartaProviderOf(Type type) {
+ return Types.newParameterizedType(jakarta.inject.Provider.class, type);
+ }
}
diff --git a/core/test/com/google/inject/BUILD b/core/test/com/google/inject/BUILD
index d3893ffd6b..0090ae5e45 100644
--- a/core/test/com/google/inject/BUILD
+++ b/core/test/com/google/inject/BUILD
@@ -57,6 +57,7 @@ java_library(
"//third_party/java/guava/collect",
"//third_party/java/guava/testing",
"//third_party/java/guava/util/concurrent",
+ "//third_party/java/jakarta_inject",
"//third_party/java/jsr330_inject",
"//third_party/java/junit",
"//third_party/java/truth",
diff --git a/core/test/com/google/inject/name/NamedEquivalanceTest.java b/core/test/com/google/inject/name/NamedEquivalanceTest.java
index 67ca44f198..7db3c7b254 100644
--- a/core/test/com/google/inject/name/NamedEquivalanceTest.java
+++ b/core/test/com/google/inject/name/NamedEquivalanceTest.java
@@ -17,7 +17,9 @@
package com.google.inject.name;
import static com.google.inject.Asserts.assertContains;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import com.google.common.testing.EqualsTester;
import com.google.inject.AbstractModule;
import com.google.inject.ConfigurationException;
import com.google.inject.CreationException;
@@ -30,12 +32,15 @@
import com.google.inject.internal.Annotations;
import java.io.Serializable;
import java.lang.annotation.Annotation;
+import java.lang.annotation.Retention;
import java.util.Properties;
+import javax.inject.Qualifier;
import junit.framework.TestCase;
/**
- * Tests that {@code javax.inject.Named} and {@code com.google.inject.name.Named} are completely
- * interchangeable: bindings for one can be used to inject the other.
+ * Tests that {@code javax.inject.Named}, {@code jakarta.inject.Named} and {@code
+ * com.google.inject.name.Named} are completely interchangeable: bindings for one can be used to
+ * inject the other.
*
* @author cgdecker@gmail.com (Colin Decker)
*/
@@ -43,18 +48,36 @@ public class NamedEquivalanceTest extends TestCase {
private static final Module GUICE_BINDING_MODULE = moduleWithAnnotation(Names.named("foo"));
private static final Module JSR330_BINDING_MODULE = moduleWithAnnotation(new JsrNamed("foo"));
+ private static final Module JAKARTA_BINDING_MODULE =
+ moduleWithAnnotation(new JakartaNamed("foo"));
private static final Module GUICE_PROVIDER_METHOD_MODULE = getGuiceBindingProviderMethodModule();
private static final Module JSR330_PROVIDER_METHOD_MODULE =
getJsr330BindingProviderMethodModule();
+ private static final Module JAKARTA_PROVIDER_METHOD_MODULE =
+ getJakartaBindingProviderMethodModule();
- public void testKeysCreatedWithDifferentTypesAreEqual() {
- assertEquals(keyForAnnotation(new GuiceNamed("foo")), keyForAnnotation(new JsrNamed("foo")));
- assertEquals(keyForAnnotation(Names.named("foo")), keyForAnnotation(new GuiceNamed("foo")));
- assertEquals(keyForAnnotation(Names.named("foo")), keyForAnnotation(new JsrNamed("foo")));
+ @Qualifier
+ @Retention(RUNTIME)
+ @interface NotNamed {}
- assertEquals(
- keyForAnnotationType(com.google.inject.name.Named.class),
- keyForAnnotationType(javax.inject.Named.class));
+ public void testKeysCreatedWithDifferentTypesAreEqual() {
+ new EqualsTester()
+ .addEqualityGroup(
+ keyForAnnotation(new GuiceNamed("foo")),
+ keyForAnnotation(new JsrNamed("foo")),
+ keyForAnnotation(new JakartaNamed("foo")))
+ .addEqualityGroup(
+ keyForAnnotation(new GuiceNamed("bar")),
+ keyForAnnotation(new JsrNamed("bar")),
+ keyForAnnotation(new JakartaNamed("bar")))
+ .testEquals();
+
+ new EqualsTester()
+ .addEqualityGroup(
+ keyForAnnotationType(com.google.inject.name.Named.class),
+ keyForAnnotationType(javax.inject.Named.class),
+ keyForAnnotationType(jakarta.inject.Named.class))
+ .addEqualityGroup(keyForAnnotationType(NotNamed.class));
}
private static Key keyForAnnotation(Annotation annotation) {
@@ -65,35 +88,50 @@ private static Key keyForAnnotationType(Class extends Annotation> anno
return Key.get(String.class, annotationType);
}
- public void testBindingWithNamesCanInjectBothTypes() {
+ public void testBindingWithNamesCanInjectAllTypes() {
assertInjectionsSucceed(GUICE_BINDING_MODULE);
}
- public void testBindingWithJsr330AnnotationCanInjectBothTypes() {
+ public void testBindingWithJsr330AnnotationCanInjectAllTypes() {
assertInjectionsSucceed(JSR330_BINDING_MODULE);
}
- public void testBindingWithGuiceNamedAnnotatedProviderMethodCanInjectBothTypes() {
+ public void testBindingWithJakartannotationCanInjectAllTypes() {
+ assertInjectionsSucceed(JAKARTA_BINDING_MODULE);
+ }
+
+ public void testBindingWithGuiceNamedAnnotatedProviderMethodCanInjectAllTypes() {
assertInjectionsSucceed(GUICE_PROVIDER_METHOD_MODULE);
}
- public void testBindingWithJsr330NamedAnnotatedProviderMethodCanInjectBothTypes() {
+ public void testBindingWithJsr330NamedAnnotatedProviderMethodCanInjectAllTypes() {
assertInjectionsSucceed(JSR330_PROVIDER_METHOD_MODULE);
}
+ public void testBindingWithJakartaNamedAnnotatedProviderMethodCanInjectAllTypes() {
+ assertInjectionsSucceed(JAKARTA_PROVIDER_METHOD_MODULE);
+ }
+
public void testBindingDifferentTypesWithSameValueIsIgnored() {
- assertDuplicateBinding(GUICE_BINDING_MODULE, JSR330_BINDING_MODULE, false);
- assertDuplicateBinding(JSR330_BINDING_MODULE, GUICE_BINDING_MODULE, false);
+ assertDuplicateBinding(
+ false, GUICE_BINDING_MODULE, JSR330_BINDING_MODULE, JAKARTA_BINDING_MODULE);
}
public void testBindingDifferentTypesWithSameValueIsAnErrorWithProviderMethods() {
- assertDuplicateBinding(GUICE_PROVIDER_METHOD_MODULE, JSR330_PROVIDER_METHOD_MODULE, true);
- assertDuplicateBinding(JSR330_PROVIDER_METHOD_MODULE, GUICE_PROVIDER_METHOD_MODULE, true);
+ assertDuplicateBinding(true, GUICE_PROVIDER_METHOD_MODULE, JSR330_PROVIDER_METHOD_MODULE);
+ assertDuplicateBinding(true, GUICE_PROVIDER_METHOD_MODULE, JAKARTA_PROVIDER_METHOD_MODULE);
+ assertDuplicateBinding(true, JSR330_PROVIDER_METHOD_MODULE, GUICE_PROVIDER_METHOD_MODULE);
+ assertDuplicateBinding(true, JSR330_PROVIDER_METHOD_MODULE, JAKARTA_PROVIDER_METHOD_MODULE);
+ assertDuplicateBinding(true, JAKARTA_PROVIDER_METHOD_MODULE, JSR330_PROVIDER_METHOD_MODULE);
+ assertDuplicateBinding(true, JAKARTA_PROVIDER_METHOD_MODULE, GUICE_PROVIDER_METHOD_MODULE);
}
public void testBindingDifferentTypesWithSameValueIsAnErrorMixed() {
- assertDuplicateBinding(GUICE_BINDING_MODULE, JSR330_PROVIDER_METHOD_MODULE, true);
- assertDuplicateBinding(JSR330_BINDING_MODULE, GUICE_PROVIDER_METHOD_MODULE, true);
+ assertDuplicateBinding(true, GUICE_BINDING_MODULE, JSR330_PROVIDER_METHOD_MODULE);
+ assertDuplicateBinding(true, GUICE_BINDING_MODULE, JAKARTA_PROVIDER_METHOD_MODULE);
+ assertDuplicateBinding(true, JSR330_BINDING_MODULE, GUICE_PROVIDER_METHOD_MODULE);
+ assertDuplicateBinding(true, JSR330_BINDING_MODULE, JAKARTA_PROVIDER_METHOD_MODULE);
+ assertDuplicateBinding(true, JAKARTA_BINDING_MODULE, GUICE_PROVIDER_METHOD_MODULE);
}
public void testMissingBindingForGuiceNamedUsesSameTypeInErrorMessage() {
@@ -104,6 +142,10 @@ public void testMissingBindingForJsr330NamedUsesSameTypeInErrorMessage() {
assertMissingBindingErrorMessageUsesType(Jsr330NamedClient.class);
}
+ public void testMissingBindingForJakartaamedUsesSameTypeInErrorMessage() {
+ assertMissingBindingErrorMessageUsesType(Jsr330NamedClient.class);
+ }
+
public void testBindPropertiesWorksWithJsr330() {
assertInjectionsSucceed(
new AbstractModule() {
@@ -130,9 +172,9 @@ private static void assertMissingBindingErrorMessageUsesType(Class> clientType
}
}
- private static void assertDuplicateBinding(Module a, Module b, boolean fails) {
+ private static void assertDuplicateBinding(boolean fails, Module... modules) {
try {
- Guice.createInjector(a, b);
+ Guice.createInjector(modules);
if (fails) {
fail("should have thrown CreationException");
}
@@ -162,12 +204,29 @@ private static void assertInjectionsSucceed(Module module) {
Injector injector = Guice.createInjector(module);
assertInjected(
injector.getInstance(GuiceNamedClient.class),
- injector.getInstance(Jsr330NamedClient.class));
+ injector.getInstance(Jsr330NamedClient.class),
+ injector.getInstance(JakartaNamedClient.class));
}
- private static void assertInjected(GuiceNamedClient guiceClient, Jsr330NamedClient jsr330Client) {
+ private static void assertInjected(
+ GuiceNamedClient guiceClient,
+ Jsr330NamedClient jsr330Client,
+ JakartaNamedClient jakartaClient) {
assertEquals("bar", guiceClient.foo);
assertEquals("bar", jsr330Client.foo);
+ assertEquals("bar", jakartaClient.foo);
+ }
+
+ private static Module getJakartaBindingProviderMethodModule() {
+ return new AbstractModule() {
+
+ @SuppressWarnings("unused")
+ @Provides
+ @jakarta.inject.Named("foo")
+ String provideFoo() {
+ return "bar";
+ }
+ };
}
private static Module getJsr330BindingProviderMethodModule() {
@@ -206,6 +265,57 @@ private static class Jsr330NamedClient {
String foo;
}
+ private static class JakartaNamedClient {
+ @Inject
+ @jakarta.inject.Named("foo")
+ String foo;
+ }
+
+ private static class JakartaNamed implements jakarta.inject.Named, Serializable {
+ private final String value;
+
+ public JakartaNamed(String value) {
+ this.value = value;
+ }
+
+ @Override
+ public String value() {
+ return this.value;
+ }
+
+ @Override
+ public int hashCode() {
+ // This is specified in java.lang.Annotation.
+ return (127 * "value".hashCode()) ^ value.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof jakarta.inject.Named)) {
+ return false;
+ }
+
+ jakarta.inject.Named other = (jakarta.inject.Named) o;
+ return value.equals(other.value());
+ }
+
+ @Override
+ public String toString() {
+ return "@"
+ + jakarta.inject.Named.class.getName()
+ + "(value="
+ + Annotations.memberValueString("value", value)
+ + ")";
+ }
+
+ @Override
+ public Class extends Annotation> annotationType() {
+ return jakarta.inject.Named.class;
+ }
+
+ private static final long serialVersionUID = 0;
+ }
+
private static class JsrNamed implements javax.inject.Named, Serializable {
private final String value;
diff --git a/core/test/com/google/inject/spi/InjectorSpiTest.java b/core/test/com/google/inject/spi/InjectorSpiTest.java
index 2f3b82190d..dba27424fa 100644
--- a/core/test/com/google/inject/spi/InjectorSpiTest.java
+++ b/core/test/com/google/inject/spi/InjectorSpiTest.java
@@ -124,7 +124,8 @@ protected Void visitOther(Element element) {
}
assertThat(actualKeys)
.containsExactly(Key.get(Stage.class), Key.get(Injector.class), Key.get(Logger.class));
- assertThat(scopes).hasSize(2); // singleton for javax.inject.Singleton & c.g.i.Singleton
+ // singleton for javax.inject.Singleton, jakarta.inject.Singleton & c.g.i.Singleton
+ assertThat(scopes).hasSize(3);
assertThat(typeConverters).hasSize(10); // all the built-in converters.
}
diff --git a/core/test/com/google/inject/util/ProvidersTest.java b/core/test/com/google/inject/util/ProvidersTest.java
index 7c3c8b7b2e..e10adbb6e6 100644
--- a/core/test/com/google/inject/util/ProvidersTest.java
+++ b/core/test/com/google/inject/util/ProvidersTest.java
@@ -52,11 +52,18 @@ public void testGuicifyEquality() {
new EqualsTester()
.addEqualityGroup(
Providers.guicify(new JavaxProvider(10)), Providers.guicify(new JavaxProvider(10)))
+ .addEqualityGroup(
+ Providers.guicify(new JakartaProvider(10)), Providers.guicify(new JakartaProvider(10)))
.addEqualityGroup(
Providers.guicify(new JavaxProvider(11)), Providers.guicify(new JavaxProvider(11)))
+ .addEqualityGroup(
+ Providers.guicify(new JakartaProvider(11)), Providers.guicify(new JakartaProvider(11)))
.addEqualityGroup(
Providers.guicify(new JavaxProviderWithDependencies()),
Providers.guicify(new JavaxProviderWithDependencies()))
+ .addEqualityGroup(
+ Providers.guicify(new JakartaProviderWithDependencies()),
+ Providers.guicify(new JakartaProviderWithDependencies()))
.testEquals();
}
@@ -83,6 +90,29 @@ public boolean equals(Object obj) {
}
}
+ private static class JakartaProvider implements jakarta.inject.Provider {
+ private final int value;
+
+ public JakartaProvider(int value) {
+ this.value = value;
+ }
+
+ @Override
+ public Integer get() {
+ return value;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(value);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return (obj instanceof JakartaProvider) && (value == ((JakartaProvider) obj).value);
+ }
+ }
+
private static class JavaxProviderWithDependencies implements javax.inject.Provider {
private int value;
@@ -106,4 +136,28 @@ public boolean equals(Object obj) {
return (obj instanceof JavaxProviderWithDependencies);
}
}
+
+ private static class JakartaProviderWithDependencies implements jakarta.inject.Provider {
+ private int value;
+
+ @Inject
+ void setValue(int value) {
+ this.value = value;
+ }
+
+ @Override
+ public Integer get() {
+ return value;
+ }
+
+ @Override
+ public int hashCode() {
+ return 42;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return (obj instanceof JakartaProviderWithDependencies);
+ }
+ }
}
diff --git a/core/test/com/googlecode/guice/BUILD b/core/test/com/googlecode/guice/BUILD
index 6e78470a82..cd13b76e9b 100644
--- a/core/test/com/googlecode/guice/BUILD
+++ b/core/test/com/googlecode/guice/BUILD
@@ -10,24 +10,54 @@ package(
java_library(
name = "tests",
- srcs = glob([
- "**/*.java",
- ]),
+ srcs = glob(
+ ["**/*.java"],
+ exclude = ["*Tck.java"],
+ ),
deps = [
"//core/src/com/google/inject",
"//core/test/com/google/inject:testsupport",
"//third_party/java/aopalliance",
"//third_party/java/guava/testing",
+ "//third_party/java/jakarta_inject",
"//third_party/java/jsr330_inject",
- "//third_party/java/jsr330_inject:tck",
"//third_party/java/junit",
"@maven//:biz_aQute_bnd",
"@maven//:org_apache_felix_org_apache_felix_framework",
],
)
+# We split the TCK tests out from the normal libraries, because the jakarta.inject TCK uses the same
+# packages as the javax.inject TCK, so the two cannot be in the same classloader.
+
+java_library(
+ name = "jakarta_tck_test",
+ srcs = ["GuiceJakartaTck.java"],
+ deps = [
+ "//core/src/com/google/inject",
+ "//third_party/java/jakarta_inject",
+ "//third_party/java/jakarta_inject_tck",
+ "//third_party/java/junit",
+ ],
+)
+
+java_library(
+ name = "javax_inject_tck_test",
+ srcs = ["GuiceTck.java"],
+ deps = [
+ "//core/src/com/google/inject",
+ "//third_party/java/jsr330_inject",
+ "//third_party/java/jsr330_inject:tck",
+ "//third_party/java/junit",
+ ],
+)
+
guice_test_suites(
name = "gen_tests",
+ srcs = glob(
+ ["**/*.java"],
+ exclude = ["*Tck.java"],
+ ),
jvm_flags = [
# those 2 options are required for some tests that checks stack traces
"-XX:+UnlockDiagnosticVMOptions",
@@ -42,6 +72,10 @@ guice_test_suites(
[guice_test_suites(
name = "gen_tests_class_loading_%s" % custom_class_loading_option,
+ srcs = glob(
+ ["**/*.java"],
+ exclude = ["*Tck.java"],
+ ),
args = [
"--guice_custom_class_loading=%s" % custom_class_loading_option,
],
@@ -61,3 +95,19 @@ guice_test_suites(
"CHILD",
"ANONYMOUS",
]]
+
+guice_test_suites(
+ name = "gen_jakarta_tck_tests",
+ srcs = ["GuiceJakartaTck.java"],
+ sizes = ["small"],
+ suffix = "_jakarta_tck",
+ deps = [":jakarta_tck_test"],
+)
+
+guice_test_suites(
+ name = "gen_javax_inject_tck_tests",
+ srcs = ["GuiceTck.java"],
+ sizes = ["small"],
+ suffix = "_javax_inject_tck",
+ deps = [":javax_inject_tck_test"],
+)
diff --git a/core/test/com/googlecode/guice/GuiceJakartaTck.java b/core/test/com/googlecode/guice/GuiceJakartaTck.java
new file mode 100644
index 0000000000..a6d951537e
--- /dev/null
+++ b/core/test/com/googlecode/guice/GuiceJakartaTck.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2009 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.googlecode.guice;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Guice;
+import com.google.inject.Provides;
+import jakarta.inject.Named;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import org.atinject.tck.Tck;
+import org.atinject.tck.auto.Car;
+import org.atinject.tck.auto.Convertible;
+import org.atinject.tck.auto.Drivers;
+import org.atinject.tck.auto.DriversSeat;
+import org.atinject.tck.auto.Engine;
+import org.atinject.tck.auto.FuelTank;
+import org.atinject.tck.auto.Seat;
+import org.atinject.tck.auto.Tire;
+import org.atinject.tck.auto.V8Engine;
+import org.atinject.tck.auto.accessories.Cupholder;
+import org.atinject.tck.auto.accessories.SpareTire;
+
+public class GuiceJakartaTck extends TestCase {
+
+ public static Test suite() {
+ return Tck.testsFor(
+ Guice.createInjector(
+ new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(Car.class).to(Convertible.class);
+ bind(Seat.class).annotatedWith(Drivers.class).to(DriversSeat.class);
+ bind(Engine.class).to(V8Engine.class);
+ bind(Cupholder.class);
+ bind(Tire.class);
+ bind(FuelTank.class);
+ requestStaticInjection(Convertible.class, SpareTire.class);
+ }
+
+ @Provides
+ @Named("spare")
+ Tire provideSpareTire(SpareTire spare) {
+ return spare;
+ }
+ })
+ .getInstance(Car.class),
+ true,
+ true);
+ }
+}
diff --git a/core/test/com/googlecode/guice/JakartaTest.java b/core/test/com/googlecode/guice/JakartaTest.java
new file mode 100644
index 0000000000..72e605429b
--- /dev/null
+++ b/core/test/com/googlecode/guice/JakartaTest.java
@@ -0,0 +1,538 @@
+/*
+ * Copyright (C) 2023 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.googlecode.guice;
+
+import static com.google.inject.Asserts.assertContains;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Binding;
+import com.google.inject.CreationException;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.Scope;
+import com.google.inject.Scopes;
+import com.google.inject.Stage;
+import com.google.inject.TypeLiteral;
+import com.google.inject.name.Names;
+import com.google.inject.spi.Dependency;
+import com.google.inject.spi.HasDependencies;
+import com.google.inject.util.Providers;
+import jakarta.inject.Inject;
+import jakarta.inject.Named;
+import jakarta.inject.Provider;
+import jakarta.inject.Qualifier;
+import jakarta.inject.Singleton;
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Retention;
+import java.util.Set;
+import junit.framework.TestCase;
+
+public class JakartaTest extends TestCase {
+
+ private final B b = new B();
+ private final C c = new C();
+ private final D d = new D();
+ private final E e = new E();
+
+ @Override
+ protected void setUp() throws Exception {
+ J.nextInstanceId = 0;
+ K.nextInstanceId = 0;
+ }
+
+ public void testInject() {
+ Injector injector =
+ Guice.createInjector(
+ new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(B.class).toInstance(b);
+ bind(C.class).toInstance(c);
+ bind(D.class).toInstance(d);
+ bind(E.class).toInstance(e);
+ bind(A.class);
+ }
+ });
+
+ A a = injector.getInstance(A.class);
+ assertSame(b, a.b);
+ assertSame(c, a.c);
+ assertSame(d, a.d);
+ assertSame(e, a.e);
+ }
+
+ public void testQualifiedInject() {
+ Injector injector =
+ Guice.createInjector(
+ new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(B.class).annotatedWith(Names.named("jodie")).toInstance(b);
+ bind(C.class).annotatedWith(Red.class).toInstance(c);
+ bind(D.class).annotatedWith(RED).toInstance(d);
+ bind(E.class).annotatedWith(Names.named("jesse")).toInstance(e);
+ bind(F.class);
+ }
+ });
+
+ F f = injector.getInstance(F.class);
+ assertSame(b, f.b);
+ assertSame(c, f.c);
+ assertSame(d, f.d);
+ assertSame(e, f.e);
+ }
+
+ public void testProviderInject() {
+ Injector injector =
+ Guice.createInjector(
+ new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(B.class).annotatedWith(Names.named("jodie")).toInstance(b);
+ bind(C.class).toInstance(c);
+ bind(D.class).annotatedWith(RED).toInstance(d);
+ bind(E.class).toInstance(e);
+ bind(G.class);
+ }
+ });
+
+ G g = injector.getInstance(G.class);
+ assertSame(b, g.bProvider.get());
+ assertSame(c, g.cProvider.get());
+ assertSame(d, g.dProvider.get());
+ assertSame(e, g.eProvider.get());
+ }
+
+ public void testScopeAnnotation() {
+ final TestScope scope = new TestScope();
+
+ Injector injector =
+ Guice.createInjector(
+ new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(B.class).in(scope);
+ bind(C.class).in(TestScoped.class);
+ bindScope(TestScoped.class, scope);
+ }
+ });
+
+ B b = injector.getInstance(B.class);
+ assertSame(b, injector.getInstance(B.class));
+ assertSame(b, injector.getInstance(B.class));
+
+ C c = injector.getInstance(C.class);
+ assertSame(c, injector.getInstance(C.class));
+ assertSame(c, injector.getInstance(C.class));
+
+ H h = injector.getInstance(H.class);
+ assertSame(h, injector.getInstance(H.class));
+ assertSame(h, injector.getInstance(H.class));
+
+ scope.reset();
+
+ assertNotSame(b, injector.getInstance(B.class));
+ assertNotSame(c, injector.getInstance(C.class));
+ assertNotSame(h, injector.getInstance(H.class));
+ }
+
+ public void testSingleton() {
+ Injector injector =
+ Guice.createInjector(
+ new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(B.class).in(Singleton.class);
+ }
+ });
+
+ B b = injector.getInstance(B.class);
+ assertSame(b, injector.getInstance(B.class));
+ assertSame(b, injector.getInstance(B.class));
+
+ J j = injector.getInstance(J.class);
+ assertSame(j, injector.getInstance(J.class));
+ assertSame(j, injector.getInstance(J.class));
+ }
+
+ public void testEagerSingleton() {
+ Guice.createInjector(
+ Stage.PRODUCTION,
+ new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(J.class);
+ bind(K.class).in(Singleton.class);
+ }
+ });
+
+ assertEquals(1, J.nextInstanceId);
+ assertEquals(1, K.nextInstanceId);
+ }
+
+ public void testScopesIsSingleton() {
+ Injector injector =
+ Guice.createInjector(
+ new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(J.class);
+ bind(K.class).in(Singleton.class);
+ }
+ });
+
+ assertTrue(Scopes.isSingleton(injector.getBinding(J.class)));
+ assertTrue(Scopes.isSingleton(injector.getBinding(K.class)));
+ }
+
+ public void testInjectingFinalFieldsIsForbidden() {
+ try {
+ Guice.createInjector(
+ new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(L.class);
+ }
+ });
+ fail();
+ } catch (CreationException expected) {
+ assertContains(expected.getMessage(), "Injected field JakartaTest$L.b cannot be final.");
+ }
+ }
+
+ public void testInjectingAbstractMethodsIsForbidden() {
+ try {
+ Guice.createInjector(
+ new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(M.class);
+ }
+ });
+ fail();
+ } catch (CreationException expected) {
+ assertContains(
+ expected.getMessage(),
+ "Injected method JakartaTest$AbstractM.setB() cannot be abstract.");
+ }
+ }
+
+ public void testInjectingMethodsWithTypeParametersIsForbidden() {
+ try {
+ Guice.createInjector(
+ new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(N.class);
+ }
+ });
+ fail();
+ } catch (CreationException expected) {
+ assertContains(
+ expected.getMessage(), "Injected method JakartaTest$N.setB() cannot declare type ");
+ }
+ }
+
+ public void testInjectingMethodsWithNonVoidReturnTypes() {
+ Guice.createInjector(
+ new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(P.class);
+ }
+ });
+ }
+
+ // TODO(sameb): Uncomment this when Guice supports toProvider on jakarta.inject.Provider types.
+ // /**
+ // * This test verifies that we can compile bindings to provider instances whose compile-time
+ // type
+ // * implements jakarta.inject.Provider but not com.google.inject.Provider
+ // */
+ // public void testBindProviderClass() {
+ // Injector injector =
+ // Guice.createInjector(
+ // new AbstractModule() {
+ // @Override
+ // protected void configure() {
+ // bind(B.class).toProvider(BProvider.class);
+ // bind(B.class).annotatedWith(Names.named("1")).toProvider(BProvider.class);
+ //
+ // bind(B.class).annotatedWith(Names.named("2")).toProvider(Key.get(BProvider.class));
+ // bind(B.class)
+ // .annotatedWith(Names.named("3"))
+ // .toProvider(TypeLiteral.get(BProvider.class));
+ // }
+ // });
+ //
+ // injector.getInstance(Key.get(B.class));
+ // injector.getInstance(Key.get(B.class, Names.named("1")));
+ // injector.getInstance(Key.get(B.class, Names.named("2")));
+ // injector.getInstance(Key.get(B.class, Names.named("3")));
+ // }
+
+ public void testGuicifyJakartaProvider() {
+ Provider jakartaProvider =
+ new Provider() {
+ @Override
+ public String get() {
+ return "A";
+ }
+
+ @Override
+ public String toString() {
+ return "jakartaProvider";
+ }
+ };
+
+ com.google.inject.Provider guicified = Providers.guicify(jakartaProvider);
+ assertEquals("guicified(jakartaProvider)", guicified.toString());
+ assertEquals("A", guicified.get());
+
+ // when you guicify the Guice-friendly, it's a no-op
+ assertSame(guicified, Providers.guicify(guicified));
+
+ assertFalse(guicified instanceof HasDependencies);
+ }
+
+ public void testGuicifyWithDependencies() {
+ Provider jakartaProvider =
+ new Provider() {
+ @Inject double d;
+ int i;
+
+ @Inject
+ void injectMe(int i) {
+ this.i = i;
+ }
+
+ @Override
+ public String get() {
+ return d + "-" + i;
+ }
+ };
+
+ final com.google.inject.Provider guicified = Providers.guicify(jakartaProvider);
+ assertTrue(guicified instanceof HasDependencies);
+ Set> actual = ((HasDependencies) guicified).getDependencies();
+ validateDependencies(actual, jakartaProvider.getClass());
+
+ Injector injector =
+ Guice.createInjector(
+ new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(String.class).toProvider(guicified);
+ bind(int.class).toInstance(1);
+ bind(double.class).toInstance(2.0d);
+ }
+ });
+
+ Binding binding = injector.getBinding(String.class);
+ assertEquals("2.0-1", binding.getProvider().get());
+ validateDependencies(actual, jakartaProvider.getClass());
+ }
+
+ private void validateDependencies(Set> actual, Class> owner) {
+ assertEquals(actual.toString(), 2, actual.size());
+ Dependency> dDep = null;
+ Dependency> iDep = null;
+ for (Dependency> dep : actual) {
+ if (dep.getKey().equals(Key.get(Double.class))) {
+ dDep = dep;
+ } else if (dep.getKey().equals(Key.get(Integer.class))) {
+ iDep = dep;
+ }
+ }
+ assertNotNull(dDep);
+ assertNotNull(iDep);
+ assertEquals(TypeLiteral.get(owner), dDep.getInjectionPoint().getDeclaringType());
+ assertEquals("d", dDep.getInjectionPoint().getMember().getName());
+ assertEquals(-1, dDep.getParameterIndex());
+
+ assertEquals(TypeLiteral.get(owner), iDep.getInjectionPoint().getDeclaringType());
+ assertEquals("injectMe", iDep.getInjectionPoint().getMember().getName());
+ assertEquals(0, iDep.getParameterIndex());
+ }
+
+ static class A {
+ final B b;
+ @Inject C c;
+ D d;
+ E e;
+
+ @Inject
+ A(B b) {
+ this.b = b;
+ }
+
+ @Inject
+ void injectD(D d, E e) {
+ this.d = d;
+ this.e = e;
+ }
+ }
+
+ static class B {}
+
+ static class C {}
+
+ static class D {}
+
+ static class E {}
+
+ static class F {
+ final B b;
+ @Inject @Red C c;
+ D d;
+ E e;
+
+ @Inject
+ F(@Named("jodie") B b) {
+ this.b = b;
+ }
+
+ @Inject
+ void injectD(@Red D d, @Named("jesse") E e) {
+ this.d = d;
+ this.e = e;
+ }
+ }
+
+ @Qualifier
+ @Retention(RUNTIME)
+ @interface Red {}
+
+ public static final Red RED =
+ new Red() {
+ @Override
+ public Class extends Annotation> annotationType() {
+ return Red.class;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof Red;
+ }
+
+ @Override
+ public int hashCode() {
+ return 0;
+ }
+ };
+
+ static class G {
+ final Provider bProvider;
+ @Inject Provider cProvider;
+ Provider dProvider;
+ Provider eProvider;
+
+ @Inject
+ G(@Named("jodie") Provider bProvider) {
+ this.bProvider = bProvider;
+ }
+
+ @Inject
+ void injectD(@Red Provider dProvider, Provider eProvider) {
+ this.dProvider = dProvider;
+ this.eProvider = eProvider;
+ }
+ }
+
+ @jakarta.inject.Scope
+ @Retention(RUNTIME)
+ @interface TestScoped {}
+
+ static class TestScope implements Scope {
+ private int now = 0;
+
+ @Override
+ public com.google.inject.Provider scope(
+ Key key, final com.google.inject.Provider unscoped) {
+ return new com.google.inject.Provider() {
+ private T value;
+ private int snapshotTime = -1;
+
+ @Override
+ public T get() {
+ if (snapshotTime != now) {
+ value = unscoped.get();
+ snapshotTime = now;
+ }
+ return value;
+ }
+ };
+ }
+
+ public void reset() {
+ now++;
+ }
+ }
+
+ @TestScoped
+ static class H {}
+
+ @Singleton
+ static class J {
+ static int nextInstanceId = 0;
+ int instanceId = nextInstanceId++;
+ }
+
+ static class K {
+ static int nextInstanceId = 0;
+ int instanceId = nextInstanceId++;
+ }
+
+ static class L {
+ @SuppressWarnings("InjectJakartaInjectOnFinalField")
+ @Inject
+ final B b = null;
+ }
+
+ abstract static class AbstractM {
+ @SuppressWarnings("JakartaInjectOnAbstractMethod")
+ @Inject
+ abstract void setB(B b);
+ }
+
+ static class M extends AbstractM {
+ @Override
+ @SuppressWarnings("OverridesJakartaInjectableMethod")
+ void setB(B b) {}
+ }
+
+ static class N {
+ @Inject
+ void setB(B b) {}
+ }
+
+ static class P {
+ @Inject
+ B setB(B b) {
+ return b;
+ }
+ }
+
+ static class BProvider implements Provider {
+ @Override
+ public B get() {
+ return new B();
+ }
+ }
+}
diff --git a/extensions/assistedinject/src/com/google/inject/assistedinject/BUILD b/extensions/assistedinject/src/com/google/inject/assistedinject/BUILD
index ba92f52bba..17bc7dd29d 100644
--- a/extensions/assistedinject/src/com/google/inject/assistedinject/BUILD
+++ b/extensions/assistedinject/src/com/google/inject/assistedinject/BUILD
@@ -24,6 +24,7 @@ java_library(
"//third_party/java/error_prone:annotations",
"//third_party/java/guava/base",
"//third_party/java/guava/collect",
+ "//third_party/java/jakarta_inject",
"//third_party/java/jsr330_inject",
],
)
diff --git a/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider2.java b/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider2.java
index 91ba86b1b0..1cb407eab8 100644
--- a/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider2.java
+++ b/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider2.java
@@ -304,7 +304,8 @@ public TypeLiteral> getImplementationType() {
Key> paramKey = Annotations.getKey(param, method, paramAnnotations[p++], errors);
Class> underlylingType = paramKey.getTypeLiteral().getRawType();
if (underlylingType.equals(Provider.class)
- || underlylingType.equals(javax.inject.Provider.class)) {
+ || underlylingType.equals(javax.inject.Provider.class)
+ || underlylingType.equals(jakarta.inject.Provider.class)) {
errors.addMessage(
"A Provider may not be a type in a factory method of an AssistedInject."
+ "\n Offending instance is parameter [%s] with key [%s] on method [%s]",
diff --git a/extensions/assistedinject/test/com/google/inject/assistedinject/BUILD b/extensions/assistedinject/test/com/google/inject/assistedinject/BUILD
index a05c1a11df..6d02380642 100644
--- a/extensions/assistedinject/test/com/google/inject/assistedinject/BUILD
+++ b/extensions/assistedinject/test/com/google/inject/assistedinject/BUILD
@@ -22,6 +22,7 @@ java_library(
"//third_party/java/aopalliance",
"//third_party/java/guava/base",
"//third_party/java/guava/collect",
+ "//third_party/java/jakarta_inject",
"//third_party/java/jsr330_inject",
"//third_party/java/junit",
"//third_party/java/truth",
diff --git a/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryProvider2Test.java b/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryProvider2Test.java
index 96cd219bda..f4b5383cf3 100644
--- a/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryProvider2Test.java
+++ b/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryProvider2Test.java
@@ -756,6 +756,56 @@ protected void configure() {
}
}
+ interface JakartaProviderBasedColoredCarFactory {
+ Car createCar(
+ jakarta.inject.Provider colorProvider,
+ jakarta.inject.Provider stringProvider);
+
+ Mustang createMustang(@Assisted("color") jakarta.inject.Provider colorProvider);
+ }
+
+ @Test
+ public void testAssistedJakartaProviderIsDisallowed() {
+ try {
+ Guice.createInjector(
+ new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(JakartaProviderBasedColoredCarFactory.class)
+ .toProvider(
+ FactoryProvider.newFactory(
+ JakartaProviderBasedColoredCarFactory.class, Subaru.class));
+ }
+ });
+ fail();
+ } catch (CreationException expected) {
+ assertEquals(expected.getMessage(), 4, expected.getErrorMessages().size());
+ assertContains(
+ expected.getMessage(),
+ ") A Provider may not be a type in a factory method of an AssistedInject.\n"
+ + " Offending instance is parameter [1] with key"
+ + " [Provider annotated with @Assisted("
+ + Annotations.memberValueString("value", "color")
+ + ")]"
+ + " on method"
+ + " [FactoryProvider2Test$JakartaProviderBasedColoredCarFactory.createMustang()]");
+ assertContains(
+ expected.getMessage(),
+ ") A Provider may not be a type in a factory method of an AssistedInject.",
+ "Offending instance is parameter [1] with key [Provider] on"
+ + " method [FactoryProvider2Test$JakartaProviderBasedColoredCarFactory.createCar()]");
+ assertContains(
+ expected.getMessage(),
+ ") A Provider may not be a type in a factory method of an AssistedInject.",
+ "Offending instance is parameter [2] with key [Provider] on method"
+ + " [FactoryProvider2Test$JakartaProviderBasedColoredCarFactory.createCar()]");
+ assertContains(
+ expected.getMessage(),
+ "No implementation for FactoryProvider2Test$JakartaProviderBasedColoredCarFactory was"
+ + " bound.");
+ }
+ }
+
@Test
public void testFactoryUseBeforeInitialization() {
ColoredCarFactory carFactory =
diff --git a/extensions/dagger-adapter/src/com/google/inject/daggeradapter/Annotations.java b/extensions/dagger-adapter/src/com/google/inject/daggeradapter/Annotations.java
index d365b4c191..e92c9d0034 100644
--- a/extensions/dagger-adapter/src/com/google/inject/daggeradapter/Annotations.java
+++ b/extensions/dagger-adapter/src/com/google/inject/daggeradapter/Annotations.java
@@ -22,9 +22,20 @@
import java.lang.reflect.AnnotatedElement;
import java.util.Arrays;
import java.util.Optional;
+import java.util.Set;
/** Extensions for {@link Annotation}. */
final class Annotations {
+ static Optional getAnnotatedAnnotation(
+ AnnotatedElement element, Set> annotationClasses) {
+ return Arrays.stream(element.getAnnotations())
+ .filter(
+ annotation ->
+ annotationClasses.stream()
+ .anyMatch(an -> annotation.annotationType().isAnnotationPresent(an)))
+ .collect(toOptional());
+ }
+
static Optional getAnnotatedAnnotation(
AnnotatedElement element, Class extends Annotation> annotationClass) {
return Arrays.stream(element.getAnnotations())
diff --git a/extensions/dagger-adapter/src/com/google/inject/daggeradapter/BUILD b/extensions/dagger-adapter/src/com/google/inject/daggeradapter/BUILD
index cb87e8aaed..bd7f8ac630 100644
--- a/extensions/dagger-adapter/src/com/google/inject/daggeradapter/BUILD
+++ b/extensions/dagger-adapter/src/com/google/inject/daggeradapter/BUILD
@@ -24,6 +24,7 @@ java_library(
"//third_party/java/dagger",
"//third_party/java/guava/base",
"//third_party/java/guava/collect",
+ "//third_party/java/jakarta_inject",
"//third_party/java/jsr330_inject",
],
)
diff --git a/extensions/dagger-adapter/src/com/google/inject/daggeradapter/Keys.java b/extensions/dagger-adapter/src/com/google/inject/daggeradapter/Keys.java
index ce523188ec..c2e7ad9d31 100644
--- a/extensions/dagger-adapter/src/com/google/inject/daggeradapter/Keys.java
+++ b/extensions/dagger-adapter/src/com/google/inject/daggeradapter/Keys.java
@@ -18,17 +18,20 @@
import static com.google.inject.daggeradapter.Annotations.getAnnotatedAnnotation;
+import com.google.common.collect.ImmutableSet;
import com.google.inject.Key;
import java.lang.annotation.Annotation;
import java.lang.reflect.Parameter;
import java.lang.reflect.Type;
import java.util.Optional;
-import javax.inject.Qualifier;
/** Utility methods for creating {@link Key}s. */
final class Keys {
+ private static final ImmutableSet> QUALIFIERS =
+ ImmutableSet.of(javax.inject.Qualifier.class, jakarta.inject.Qualifier.class);
+
static Key> parameterKey(Parameter parameter) {
- Optional qualifier = getAnnotatedAnnotation(parameter, Qualifier.class);
+ Optional qualifier = getAnnotatedAnnotation(parameter, QUALIFIERS);
Type type = parameter.getParameterizedType();
return qualifier.isPresent() ? Key.get(type, qualifier.get()) : Key.get(type);
}
diff --git a/extensions/dagger-adapter/test/com/google/inject/daggeradapter/BUILD b/extensions/dagger-adapter/test/com/google/inject/daggeradapter/BUILD
index 91a60ec0c2..9acd27104a 100644
--- a/extensions/dagger-adapter/test/com/google/inject/daggeradapter/BUILD
+++ b/extensions/dagger-adapter/test/com/google/inject/daggeradapter/BUILD
@@ -18,6 +18,7 @@ java_library(
"//third_party/java/dagger:producers",
"//third_party/java/guava/base",
"//third_party/java/guava/collect",
+ "//third_party/java/jakarta_inject",
"//third_party/java/jsr330_inject",
"//third_party/java/junit",
"//third_party/java/truth",
diff --git a/extensions/dagger-adapter/test/com/google/inject/daggeradapter/BindsTest.java b/extensions/dagger-adapter/test/com/google/inject/daggeradapter/BindsTest.java
index a5f57ef4ae..c8f38e8f66 100644
--- a/extensions/dagger-adapter/test/com/google/inject/daggeradapter/BindsTest.java
+++ b/extensions/dagger-adapter/test/com/google/inject/daggeradapter/BindsTest.java
@@ -164,4 +164,43 @@ public void testQualifiers() {
assertThat(qualifiedBinds).hasProvidedValueThat().isEqualTo("qualifiers");
assertThat(qualifiedBinds).hasSource(QualifiedBinds.class, "bindsToProvides", String.class);
}
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @jakarta.inject.Qualifier
+ @interface JakartaProvidesQualifier {}
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @jakarta.inject.Qualifier
+ @interface JakartaBindsQualifier {}
+
+ @Module
+ interface JakartaQualifiedBinds {
+ @Provides
+ @JakartaProvidesQualifier
+ static String provides() {
+ return "jakarta qualified!";
+ }
+
+ @Binds
+ @JakartaBindsQualifier
+ String bindsToProvides(@JakartaProvidesQualifier String provides);
+
+ @Binds
+ String unqualifiedToBinds(@JakartaBindsQualifier String binds);
+ }
+
+ public void testJakartaQualifiers() {
+ Injector injector = Guice.createInjector(DaggerAdapter.from(JakartaQualifiedBinds.class));
+
+ Binding stringBinding = injector.getBinding(String.class);
+ assertThat(stringBinding).hasProvidedValueThat().isEqualTo("jakarta qualified!");
+ assertThat(stringBinding)
+ .hasSource(JakartaQualifiedBinds.class, "unqualifiedToBinds", String.class);
+
+ Binding qualifiedBinds =
+ injector.getBinding(Key.get(String.class, JakartaBindsQualifier.class));
+ assertThat(qualifiedBinds).hasProvidedValueThat().isEqualTo("jakarta qualified!");
+ assertThat(qualifiedBinds)
+ .hasSource(JakartaQualifiedBinds.class, "bindsToProvides", String.class);
+ }
}
diff --git a/extensions/persist/src/com/google/inject/persist/BUILD b/extensions/persist/src/com/google/inject/persist/BUILD
index b0e35d1f33..b918644afd 100644
--- a/extensions/persist/src/com/google/inject/persist/BUILD
+++ b/extensions/persist/src/com/google/inject/persist/BUILD
@@ -26,6 +26,7 @@ java_library(
"//third_party/java/guava/annotations",
"//third_party/java/guava/base",
"//third_party/java/guava/collect",
+ "//third_party/java/jakarta_inject",
"//third_party/java/javax_persistence",
"//third_party/java/jsr330_inject",
"//third_party/java/servlet/servlet_api",
diff --git a/extensions/persist/src/com/google/inject/persist/jpa/JpaFinderProxy.java b/extensions/persist/src/com/google/inject/persist/jpa/JpaFinderProxy.java
index e37ddca199..a190b097df 100644
--- a/extensions/persist/src/com/google/inject/persist/jpa/JpaFinderProxy.java
+++ b/extensions/persist/src/com/google/inject/persist/jpa/JpaFinderProxy.java
@@ -124,6 +124,9 @@ private void bindQueryNamedParameters(
} else if (annotation instanceof javax.inject.Named) {
javax.inject.Named named = (javax.inject.Named) annotation;
jpaQuery.setParameter(named.value(), argument);
+ } else if (annotation instanceof jakarta.inject.Named) {
+ jakarta.inject.Named named = (jakarta.inject.Named) annotation;
+ jpaQuery.setParameter(named.value(), argument);
} else if (annotation instanceof FirstResult) {
jpaQuery.setFirstResult((Integer) argument);
} else if (annotation instanceof MaxResults) {
@@ -182,7 +185,9 @@ private JpaFinderProxy.FinderDescriptor getFinderDescriptor(MethodInvocation inv
for (Annotation annotation : annotations) {
//discover the named, first or max annotations then break out
Class extends Annotation> annotationType = annotation.annotationType();
- if (Named.class.equals(annotationType) || javax.inject.Named.class.equals(annotationType)) {
+ if (Named.class.equals(annotationType)
+ || javax.inject.Named.class.equals(annotationType)
+ || jakarta.inject.Named.class.equals(annotationType)) {
discoveredAnnotations[i] = annotation;
finderDescriptor.isBindAsRawParameters = false;
break;
diff --git a/extensions/servlet/test/com/google/inject/servlet/ContextPathTest.java b/extensions/servlet/test/com/google/inject/servlet/ContextPathTest.java
index 300462fbd5..973aa9d24c 100644
--- a/extensions/servlet/test/com/google/inject/servlet/ContextPathTest.java
+++ b/extensions/servlet/test/com/google/inject/servlet/ContextPathTest.java
@@ -71,7 +71,7 @@ protected void configureServlets() {
.in(Scopes.SINGLETON);
serve("/foo/*").with(Key.get(TestServlet.class, Names.named("foo")));
serve("/bar/*").with(Key.get(TestServlet.class, Names.named("bar")));
- // TODO - call and validate it is correct
+ // TODO: add a filter(..) call and validate it is correct
}
});
injector.injectMembers(this);
diff --git a/extensions/testlib/src/com/google/inject/testing/fieldbinder/BUILD b/extensions/testlib/src/com/google/inject/testing/fieldbinder/BUILD
index 4611ab65e4..7adcd2c33b 100644
--- a/extensions/testlib/src/com/google/inject/testing/fieldbinder/BUILD
+++ b/extensions/testlib/src/com/google/inject/testing/fieldbinder/BUILD
@@ -22,6 +22,7 @@ java_library(
"//third_party/java/error_prone:annotations",
"//third_party/java/guava/base",
"//third_party/java/guava/collect",
+ "//third_party/java/jakarta_inject",
"//third_party/java/jsr330_inject",
],
)
diff --git a/extensions/testlib/src/com/google/inject/testing/fieldbinder/BoundFieldModule.java b/extensions/testlib/src/com/google/inject/testing/fieldbinder/BoundFieldModule.java
index d4a7845f74..e04b4c1dfb 100644
--- a/extensions/testlib/src/com/google/inject/testing/fieldbinder/BoundFieldModule.java
+++ b/extensions/testlib/src/com/google/inject/testing/fieldbinder/BoundFieldModule.java
@@ -64,11 +64,12 @@
* If {@link Bind#lazy} is true, this module will delay reading the value from the field until
* injection time, allowing the field's value to be reassigned during the course of a test's
* execution.
- * If a {@link BindingAnnotation} or {@link javax.inject.Qualifier} is present on the field,
- * that field will be bound using that annotation via {@link
- * AnnotatedBindingBuilder#annotatedWith}. For example, {@code
+ * If a {@link BindingAnnotation}, {@link javax.inject.Qualifier} or {@link
+ * jakarta.inject.Qualifier} is present on the field, that field will be bound using that
+ * annotation via {@link AnnotatedBindingBuilder#annotatedWith}. For example, {@code
* bind(Foo.class).annotatedWith(BarAnnotation.class).toInstance(theValue)}. It is an error to
- * supply more than one {@link BindingAnnotation} or {@link javax.inject.Qualifier}.
+ * supply more than one {@link BindingAnnotation}, {@link javax.inject.Qualifier} or {@link
+ * jakarta.inject.Qualifier}.
* If the field is of type {@link Provider}, the field's value will be bound as a {@link
* Provider} using {@link LinkedBindingBuilder#toProvider} to the provider's parameterized
* type. For example, {@code Provider} binds to {@link Integer}. Attempting to bind a
@@ -434,7 +435,8 @@ private Optional getBoundFieldInfo(
private static boolean hasInject(Field field) {
return field.isAnnotationPresent(javax.inject.Inject.class)
- || field.isAnnotationPresent(com.google.inject.Inject.class);
+ || field.isAnnotationPresent(com.google.inject.Inject.class)
+ || field.isAnnotationPresent(jakarta.inject.Inject.class);
}
/**
@@ -457,7 +459,9 @@ private static boolean hasInject(Field field) {
* bind those subclasses directly, enabling them to inject the providers themselves.
*/
private static boolean isTransparentProvider(Class> clazz) {
- return com.google.inject.Provider.class == clazz || javax.inject.Provider.class == clazz;
+ return com.google.inject.Provider.class == clazz
+ || javax.inject.Provider.class == clazz
+ || jakarta.inject.Provider.class == clazz;
}
private static void bindField(Binder binder, final BoundFieldInfo fieldInfo) {
@@ -480,15 +484,28 @@ private static void bindField(Binder binder, final BoundFieldInfo fieldInfo) {
@Override
// @Nullable
public Object get() {
- javax.inject.Provider> provider =
- (javax.inject.Provider>) getFieldValue(fieldInfo);
- return provider.get();
+ Object val = getFieldValue(fieldInfo);
+ if (val instanceof javax.inject.Provider) {
+ return ((javax.inject.Provider>) val).get();
+ } else if (val instanceof jakarta.inject.Provider) {
+ return ((jakarta.inject.Provider>) val).get();
+ } else {
+ throw new IllegalStateException(
+ "unexpected field value: " + val + ", of type: " + val.getClass());
+ }
}
});
} else {
- javax.inject.Provider> fieldValueUnsafe =
- (javax.inject.Provider>) getFieldValue(fieldInfo);
- binderUnsafe.toProvider(fieldValueUnsafe);
+ Object val = getFieldValue(fieldInfo);
+ if (val instanceof javax.inject.Provider) {
+ binderUnsafe.toProvider((javax.inject.Provider>) val);
+ } else if (val instanceof jakarta.inject.Provider) {
+ // TODO(sameb): bind directly without using guicify when the API exists.
+ binderUnsafe.toProvider(Providers.guicify((jakarta.inject.Provider>) val));
+ } else {
+ throw new IllegalStateException(
+ "unexpected field value: " + val + ", of type: " + val.getClass());
+ }
}
} else if (fieldInfo.bindAnnotation.lazy()) {
binderUnsafe.toProvider(
diff --git a/extensions/testlib/test/com/google/inject/testing/fieldbinder/BUILD b/extensions/testlib/test/com/google/inject/testing/fieldbinder/BUILD
index c7c3db3703..b51b6f8e13 100644
--- a/extensions/testlib/test/com/google/inject/testing/fieldbinder/BUILD
+++ b/extensions/testlib/test/com/google/inject/testing/fieldbinder/BUILD
@@ -15,6 +15,7 @@ java_library(
"//core/test/com/google/inject:testsupport",
"//extensions/testlib/src/com/google/inject/testing/fieldbinder",
"//third_party/java/guava/collect",
+ "//third_party/java/jakarta_inject",
"//third_party/java/jsr330_inject",
"//third_party/java/junit",
],
diff --git a/extensions/testlib/test/com/google/inject/testing/fieldbinder/BoundFieldModuleTest.java b/extensions/testlib/test/com/google/inject/testing/fieldbinder/BoundFieldModuleTest.java
index d39a74613b..11cfe61b01 100644
--- a/extensions/testlib/test/com/google/inject/testing/fieldbinder/BoundFieldModuleTest.java
+++ b/extensions/testlib/test/com/google/inject/testing/fieldbinder/BoundFieldModuleTest.java
@@ -258,6 +258,27 @@ public void testBindingWithQualifier() {
assertEquals(testValue2, injector.getInstance(Key.get(Integer.class, SomeQualifier.class)));
}
+ @jakarta.inject.Qualifier
+ @Retention(RUNTIME)
+ private static @interface SomeJakartaQualifier {}
+
+ public void testBindingWithJakartaQualifier() {
+ final Integer testValue1 = 1024, testValue2 = 2048;
+ Object instance =
+ new Object() {
+ @Bind private Integer anInt = testValue1;
+
+ @Bind @SomeJakartaQualifier private Integer anotherInt = testValue2;
+ };
+
+ BoundFieldModule module = BoundFieldModule.of(instance);
+ Injector injector = Guice.createInjector(module);
+
+ assertEquals(testValue1, injector.getInstance(Integer.class));
+ assertEquals(
+ testValue2, injector.getInstance(Key.get(Integer.class, SomeJakartaQualifier.class)));
+ }
+
public void testCanReuseBindingAnnotationsWithDifferentValues() {
final Integer testValue1 = 1024, testValue2 = 2048;
final String name1 = "foo", name2 = "bar";
@@ -441,6 +462,26 @@ public Integer get() {
assertEquals(testValue, injector.getInstance(Integer.class));
}
+ public void testBindingJakartaProvider() {
+ final Integer testValue = 1024;
+ Object instance =
+ new Object() {
+ @Bind
+ private jakarta.inject.Provider anInt =
+ new jakarta.inject.Provider() {
+ @Override
+ public Integer get() {
+ return testValue;
+ }
+ };
+ };
+
+ BoundFieldModule module = BoundFieldModule.of(instance);
+ Injector injector = Guice.createInjector(module);
+
+ assertEquals(testValue, injector.getInstance(Integer.class));
+ }
+
public void testBindingNonNullableNullField() {
Object instance =
new Object() {
diff --git a/pom.xml b/pom.xml
index 5935c59e1e..796568a3a8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -136,6 +136,16 @@ See the Apache License Version 2.0 for the specific language governing permissio
javax.inject-tck
1
+
+ jakarta.inject
+ jakarta.inject-api
+ 2.0.1
+
+
+ jakarta.inject
+ jakarta.inject-tck
+ 2.0.1
+
aopalliance
aopalliance
diff --git a/third_party/java/jakarta_inject/BUILD b/third_party/java/jakarta_inject/BUILD
new file mode 100644
index 0000000000..b38dc7046c
--- /dev/null
+++ b/third_party/java/jakarta_inject/BUILD
@@ -0,0 +1,6 @@
+package(default_visibility = ["//:src"])
+
+alias(
+ name = "jakarta_inject",
+ actual = "@maven//:jakarta_inject_jakarta_inject_api",
+)
diff --git a/third_party/java/jakarta_inject_tck/BUILD b/third_party/java/jakarta_inject_tck/BUILD
new file mode 100644
index 0000000000..83692b7ae5
--- /dev/null
+++ b/third_party/java/jakarta_inject_tck/BUILD
@@ -0,0 +1,6 @@
+package(default_visibility = ["//:src"])
+
+alias(
+ name = "jakarta_inject_tck",
+ actual = "@maven//:jakarta_inject_jakarta_inject_tck",
+)