Skip to content

Commit 494fe2e

Browse files
authored
Let Jackson use JAXB3 (#4963)
* Let Jackson use JAXB3 Keep optional dependency for JAXB2 to keep it working Signed-off-by: jansupol <[email protected]>
1 parent f2ffd7a commit 494fe2e

File tree

17 files changed

+289
-56
lines changed

17 files changed

+289
-56
lines changed

examples/json-jackson/src/main/java/org/glassfish/jersey/examples/jackson/CombinedAnnotationBean.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2010, 2022 Oracle and/or its affiliates. All rights reserved.
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Distribution License v. 1.0, which is available at
@@ -10,7 +10,7 @@
1010

1111
package org.glassfish.jersey.examples.jackson;
1212

13-
import javax.xml.bind.annotation.XmlRootElement;
13+
import jakarta.xml.bind.annotation.XmlRootElement;
1414

1515
import com.fasterxml.jackson.annotation.JsonProperty;
1616

examples/json-jackson/src/main/java/org/glassfish/jersey/examples/jackson/MyObjectMapperProvider.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2010, 2022 Oracle and/or its affiliates. All rights reserved.
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Distribution License v. 1.0, which is available at
@@ -19,7 +19,7 @@
1919
import com.fasterxml.jackson.databind.SerializationFeature;
2020
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
2121
import com.fasterxml.jackson.databind.type.TypeFactory;
22-
import com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector;
22+
import com.fasterxml.jackson.module.jakarta.xmlbind.JakartaXmlBindAnnotationIntrospector;
2323

2424
/**
2525
* TODO javadoc.
@@ -63,7 +63,7 @@ private static ObjectMapper createDefaultMapper() {
6363

6464
private static AnnotationIntrospector createJaxbJacksonAnnotationIntrospector() {
6565

66-
final AnnotationIntrospector jaxbIntrospector = new JaxbAnnotationIntrospector(TypeFactory.defaultInstance());
66+
final AnnotationIntrospector jaxbIntrospector = new JakartaXmlBindAnnotationIntrospector(TypeFactory.defaultInstance());
6767
final AnnotationIntrospector jacksonIntrospector = new JacksonAnnotationIntrospector();
6868

6969
return AnnotationIntrospector.pair(jacksonIntrospector, jaxbIntrospector);

media/json-jackson/pom.xml

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<!--
33
4-
Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved.
4+
Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved.
55
66
This program and the accompanying materials are made available under the
77
terms of the Eclipse Public License v. 2.0, which is available at
@@ -131,13 +131,34 @@
131131
<artifactId>jakarta.activation-api</artifactId>
132132
</exclusion>
133133
</exclusions>
134+
<optional>true</optional>
135+
<scope>provided</scope>
134136
</dependency>
135137
<dependency>
136-
<!-- The last JAX-B API that has maven coordinates which do not colide with Jakarta coordinates -->
138+
<groupId>com.fasterxml.jackson.module</groupId>
139+
<artifactId>jackson-module-jakarta-xmlbind-annotations</artifactId>
140+
<exclusions>
141+
<exclusion>
142+
<groupId>jakarta.xml.bind</groupId>
143+
<artifactId>jakarta.xml.bind-api</artifactId>
144+
</exclusion>
145+
<exclusion>
146+
<groupId>jakarta.activation</groupId>
147+
<artifactId>jakarta.activation-api</artifactId>
148+
</exclusion>
149+
</exclusions>
150+
</dependency>
151+
<dependency>
152+
<!-- The last JAX-B API that has maven coordinates which do not collide with Jakarta coordinates -->
137153
<!-- Do not update -->
138154
<groupId>javax.xml.bind</groupId>
139155
<artifactId>jaxb-api</artifactId>
140156
<version>2.3.1</version>
157+
<scope>test</scope>
158+
</dependency>
159+
<dependency>
160+
<groupId>jakarta.xml.bind</groupId>
161+
<artifactId>jakarta.xml.bind-api</artifactId>
141162
</dependency>
142163
<dependency>
143164
<groupId>junit</groupId>

media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/DefaultJacksonJaxbJsonProvider.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2020, 2021 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2020, 2022 Oracle and/or its affiliates. All rights reserved.
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -31,15 +31,15 @@
3131
public class DefaultJacksonJaxbJsonProvider extends JacksonJaxbJsonProvider {
3232

3333
//do not register JaxbAnnotationModule because it brakes default annotations processing
34-
private static final String EXCLUDE_MODULE_NAME = "JaxbAnnotationModule";
34+
private static final String[] EXCLUDE_MODULE_NAMES = {"JaxbAnnotationModule", "JakartaXmlBindAnnotationModule"};
3535

3636
public DefaultJacksonJaxbJsonProvider() {
37-
super();
37+
super(new JacksonMapperConfigurator(null, DEFAULT_ANNOTATIONS));
3838
findAndRegisterModules();
3939
}
4040

4141
public DefaultJacksonJaxbJsonProvider(final Annotations... annotationsToUse) {
42-
super(annotationsToUse);
42+
super(new JacksonMapperConfigurator(null, annotationsToUse));
4343
findAndRegisterModules();
4444
}
4545

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

5151
final List<Module> modules = ObjectMapper.findModules();
52-
modules.removeIf(mod -> mod.getModuleName().contains(EXCLUDE_MODULE_NAME));
52+
for (String exludeModuleName : EXCLUDE_MODULE_NAMES) {
53+
modules.removeIf(mod -> mod.getModuleName().contains(exludeModuleName));
54+
}
5355

5456
defaultMapper.registerModules(modules);
5557
if (mapper != null) {
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*
2+
* Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License v. 2.0, which is available at
6+
* http://www.eclipse.org/legal/epl-2.0.
7+
*
8+
* This Source Code may also be made available under the following Secondary
9+
* Licenses when the conditions for such availability set forth in the
10+
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
11+
* version 2 with the GNU Classpath Exception, which is available at
12+
* https://www.gnu.org/software/classpath/license.html.
13+
*
14+
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15+
*/
16+
17+
package org.glassfish.jersey.jackson.internal;
18+
19+
import com.fasterxml.jackson.databind.AnnotationIntrospector;
20+
import com.fasterxml.jackson.databind.ObjectMapper;
21+
import com.fasterxml.jackson.module.jakarta.xmlbind.JakartaXmlBindAnnotationIntrospector;
22+
import org.glassfish.jersey.internal.util.ReflectionHelper;
23+
import org.glassfish.jersey.internal.util.collection.LazyValue;
24+
import org.glassfish.jersey.internal.util.collection.Value;
25+
import org.glassfish.jersey.internal.util.collection.Values;
26+
import org.glassfish.jersey.jackson.internal.jackson.jaxrs.cfg.Annotations;
27+
import org.glassfish.jersey.jackson.internal.jackson.jaxrs.json.JsonMapperConfigurator;
28+
29+
import java.security.AccessController;
30+
import java.util.ArrayList;
31+
32+
public class JacksonMapperConfigurator extends JsonMapperConfigurator {
33+
public JacksonMapperConfigurator(ObjectMapper mapper, Annotations[] defAnnotations) {
34+
super(mapper, defAnnotations);
35+
}
36+
37+
@Override
38+
protected AnnotationIntrospector _resolveIntrospectors(Annotations[] annotationsToUse) {
39+
// Let's ensure there are no dups there first, filter out nulls
40+
ArrayList<AnnotationIntrospector> intr = new ArrayList<AnnotationIntrospector>();
41+
for (Annotations a : annotationsToUse) {
42+
if (a != null) {
43+
_resolveIntrospector(a, intr);
44+
}
45+
}
46+
int count = intr.size();
47+
if (count == 0) {
48+
return AnnotationIntrospector.nopInstance();
49+
}
50+
AnnotationIntrospector curr = intr.get(0);
51+
for (int i = 1, len = intr.size(); i < len; ++i) {
52+
curr = AnnotationIntrospector.pair(curr, intr.get(i));
53+
}
54+
return curr;
55+
}
56+
57+
protected void _resolveIntrospector(Annotations ann, ArrayList<AnnotationIntrospector> intr) {
58+
switch (ann) {
59+
case JAXB:
60+
/* For this, need to use indirection just so that error occurs
61+
* when we get here, and not when this class is being loaded
62+
*/
63+
try {
64+
if (_jaxbIntrospectorClass == null) {
65+
_jaxbIntrospectorClass = JakartaXmlBindAnnotationIntrospector.class;
66+
}
67+
intr.add(JakartaXmlBindAnnotationIntrospector.class.newInstance());
68+
} catch (Exception e) {
69+
throw new IllegalStateException("Failed to instantiate JakartaXmlBindAnnotationIntrospector: "
70+
+ e.getMessage(), e);
71+
}
72+
73+
if (jaxb2AnnotationIntrospector.get() == true) {
74+
Class<? extends AnnotationIntrospector> tempJaxbIntrospectorClass = _jaxbIntrospectorClass;
75+
_jaxbIntrospectorClass = null;
76+
intr.add(super._resolveIntrospector(ann));
77+
_jaxbIntrospectorClass = tempJaxbIntrospectorClass;
78+
}
79+
break;
80+
default:
81+
intr.add(super._resolveIntrospector(ann));
82+
}
83+
}
84+
85+
private static LazyValue<Boolean> jaxb2AnnotationIntrospector = Values.lazy((Value<Boolean>) () -> {
86+
final Class<?> aClass = AccessController.doPrivileged(
87+
ReflectionHelper.classForNamePA("com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector")
88+
);
89+
return aClass != null;
90+
});
91+
}

media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/json/JacksonJaxbJsonProvider.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,10 @@ public JacksonJaxbJsonProvider(ObjectMapper mapper, Annotations[] annotationsToU
6363
{
6464
super(mapper, annotationsToUse);
6565
}
66+
67+
// Do not erase - Jersey required constructor
68+
protected JacksonJaxbJsonProvider(JsonMapperConfigurator configurator)
69+
{
70+
super(configurator);
71+
}
6672
}

media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/json/JacksonJsonProvider.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,11 @@ public JacksonJsonProvider(ObjectMapper mapper, Annotations[] annotationsToUse)
137137
super(new JsonMapperConfigurator(mapper, annotationsToUse));
138138
}
139139

140+
// Do not erase - Jersey required constructor
141+
protected JacksonJsonProvider(JsonMapperConfigurator configurator) {
142+
super(configurator);
143+
}
144+
140145
/**
141146
* Method that will return version information stored in and read from jar
142147
* that contains this class.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License v. 2.0, which is available at
6+
* http://www.eclipse.org/legal/epl-2.0.
7+
*
8+
* This Source Code may also be made available under the following Secondary
9+
* Licenses when the conditions for such availability set forth in the
10+
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
11+
* version 2 with the GNU Classpath Exception, which is available at
12+
* https://www.gnu.org/software/classpath/license.html.
13+
*
14+
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15+
*/
16+
17+
package org.glassfish.jersey.jackson.internal;
18+
19+
import jakarta.ws.rs.core.Application;
20+
import org.glassfish.jersey.jackson.internal.model.Jaxb2ServiceTest;
21+
import org.glassfish.jersey.server.ResourceConfig;
22+
import org.glassfish.jersey.test.JerseyTest;
23+
import org.junit.Test;
24+
25+
import static junit.framework.TestCase.assertEquals;
26+
27+
public final class JacksonJaxb2JsonProviderTest extends JerseyTest {
28+
29+
@Override
30+
protected final Application configure() {
31+
return new ResourceConfig(Jaxb2ServiceTest.class);
32+
}
33+
34+
@Test
35+
public final void testJavaOptional() {
36+
final String response = target("entity/simple").request().get(String.class);
37+
assertEquals("{\"name\":\"Hello\",\"value\":\"World\"}", response);
38+
}
39+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright (c) 2020, 2022 Oracle and/or its affiliates. All rights reserved.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License v. 2.0, which is available at
6+
* http://www.eclipse.org/legal/epl-2.0.
7+
*
8+
* This Source Code may also be made available under the following Secondary
9+
* Licenses when the conditions for such availability set forth in the
10+
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
11+
* version 2 with the GNU Classpath Exception, which is available at
12+
* https://www.gnu.org/software/classpath/license.html.
13+
*
14+
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15+
*/
16+
17+
package org.glassfish.jersey.jackson.internal.model;
18+
19+
import com.fasterxml.jackson.annotation.JsonGetter;
20+
import jakarta.ws.rs.GET;
21+
import jakarta.ws.rs.Path;
22+
import jakarta.ws.rs.Produces;
23+
import jakarta.ws.rs.core.MediaType;
24+
import javax.xml.bind.annotation.XmlElement;
25+
26+
import java.util.Optional;
27+
28+
@Path("/entity/")
29+
public final class Jaxb2ServiceTest {
30+
31+
@GET
32+
@Produces(MediaType.APPLICATION_JSON)
33+
@Path("/simple")
34+
public final EntityTest simple() {
35+
return new EntityTest("Hello", "World");
36+
}
37+
38+
private static final class EntityTest {
39+
40+
private final String name;
41+
42+
private final String value;
43+
44+
EntityTest(final String name, final String value) {
45+
this.name = name;
46+
this.value = value;
47+
}
48+
49+
@XmlElement(name = "jaxb")
50+
@JsonGetter("name")
51+
public final String getName() {
52+
return name;
53+
}
54+
55+
@JsonGetter("value")
56+
public final Optional<String> getValue() {
57+
return Optional.ofNullable(value);
58+
}
59+
}
60+
}

media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/model/ServiceTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2020, 2021 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2020, 2022 Oracle and/or its affiliates. All rights reserved.
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Public License v. 2.0, which is available at

0 commit comments

Comments
 (0)