diff --git a/pom.xml b/pom.xml
index 041d55bc77..6e0f50c4f8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -86,13 +86,6 @@
-
- com.fasterxml.jackson
- jackson-bom
- 2.20.0
- pom
- import
-
org.junit
junit-bom
@@ -114,6 +107,19 @@
pom
import
+
+ tools.jackson
+ jackson-bom
+ 3.0.3
+ pom
+ import
+
+
+
+ com.fasterxml.jackson.core
+ jackson-annotations
+ 2.20
+
junit
junit
@@ -138,14 +144,6 @@
-
- com.fasterxml.jackson.core
- jackson-databind
-
-
- com.fasterxml.jackson.datatype
- jackson-datatype-jsr310
-
com.github.spotbugs
spotbugs-annotations
@@ -196,6 +194,10 @@
commons-lang3
3.19.0
+
+ tools.jackson.core
+ jackson-databind
+
com.github.npathai
hamcrest-optional
diff --git a/src/main/java/org/kohsuke/github/EnterpriseManagedSupport.java b/src/main/java/org/kohsuke/github/EnterpriseManagedSupport.java
index 9d3030558d..db73aae935 100644
--- a/src/main/java/org/kohsuke/github/EnterpriseManagedSupport.java
+++ b/src/main/java/org/kohsuke/github/EnterpriseManagedSupport.java
@@ -1,6 +1,6 @@
package org.kohsuke.github;
-import com.fasterxml.jackson.core.JsonProcessingException;
+import tools.jackson.core.JacksonException;
import java.io.PrintWriter;
import java.io.StringWriter;
@@ -20,7 +20,7 @@ class EnterpriseManagedSupport {
static final String TEAM_CANNOT_BE_EXTERNALLY_MANAGED_ERROR = "This team cannot be externally managed since it has explicit members.";
- private static String logUnexpectedFailure(final JsonProcessingException exception, final String payload) {
+ private static String logUnexpectedFailure(final JacksonException exception, final String payload) {
final StringWriter sw = new StringWriter();
final PrintWriter pw = new PrintWriter(sw);
exception.printStackTrace(pw);
@@ -58,7 +58,7 @@ Optional filterException(final HttpException he, final String sce
} else if (TEAM_CANNOT_BE_EXTERNALLY_MANAGED_ERROR.equals(error.getMessage())) {
return Optional.of(new GHTeamCannotBeExternallyManagedException(scenario, error, he));
}
- } catch (final JsonProcessingException e) {
+ } catch (final JacksonException e) {
// We can ignore it
LOGGER.warning(() -> logUnexpectedFailure(e, responseMessage));
}
diff --git a/src/main/java/org/kohsuke/github/GHEventInfo.java b/src/main/java/org/kohsuke/github/GHEventInfo.java
index b9adccf2e6..87030f478a 100644
--- a/src/main/java/org/kohsuke/github/GHEventInfo.java
+++ b/src/main/java/org/kohsuke/github/GHEventInfo.java
@@ -1,8 +1,8 @@
package org.kohsuke.github;
-import com.fasterxml.jackson.databind.node.ObjectNode;
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import tools.jackson.databind.node.ObjectNode;
import java.io.IOException;
import java.time.Instant;
@@ -174,7 +174,7 @@ public GHOrganization getOrganization() throws IOException {
* if payload cannot be parsed
*/
public T getPayload(Class type) throws IOException {
- T v = GitHubClient.getMappingObjectReader(root()).readValue(payload.traverse(), type);
+ T v = GitHubClient.getMappingObjectReader(root()).forType(type).readValue(payload);
v.lateBind();
return v;
}
diff --git a/src/main/java/org/kohsuke/github/GHRef.java b/src/main/java/org/kohsuke/github/GHRef.java
index 33d54792db..1214f21c5c 100644
--- a/src/main/java/org/kohsuke/github/GHRef.java
+++ b/src/main/java/org/kohsuke/github/GHRef.java
@@ -1,7 +1,7 @@
package org.kohsuke.github;
-import com.fasterxml.jackson.databind.JsonMappingException;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import tools.jackson.databind.DatabindException;
import java.io.IOException;
import java.net.URL;
@@ -59,6 +59,17 @@ public URL getUrl() {
}
}
+ private static boolean hasDatabindExceptionCause(Throwable e) {
+ Throwable cause = e;
+ while (cause != null) {
+ if (cause instanceof DatabindException) {
+ return true;
+ }
+ cause = cause.getCause();
+ }
+ return false;
+ }
+
/**
* Retrieve a ref of the given type for the current GitHub repository.
*
@@ -88,7 +99,8 @@ static GHRef read(GHRepository repository, String refName) throws IOException {
// If the parse exception is due to the above returning an array instead of a single ref
// that means the individual ref did not exist. Handled by result check below.
// Otherwise, rethrow.
- if (!(e.getCause() instanceof JsonMappingException)) {
+ // In Jackson 3, DatabindException is wrapped in IOException, so check the nested cause chain
+ if (!hasDatabindExceptionCause(e)) {
throw e;
}
}
diff --git a/src/main/java/org/kohsuke/github/GHRepositoryRule.java b/src/main/java/org/kohsuke/github/GHRepositoryRule.java
index d348153e85..ebd5a09ace 100644
--- a/src/main/java/org/kohsuke/github/GHRepositoryRule.java
+++ b/src/main/java/org/kohsuke/github/GHRepositoryRule.java
@@ -1,9 +1,9 @@
package org.kohsuke.github;
-import com.fasterxml.jackson.core.type.TypeReference;
-import com.fasterxml.jackson.databind.JsonNode;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.kohsuke.github.internal.EnumUtils;
+import tools.jackson.core.type.TypeReference;
+import tools.jackson.databind.JsonNode;
import java.io.IOException;
import java.util.List;
diff --git a/src/main/java/org/kohsuke/github/GHRepositoryStatistics.java b/src/main/java/org/kohsuke/github/GHRepositoryStatistics.java
index ece1e9d87d..0eafc735b0 100644
--- a/src/main/java/org/kohsuke/github/GHRepositoryStatistics.java
+++ b/src/main/java/org/kohsuke/github/GHRepositoryStatistics.java
@@ -1,8 +1,8 @@
package org.kohsuke.github;
import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.databind.exc.MismatchedInputException;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import tools.jackson.databind.exc.MismatchedInputException;
import java.io.IOException;
import java.util.Arrays;
diff --git a/src/main/java/org/kohsuke/github/GitHub.java b/src/main/java/org/kohsuke/github/GitHub.java
index cb47de47af..e4e2a5fb7f 100644
--- a/src/main/java/org/kohsuke/github/GitHub.java
+++ b/src/main/java/org/kohsuke/github/GitHub.java
@@ -23,13 +23,13 @@
*/
package org.kohsuke.github;
-import com.fasterxml.jackson.databind.ObjectReader;
-import com.fasterxml.jackson.databind.ObjectWriter;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.kohsuke.github.authorization.AuthorizationProvider;
import org.kohsuke.github.authorization.ImmutableAuthorizationProvider;
import org.kohsuke.github.authorization.UserAuthorizationProvider;
import org.kohsuke.github.connector.GitHubConnector;
+import tools.jackson.databind.ObjectReader;
+import tools.jackson.databind.ObjectWriter;
import java.io.*;
import java.util.*;
diff --git a/src/main/java/org/kohsuke/github/GitHubClient.java b/src/main/java/org/kohsuke/github/GitHubClient.java
index 70eec892ec..a0a7603f99 100644
--- a/src/main/java/org/kohsuke/github/GitHubClient.java
+++ b/src/main/java/org/kohsuke/github/GitHubClient.java
@@ -1,8 +1,5 @@
package org.kohsuke.github;
-import com.fasterxml.jackson.databind.*;
-import com.fasterxml.jackson.databind.introspect.VisibilityChecker;
-import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.apache.commons.io.IOUtils;
import org.kohsuke.github.authorization.AuthorizationProvider;
import org.kohsuke.github.authorization.UserAuthorizationProvider;
@@ -10,6 +7,13 @@
import org.kohsuke.github.connector.GitHubConnectorRequest;
import org.kohsuke.github.connector.GitHubConnectorResponse;
import org.kohsuke.github.function.FunctionThrows;
+import tools.jackson.databind.DeserializationFeature;
+import tools.jackson.databind.InjectableValues;
+import tools.jackson.databind.MapperFeature;
+import tools.jackson.databind.ObjectReader;
+import tools.jackson.databind.ObjectWriter;
+import tools.jackson.databind.PropertyNamingStrategies;
+import tools.jackson.databind.json.JsonMapper;
import java.io.*;
import java.net.*;
@@ -109,21 +113,34 @@ static class RetryRequestException extends IOException {
/** The Constant DEFAULT_MINIMUM_RETRY_TIMEOUT_MILLIS. */
private static final int DEFAULT_MINIMUM_RETRY_MILLIS = DEFAULT_MAXIMUM_RETRY_MILLIS;
private static final Logger LOGGER = Logger.getLogger(GitHubClient.class.getName());
- private static final ObjectMapper MAPPER = new ObjectMapper();
+ private static final JsonMapper MAPPER = JsonMapper.builder()
+ // Use annotations and enable access to private fields
+ .enable(MapperFeature.USE_ANNOTATIONS)
+ .enable(MapperFeature.CAN_OVERRIDE_ACCESS_MODIFIERS)
+ .enable(MapperFeature.INFER_PROPERTY_MUTATORS)
+ .enable(MapperFeature.ALLOW_FINAL_FIELDS_AS_MUTATORS)
+ // Set visibility to detect all fields (including private fields)
+ // This matches the original Jackson 2 config: new VisibilityChecker.Std(NONE, NONE, NONE, NONE, ANY)
+ .changeDefaultVisibility(vc -> {
+ return vc.withGetterVisibility(NONE)
+ .withIsGetterVisibility(NONE)
+ .withSetterVisibility(NONE)
+ .withCreatorVisibility(NONE)
+ .withFieldVisibility(ANY);
+ })
+ .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
+ // Jackson 3 enables these by default - disable to match Jackson 2.x behavior
+ .disable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS)
+ .disable(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES)
+ .enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS)
+ .propertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE)
+ .build();
private static final ThreadLocal sendRequestTraceId = new ThreadLocal<>();
/** The Constant GITHUB_URL. */
static final String GITHUB_URL = "https://api.github.com";
- static {
- MAPPER.registerModule(new JavaTimeModule());
- MAPPER.setVisibility(new VisibilityChecker.Std(NONE, NONE, NONE, NONE, ANY));
- MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
- MAPPER.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS, true);
- MAPPER.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
- }
-
@Nonnull
private static GitHubResponse createResponse(@Nonnull GitHubConnectorResponse connectorResponse,
@CheckForNull BodyHandler handler) throws IOException {
diff --git a/src/main/java/org/kohsuke/github/GitHubResponse.java b/src/main/java/org/kohsuke/github/GitHubResponse.java
index 8ac65391f7..8e0bcf7746 100644
--- a/src/main/java/org/kohsuke/github/GitHubResponse.java
+++ b/src/main/java/org/kohsuke/github/GitHubResponse.java
@@ -1,10 +1,9 @@
package org.kohsuke.github;
-import com.fasterxml.jackson.core.JsonParseException;
-import com.fasterxml.jackson.databind.InjectableValues;
-import com.fasterxml.jackson.databind.JsonMappingException;
import org.apache.commons.io.IOUtils;
import org.kohsuke.github.connector.GitHubConnectorResponse;
+import tools.jackson.core.JacksonException;
+import tools.jackson.databind.InjectableValues;
import java.io.IOException;
import java.io.InputStream;
@@ -99,10 +98,10 @@ static T parseBody(GitHubConnectorResponse connectorResponse, Class type)
inject.addValue(GitHubConnectorResponse.class, connectorResponse);
return GitHubClient.getMappingObjectReader(connectorResponse).forType(type).readValue(data);
- } catch (JsonMappingException | JsonParseException e) {
+ } catch (JacksonException e) {
String message = "Failed to deserialize: " + data;
LOGGER.log(Level.FINE, message);
- throw e;
+ throw new IOException(message, e);
}
}
@@ -125,10 +124,10 @@ static T parseBody(GitHubConnectorResponse connectorResponse, T instance) th
String data = getBodyAsString(connectorResponse);
try {
return GitHubClient.getMappingObjectReader(connectorResponse).withValueToUpdate(instance).readValue(data);
- } catch (JsonMappingException | JsonParseException e) {
+ } catch (JacksonException e) {
String message = "Failed to deserialize: " + data;
LOGGER.log(Level.FINE, message);
- throw e;
+ throw new IOException(message, e);
}
}
diff --git a/src/test/java/org/kohsuke/github/AotIntegrationTest.java b/src/test/java/org/kohsuke/github/AotIntegrationTest.java
index a8b458f792..e1e28a348d 100644
--- a/src/test/java/org/kohsuke/github/AotIntegrationTest.java
+++ b/src/test/java/org/kohsuke/github/AotIntegrationTest.java
@@ -1,10 +1,10 @@
package org.kohsuke.github;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.node.ArrayNode;
import org.junit.Test;
import org.springframework.boot.test.context.SpringBootTest;
+import tools.jackson.databind.JsonNode;
+import tools.jackson.databind.json.JsonMapper;
+import tools.jackson.databind.node.ArrayNode;
import java.io.IOException;
import java.nio.file.Files;
@@ -80,7 +80,9 @@ public void testIfAllRequiredClassesAreRegisteredForAot() throws IOException {
private Stream readAotConfigToStreamOfClassNames(String reflectionConfig) throws IOException {
byte[] reflectionConfigFileAsBytes = Files.readAllBytes(Path.of(reflectionConfig));
- ArrayNode reflectConfigJsonArray = (ArrayNode) new ObjectMapper().readTree(reflectionConfigFileAsBytes);
+ ArrayNode reflectConfigJsonArray = (ArrayNode) JsonMapper.builder()
+ .build()
+ .readTree(reflectionConfigFileAsBytes);
return StreamSupport
.stream(Spliterators.spliteratorUnknownSize(reflectConfigJsonArray.iterator(), Spliterator.ORDERED),
false)
diff --git a/src/test/java/org/kohsuke/github/GHRateLimitTest.java b/src/test/java/org/kohsuke/github/GHRateLimitTest.java
index 66fcc21df1..068dbd59ba 100644
--- a/src/test/java/org/kohsuke/github/GHRateLimitTest.java
+++ b/src/test/java/org/kohsuke/github/GHRateLimitTest.java
@@ -1,9 +1,9 @@
package org.kohsuke.github;
-import com.fasterxml.jackson.databind.exc.MismatchedInputException;
-import com.fasterxml.jackson.databind.exc.ValueInstantiationException;
import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
import org.junit.Test;
+import tools.jackson.databind.exc.MismatchedInputException;
+import tools.jackson.databind.exc.ValueInstantiationException;
import java.io.IOException;
import java.time.Duration;
@@ -440,8 +440,10 @@ public void testGitHubRateLimitWithBadData() throws Exception {
fail("Invalid rate limit missing some records should throw");
} catch (Exception e) {
assertThat(e, instanceOf(HttpException.class));
- assertThat(e.getCause(), instanceOf(ValueInstantiationException.class));
- assertThat(e.getCause().getMessage(),
+ // In Jackson 3, the exception is wrapped in IOException
+ assertThat(e.getCause(), instanceOf(IOException.class));
+ assertThat(e.getCause().getCause(), instanceOf(ValueInstantiationException.class));
+ assertThat(e.getCause().getCause().getMessage(),
containsString(
"Cannot construct instance of `org.kohsuke.github.GHRateLimit`, problem: `java.lang.NullPointerException`"));
}
@@ -451,8 +453,10 @@ public void testGitHubRateLimitWithBadData() throws Exception {
fail("Invalid rate limit record missing a value should throw");
} catch (Exception e) {
assertThat(e, instanceOf(HttpException.class));
- assertThat(e.getCause(), instanceOf(MismatchedInputException.class));
- assertThat(e.getCause().getMessage(),
+ // In Jackson 3, the exception is wrapped in IOException
+ assertThat(e.getCause(), instanceOf(IOException.class));
+ assertThat(e.getCause().getCause(), instanceOf(MismatchedInputException.class));
+ assertThat(e.getCause().getCause().getMessage(),
containsString("Missing required creator property 'reset' (index 2)"));
}
diff --git a/src/test/java/org/kohsuke/github/GHRepositoryTest.java b/src/test/java/org/kohsuke/github/GHRepositoryTest.java
index db5d892f85..63e1496cea 100644
--- a/src/test/java/org/kohsuke/github/GHRepositoryTest.java
+++ b/src/test/java/org/kohsuke/github/GHRepositoryTest.java
@@ -1,6 +1,5 @@
package org.kohsuke.github;
-import com.fasterxml.jackson.databind.JsonMappingException;
import com.google.common.collect.Sets;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.apache.commons.io.IOUtils;
@@ -9,6 +8,7 @@
import org.kohsuke.github.GHCheckRun.Conclusion;
import org.kohsuke.github.GHOrganization.RepositoryRole;
import org.kohsuke.github.GHRepository.Visibility;
+import tools.jackson.databind.DatabindException;
import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException;
@@ -1039,7 +1039,9 @@ public void listRefs() throws Exception {
fail();
} catch (Exception e) {
assertThat(e, instanceOf(HttpException.class));
- assertThat(e.getCause(), instanceOf(JsonMappingException.class));
+ // In Jackson 3, DatabindException is wrapped in IOException
+ assertThat(e.getCause(), instanceOf(IOException.class));
+ assertThat(e.getCause().getCause(), instanceOf(DatabindException.class));
}
// git/refs/heads/gh
diff --git a/src/test/java/org/kohsuke/github/connector/GitHubConnectorResponseTest.java b/src/test/java/org/kohsuke/github/connector/GitHubConnectorResponseTest.java
index daef1d758f..298d60cb57 100644
--- a/src/test/java/org/kohsuke/github/connector/GitHubConnectorResponseTest.java
+++ b/src/test/java/org/kohsuke/github/connector/GitHubConnectorResponseTest.java
@@ -1,6 +1,5 @@
package org.kohsuke.github.connector;
-import com.fasterxml.jackson.databind.util.ByteBufferBackedInputStream;
import org.apache.commons.io.IOUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -8,6 +7,7 @@
import org.junit.Test;
import org.kohsuke.github.AbstractGitHubWireMockTest;
import org.kohsuke.github.connector.GitHubConnectorResponse.ByteArrayResponse;
+import tools.jackson.databind.util.ByteBufferBackedInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
diff --git a/src/test/java/org/kohsuke/github/internal/graphql/response/GHGraphQLResponseMockTest.java b/src/test/java/org/kohsuke/github/internal/graphql/response/GHGraphQLResponseMockTest.java
index a98870c6ec..0ae97db56a 100644
--- a/src/test/java/org/kohsuke/github/internal/graphql/response/GHGraphQLResponseMockTest.java
+++ b/src/test/java/org/kohsuke/github/internal/graphql/response/GHGraphQLResponseMockTest.java
@@ -1,11 +1,11 @@
package org.kohsuke.github.internal.graphql.response;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.DeserializationFeature;
-import com.fasterxml.jackson.databind.JavaType;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.ObjectReader;
import org.junit.jupiter.api.Test;
+import tools.jackson.core.JacksonException;
+import tools.jackson.databind.DeserializationFeature;
+import tools.jackson.databind.JavaType;
+import tools.jackson.databind.ObjectReader;
+import tools.jackson.databind.json.JsonMapper;
import java.util.List;
@@ -19,11 +19,10 @@
*/
class GHGraphQLResponseMockTest {
- private GHGraphQLResponse