diff --git a/README.md b/README.md
index de80c0d9..2fad67c4 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,9 @@ Currently included are:
* [Afterburner](afterburner/) --
* [Blackbird](blackbird/) (NEW in 2.12 -- to eventually replace Afterburner)
-* [Guice](guice/)
+* Guice
+ * "Old" (`javax.inject`) based versions: [Guice](guice/)
+ * New "Jakarta" (`jakarta.inject`) based versions: [Guice 7](guice7/) (added in 2.16)
* Java XML Binding Annotation compatibility
* "Old" (`java.xml.bind`) annotations: [JAXB Annotations](jaxb/)
* New "Jakarta" (`jakarta.xml.bind`): [Jakarta XML Bind Annotations](jakarta-xmlbind/) (added in 2.13)
diff --git a/guice/pom.xml b/guice/pom.xml
index 3e7c30ba..215a02bd 100644
--- a/guice/pom.xml
+++ b/guice/pom.xml
@@ -12,7 +12,7 @@
2.16.0-SNAPSHOT
jackson-module-guice
- Jackson module: Guice
+ Jackson module: Guice (javax.inject)
bundle
Stuff to make integration with Guice a bit easier
diff --git a/guice7/README.md b/guice7/README.md
new file mode 100644
index 00000000..e15e4b77
--- /dev/null
+++ b/guice7/README.md
@@ -0,0 +1,106 @@
+# jackson-module-guice7
+
+## Documentation
+
+This is a copy of jackson-module-guice that works with guice version 7, using the jakarta.inject namespace.
+
+This extension allows Jackson to delegate ObjectMapper creation and value injection to Guice when handling data bindings.
+Using the ObjectMapperModule you can register Jackson data binding modules like so:
+
+~~~~~
+
+ Injector injector = Guice.createInjector(
+ new ObjectMapperModule().registerModule(new IntegerAsBase16Module())
+ );
+
+
+ public class IntegerAsBase16Module extends SimpleModule
+ {
+ public IntegerAsBase16Module() {
+ super("IntegerAsBase16");
+
+ addSerializer( Integer.class,
+ new JsonSerializer() {
+ @Override
+ public void serialize( Integer integer, JsonGenerator jsonGenerator, SerializerProvider serializerProvider )
+ throws IOException, JsonProcessingException
+ {
+ jsonGenerator.writeString(new BigInteger(String.valueOf(integer)).toString(16).toUpperCase());
+ }
+ }
+ );
+ }
+ }
+
+~~~~~
+
+Subsequently, the ObjectMapper, created from the Guice injector above, will apply the proper data bindings to serialize
+Integers as base 16 strings:
+
+~~~~~
+
+ mapper.writeValueAsString(new Integer(10)) ==> "A"
+
+~~~~~
+
+Additional Guice Modules can be used when creating the Injector to automatically inject values into value objects
+being de-serialized. The @JacksonInject annotation can be used to trigger Guice driven injection.
+
+Here's an example of a value object where Guice injects three of the members on behalf of Jackson. The first
+uses the @JacksonInject annotation, the second uses @JacksonInject with a specific Named binding, and the
+third uses @JacksonInject combined with another annotation (@Ann).
+
+~~~~~
+
+ public class SomeBean {
+ @JacksonInject
+ private int one;
+
+ @JacksonInject
+ @Named("two")
+ private int two;
+
+ @JacksonInject
+ @Ann
+ private int three;
+
+ @JsonProperty
+ private int four;
+
+ public boolean verify() {
+ Assert.assertEquals(1, one);
+ Assert.assertEquals(2, two);
+ Assert.assertEquals(3, three);
+ Assert.assertEquals(4, four);
+ return true;
+ }
+ }
+
+~~~~~
+
+The last, the fourth field, annotated with @JsonProperty uses standard ObjectMapper behavior unlike the other three
+which are injected by Guice. The following code snippet demonstrates Guice injection leading to a true return on the
+verify() method:
+
+
+~~~~~
+
+ final Injector injector = Guice.createInjector(
+ new ObjectMapperModule(),
+ new Module()
+ {
+ @Override
+ public void configure(Binder binder)
+ {
+ binder.bind(Integer.class).toInstance(1);
+ binder.bind(Integer.class).annotatedWith(Names.named("two")).toInstance(2);
+ binder.bind(Integer.class).annotatedWith(Ann.class).toInstance(3);
+ }
+ }
+ );
+
+ final ObjectMapper mapper = injector.getInstance(ObjectMapper.class);
+ mapper.readValue("{\"four\": 4}", SomeBean.class).verify();
+
+~~~~~
+
diff --git a/guice7/pom.xml b/guice7/pom.xml
new file mode 100644
index 00000000..729520a5
--- /dev/null
+++ b/guice7/pom.xml
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+
+ 4.0.0
+
+ com.fasterxml.jackson.module
+ jackson-modules-base
+ 2.16.0-SNAPSHOT
+
+ jackson-module-guice7
+ Jackson module: Guice 7+ (jakarta.inject)
+ bundle
+
+ Stuff to make integration with Guice 7+ a bit easier
+ https://github.com/FasterXML/jackson-modules-base
+
+
+
+ The Apache Software License, Version 2.0
+ https://www.apache.org/licenses/LICENSE-2.0.txt
+ repo
+
+
+
+
+ 7.0.0
+
+
+ com/fasterxml/jackson/module/guice7
+ ${project.groupId}.guice7
+
+
+
+
+
+
+ com.fasterxml.jackson.core
+ jackson-annotations
+
+
+ com.fasterxml.jackson.core
+ jackson-core
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+
+
+ com.google.inject
+ guice
+ ${version.guice}
+
+
+
+
+
+
+ com.google.code.maven-replacer-plugin
+ replacer
+
+
+
+ org.moditect
+ moditect-maven-plugin
+
+
+
+
+
diff --git a/guice7/src/main/java/com/fasterxml/jackson/module/guice7/GuiceAnnotationIntrospector.java b/guice7/src/main/java/com/fasterxml/jackson/module/guice7/GuiceAnnotationIntrospector.java
new file mode 100644
index 00000000..ce535e2a
--- /dev/null
+++ b/guice7/src/main/java/com/fasterxml/jackson/module/guice7/GuiceAnnotationIntrospector.java
@@ -0,0 +1,114 @@
+package com.fasterxml.jackson.module.guice;
+
+import com.fasterxml.jackson.annotation.JacksonInject;
+import com.fasterxml.jackson.databind.introspect.*;
+import com.google.inject.BindingAnnotation;
+import com.google.inject.Key;
+
+import jakarta.inject.Qualifier;
+import java.lang.annotation.Annotation;
+import java.util.Arrays;
+
+public class GuiceAnnotationIntrospector extends NopAnnotationIntrospector
+{
+ private static final long serialVersionUID = 1L;
+
+ @Override // since 2.9
+ public JacksonInject.Value findInjectableValue(AnnotatedMember m) {
+ Object id = _findGuiceInjectId(m);
+ if (id == null) {
+ return null;
+ }
+ return JacksonInject.Value.forId(id);
+ }
+
+ @Deprecated // since 2.9
+ @Override
+ public Object findInjectableValueId(AnnotatedMember m) {
+ return _findGuiceInjectId(m);
+ }
+
+ private Object _findGuiceInjectId(AnnotatedMember m)
+ {
+ /*
+ * We check on three kinds of annotations: @JacksonInject for types
+ * that were actually created for Jackson, and @Inject (both Guice's
+ * and jakarta.inject) for types that (for example) extend already
+ * annotated objects.
+ *
+ * Postel's law: http://en.wikipedia.org/wiki/Robustness_principle
+ */
+ // 19-Apr-2017, tatu: Actually this is something that should not be done;
+ // instead, pair of AnnotationIntrospector should be used... Leaving in
+ // for now, however.
+ if ((m.getAnnotation(JacksonInject.class) == null) &&
+ (m.getAnnotation(jakarta.inject.Inject.class) == null) &&
+ (m.getAnnotation(com.google.inject.Inject.class) == null))
+ {
+ return null;
+ }
+
+ final AnnotatedMember guiceMember;
+ final Annotation guiceAnnotation;
+
+ if ((m instanceof AnnotatedField) || (m instanceof AnnotatedParameter)) {
+ /* On fields and parameters the @Qualifier annotation and type to
+ * inject are the member itself, so, nothing to do here...
+ */
+ guiceMember = m;
+ AnnotationMap anns = ((AnnotatedMember) m).getAllAnnotations();
+ guiceAnnotation = findBindingAnnotation(anns.annotations());
+ } else if (m instanceof AnnotatedMethod) {
+ /* For method injection, the @Qualifier and type to inject are
+ * specified on the parameter. Here, we only consider methods with
+ * a single parameter.
+ */
+ final AnnotatedMethod a = (AnnotatedMethod) m;
+ if (a.getParameterCount() != 1) {
+ return null;
+ }
+
+ /* Jackson does not *YET* give us parameter annotations on methods,
+ * only on constructors, henceforth we have to do a bit of work
+ * ourselves!
+ */
+ guiceMember = a.getParameter(0);
+ final Annotation[] annotations = a.getMember().getParameterAnnotations()[0];
+ guiceAnnotation = findBindingAnnotation(Arrays.asList(annotations));
+ } else {
+ /* Ignore constructors */
+ return null;
+ }
+
+ /* Depending on whether we have an annotation (or not) return the
+ * correct Guice key that Jackson will use to query the Injector.
+ */
+ if (guiceAnnotation == null) {
+ // 19-Sep-2016, tatu: Used to pass `getGenericType()`, but that is now deprecated.
+ // Looking at code in Guice Key, I don't think it does particularly good job
+ // in resolving generic types, so this is probably safe...
+// return Key.get(guiceMember.getGenericType());
+ return Key.get((java.lang.reflect.Type) guiceMember.getRawType());
+ }
+// return Key.get(guiceMember.getGenericType(), guiceAnnotation);
+ return Key.get((java.lang.reflect.Type) guiceMember.getRawType(), guiceAnnotation);
+ }
+
+ /*
+ * We want to figure out if a @BindingAnnotation or @Qualifier
+ * annotation are present on what we're trying to inject.
+ * Those annotations are only possible on fields or parameters.
+ */
+ private Annotation findBindingAnnotation(Iterable annotations)
+ {
+ for (Annotation annotation : annotations) {
+ // Check on guice (BindingAnnotation) & jakarta (Qualifier) based injections
+ if (annotation.annotationType().isAnnotationPresent(BindingAnnotation.class) ||
+ annotation.annotationType().isAnnotationPresent(Qualifier.class))
+ {
+ return annotation;
+ }
+ }
+ return null;
+ }
+}
diff --git a/guice7/src/main/java/com/fasterxml/jackson/module/guice7/GuiceInjectableValues.java b/guice7/src/main/java/com/fasterxml/jackson/module/guice7/GuiceInjectableValues.java
new file mode 100644
index 00000000..5080abe4
--- /dev/null
+++ b/guice7/src/main/java/com/fasterxml/jackson/module/guice7/GuiceInjectableValues.java
@@ -0,0 +1,22 @@
+package com.fasterxml.jackson.module.guice;
+
+import com.fasterxml.jackson.databind.BeanProperty;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.InjectableValues;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+
+public class GuiceInjectableValues extends InjectableValues
+{
+ private final Injector injector;
+
+ public GuiceInjectableValues(Injector injector) {this.injector = injector;}
+
+ @Override
+ public Object findInjectableValue(
+ Object valueId, DeserializationContext ctxt, BeanProperty forProperty, Object beanInstance
+ )
+ {
+ return injector.getInstance((Key>) valueId);
+ }
+}
diff --git a/guice7/src/main/java/com/fasterxml/jackson/module/guice7/ObjectMapperModule.java b/guice7/src/main/java/com/fasterxml/jackson/module/guice7/ObjectMapperModule.java
new file mode 100644
index 00000000..2a1a85c2
--- /dev/null
+++ b/guice7/src/main/java/com/fasterxml/jackson/module/guice7/ObjectMapperModule.java
@@ -0,0 +1,156 @@
+package com.fasterxml.jackson.module.guice;
+
+import java.lang.annotation.Annotation;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.fasterxml.jackson.databind.Module;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.introspect.AnnotationIntrospectorPair;
+import com.google.inject.Binder;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.Provider;
+import com.google.inject.binder.ScopedBindingBuilder;
+
+public class ObjectMapperModule implements com.google.inject.Module
+{
+ private final List modulesToAdd = new ArrayList();
+ private final List> modulesToInject = new ArrayList>();
+ private final Key objectMapperKey;
+
+ // @since 2.8
+ private ObjectMapper objectMapper;
+
+ private Class extends Annotation> scope = null;
+
+ public ObjectMapperModule()
+ {
+ this(Key.get(ObjectMapper.class));
+ }
+
+ public ObjectMapperModule(Class extends Annotation> annotation)
+ {
+ this(Key.get(ObjectMapper.class, annotation));
+ }
+
+ public ObjectMapperModule(Annotation annotation)
+ {
+ this(Key.get(ObjectMapper.class, annotation));
+ }
+
+ public ObjectMapperModule(Key objectMapperKey)
+ {
+ this.objectMapperKey = objectMapperKey;
+ }
+
+ public ObjectMapperModule in(Class extends Annotation> scopeAnnotation)
+ {
+ scope = scopeAnnotation;
+ return this;
+ }
+
+ public ObjectMapperModule registerModule(Module module)
+ {
+ modulesToAdd.add(module);
+ return this;
+ }
+
+ public ObjectMapperModule registerModule(Class extends Module> clazz)
+ {
+ return registerModule(Key.get(clazz));
+ }
+
+ public ObjectMapperModule registerModule(Class extends Module> clazz, Class extends Annotation> annotation)
+ {
+ return registerModule(Key.get(clazz, annotation));
+ }
+
+ public ObjectMapperModule registerModule(Class extends Module> clazz, Annotation annotation)
+ {
+ return registerModule(Key.get(clazz, annotation));
+ }
+
+ public ObjectMapperModule registerModule(Key extends Module> key)
+ {
+ modulesToInject.add(key);
+ return this;
+ }
+
+ /**
+ * @param m ObjectMapper to use for newly constructed module
+ *
+ * @since 2.8
+ */
+ public ObjectMapperModule withObjectMapper(ObjectMapper m)
+ {
+ objectMapper = m;
+ return this;
+ }
+
+ @Override
+ public void configure(Binder binder)
+ {
+ final ScopedBindingBuilder builder = binder.bind(objectMapperKey)
+ .toProvider(new ObjectMapperProvider(modulesToInject, modulesToAdd, objectMapper));
+
+ if (scope != null) {
+ builder.in(scope);
+ }
+ }
+
+ private static class ObjectMapperProvider implements Provider
+ {
+ private final List> modulesToInject;
+ private final List modulesToAdd;
+
+ private final List> providedModules;
+ private Injector injector;
+ private final ObjectMapper objectMapper;
+
+ public ObjectMapperProvider(List> modulesToInject,
+ List modulesToAdd, ObjectMapper mapper)
+ {
+ this.modulesToInject = modulesToInject;
+ this.modulesToAdd = modulesToAdd;
+ objectMapper = mapper;
+ this.providedModules = new ArrayList>();
+ }
+
+ @Inject
+ public void configure(Injector inj) {
+ injector = inj;
+ for (Key extends Module> key : modulesToInject) {
+ providedModules.add(injector.getProvider(key));
+ }
+ }
+
+ @Override
+ public ObjectMapper get()
+ {
+ ObjectMapper mapper = objectMapper;
+ if (mapper == null) {
+ mapper = new ObjectMapper();
+ } else {
+ mapper = mapper.copy();
+ }
+ mapper.registerModules(modulesToAdd);
+ for (Provider extends Module> provider : providedModules) {
+ mapper.registerModule(provider.get());
+ }
+
+ final GuiceAnnotationIntrospector guiceIntrospector = new GuiceAnnotationIntrospector();
+ mapper.setInjectableValues(new GuiceInjectableValues(injector));
+ mapper.setAnnotationIntrospectors(
+ new AnnotationIntrospectorPair(
+ guiceIntrospector, mapper.getSerializationConfig().getAnnotationIntrospector()
+ ),
+ new AnnotationIntrospectorPair(
+ guiceIntrospector, mapper.getDeserializationConfig().getAnnotationIntrospector()
+ )
+ );
+ return mapper;
+ }
+ }
+}
diff --git a/guice7/src/main/java/com/fasterxml/jackson/module/guice7/PackageVersion.java.in b/guice7/src/main/java/com/fasterxml/jackson/module/guice7/PackageVersion.java.in
new file mode 100644
index 00000000..7860aa14
--- /dev/null
+++ b/guice7/src/main/java/com/fasterxml/jackson/module/guice7/PackageVersion.java.in
@@ -0,0 +1,20 @@
+package @package@;
+
+import com.fasterxml.jackson.core.Version;
+import com.fasterxml.jackson.core.Versioned;
+import com.fasterxml.jackson.core.util.VersionUtil;
+
+/**
+ * Automatically generated from PackageVersion.java.in during
+ * packageVersion-generate execution of maven-replacer-plugin in
+ * pom.xml.
+ */
+public final class PackageVersion implements Versioned {
+ public final static Version VERSION = VersionUtil.parseVersion(
+ "@projectversion@", "@projectgroupid@", "@projectartifactid@");
+
+ @Override
+ public Version version() {
+ return VERSION;
+ }
+}
diff --git a/guice7/src/main/resources/META-INF/LICENSE b/guice7/src/main/resources/META-INF/LICENSE
new file mode 100644
index 00000000..a9e54621
--- /dev/null
+++ b/guice7/src/main/resources/META-INF/LICENSE
@@ -0,0 +1,8 @@
+This copy of Jackson JSON processor `jackson-module-guice` module is licensed under the
+Apache (Software) License, version 2.0 ("the License").
+See the License for details about distribution rights, and the
+specific rights regarding derivative works.
+
+You may obtain a copy of the License at:
+
+http://www.apache.org/licenses/LICENSE-2.0
diff --git a/guice7/src/main/resources/META-INF/NOTICE b/guice7/src/main/resources/META-INF/NOTICE
new file mode 100644
index 00000000..4c976b7b
--- /dev/null
+++ b/guice7/src/main/resources/META-INF/NOTICE
@@ -0,0 +1,20 @@
+# Jackson JSON processor
+
+Jackson is a high-performance, Free/Open Source JSON processing library.
+It was originally written by Tatu Saloranta (tatu.saloranta@iki.fi), and has
+been in development since 2007.
+It is currently developed by a community of developers, as well as supported
+commercially by FasterXML.com.
+
+## Licensing
+
+Jackson core and extension components may licensed under different licenses.
+To find the details that apply to this artifact see the accompanying LICENSE file.
+For more information, including possible other licensing options, contact
+FasterXML.com (http://fasterxml.com).
+
+## Credits
+
+A list of contributors may be found from CREDITS file, which is included
+in some artifacts (usually source distributions); but is always available
+from the source code management (SCM) system project uses.
diff --git a/guice7/src/moditect/module-info.java b/guice7/src/moditect/module-info.java
new file mode 100644
index 00000000..8785687c
--- /dev/null
+++ b/guice7/src/moditect/module-info.java
@@ -0,0 +1,14 @@
+module com.fasterxml.jackson.module.guice7 {
+ //Jakarta Reference Implementation
+ requires static jakarta.inject;
+
+ requires com.fasterxml.jackson.annotation;
+ requires com.fasterxml.jackson.core;
+ requires com.fasterxml.jackson.databind;
+
+ requires com.google.guice;
+
+ exports com.fasterxml.jackson.module.guice7;
+
+ //NOTE : Don't auto provide jackson guice module as some may want to bind their own ObjectMapper?
+}
diff --git a/guice7/src/test/java/com/fasterxml/jackson/module/guice7/Ann.java b/guice7/src/test/java/com/fasterxml/jackson/module/guice7/Ann.java
new file mode 100644
index 00000000..8b9dc1cc
--- /dev/null
+++ b/guice7/src/test/java/com/fasterxml/jackson/module/guice7/Ann.java
@@ -0,0 +1,18 @@
+package com.fasterxml.jackson.module.guice;
+
+import com.google.inject.BindingAnnotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ */
+@Retention(RUNTIME)
+@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
+@BindingAnnotation
+public @interface Ann
+{
+}
diff --git a/guice7/src/test/java/com/fasterxml/jackson/module/guice7/ExtendInjectableTest.java b/guice7/src/test/java/com/fasterxml/jackson/module/guice7/ExtendInjectableTest.java
new file mode 100644
index 00000000..dd81a922
--- /dev/null
+++ b/guice7/src/test/java/com/fasterxml/jackson/module/guice7/ExtendInjectableTest.java
@@ -0,0 +1,199 @@
+package com.fasterxml.jackson.module.guice;
+
+import com.fasterxml.jackson.annotation.JacksonInject;
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.inject.Binder;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Module;
+import com.google.inject.name.Names;
+import org.junit.Assert;
+import org.junit.Test;
+
+import jakarta.inject.Inject;
+
+/**
+ */
+public class ExtendInjectableTest
+{
+ final ConstructorDependency constructorInjected = new ConstructorDependency();
+ final ConstructorDependency constructorInjectedWithCustomAnnotation = new ConstructorDependency();
+ final ConstructorDependency constructorInjectedWithGuiceAnnotation = new ConstructorDependency();
+ final ConstructorDependency constructorInjectedWithJavaAnnotation = new ConstructorDependency();
+ final FieldDependency fieldInjected = new FieldDependency();
+ final FieldDependency fieldInjectedWithCustomAnnotation = new FieldDependency();
+ final FieldDependency fieldInjectedWithGuiceAnnotation = new FieldDependency();
+ final FieldDependency fieldInjectedWithJavaAnnotation = new FieldDependency();
+ final MethodDependency methodInjected = new MethodDependency();
+ final MethodDependency methodInjectedWithCustomAnnotation = new MethodDependency();
+ final MethodDependency methodInjectedWithGuiceAnnotation = new MethodDependency();
+ final MethodDependency methodInjectedWithJavaAnnotation = new MethodDependency();
+
+ @Test
+ public void testModulesRegisteredThroughInjectionWithKey() throws Exception
+ {
+ final Injector injector = Guice.createInjector(new ObjectMapperModule(),
+ new Module() {
+ @Override
+ public void configure(Binder binder)
+ {
+ binder.bind(ConstructorDependency.class).toInstance(constructorInjected);
+ binder.bind(ConstructorDependency.class).annotatedWith(Ann.class).toInstance(constructorInjectedWithCustomAnnotation);
+ binder.bind(ConstructorDependency.class).annotatedWith(Names.named("guice")).toInstance(constructorInjectedWithGuiceAnnotation);
+ binder.bind(ConstructorDependency.class).annotatedWith(Names.named("jakarta")).toInstance(constructorInjectedWithJavaAnnotation);
+ binder.bind(FieldDependency.class).toInstance(fieldInjected);
+ binder.bind(FieldDependency.class).annotatedWith(Ann.class).toInstance(fieldInjectedWithCustomAnnotation);
+ binder.bind(FieldDependency.class).annotatedWith(Names.named("guice")).toInstance(fieldInjectedWithGuiceAnnotation);
+ binder.bind(FieldDependency.class).annotatedWith(Names.named("jakarta")).toInstance(fieldInjectedWithJavaAnnotation);
+ binder.bind(MethodDependency.class).toInstance(methodInjected);
+ binder.bind(MethodDependency.class).annotatedWith(Ann.class).toInstance(methodInjectedWithCustomAnnotation);
+ binder.bind(MethodDependency.class).annotatedWith(Names.named("guice")).toInstance(methodInjectedWithGuiceAnnotation);
+ binder.bind(MethodDependency.class).annotatedWith(Names.named("jakarta")).toInstance(methodInjectedWithJavaAnnotation);
+ }
+ });
+
+ /* First of all, just get an InjectableBean out of guice to make sure it's correct (test the test) */
+ verifyInjections("From Guice's Injector", injector.getInstance(InjectableBean.class));
+
+ /* Now let's try injections via our ObjectMapper (plus some values) */
+ final ObjectMapper mapper = injector.getInstance(ObjectMapper.class);
+
+ final ReadableBean bean = mapper.readValue("{\"constructor_value\":\"myConstructor\",\"field_value\":\"myField\",\"method_value\":\"myMethod\"}", ReadableBean.class);
+
+ Assert.assertEquals("myConstructor", bean.constructorValue);
+ Assert.assertEquals("myMethod", bean.methodValue);
+ Assert.assertEquals("myField", bean.fieldValue);
+
+ verifyInjections("From Jackson's ObjectMapper", bean);
+
+ }
+
+ private void verifyInjections(String message, InjectableBean injected)
+ {
+ Assert.assertSame(message, constructorInjected, injected.constructorInjected);
+ Assert.assertSame(message, constructorInjectedWithJavaAnnotation, injected.constructorInjectedWithJavaAnnotation);
+ Assert.assertSame(message, constructorInjectedWithGuiceAnnotation, injected.constructorInjectedWithGuiceAnnotation);
+ Assert.assertSame(message, constructorInjectedWithCustomAnnotation, injected.constructorInjectedWithCustomAnnotation);
+
+ Assert.assertSame(message, methodInjected, injected.methodInjected);
+ Assert.assertSame(message, methodInjectedWithJavaAnnotation, injected.methodInjectedWithJavaAnnotation);
+ Assert.assertSame(message, methodInjectedWithGuiceAnnotation, injected.methodInjectedWithGuiceAnnotation);
+ Assert.assertSame(message, methodInjectedWithCustomAnnotation, injected.methodInjectedWithCustomAnnotation);
+
+ Assert.assertSame(message, fieldInjected, injected.fieldInjected);
+ Assert.assertSame(message, fieldInjectedWithJavaAnnotation, injected.fieldInjectedWithJavaAnnotation);
+ Assert.assertSame(message, fieldInjectedWithGuiceAnnotation, injected.fieldInjectedWithGuiceAnnotation);
+ Assert.assertSame(message, fieldInjectedWithCustomAnnotation, injected.fieldInjectedWithCustomAnnotation);
+ }
+
+ /* ===================================================================== */
+ /* SUPPORT CLASSES */
+ /* ===================================================================== */
+
+ static class ReadableBean extends InjectableBean {
+
+ @JsonProperty("field_value")
+ String fieldValue;
+ String methodValue;
+ final String constructorValue;
+
+ @JsonCreator
+ private ReadableBean(@JacksonInject ConstructorDependency constructorInjected,
+ @JacksonInject @jakarta.inject.Named("jakarta") ConstructorDependency constructorInjectedWithJavaAnnotation,
+ @JacksonInject @com.google.inject.name.Named("guice") ConstructorDependency constructorInjectedWithGuiceAnnotation,
+ @JacksonInject @Ann ConstructorDependency constructorInjectedWithCustomAnnotation,
+ @JsonProperty("constructor_value") String constructorValue)
+ {
+ super(constructorInjected,
+ constructorInjectedWithJavaAnnotation,
+ constructorInjectedWithGuiceAnnotation,
+ constructorInjectedWithCustomAnnotation);
+ this.constructorValue = constructorValue;
+ }
+
+ @JsonProperty("method_value")
+ public void setMethodValue(String methodValue)
+ {
+ this.methodValue = methodValue;
+ }
+
+ }
+
+ /* ===================================================================== */
+
+ static class ConstructorDependency {};
+ static class MethodDependency {};
+ static class FieldDependency {};
+
+ static class InjectableBean
+ {
+ @Inject
+ FieldDependency fieldInjected;
+ MethodDependency methodInjected;
+ final ConstructorDependency constructorInjected;
+
+ @JacksonInject
+ @Inject
+ @jakarta.inject.Named("jakarta")
+ FieldDependency fieldInjectedWithJavaAnnotation;
+ MethodDependency methodInjectedWithJavaAnnotation;
+ final ConstructorDependency constructorInjectedWithJavaAnnotation;
+
+ @JacksonInject
+ @Inject
+ @com.google.inject.name.Named("guice")
+ FieldDependency fieldInjectedWithGuiceAnnotation;
+ MethodDependency methodInjectedWithGuiceAnnotation;
+ final ConstructorDependency constructorInjectedWithGuiceAnnotation;
+
+ @JacksonInject
+ @Inject
+ @Ann
+ FieldDependency fieldInjectedWithCustomAnnotation;
+ MethodDependency methodInjectedWithCustomAnnotation;
+ final ConstructorDependency constructorInjectedWithCustomAnnotation;
+
+ @Inject // this is simply to make sure we *can* build this correctly
+ protected InjectableBean(ConstructorDependency constructorInjected,
+ @jakarta.inject.Named("jakarta") ConstructorDependency constructorInjectedWithJavaAnnotation,
+ @com.google.inject.name.Named("guice") ConstructorDependency constructorInjectedWithGuiceAnnotation,
+ @Ann ConstructorDependency constructorInjectedWithCustomAnnotation)
+ {
+ this.constructorInjected = constructorInjected;
+ this.constructorInjectedWithJavaAnnotation = constructorInjectedWithJavaAnnotation;
+ this.constructorInjectedWithGuiceAnnotation = constructorInjectedWithGuiceAnnotation;
+ this.constructorInjectedWithCustomAnnotation = constructorInjectedWithCustomAnnotation;
+ }
+
+ @JacksonInject
+ @Inject // not annotated
+ private void inject1(MethodDependency dependency)
+ {
+ this.methodInjected = dependency;
+ }
+
+ @JacksonInject
+ @Inject
+ private void inject2(@jakarta.inject.Named("jakarta") MethodDependency dependency)
+ {
+ this.methodInjectedWithJavaAnnotation = dependency;
+ }
+
+ @JacksonInject
+ @Inject
+ private void inject3(@com.google.inject.name.Named("guice") MethodDependency dependency)
+ {
+ this.methodInjectedWithGuiceAnnotation = dependency;
+ }
+
+ @JacksonInject
+ @Inject
+ private void inject4(@Ann MethodDependency dependency)
+ {
+ this.methodInjectedWithCustomAnnotation = dependency;
+ }
+
+ }
+}
diff --git a/guice7/src/test/java/com/fasterxml/jackson/module/guice7/ObjectMapperModuleTest.java b/guice7/src/test/java/com/fasterxml/jackson/module/guice7/ObjectMapperModuleTest.java
new file mode 100644
index 00000000..6520a779
--- /dev/null
+++ b/guice7/src/test/java/com/fasterxml/jackson/module/guice7/ObjectMapperModuleTest.java
@@ -0,0 +1,226 @@
+package com.fasterxml.jackson.module.guice;
+
+import java.io.IOException;
+import java.math.BigInteger;
+
+import com.fasterxml.jackson.annotation.JacksonInject;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonProcessingException;
+
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+
+import com.google.inject.Binder;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.Module;
+import com.google.inject.name.Names;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ObjectMapperModuleTest
+{
+ @Test
+ public void testJacksonInjectThroughGuice() throws Exception
+ {
+ final Injector injector = Guice.createInjector(
+ new ObjectMapperModule(),
+ new Module()
+ {
+ @Override
+ public void configure(Binder binder)
+ {
+ binder.bind(Integer.class).toInstance(1);
+ // guice based named injection
+ binder.bind(Integer.class).annotatedWith(Names.named("two")).toInstance(2);
+ binder.bind(Integer.class).annotatedWith(Ann.class).toInstance(3);
+ // jakarta based named injection
+ binder.bind(Integer.class).annotatedWith(Names.named("five")).toInstance(5);
+ // guice based method injection
+ binder.bind(Integer.class).annotatedWith(Names.named("six")).toInstance(6);
+ // jakarta based method injection
+ binder.bind(Integer.class).annotatedWith(Names.named("seven")).toInstance(7);
+ // test other method injections (need different keys, so use Long
+ binder.bind(Long.class).annotatedWith(Ann.class).toInstance(8L);
+ binder.bind(Long.class).toInstance(9L);
+ }
+ }
+ );
+
+ final ObjectMapper mapper = injector.getInstance(ObjectMapper.class);
+
+ mapper.readValue("{\"four\": 4}", SomeBean.class).verify();
+ }
+
+ @Test
+ public void testModulesRegisteredThroughNormalInstantiation() throws Exception
+ {
+ final Injector injector = Guice.createInjector(
+ new ObjectMapperModule().registerModule(new IntegerAsBase16Module())
+ );
+
+ final ObjectMapper mapper = injector.getInstance(ObjectMapper.class);
+
+ Assert.assertEquals(mapper.writeValueAsString(new Integer(10)), "\"A\"");
+ }
+
+ @Test
+ public void testModulesRegisteredThroughInjection() throws Exception
+ {
+ final Injector injector = Guice.createInjector(
+ new ObjectMapperModule().registerModule(IntegerAsBase16Module.class)
+ );
+
+ final ObjectMapper mapper = injector.getInstance(ObjectMapper.class);
+
+ Assert.assertEquals(mapper.writeValueAsString(new Integer(10)), "\"A\"");
+ }
+
+ @Test
+ public void testModulesRegisteredThroughInjectionWithAnnotation() throws Exception
+ {
+ final Injector injector = Guice.createInjector(
+ new ObjectMapperModule().registerModule(IntegerAsBase16Module.class, Ann.class),
+ new Module()
+ {
+ @Override
+ public void configure(Binder binder)
+ {
+ binder.bind(IntegerAsBase16Module.class).annotatedWith(Ann.class).to(IntegerAsBase16Module.class);
+ }
+ }
+ );
+
+ final ObjectMapper mapper = injector.getInstance(ObjectMapper.class);
+
+ Assert.assertEquals(mapper.writeValueAsString(new Integer(10)), "\"A\"");
+ }
+
+ @Test
+ public void testModulesRegisteredThroughInjectionWithNameAnnotation() throws Exception
+ {
+ final Injector injector = Guice.createInjector(
+ new ObjectMapperModule().registerModule(IntegerAsBase16Module.class, Names.named("billy")),
+ new Module()
+ {
+ @Override
+ public void configure(Binder binder)
+ {
+ binder.bind(IntegerAsBase16Module.class)
+ .annotatedWith(Names.named("billy"))
+ .to(IntegerAsBase16Module.class);
+ }
+ }
+ );
+
+ final ObjectMapper mapper = injector.getInstance(ObjectMapper.class);
+
+ Assert.assertEquals(mapper.writeValueAsString(new Integer(10)), "\"A\"");
+ }
+
+ @Test
+ public void testModulesRegisteredThroughInjectionWithKey() throws Exception
+ {
+ final Injector injector = Guice.createInjector(
+ new ObjectMapperModule().registerModule(Key.get(IntegerAsBase16Module.class))
+ );
+
+ final ObjectMapper mapper = injector.getInstance(ObjectMapper.class);
+
+ Assert.assertEquals(mapper.writeValueAsString(new Integer(10)), "\"A\"");
+ }
+
+ private static class SomeBean
+ {
+ @JacksonInject
+ private int one;
+
+ @JacksonInject
+ @com.google.inject.name.Named("two")
+ private int two;
+
+ @JacksonInject
+ @Ann
+ private int three;
+
+ @JsonProperty
+ private int four;
+
+ @JacksonInject
+ @jakarta.inject.Named("five")
+ private int five;
+
+ // Those will be injected by methods
+ private int six;
+ private int seven;
+ private long eight;
+ private long nine;
+
+ @JacksonInject
+ private void injectSix(@com.google.inject.name.Named("six") int s)
+ {
+ this.six = s;
+ }
+
+ @JacksonInject
+ private void injectSeven(@jakarta.inject.Named("seven") int s)
+ {
+ this.seven = s;
+ }
+
+ @JacksonInject
+ private void injectEight(@Ann long e)
+ {
+ this.eight = e;
+ }
+
+ @JacksonInject
+ private void injectNine(long n)
+ {
+ this.nine = n;
+ }
+
+ public boolean verify()
+ {
+ Assert.assertEquals(1, one);
+ Assert.assertEquals(2, two);
+ Assert.assertEquals(3, three);
+ Assert.assertEquals(4, four);
+ Assert.assertEquals(5, five);
+ Assert.assertEquals(6, six);
+ Assert.assertEquals(7, seven);
+ Assert.assertEquals(8, eight);
+ Assert.assertEquals(9, nine);
+ return true;
+ }
+
+ }
+
+ @SuppressWarnings("serial")
+ static class IntegerAsBase16Module extends SimpleModule
+ {
+ public IntegerAsBase16Module() {
+ super("IntegerAsBase16");
+
+ addSerializer(
+ Integer.class,
+ new JsonSerializer()
+ {
+ @Override
+ public void serialize(
+ Integer integer, JsonGenerator jsonGenerator, SerializerProvider serializerProvider
+ ) throws IOException, JsonProcessingException
+ {
+ jsonGenerator.writeString(new BigInteger(String.valueOf(integer)).toString(16).toUpperCase());
+ }
+ }
+ );
+ }
+ }
+}
diff --git a/pom.xml b/pom.xml
index f98edf7c..c19c0a5a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -26,6 +26,7 @@ not datatype, data format, or JAX-RS provider modules.
afterburner
blackbird
guice
+ guice7
jakarta-xmlbind
jaxb
mrbean