Skip to content

Commit

Permalink
Let Jackson use JAXB3 (#4963)
Browse files Browse the repository at this point in the history
* Let Jackson use JAXB3
Keep optional dependency for JAXB2 to keep it working

Signed-off-by: jansupol <[email protected]>
  • Loading branch information
jansupol authored Jan 27, 2022
1 parent f2ffd7a commit 494fe2e
Show file tree
Hide file tree
Showing 17 changed files with 289 additions and 56 deletions.
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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;

Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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.
Expand Down Expand Up @@ -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);
Expand Down
25 changes: 23 additions & 2 deletions media/json-jackson/pom.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved.
Copyright (c) 2012, 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
Expand Down Expand Up @@ -131,13 +131,34 @@
<artifactId>jakarta.activation-api</artifactId>
</exclusion>
</exclusions>
<optional>true</optional>
<scope>provided</scope>
</dependency>
<dependency>
<!-- The last JAX-B API that has maven coordinates which do not colide with Jakarta coordinates -->
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-jakarta-xmlbind-annotations</artifactId>
<exclusions>
<exclusion>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
</exclusion>
<exclusion>
<groupId>jakarta.activation</groupId>
<artifactId>jakarta.activation-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<!-- The last JAX-B API that has maven coordinates which do not collide with Jakarta coordinates -->
<!-- Do not update -->
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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();
}

Expand All @@ -49,7 +49,9 @@ private void findAndRegisterModules() {
final ObjectMapper mapper = _mapperConfig.getConfiguredMapper();

final List<Module> 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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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<AnnotationIntrospector> intr = new ArrayList<AnnotationIntrospector>();
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<AnnotationIntrospector> 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<? extends AnnotationIntrospector> tempJaxbIntrospectorClass = _jaxbIntrospectorClass;
_jaxbIntrospectorClass = null;
intr.add(super._resolveIntrospector(ann));
_jaxbIntrospectorClass = tempJaxbIntrospectorClass;
}
break;
default:
intr.add(super._resolveIntrospector(ann));
}
}

private static LazyValue<Boolean> jaxb2AnnotationIntrospector = Values.lazy((Value<Boolean>) () -> {
final Class<?> aClass = AccessController.doPrivileged(
ReflectionHelper.classForNamePA("com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector")
);
return aClass != null;
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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<String> getValue() {
return Optional.ofNullable(value);
}
}
}
Original file line number Diff line number Diff line change
@@ -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
Expand Down
8 changes: 7 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2010, 2021 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 Public License v. 2.0, which is available at
Expand Down Expand Up @@ -1750,6 +1750,12 @@
<version>${jackson.version}</version>
</dependency>

<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-jakarta-xmlbind-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>

<dependency>
<groupId>xerces</groupId>
<artifactId>xercesImpl</artifactId>
Expand Down
Loading

0 comments on commit 494fe2e

Please sign in to comment.