Skip to content

Commit b1a8d76

Browse files
committed
Move to Jackson 3
1 parent 9e09d67 commit b1a8d76

14 files changed

Lines changed: 109 additions & 72 deletions

pom.xml

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -86,13 +86,6 @@
8686

8787
<dependencyManagement>
8888
<dependencies>
89-
<dependency>
90-
<groupId>com.fasterxml.jackson</groupId>
91-
<artifactId>jackson-bom</artifactId>
92-
<version>2.20.0</version>
93-
<type>pom</type>
94-
<scope>import</scope>
95-
</dependency>
9689
<dependency>
9790
<groupId>org.junit</groupId>
9891
<artifactId>junit-bom</artifactId>
@@ -114,6 +107,19 @@
114107
<type>pom</type>
115108
<scope>import</scope>
116109
</dependency>
110+
<dependency>
111+
<groupId>tools.jackson</groupId>
112+
<artifactId>jackson-bom</artifactId>
113+
<version>3.0.3</version>
114+
<type>pom</type>
115+
<scope>import</scope>
116+
</dependency>
117+
<!-- Jackson 3.0 requires jackson-annotations 2.20 -->
118+
<dependency>
119+
<groupId>com.fasterxml.jackson.core</groupId>
120+
<artifactId>jackson-annotations</artifactId>
121+
<version>2.20</version>
122+
</dependency>
117123
<dependency>
118124
<groupId>junit</groupId>
119125
<artifactId>junit</artifactId>
@@ -138,14 +144,6 @@
138144
</dependencyManagement>
139145

140146
<dependencies>
141-
<dependency>
142-
<groupId>com.fasterxml.jackson.core</groupId>
143-
<artifactId>jackson-databind</artifactId>
144-
</dependency>
145-
<dependency>
146-
<groupId>com.fasterxml.jackson.datatype</groupId>
147-
<artifactId>jackson-datatype-jsr310</artifactId>
148-
</dependency>
149147
<dependency>
150148
<groupId>com.github.spotbugs</groupId>
151149
<artifactId>spotbugs-annotations</artifactId>
@@ -196,6 +194,10 @@
196194
<artifactId>commons-lang3</artifactId>
197195
<version>3.19.0</version>
198196
</dependency>
197+
<dependency>
198+
<groupId>tools.jackson.core</groupId>
199+
<artifactId>jackson-databind</artifactId>
200+
</dependency>
199201
<dependency>
200202
<groupId>com.github.npathai</groupId>
201203
<artifactId>hamcrest-optional</artifactId>

src/main/java/org/kohsuke/github/EnterpriseManagedSupport.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package org.kohsuke.github;
22

3-
import com.fasterxml.jackson.core.JsonProcessingException;
3+
import tools.jackson.core.JacksonException;
44

55
import java.io.PrintWriter;
66
import java.io.StringWriter;
@@ -20,7 +20,7 @@ class EnterpriseManagedSupport {
2020

2121
static final String TEAM_CANNOT_BE_EXTERNALLY_MANAGED_ERROR = "This team cannot be externally managed since it has explicit members.";
2222

23-
private static String logUnexpectedFailure(final JsonProcessingException exception, final String payload) {
23+
private static String logUnexpectedFailure(final JacksonException exception, final String payload) {
2424
final StringWriter sw = new StringWriter();
2525
final PrintWriter pw = new PrintWriter(sw);
2626
exception.printStackTrace(pw);
@@ -58,7 +58,7 @@ Optional<GHIOException> filterException(final HttpException he, final String sce
5858
} else if (TEAM_CANNOT_BE_EXTERNALLY_MANAGED_ERROR.equals(error.getMessage())) {
5959
return Optional.of(new GHTeamCannotBeExternallyManagedException(scenario, error, he));
6060
}
61-
} catch (final JsonProcessingException e) {
61+
} catch (final JacksonException e) {
6262
// We can ignore it
6363
LOGGER.warning(() -> logUnexpectedFailure(e, responseMessage));
6464
}

src/main/java/org/kohsuke/github/GHEventInfo.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package org.kohsuke.github;
22

3-
import com.fasterxml.jackson.databind.node.ObjectNode;
43
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
54
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
5+
import tools.jackson.databind.node.ObjectNode;
66

77
import java.io.IOException;
88
import java.time.Instant;
@@ -174,7 +174,7 @@ public GHOrganization getOrganization() throws IOException {
174174
* if payload cannot be parsed
175175
*/
176176
public <T extends GHEventPayload> T getPayload(Class<T> type) throws IOException {
177-
T v = GitHubClient.getMappingObjectReader(root()).readValue(payload.traverse(), type);
177+
T v = GitHubClient.getMappingObjectReader(root()).forType(type).readValue(payload);
178178
v.lateBind();
179179
return v;
180180
}

src/main/java/org/kohsuke/github/GHRef.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package org.kohsuke.github;
22

3-
import com.fasterxml.jackson.databind.JsonMappingException;
43
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
4+
import tools.jackson.databind.DatabindException;
55

66
import java.io.IOException;
77
import java.net.URL;
@@ -59,6 +59,17 @@ public URL getUrl() {
5959
}
6060
}
6161

62+
private static boolean hasDatabindExceptionCause(Throwable e) {
63+
Throwable cause = e;
64+
while (cause != null) {
65+
if (cause instanceof DatabindException) {
66+
return true;
67+
}
68+
cause = cause.getCause();
69+
}
70+
return false;
71+
}
72+
6273
/**
6374
* Retrieve a ref of the given type for the current GitHub repository.
6475
*
@@ -88,7 +99,8 @@ static GHRef read(GHRepository repository, String refName) throws IOException {
8899
// If the parse exception is due to the above returning an array instead of a single ref
89100
// that means the individual ref did not exist. Handled by result check below.
90101
// Otherwise, rethrow.
91-
if (!(e.getCause() instanceof JsonMappingException)) {
102+
// In Jackson 3, DatabindException is wrapped in IOException, so check the nested cause chain
103+
if (!hasDatabindExceptionCause(e)) {
92104
throw e;
93105
}
94106
}

src/main/java/org/kohsuke/github/GHRepositoryRule.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
package org.kohsuke.github;
22

3-
import com.fasterxml.jackson.core.type.TypeReference;
4-
import com.fasterxml.jackson.databind.JsonNode;
53
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
64
import org.kohsuke.github.internal.EnumUtils;
5+
import tools.jackson.core.type.TypeReference;
6+
import tools.jackson.databind.JsonNode;
77

88
import java.io.IOException;
99
import java.util.List;

src/main/java/org/kohsuke/github/GHRepositoryStatistics.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package org.kohsuke.github;
22

33
import com.fasterxml.jackson.annotation.JsonCreator;
4-
import com.fasterxml.jackson.databind.exc.MismatchedInputException;
54
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
5+
import tools.jackson.databind.exc.MismatchedInputException;
66

77
import java.io.IOException;
88
import java.util.Arrays;

src/main/java/org/kohsuke/github/GitHub.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,13 @@
2323
*/
2424
package org.kohsuke.github;
2525

26-
import com.fasterxml.jackson.databind.ObjectReader;
27-
import com.fasterxml.jackson.databind.ObjectWriter;
2826
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
2927
import org.kohsuke.github.authorization.AuthorizationProvider;
3028
import org.kohsuke.github.authorization.ImmutableAuthorizationProvider;
3129
import org.kohsuke.github.authorization.UserAuthorizationProvider;
3230
import org.kohsuke.github.connector.GitHubConnector;
31+
import tools.jackson.databind.ObjectReader;
32+
import tools.jackson.databind.ObjectWriter;
3333

3434
import java.io.*;
3535
import java.util.*;

src/main/java/org/kohsuke/github/GitHubClient.java

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
package org.kohsuke.github;
22

3-
import com.fasterxml.jackson.databind.*;
4-
import com.fasterxml.jackson.databind.introspect.VisibilityChecker;
5-
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
63
import org.apache.commons.io.IOUtils;
74
import org.kohsuke.github.authorization.AuthorizationProvider;
85
import org.kohsuke.github.authorization.UserAuthorizationProvider;
96
import org.kohsuke.github.connector.GitHubConnector;
107
import org.kohsuke.github.connector.GitHubConnectorRequest;
118
import org.kohsuke.github.connector.GitHubConnectorResponse;
129
import org.kohsuke.github.function.FunctionThrows;
10+
import tools.jackson.databind.DeserializationFeature;
11+
import tools.jackson.databind.InjectableValues;
12+
import tools.jackson.databind.MapperFeature;
13+
import tools.jackson.databind.ObjectReader;
14+
import tools.jackson.databind.ObjectWriter;
15+
import tools.jackson.databind.PropertyNamingStrategies;
16+
import tools.jackson.databind.json.JsonMapper;
1317

1418
import java.io.*;
1519
import java.net.*;
@@ -109,21 +113,34 @@ static class RetryRequestException extends IOException {
109113
/** The Constant DEFAULT_MINIMUM_RETRY_TIMEOUT_MILLIS. */
110114
private static final int DEFAULT_MINIMUM_RETRY_MILLIS = DEFAULT_MAXIMUM_RETRY_MILLIS;
111115
private static final Logger LOGGER = Logger.getLogger(GitHubClient.class.getName());
112-
private static final ObjectMapper MAPPER = new ObjectMapper();
116+
private static final JsonMapper MAPPER = JsonMapper.builder()
117+
// Use annotations and enable access to private fields
118+
.enable(MapperFeature.USE_ANNOTATIONS)
119+
.enable(MapperFeature.CAN_OVERRIDE_ACCESS_MODIFIERS)
120+
.enable(MapperFeature.INFER_PROPERTY_MUTATORS)
121+
.enable(MapperFeature.ALLOW_FINAL_FIELDS_AS_MUTATORS)
122+
// Set visibility to detect all fields (including private fields)
123+
// This matches the original Jackson 2 config: new VisibilityChecker.Std(NONE, NONE, NONE, NONE, ANY)
124+
.changeDefaultVisibility(vc -> {
125+
return vc.withGetterVisibility(NONE)
126+
.withIsGetterVisibility(NONE)
127+
.withSetterVisibility(NONE)
128+
.withCreatorVisibility(NONE)
129+
.withFieldVisibility(ANY);
130+
})
131+
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
132+
// Jackson 3 enables these by default - disable to match Jackson 2.x behavior
133+
.disable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS)
134+
.disable(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES)
135+
.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS)
136+
.propertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE)
137+
.build();
113138

114139
private static final ThreadLocal<String> sendRequestTraceId = new ThreadLocal<>();
115140

116141
/** The Constant GITHUB_URL. */
117142
static final String GITHUB_URL = "https://api.github.com";
118143

119-
static {
120-
MAPPER.registerModule(new JavaTimeModule());
121-
MAPPER.setVisibility(new VisibilityChecker.Std(NONE, NONE, NONE, NONE, ANY));
122-
MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
123-
MAPPER.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS, true);
124-
MAPPER.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
125-
}
126-
127144
@Nonnull
128145
private static <T> GitHubResponse<T> createResponse(@Nonnull GitHubConnectorResponse connectorResponse,
129146
@CheckForNull BodyHandler<T> handler) throws IOException {

src/main/java/org/kohsuke/github/GitHubResponse.java

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
package org.kohsuke.github;
22

3-
import com.fasterxml.jackson.core.JsonParseException;
4-
import com.fasterxml.jackson.databind.InjectableValues;
5-
import com.fasterxml.jackson.databind.JsonMappingException;
63
import org.apache.commons.io.IOUtils;
74
import org.kohsuke.github.connector.GitHubConnectorResponse;
5+
import tools.jackson.core.JacksonException;
6+
import tools.jackson.databind.InjectableValues;
87

98
import java.io.IOException;
109
import java.io.InputStream;
@@ -99,10 +98,10 @@ static <T> T parseBody(GitHubConnectorResponse connectorResponse, Class<T> type)
9998
inject.addValue(GitHubConnectorResponse.class, connectorResponse);
10099

101100
return GitHubClient.getMappingObjectReader(connectorResponse).forType(type).readValue(data);
102-
} catch (JsonMappingException | JsonParseException e) {
101+
} catch (JacksonException e) {
103102
String message = "Failed to deserialize: " + data;
104103
LOGGER.log(Level.FINE, message);
105-
throw e;
104+
throw new IOException(message, e);
106105
}
107106
}
108107

@@ -125,10 +124,10 @@ static <T> T parseBody(GitHubConnectorResponse connectorResponse, T instance) th
125124
String data = getBodyAsString(connectorResponse);
126125
try {
127126
return GitHubClient.getMappingObjectReader(connectorResponse).withValueToUpdate(instance).readValue(data);
128-
} catch (JsonMappingException | JsonParseException e) {
127+
} catch (JacksonException e) {
129128
String message = "Failed to deserialize: " + data;
130129
LOGGER.log(Level.FINE, message);
131-
throw e;
130+
throw new IOException(message, e);
132131
}
133132
}
134133

src/test/java/org/kohsuke/github/AotIntegrationTest.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
package org.kohsuke.github;
22

3-
import com.fasterxml.jackson.databind.JsonNode;
4-
import com.fasterxml.jackson.databind.ObjectMapper;
5-
import com.fasterxml.jackson.databind.node.ArrayNode;
63
import org.junit.Test;
74
import org.springframework.boot.test.context.SpringBootTest;
5+
import tools.jackson.databind.JsonNode;
6+
import tools.jackson.databind.json.JsonMapper;
7+
import tools.jackson.databind.node.ArrayNode;
88

99
import java.io.IOException;
1010
import java.nio.file.Files;
@@ -80,7 +80,9 @@ public void testIfAllRequiredClassesAreRegisteredForAot() throws IOException {
8080

8181
private Stream<String> readAotConfigToStreamOfClassNames(String reflectionConfig) throws IOException {
8282
byte[] reflectionConfigFileAsBytes = Files.readAllBytes(Path.of(reflectionConfig));
83-
ArrayNode reflectConfigJsonArray = (ArrayNode) new ObjectMapper().readTree(reflectionConfigFileAsBytes);
83+
ArrayNode reflectConfigJsonArray = (ArrayNode) JsonMapper.builder()
84+
.build()
85+
.readTree(reflectionConfigFileAsBytes);
8486
return StreamSupport
8587
.stream(Spliterators.spliteratorUnknownSize(reflectConfigJsonArray.iterator(), Spliterator.ORDERED),
8688
false)

0 commit comments

Comments
 (0)