diff --git a/examples/json-jackson/src/main/java/org/glassfish/jersey/examples/jackson/CombinedAnnotationBean.java b/examples/json-jackson/src/main/java/org/glassfish/jersey/examples/jackson/CombinedAnnotationBean.java index 06a23c47be..2ac0ac634b 100644 --- a/examples/json-jackson/src/main/java/org/glassfish/jersey/examples/jackson/CombinedAnnotationBean.java +++ b/examples/json-jackson/src/main/java/org/glassfish/jersey/examples/jackson/CombinedAnnotationBean.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v. 1.0, which is available at @@ -10,7 +10,7 @@ package org.glassfish.jersey.examples.jackson; -import javax.xml.bind.annotation.XmlRootElement; +import jakarta.xml.bind.annotation.XmlRootElement; import com.fasterxml.jackson.annotation.JsonProperty; diff --git a/examples/json-jackson/src/main/java/org/glassfish/jersey/examples/jackson/MyObjectMapperProvider.java b/examples/json-jackson/src/main/java/org/glassfish/jersey/examples/jackson/MyObjectMapperProvider.java index e32135878f..81bda853a0 100644 --- a/examples/json-jackson/src/main/java/org/glassfish/jersey/examples/jackson/MyObjectMapperProvider.java +++ b/examples/json-jackson/src/main/java/org/glassfish/jersey/examples/jackson/MyObjectMapperProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v. 1.0, which is available at @@ -19,7 +19,7 @@ import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector; import com.fasterxml.jackson.databind.type.TypeFactory; -import com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector; +import com.fasterxml.jackson.module.jakarta.xmlbind.JakartaXmlBindAnnotationIntrospector; /** * TODO javadoc. @@ -63,7 +63,7 @@ private static ObjectMapper createDefaultMapper() { private static AnnotationIntrospector createJaxbJacksonAnnotationIntrospector() { - final AnnotationIntrospector jaxbIntrospector = new JaxbAnnotationIntrospector(TypeFactory.defaultInstance()); + final AnnotationIntrospector jaxbIntrospector = new JakartaXmlBindAnnotationIntrospector(TypeFactory.defaultInstance()); final AnnotationIntrospector jacksonIntrospector = new JacksonAnnotationIntrospector(); return AnnotationIntrospector.pair(jacksonIntrospector, jaxbIntrospector); diff --git a/media/json-jackson/pom.xml b/media/json-jackson/pom.xml index cbc01771b5..9456ee6234 100644 --- a/media/json-jackson/pom.xml +++ b/media/json-jackson/pom.xml @@ -1,7 +1,7 @@ + com.fasterxml.jackson.module + jackson-module-jakarta-xmlbind-annotations + + + jakarta.xml.bind + jakarta.xml.bind-api + + + jakarta.activation + jakarta.activation-api + + + + + javax.xml.bind jaxb-api 2.3.1 + test + + + jakarta.xml.bind + jakarta.xml.bind-api junit diff --git a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/DefaultJacksonJaxbJsonProvider.java b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/DefaultJacksonJaxbJsonProvider.java index 2ad04c18ca..f4f0f3f71f 100644 --- a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/DefaultJacksonJaxbJsonProvider.java +++ b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/DefaultJacksonJaxbJsonProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -31,15 +31,15 @@ public class DefaultJacksonJaxbJsonProvider extends JacksonJaxbJsonProvider { //do not register JaxbAnnotationModule because it brakes default annotations processing - private static final String EXCLUDE_MODULE_NAME = "JaxbAnnotationModule"; + private static final String[] EXCLUDE_MODULE_NAMES = {"JaxbAnnotationModule", "JakartaXmlBindAnnotationModule"}; public DefaultJacksonJaxbJsonProvider() { - super(); + super(new JacksonMapperConfigurator(null, DEFAULT_ANNOTATIONS)); findAndRegisterModules(); } public DefaultJacksonJaxbJsonProvider(final Annotations... annotationsToUse) { - super(annotationsToUse); + super(new JacksonMapperConfigurator(null, annotationsToUse)); findAndRegisterModules(); } @@ -49,7 +49,9 @@ private void findAndRegisterModules() { final ObjectMapper mapper = _mapperConfig.getConfiguredMapper(); final List modules = ObjectMapper.findModules(); - modules.removeIf(mod -> mod.getModuleName().contains(EXCLUDE_MODULE_NAME)); + for (String exludeModuleName : EXCLUDE_MODULE_NAMES) { + modules.removeIf(mod -> mod.getModuleName().contains(exludeModuleName)); + } defaultMapper.registerModules(modules); if (mapper != null) { diff --git a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/JacksonMapperConfigurator.java b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/JacksonMapperConfigurator.java new file mode 100644 index 0000000000..7be7b11c6c --- /dev/null +++ b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/JacksonMapperConfigurator.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.jackson.internal; + +import com.fasterxml.jackson.databind.AnnotationIntrospector; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.module.jakarta.xmlbind.JakartaXmlBindAnnotationIntrospector; +import org.glassfish.jersey.internal.util.ReflectionHelper; +import org.glassfish.jersey.internal.util.collection.LazyValue; +import org.glassfish.jersey.internal.util.collection.Value; +import org.glassfish.jersey.internal.util.collection.Values; +import org.glassfish.jersey.jackson.internal.jackson.jaxrs.cfg.Annotations; +import org.glassfish.jersey.jackson.internal.jackson.jaxrs.json.JsonMapperConfigurator; + +import java.security.AccessController; +import java.util.ArrayList; + +public class JacksonMapperConfigurator extends JsonMapperConfigurator { + public JacksonMapperConfigurator(ObjectMapper mapper, Annotations[] defAnnotations) { + super(mapper, defAnnotations); + } + + @Override + protected AnnotationIntrospector _resolveIntrospectors(Annotations[] annotationsToUse) { + // Let's ensure there are no dups there first, filter out nulls + ArrayList intr = new ArrayList(); + for (Annotations a : annotationsToUse) { + if (a != null) { + _resolveIntrospector(a, intr); + } + } + int count = intr.size(); + if (count == 0) { + return AnnotationIntrospector.nopInstance(); + } + AnnotationIntrospector curr = intr.get(0); + for (int i = 1, len = intr.size(); i < len; ++i) { + curr = AnnotationIntrospector.pair(curr, intr.get(i)); + } + return curr; + } + + protected void _resolveIntrospector(Annotations ann, ArrayList intr) { + switch (ann) { + case JAXB: + /* For this, need to use indirection just so that error occurs + * when we get here, and not when this class is being loaded + */ + try { + if (_jaxbIntrospectorClass == null) { + _jaxbIntrospectorClass = JakartaXmlBindAnnotationIntrospector.class; + } + intr.add(JakartaXmlBindAnnotationIntrospector.class.newInstance()); + } catch (Exception e) { + throw new IllegalStateException("Failed to instantiate JakartaXmlBindAnnotationIntrospector: " + + e.getMessage(), e); + } + + if (jaxb2AnnotationIntrospector.get() == true) { + Class tempJaxbIntrospectorClass = _jaxbIntrospectorClass; + _jaxbIntrospectorClass = null; + intr.add(super._resolveIntrospector(ann)); + _jaxbIntrospectorClass = tempJaxbIntrospectorClass; + } + break; + default: + intr.add(super._resolveIntrospector(ann)); + } + } + + private static LazyValue jaxb2AnnotationIntrospector = Values.lazy((Value) () -> { + final Class aClass = AccessController.doPrivileged( + ReflectionHelper.classForNamePA("com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector") + ); + return aClass != null; + }); +} diff --git a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/json/JacksonJaxbJsonProvider.java b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/json/JacksonJaxbJsonProvider.java index 1c0a882fc2..eb7c072f0c 100644 --- a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/json/JacksonJaxbJsonProvider.java +++ b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/json/JacksonJaxbJsonProvider.java @@ -63,4 +63,10 @@ public JacksonJaxbJsonProvider(ObjectMapper mapper, Annotations[] annotationsToU { super(mapper, annotationsToUse); } + + // Do not erase - Jersey required constructor + protected JacksonJaxbJsonProvider(JsonMapperConfigurator configurator) + { + super(configurator); + } } diff --git a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/json/JacksonJsonProvider.java b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/json/JacksonJsonProvider.java index 9fecf427a9..0871ece423 100644 --- a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/json/JacksonJsonProvider.java +++ b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/json/JacksonJsonProvider.java @@ -137,6 +137,11 @@ public JacksonJsonProvider(ObjectMapper mapper, Annotations[] annotationsToUse) super(new JsonMapperConfigurator(mapper, annotationsToUse)); } + // Do not erase - Jersey required constructor + protected JacksonJsonProvider(JsonMapperConfigurator configurator) { + super(configurator); + } + /** * Method that will return version information stored in and read from jar * that contains this class. diff --git a/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/JacksonJaxb2JsonProviderTest.java b/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/JacksonJaxb2JsonProviderTest.java new file mode 100644 index 0000000000..09a3c8b7c2 --- /dev/null +++ b/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/JacksonJaxb2JsonProviderTest.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.jackson.internal; + +import jakarta.ws.rs.core.Application; +import org.glassfish.jersey.jackson.internal.model.Jaxb2ServiceTest; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.test.JerseyTest; +import org.junit.Test; + +import static junit.framework.TestCase.assertEquals; + +public final class JacksonJaxb2JsonProviderTest extends JerseyTest { + + @Override + protected final Application configure() { + return new ResourceConfig(Jaxb2ServiceTest.class); + } + + @Test + public final void testJavaOptional() { + final String response = target("entity/simple").request().get(String.class); + assertEquals("{\"name\":\"Hello\",\"value\":\"World\"}", response); + } +} diff --git a/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/model/Jaxb2ServiceTest.java b/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/model/Jaxb2ServiceTest.java new file mode 100644 index 0000000000..a679142c55 --- /dev/null +++ b/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/model/Jaxb2ServiceTest.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2020, 2022 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.jackson.internal.model; + +import com.fasterxml.jackson.annotation.JsonGetter; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import javax.xml.bind.annotation.XmlElement; + +import java.util.Optional; + +@Path("/entity/") +public final class Jaxb2ServiceTest { + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("/simple") + public final EntityTest simple() { + return new EntityTest("Hello", "World"); + } + + private static final class EntityTest { + + private final String name; + + private final String value; + + EntityTest(final String name, final String value) { + this.name = name; + this.value = value; + } + + @XmlElement(name = "jaxb") + @JsonGetter("name") + public final String getName() { + return name; + } + + @JsonGetter("value") + public final Optional getValue() { + return Optional.ofNullable(value); + } + } +} diff --git a/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/model/ServiceTest.java b/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/model/ServiceTest.java index 23ac90de61..314cc99d9a 100644 --- a/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/model/ServiceTest.java +++ b/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/model/ServiceTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at diff --git a/pom.xml b/pom.xml index 0d6fe4d54c..a205697b71 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@