-
Notifications
You must be signed in to change notification settings - Fork 75
Migration Guide: Upgrading to use version 6.0.0 (Jackson 3)
Jackson is a common HTTP library responsible for handling JSON processing. In the 6.0.0 release of the UnboundID SCIM SDK, the Jackson dependency was updated from version 2.21.3 to version 3.1.3. The update from Jackson 2 to Jackson 3 contains many backwards-incompatible API changes, design changes, and updated default values. This guide contains an overview of changes specific to applications that use the SCIM SDK.
To keep migration efforts minimal, it is encouraged to update your application to use version 5.1.0 first, before trying to upgrade to 6.0.0. After this, migration work that is unrelated to Jackson is expected to be low. For a comprehensive overview of what changed in Jackson 3, see the official Jackson 3 Migration Guide.
The only dependency changed within the UnboundID SCIM SDK between 5.1.0 and 6.0.0 was Jackson. Make sure your application is updated to use:
- At least version
3.1.3of jackson-databind - Version
2.21of jackson-annotations (Jackson 3 still uses the 2.x annotations package)
With the exception of annotations, it's generally best to avoid having Jackson 2 and Jackson 3 coexisting on the classpath if it can be avoided, though the Jackson migration guide states that this can be okay.
To start, update the SCIM SDK's release version:
Maven:
<dependency>
<groupId>com.unboundid.product.scim2</groupId>
<artifactId>scim2-sdk-common</artifactId>
<version>6.0.0</version>
</dependency>Gradle:
implementation 'com.unboundid.product.scim2:scim2-sdk-common:6.0.0'The most noticeable change in Jackson 3 is the Java package rename. With the exception of the
jackson-annotations package (explanation
here),
all imports in your application that reference Jackson APIs directly must be updated. Some examples
are shown below:
| Before upgrade | After upgrade |
|---|---|
com.fasterxml.jackson.annotation.* |
com.fasterxml.jackson.annotation.* (unchanged) |
com.fasterxml.jackson.core.* |
tools.jackson.core.* |
com.fasterxml.jackson.databind.* |
tools.jackson.databind.* |
com.fasterxml.jackson.databind.node.* |
tools.jackson.databind.node.* |
The import statements in your project can be changed as shown below. Note that TextNode was
renamed to StringNode in Jackson 3.
// Before (Jackson 2):
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.TextNode;
// After (Jackson 3):
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import tools.jackson.databind.JsonNode;
import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.node.StringNode;As stated above, com.fasterxml.jackson.databind.node.TextNode has been renamed to
tools.jackson.databind.node.StringNode. Related API methods have also changed:
| Jackson 2 | Jackson 3 |
|---|---|
TextNode |
StringNode |
TextNode.valueOf() |
StringNode.valueOf() |
node.isTextual() |
node.isString() |
node.textValue() |
node.asString() |
node.asText() |
node.asString() |
Jackson exception objects are no longer checked exceptions. In Jackson 2.x, exceptions were based
on java.io.IOException, but in Jackson 3.x, they are based on java.lang.RuntimeException. The
UnboundID SCIM SDK still highlights places where a JacksonException is thrown, but it is no longer
mandatory to surround these in a try/catch block. These blocks are likely still useful in cases
when a descriptive error message should be returned.
Existing references to JsonProcessingException, which was removed in Jackson 3, can be replaced
by JacksonException:
Before (Jackson 2):
try
{
UserResource user = JsonUtils.nodeToValue(objectNode, UserResource.class);
}
catch (JsonProcessingException e)
{
throw new BadRequestException("Custom message.", e);
}After (Jackson 3):
// The try/catch block is technically not mandatory anymore, but existing usages
// can keep consistent behavior by catching the runtime exception.
try
{
UserResource user = JsonUtils.nodeToValue(objectNode, UserResource.class);
}
catch (JacksonException e)
{
throw new BadRequestException("Custom message.", e);
}In Jackson 3, ObjectMapper instances are now immutable after they are built with
JsonMapper.Builder.build(), so they cannot be customized directly anymore. Existing object mappers
that only modified a few settings (e.g., FAIL_ON_UNKNOWN_PROPERTIES) can be updated to use a
builder, or simply use JsonUtils.createJsonMapper().
For SCIM uses, it is always encouraged to obtain an ObjectMapper/JsonMapper with
createJsonMapper(), as this will provide a mapper with the appropriate settings for SCIM
processing. If your object mapper can use it, existing references to customized object mappers can
be replaced with:
JsonMapper mapper = JsonUtils.createJsonMapper();If you need to customize the SCIM SDK's default object mapper behavior, the
MapperFactory
class still supports this. Note that MapperFactory was updated in 5.1.0 to obtain configuration
updates through the builder, which is detailed in the class-level Javadoc.
The JsonUtils.createObjectMapper() method used in previous releases is equivalent to the new
JsonUtils.createJsonMapper() one,
and may still be used for old code that already called the legacy method. New calls should prefer
createJsonMapper(). Note that the legacy method now returns a JsonMapper, but this is a subclass
of ObjectMapper, so the following is still permitted:
ObjectMapper mapper = JsonUtils.createObjectMapper();Jackson is known for customizability, and provides many configuration options to change the behavior of JSON processing. Jackson 3 changed the default values for several configuration properties that may affect your application.
Several DeserializationFeature and SerializationFeature enum values that existed in Jackson 2
have been reorganized in Jackson 3. For example, SerializationFeature.WRITE_DATES_AS_TIMESTAMPS
is now DateTimeFeature.WRITE_DATES_AS_TIMESTAMPS. If your application configured such features,
review the official Jackson 3 migration guide
for the new property names.
The most consequential property change is to DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
which has been set to false by default in Jackson 3. Previously, deserializing a JSON object that
contained unrecognized fields would throw an exception, completely halting request processing and
leaving clients to handle the error. Now, the unknown fields are ignored instead. For an example,
see the discussion in the next section.
This behavior is aligned with modern API handling, and provides better resilience for applications
interacting with external APIs that may add new fields over time. This is still a good default for
applications that use the SCIM standard's REST API, since updates to the standard can result in new
fields being added. As an example, RFC 9865 added
previousCursor and nextCursor values into ListResponse objects. The legacy behavior can cause
problems for applications that are overly strict about JSON fields it does not recognize.
The UnboundID SCIM SDK has been updated to incorporate this new setting and will no longer throw exceptions when extra unknown fields are present in JSON objects.
Some existing SCIM SDK properties that are unrelated to Jackson have been updated as of the 6.0.0 release. These are listed for documentation purposes and are not expected to require changes outside of accommodating potential test failures.
BaseScimResource.IGNORE_UNKNOWN_FIELDS is now true by default so that unknown JSON fields on
BaseScimResources are ignored instead of throwing exceptions. This property provides very similar
functionality to the Jackson FAIL_ON_UNKNOWN_PROPERTIES that is referenced above, and lives
within the SCIM SDK. Since the default behavior in Jackson has changed, this property was updated
for alignment in behavior.
With the new behavior, when the SCIM SDK encounters unknown fields, it will ignore them by default
instead of throwing its own exceptions. In the example below, all fields except for unknownField
will be parsed, since UserResource is a subclass of BaseScimResource. Note that the processing
of schema extension values on a BaseScimResource has always been supported in the SCIM SDK.
{
"schemas": [ "urn:ietf:params:scim:schemas:core:2.0:User" ],
"userName": "Alice",
"unknownField": "NOT_USED",
"urn:pingidentity:customExtension": {
"id": "fa1afe1"
}
}You may encounter test failures if you have tests that intentionally try to trigger JacksonException errors and validate the response. Instead of using an unknown field, these tests can attempt to set a value to an incorrect data type, e.g.:
{
"schemas": [ "urn:ietf:params:scim:schemas:core:2.0:User" ],
"userName": true
}DateTimeUtils.USE_GMT_CALENDARS (added in 5.1.0) is now false by default. This update does not
not change the behavior of JSON serialization. It only updates the timezone of some Java Calendar
objects that are created from a JSON timestamp value.
In previous releases, when ISO 8601 timestamps were converted from JSON into Calendar objects, the
SCIM SDK would set the timezone to GMT+00:00 if it used the default time zone. This behavior
originally came from the JAX-B dependency (which is no longer used as of 5.0.0). Most notably, it
complicated equivalency checks in unit tests, since Calendar objects would need to be intentionally
set for this unique value. Now, the SCIM SDK uses UTC in these cases instead.
You may encounter some failed tests that have mismatched meta.created or meta.lastModified
values. This can be resolved by updating your tests to use UTC, which is a better value to use.
In the interest of better default behavior, reverting the properties to their previous behavior is discouraged, as these properties are not guaranteed to stay in the SCIM SDK in the long term. However, the previous behavior of these properties may be restored by directly updating the values:
BaseScimResource.IGNORE_UNKNOWN_FIELDS = false;An alternative approach is to set the value of the relevant JVM system property before starting the application:
-Dcom.unboundid.scim2.common.BaseScimResource.ignoreUnknownFields=false
The com.unboundid.scim2.server.PATCH annotation class has been removed, as it was deprecated in
5.1.0. At the time this class was originally written, JAX-RS did not contain support for a native
@PATCH annotation, but this now exists. Thus, any import statements that use the legacy annotation
may be updated to use jakarta.ws.rs.PATCH instead:
// import com.unboundid.scim2.server.PATCH;
import jakarta.ws.rs.PATCH;
@PATCH
@Path("{id}")
public Response patchUser(...)
{
}The following MapperFactory methods were deprecated in 5.1.0 and have been removed in 6.0.0:
setDeserializationCustomFeatures(Map<DeserializationFeature, Boolean>)setJsonParserCustomFeatures(Map<JsonParser.Feature, Boolean>)setJsonGeneratorCustomFeatures(Map<JsonGenerator.Feature, Boolean>)setMapperCustomFeatures(Map<MapperFeature, Boolean>)setSerializationCustomFeatures(Map<SerializationFeature, Boolean>)
If you have existing usages of these methods, see the MapperFactory documentation for an explanation on how to migrate these customizations.
The JsonProcessingExceptionMapper in scim2-sdk-server has been renamed to
JacksonExceptionMapper since JsonProcessingExceptions no longer exist.
If you register this provider explicitly in your JAX-RS application, update the class reference. If
you rely on auto-discovery via @Provider, no change is required.
Several SCIM SDK methods that previously declared throws ScimException (or a subclass) no longer
do so. If you run into compile errors that indicate a certain method no longer throws an exception,
remove the try/catch block.
Note that unlike JacksonException, the ScimException class is still a checked exception since it
generally represents an error condition that should be handled.
This section is included for informational purposes, and should not affect migrations.
The SCIM SDK has some custom deserializer classes, such as CalendarDeserializer. In cases where an
error is thrown, these have been updated to use a runtime exception in accordance with Jackson
expectations. All custom deserializers that throw exceptions in the SCIM SDK will use a
ScimDeserializeException object, which is based on a JacksonException.