From 4e264a8c75129d26730a6399c44e829fc63496dd Mon Sep 17 00:00:00 2001 From: Khalid Qarryzada Date: Fri, 25 Sep 2026 13:01:10 -0700 Subject: [PATCH 1/3] Rename bulk mapper to ScimResourceMapper The resource mapper is a useful utility that was originally added for bulk data processing. This commit renames it to a more general name so it can be used in non-bulk workflows. --- ...rceMapper.java => ScimResourceMapper.java} | 37 +++++++------- .../scim2/common/bulk/BulkOperation.java | 5 +- .../common/bulk/BulkOperationResult.java | 3 +- .../scim2/common/bulk/BulkRequest.java | 3 +- ...rTest.java => ScimResourceMapperTest.java} | 51 +++++++++---------- .../scim2/server/EndpointTestCase.java | 2 +- 6 files changed, 49 insertions(+), 52 deletions(-) rename scim2-sdk-common/src/main/java/com/unboundid/scim2/common/{bulk/BulkResourceMapper.java => ScimResourceMapper.java} (88%) rename scim2-sdk-common/src/test/java/com/unboundid/scim2/common/{bulk/BulkResourceMapperTest.java => ScimResourceMapperTest.java} (82%) diff --git a/scim2-sdk-common/src/main/java/com/unboundid/scim2/common/bulk/BulkResourceMapper.java b/scim2-sdk-common/src/main/java/com/unboundid/scim2/common/ScimResourceMapper.java similarity index 88% rename from scim2-sdk-common/src/main/java/com/unboundid/scim2/common/bulk/BulkResourceMapper.java rename to scim2-sdk-common/src/main/java/com/unboundid/scim2/common/ScimResourceMapper.java index 62352554..f57fb18d 100644 --- a/scim2-sdk-common/src/main/java/com/unboundid/scim2/common/bulk/BulkResourceMapper.java +++ b/scim2-sdk-common/src/main/java/com/unboundid/scim2/common/ScimResourceMapper.java @@ -30,10 +30,8 @@ * along with this program; if not, see . */ -package com.unboundid.scim2.common.bulk; +package com.unboundid.scim2.common; -import com.unboundid.scim2.common.GenericScimResource; -import com.unboundid.scim2.common.ScimResource; import com.unboundid.scim2.common.annotations.NotNull; import com.unboundid.scim2.common.annotations.Nullable; import com.unboundid.scim2.common.annotations.Schema; @@ -54,12 +52,11 @@ /** - * This class is used to simplify the process of obtaining Java objects from - * bulk requests and responses. + * This class is used to simplify the process of obtaining SCIM resources that + * are nested within a JSON payload. *

* - * When dealing with bulk requests or responses, there is often embedded JSON - * data corresponding to a SCIM resource. For example, the value of the + * An example of this structure is in bulk responses, where the value of the * {@code response} field below represents a user that was just created: *
  *  {
@@ -76,8 +73,8 @@
  * 

* * When it comes to handling and interpreting this data, it can be tedious to - * obtain the resource and convert it from JSON to a usable POJO. The JSON data - * may correspond to: + * obtain the resource and convert it from JSON to a usable POJO. This is + * because the data in the {@code response} field may correspond to: *
    *
  • A {@link UserResource}, representing a user. *
  • A {@link GroupResource}, representing a group entity. @@ -92,11 +89,11 @@ * SCIM SDK aims to simplify. *

    * - * To solve this problem, this BulkResourceMapper class defines associations + * To solve this problem, this ScimResourceMapper class defines associations * between schemas (represented as sets) and a Java type. For example, any JSON * with a schemas value of {@code urn:ietf:params:scim:schemas:core:2.0:User} * is interpreted as a {@link UserResource}. If you have custom resource types - * that need to be supported by the SCIM SDK's bulk processing, then add it with + * that need to be supported by the SCIM SDK's processing, then add it with * one of the following methods. *
      *
    • {@link #add(Class)}: If the class uses the {@link Schema} annotation. @@ -104,12 +101,12 @@ *
    *

    * - * Note that any custom class must implement the {@link ScimResource} interface - * in order to be compatible with the methods above. Furthermore, modifying the - * BulkResourceMapper with the above methods must only be done at application - * startup. + * Note that any custom class must descend from {@link BaseScimResource} or + * {@link GenericScimResource} in order to be compatible with the methods above. + * Furthermore, modifying the ScimResourceMapper with the above methods must + * only be done at application startup. */ -public class BulkResourceMapper +public class ScimResourceMapper { /** * The map that stores schema-to-class associations for this mapper class. @@ -125,7 +122,7 @@ public class BulkResourceMapper } /** - * Updates the BulkResourceMapper with a new class. The provided class must + * Updates the ScimResourceMapper with a new class. The provided class must * include the {@link Schema} annotation. To add a different * ScimResource-based class that does not use the annotation, use the * {@link #put put()} method instead. @@ -151,7 +148,7 @@ public static synchronized void add( } /** - * Updates the BulkResourceMapper with a new class. This is an alternative to + * Updates the ScimResourceMapper with a new class. This is an alternative to * the {@link #add(Class)} method that supports usage of Java classes that * do not or cannot use the {@code @Schema} annotation. * @@ -202,7 +199,7 @@ public static ScimResource asScimResource(@Nullable final ObjectNode json) // Attempt fetching the class using data from the "schemas" array. If there // is not a mapping, a GenericScimResource will be used. - Class clazz = BulkResourceMapper.get(json.get("schemas")); + Class clazz = ScimResourceMapper.get(json.get("schemas")); try { @@ -211,7 +208,7 @@ public static ScimResource asScimResource(@Nullable final ObjectNode json) catch (JacksonException e) { throw new IllegalArgumentException( - "Failed to convert bulk data into a " + clazz.getName(), e); + "Failed to convert SCIM data into a " + clazz.getName(), e); } } diff --git a/scim2-sdk-common/src/main/java/com/unboundid/scim2/common/bulk/BulkOperation.java b/scim2-sdk-common/src/main/java/com/unboundid/scim2/common/bulk/BulkOperation.java index efe68401..b3525c87 100644 --- a/scim2-sdk-common/src/main/java/com/unboundid/scim2/common/bulk/BulkOperation.java +++ b/scim2-sdk-common/src/main/java/com/unboundid/scim2/common/bulk/BulkOperation.java @@ -41,6 +41,7 @@ import com.unboundid.scim2.common.GenericScimResource; import com.unboundid.scim2.common.Path; import com.unboundid.scim2.common.ScimResource; +import com.unboundid.scim2.common.ScimResourceMapper; import com.unboundid.scim2.common.annotations.NotNull; import com.unboundid.scim2.common.annotations.Nullable; import com.unboundid.scim2.common.exceptions.runtime.BulkRequestException; @@ -354,7 +355,7 @@ public ObjectNode getData() *

    * * If you have custom classes that should be used, they may be registered with - * the {@link BulkResourceMapper}. However, any JSON object may be manually + * the {@link ScimResourceMapper}. However, any JSON object may be manually * converted to a Java object with {@link JsonUtils#nodeToValue}: *
    
        *   ObjectNode data = bulkOperation.getData();
    @@ -380,7 +381,7 @@ public ScimResource getDataAsScimResource()
       {
         try
         {
    -      return BulkResourceMapper.asScimResource(data);
    +      return ScimResourceMapper.asScimResource(data);
         }
         catch (IllegalArgumentException e)
         {
    diff --git a/scim2-sdk-common/src/main/java/com/unboundid/scim2/common/bulk/BulkOperationResult.java b/scim2-sdk-common/src/main/java/com/unboundid/scim2/common/bulk/BulkOperationResult.java
    index d18d8a03..5c4c223a 100644
    --- a/scim2-sdk-common/src/main/java/com/unboundid/scim2/common/bulk/BulkOperationResult.java
    +++ b/scim2-sdk-common/src/main/java/com/unboundid/scim2/common/bulk/BulkOperationResult.java
    @@ -38,6 +38,7 @@
     import com.fasterxml.jackson.annotation.JsonPropertyOrder;
     import com.fasterxml.jackson.annotation.JsonSetter;
     import com.unboundid.scim2.common.ScimResource;
    +import com.unboundid.scim2.common.ScimResourceMapper;
     import com.unboundid.scim2.common.annotations.NotNull;
     import com.unboundid.scim2.common.annotations.Nullable;
     import com.unboundid.scim2.common.exceptions.ScimException;
    @@ -561,7 +562,7 @@ public ScimResource getResponseAsScimResource()
       {
         try
         {
    -      return BulkResourceMapper.asScimResource(response);
    +      return ScimResourceMapper.asScimResource(response);
         }
         catch (IllegalArgumentException e)
         {
    diff --git a/scim2-sdk-common/src/main/java/com/unboundid/scim2/common/bulk/BulkRequest.java b/scim2-sdk-common/src/main/java/com/unboundid/scim2/common/bulk/BulkRequest.java
    index 1dc498f4..cfd5fa59 100644
    --- a/scim2-sdk-common/src/main/java/com/unboundid/scim2/common/bulk/BulkRequest.java
    +++ b/scim2-sdk-common/src/main/java/com/unboundid/scim2/common/bulk/BulkRequest.java
    @@ -37,6 +37,7 @@
     import com.fasterxml.jackson.annotation.JsonProperty;
     import com.unboundid.scim2.common.BaseScimResource;
     import com.unboundid.scim2.common.ScimResource;
    +import com.unboundid.scim2.common.ScimResourceMapper;
     import com.unboundid.scim2.common.annotations.Attribute;
     import com.unboundid.scim2.common.annotations.NotNull;
     import com.unboundid.scim2.common.annotations.Nullable;
    @@ -315,7 +316,7 @@
      *   }
      * 
    * - * The {@link BulkResourceMapper} is responsible for managing the conversions to + * The {@link ScimResourceMapper} is responsible for managing the conversions to * {@link ScimResource} objects. See the mapper and * {@link BulkOperation#getDataAsScimResource()} for more details. *

    diff --git a/scim2-sdk-common/src/test/java/com/unboundid/scim2/common/bulk/BulkResourceMapperTest.java b/scim2-sdk-common/src/test/java/com/unboundid/scim2/common/ScimResourceMapperTest.java similarity index 82% rename from scim2-sdk-common/src/test/java/com/unboundid/scim2/common/bulk/BulkResourceMapperTest.java rename to scim2-sdk-common/src/test/java/com/unboundid/scim2/common/ScimResourceMapperTest.java index 64b10c0b..2b529344 100644 --- a/scim2-sdk-common/src/test/java/com/unboundid/scim2/common/bulk/BulkResourceMapperTest.java +++ b/scim2-sdk-common/src/test/java/com/unboundid/scim2/common/ScimResourceMapperTest.java @@ -30,11 +30,8 @@ * along with this program; if not, see . */ -package com.unboundid.scim2.common.bulk; +package com.unboundid.scim2.common; -import com.unboundid.scim2.common.BaseScimResource; -import com.unboundid.scim2.common.GenericScimResource; -import com.unboundid.scim2.common.ScimResource; import com.unboundid.scim2.common.annotations.Schema; import com.unboundid.scim2.common.types.GroupResource; import com.unboundid.scim2.common.types.UserResource; @@ -46,16 +43,16 @@ import java.util.Set; -import static com.unboundid.scim2.common.bulk.BulkResourceMapper.SCHEMAS_MAP; +import static com.unboundid.scim2.common.ScimResourceMapper.SCHEMAS_MAP; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; /** - * Unit tests for the {@link BulkResourceMapper} class. This functionality is + * Unit tests for the {@link ScimResourceMapper} class. This functionality is * also exercised in {@code EndpointTestCase#testBulkRequestJsonProcessing}. */ -public class BulkResourceMapperTest +public class ScimResourceMapperTest { /** * Reset the bulk resource mapper to its default settings. @@ -63,7 +60,7 @@ public class BulkResourceMapperTest @AfterMethod public void tearDown() { - BulkResourceMapper.initialize(); + ScimResourceMapper.initialize(); } /** @@ -73,28 +70,28 @@ public void tearDown() public void testBasic() { // Clear the map to start. - BulkResourceMapper.clear(); + ScimResourceMapper.clear(); // Call the add() method and ensure it is registered appropriately for a // class defined with the @Schema annotation. - BulkResourceMapper.add(ClassWithAnnotation.class); + ScimResourceMapper.add(ClassWithAnnotation.class); assertThat(SCHEMAS_MAP).hasSize(1); // Now that the value is present in the map, querying the map for a schema // list of "urn:pingidentity:example" should return the correct class. Class clazz = - BulkResourceMapper.get(Set.of("urn:pingidentity:example")); + ScimResourceMapper.get(Set.of("urn:pingidentity:example")); assertThat(clazz).isEqualTo(ClassWithAnnotation.class); // Query again with a JSON node. var arrayNode = JsonUtils.getJsonNodeFactory().arrayNode() .add("urn:pingidentity:example"); - clazz = BulkResourceMapper.get(arrayNode); + clazz = ScimResourceMapper.get(arrayNode); assertThat(clazz).isEqualTo(ClassWithAnnotation.class); // A class that is not annotated with @Schema is not compatible with add() // since there is no information to fetch. - assertThatThrownBy(() -> BulkResourceMapper.add(NoAnnotation.class)) + assertThatThrownBy(() -> ScimResourceMapper.add(NoAnnotation.class)) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining("Requested schema for the") .hasMessageContaining("NoAnnotation class, which does not have a valid") @@ -102,32 +99,32 @@ public void testBasic() // It should be possible to register the un-annotated class with the put() // method. - BulkResourceMapper.clear(); - BulkResourceMapper.put(Set.of("urn:pingidentity:put"), NoAnnotation.class); + ScimResourceMapper.clear(); + ScimResourceMapper.put(Set.of("urn:pingidentity:put"), NoAnnotation.class); assertThat(SCHEMAS_MAP).hasSize(1); arrayNode.removeAll(); arrayNode.add("urn:pingidentity:put"); - Class clazzNoAnnotation = BulkResourceMapper.get(arrayNode); + Class clazzNoAnnotation = ScimResourceMapper.get(arrayNode); assertThat(clazzNoAnnotation).isEqualTo(NoAnnotation.class); // Trying to fetch an unregistered or invalid JsonNode should just return // the default GenericScimResource value. arrayNode.removeAll(); arrayNode.add("urn:notFound"); - assertThat(BulkResourceMapper.get(arrayNode)) + assertThat(ScimResourceMapper.get(arrayNode)) .isEqualTo(GenericScimResource.class); - assertThat(BulkResourceMapper.get(NullNode.getInstance())) + assertThat(ScimResourceMapper.get(NullNode.getInstance())) .isEqualTo(GenericScimResource.class); } /** * Ensures the mapper returns expected objects when a JsonNode is provided to - * the {@link BulkResourceMapper#asScimResource} method. This is the primary - * way to interface with the BulkResourceMapper. + * the {@link ScimResourceMapper#asScimResource} method. This is the primary + * way to interface with the ScimResourceMapper. */ @Test - public void testJsonNodeConversion() throws Exception + public void testJsonNodeConversion() { final var reader = JsonUtils.getObjectReader().forType(ObjectNode.class); @@ -138,7 +135,7 @@ public void testJsonNodeConversion() throws Exception "userName": "simpleUser" }"""; ObjectNode userNode = reader.readValue(userJson); - ScimResource resource = BulkResourceMapper.asScimResource(userNode); + ScimResource resource = ScimResourceMapper.asScimResource(userNode); assertThat(resource).isInstanceOfSatisfying(UserResource.class, user -> assertThat(user.getUserName()).isEqualTo("simpleUser")); @@ -149,7 +146,7 @@ public void testJsonNodeConversion() throws Exception "displayName": "simpleGroup" }"""; ObjectNode groupNode = reader.readValue(groupJson); - ScimResource groupResource = BulkResourceMapper.asScimResource(groupNode); + ScimResource groupResource = ScimResourceMapper.asScimResource(groupNode); assertThat(groupResource).isInstanceOfSatisfying(GroupResource.class, group -> assertThat(group.getDisplayName()).isEqualTo("simpleGroup")); @@ -162,14 +159,14 @@ public void testJsonNodeConversion() throws Exception ], "userName": "customName" }"""; - var gen = BulkResourceMapper.asScimResource(reader.readValue(customJson)); + var gen = ScimResourceMapper.asScimResource(reader.readValue(customJson)); assertThat(gen).isInstanceOfSatisfying(GenericScimResource.class, gsr -> { assertThat(gsr.getObjectNode().get("userName").asString()) .isEqualTo("customName"); }); // Register the custom class that was just attempted. - BulkResourceMapper.put( + ScimResourceMapper.put( Set.of("urn:ietf:params:scim:schemas:core:2.0:User", "urn:example:customExtension"), UserSubClass.class @@ -177,7 +174,7 @@ public void testJsonNodeConversion() throws Exception // Attempt reading the value again. ObjectNode customUser = reader.readValue(customJson); - ScimResource customResource = BulkResourceMapper.asScimResource(customUser); + ScimResource customResource = ScimResourceMapper.asScimResource(customUser); assertThat(customResource).isInstanceOfSatisfying(UserSubClass.class, user -> assertThat(user.getUserName()).isEqualTo("customName")); assertThat(resource).isNotInstanceOf(GenericScimResource.class); @@ -193,7 +190,7 @@ public void testJsonNodeConversion() throws Exception "userName": "customName" }"""; ObjectNode outOfOrderUser = reader.readValue(outOfOrderJson); - var outOfOrderResource = BulkResourceMapper.asScimResource(outOfOrderUser); + var outOfOrderResource = ScimResourceMapper.asScimResource(outOfOrderUser); assertThat(outOfOrderResource).isInstanceOfSatisfying(UserSubClass.class, user -> assertThat(user.getUserName()).isEqualTo("customName")); assertThat(outOfOrderResource).isEqualTo(customResource); diff --git a/scim2-sdk-server/src/test/java/com/unboundid/scim2/server/EndpointTestCase.java b/scim2-sdk-server/src/test/java/com/unboundid/scim2/server/EndpointTestCase.java index d0d8be9e..ef4e75ef 100644 --- a/scim2-sdk-server/src/test/java/com/unboundid/scim2/server/EndpointTestCase.java +++ b/scim2-sdk-server/src/test/java/com/unboundid/scim2/server/EndpointTestCase.java @@ -1424,7 +1424,7 @@ public void testBulk() throws Exception * Test the behavior of a bulk response processed by the SCIM SDK into an * object. In particular, objects within the bulk response should be * deserialized properly if they are registered with the - * {@link com.unboundid.scim2.common.bulk.BulkResourceMapper}. + * {@link com.unboundid.scim2.common.ScimResourceMapper}. *

    * * The JSON responses obtained by this test are defined in From 8d26a427c4476cd4de9aa1d5ebb79594f4e1d997 Mon Sep 17 00:00:00 2001 From: Khalid Qarryzada Date: Fri, 25 Sep 2026 13:01:16 -0700 Subject: [PATCH 2/3] Re-add the bulk resource mapper. This commit adds the BulkResourceMapper with a deprecation annotation. This was done in a separate commit so that the ScimResourceMapper will contain the original BulkResourceMapper 'git blame' history. The BulkResourceMapper class will be removed in a future release. --- .../scim2/common/bulk/BulkResourceMapper.java | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 scim2-sdk-common/src/main/java/com/unboundid/scim2/common/bulk/BulkResourceMapper.java diff --git a/scim2-sdk-common/src/main/java/com/unboundid/scim2/common/bulk/BulkResourceMapper.java b/scim2-sdk-common/src/main/java/com/unboundid/scim2/common/bulk/BulkResourceMapper.java new file mode 100644 index 00000000..028f9d96 --- /dev/null +++ b/scim2-sdk-common/src/main/java/com/unboundid/scim2/common/bulk/BulkResourceMapper.java @@ -0,0 +1,108 @@ +/* + * Copyright 2026 Ping Identity Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * Copyright 2026 Ping Identity Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License (GPLv2 only) + * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +package com.unboundid.scim2.common.bulk; + +import com.unboundid.scim2.common.ScimResource; +import com.unboundid.scim2.common.ScimResourceMapper; +import com.unboundid.scim2.common.annotations.NotNull; +import com.unboundid.scim2.common.annotations.Nullable; +import tools.jackson.databind.node.ObjectNode; + +import java.util.Set; + + +/** + * This class was the first implementation of {@link ScimResourceMapper}. It was + * renamed from "BulkResourceMapper" to "ScimResourceMapper" so that the mapper + * is more general-purpose. This allows using the mapper for similar JSON + * conversion tasks that are unrelated bulk processing. + * + * @deprecated Use the ScimResourceMapper class instead, which contains all the + * same methods as this class. + */ +@Deprecated(since = "6.1.0") +public class BulkResourceMapper +{ + /** + * Wrapper for {@link ScimResourceMapper}. + * + * @param clazz The class to register. + * @param The Java type, which must implement ScimResource. + * @throws IllegalArgumentException If the provided class does not have the + * required annotation. + */ + public static synchronized void add( + @NotNull final Class clazz) + throws IllegalArgumentException + { + ScimResourceMapper.add(clazz); + } + + /** + * Wrapper for {@link ScimResourceMapper}. + * + * @param schemas The schemas associated with the resource type. + * @param clazz The class type that is associated with the resource type. + * @param The returned Java type. + */ + public static synchronized void put( + @NotNull final Set schemas, + @NotNull final Class clazz) + { + ScimResourceMapper.put(schemas, clazz); + } + + /** + * Wrapper for {@link ScimResourceMapper}. + */ + public static void clear() + { + ScimResourceMapper.clear(); + } + + /** + * Wrapper for {@link ScimResourceMapper}. + * + * @param json The JSON to convert. + * @return A {@link ScimResource} subclass, or {@code null} if the JSON + * was also {@code null}. + * + * @throws IllegalArgumentException If the JSON was malformed. + */ + @Nullable + public static ScimResource asScimResource(@Nullable final ObjectNode json) + throws IllegalArgumentException + { + return ScimResourceMapper.asScimResource(json); + } +} From 916d93ad8c639ec53ae36b8931cb10160cfb628a Mon Sep 17 00:00:00 2001 From: Khalid Qarryzada Date: Fri, 25 Sep 2026 13:01:19 -0700 Subject: [PATCH 3/3] Deprecate system properties for future removal. Deprecated two system properties to showcase intent for removal. See the CHANGELOG for more information. Reviewer: dougbulkley JiraIssue: DS-51991 --- CHANGELOG.md | 21 +++++++++++++++++++ .../scim2/common/BaseScimResource.java | 14 ++++++++++--- .../scim2/common/utils/DateTimeUtils.java | 4 ++++ .../scim2/common/BaseScimResourceTest.java | 2 ++ .../scim2/common/DateTimeUtilsTest.java | 3 ++- 5 files changed, 40 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 337461e3..8e48b8e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,6 +48,27 @@ The time values will always be set to the start of the day. Updated the `@Attribute` annotation of all `$ref` fields to label them as not required for consistency across groups, devices, and other model classes. +Deprecated the following parameters. They will be removed in a future release. If you do not modify +these, there is no change to make. +* `BulkResourceMapper.java`: Renamed to `ScimResourceMapper.java` for use in non-bulk workflows. + +* `BaseScimResource.IGNORE_UNKNOWN_FIELDS`: This system property modifies behavior when a JSON + string is transformed into a Java object. If the JSON value contains extra fields that cannot + be stored on the object, then an exception will be thrown unless this property is `true` (the + current default as of 6.0.0). However, as we've stated elsewhere, adding fields to an API response + is not considered a breaking change by convention. The SCIM standard has done this several times + recently, such as adding `nextCursor` to a `ListResponse` in RFC 9944. In other words, a client + written with an old release of the SCIM SDK could have problems parsing valid SCIM responses from + the future if this property is `false`. To avoid brittle behavior, it is imperative that unknown + JSON fields are always ignored. Thus, this property will be removed for better interoperability. + +* `DateTimeUtils.USE_GMT_CALENDARS`: DateTimeUtils initially used JAX-B for date-time processing, + mostly for Java 7 support. JAX-B's Calendar objects always used a "GMT+00:00" timezone by default, + but Calendar objects in Java are much more likely to use UTC instead. Furthermore, UTC was + designed for date-time synchronization, so it is a better timezone to use for the default region. + JAX-B was removed in 5.0.0, but GMT support was kept with a system property for legacy uses. For + consistency, we intend to remove this property, as UTC has been in use by default since 6.0.0. + ## 6.0.0 - 2026-May-11 The UnboundID SCIM SDK has been updated to use version 3 of the Jackson library (this release ships with v3.1.3). This change aligns the SCIM SDK with HTTP libraries such as Spring Framework 7/Spring diff --git a/scim2-sdk-common/src/main/java/com/unboundid/scim2/common/BaseScimResource.java b/scim2-sdk-common/src/main/java/com/unboundid/scim2/common/BaseScimResource.java index 0e4a2e5d..eeb943b7 100644 --- a/scim2-sdk-common/src/main/java/com/unboundid/scim2/common/BaseScimResource.java +++ b/scim2-sdk-common/src/main/java/com/unboundid/scim2/common/BaseScimResource.java @@ -95,12 +95,20 @@ public abstract class BaseScimResource * ignored by default. For alignment with Jackson, this property is set to * {@code true} (as of version 6.0.0) so that unknown fields in the JSON * representation of a BaseScimResource are also ignored (if they are not a - * schema extension). It is strongly encouraged to avoid changing this - * setting, as updates to the SCIM standard can cause new fields to be added - * (e.g., RFC 9865 added fields like {@code nextCursor} to ListResponse). + * schema extension). * * @since 4.0.0 + * @deprecated The SCIM standard has been updated after the publication of + * RFC 7644, adding new fields to existing JSON models. Thus, an + * unknown JSON field (e.g., {@code nextCursor}) could be a new, + * legitimate SCIM value that is not supported by the application + * yet, so this case should never result in an error. Further, + * adding new fields is not considered a breaking change by HTTP + * convention. To encourage better forward-compatibility, callers + * who have set this property to {@code false} should avoid being + * strict on unknown properties/fields. */ + @Deprecated(since = "6.1.0") public static boolean IGNORE_UNKNOWN_FIELDS = StaticUtils.getProperty( "com.unboundid.scim2.common.BaseScimResource.ignoreUnknownFields", true); diff --git a/scim2-sdk-common/src/main/java/com/unboundid/scim2/common/utils/DateTimeUtils.java b/scim2-sdk-common/src/main/java/com/unboundid/scim2/common/utils/DateTimeUtils.java index fcc670c1..e3829483 100644 --- a/scim2-sdk-common/src/main/java/com/unboundid/scim2/common/utils/DateTimeUtils.java +++ b/scim2-sdk-common/src/main/java/com/unboundid/scim2/common/utils/DateTimeUtils.java @@ -82,7 +82,11 @@ public final class DateTimeUtils * As of the 6.0.0 release, UTC is the default timezone used. * * @since 5.1.0 + * @deprecated UTC is a time standard that provides a better foundation for + * timekeeping than GMT. If necessary, Calendar objects can be + * set to "GMT+00:00" manually with {@link Calendar#setTimeZone}. */ + @Deprecated(since = "6.1.0") public static boolean USE_GMT_CALENDARS = StaticUtils.getProperty( "com.unboundid.scim2.common.utils.DateTimeUtils.useGMTCalendars", false); diff --git a/scim2-sdk-common/src/test/java/com/unboundid/scim2/common/BaseScimResourceTest.java b/scim2-sdk-common/src/test/java/com/unboundid/scim2/common/BaseScimResourceTest.java index e0e991f1..8163aca2 100644 --- a/scim2-sdk-common/src/test/java/com/unboundid/scim2/common/BaseScimResourceTest.java +++ b/scim2-sdk-common/src/test/java/com/unboundid/scim2/common/BaseScimResourceTest.java @@ -62,6 +62,7 @@ public class BaseScimResourceTest /** * Resets the customizable property toggled by this test class. */ + @SuppressWarnings("deprecation") @AfterMethod public void resetProperty() { @@ -174,6 +175,7 @@ public void testSerializingExtension() throws Exception * * @throws Exception If an unexpected error occurs. */ + @SuppressWarnings("deprecation") @Test public void testSerializingUnknownProperty() throws Exception { diff --git a/scim2-sdk-common/src/test/java/com/unboundid/scim2/common/DateTimeUtilsTest.java b/scim2-sdk-common/src/test/java/com/unboundid/scim2/common/DateTimeUtilsTest.java index e4f046f4..1b0eeb9a 100644 --- a/scim2-sdk-common/src/test/java/com/unboundid/scim2/common/DateTimeUtilsTest.java +++ b/scim2-sdk-common/src/test/java/com/unboundid/scim2/common/DateTimeUtilsTest.java @@ -61,6 +61,7 @@ public class DateTimeUtilsTest /** * Reset the value of the system property. */ + @SuppressWarnings("deprecation") @AfterMethod public void tearDown() { @@ -74,7 +75,7 @@ public void tearDown() * Validate updates to the {@link DateTimeUtils#USE_GMT_CALENDARS} system * property. */ - @SuppressWarnings("DataFlowIssue") + @SuppressWarnings({"DataFlowIssue", "deprecation"}) @Test public void testGMTProperty() {