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/ScimResourceMapper.java b/scim2-sdk-common/src/main/java/com/unboundid/scim2/common/ScimResourceMapper.java new file mode 100644 index 00000000..f57fb18d --- /dev/null +++ b/scim2-sdk-common/src/main/java/com/unboundid/scim2/common/ScimResourceMapper.java @@ -0,0 +1,274 @@ +/* + * 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; + +import com.unboundid.scim2.common.annotations.NotNull; +import com.unboundid.scim2.common.annotations.Nullable; +import com.unboundid.scim2.common.annotations.Schema; +import com.unboundid.scim2.common.messages.ErrorResponse; +import com.unboundid.scim2.common.messages.PatchRequest; +import com.unboundid.scim2.common.types.GroupResource; +import com.unboundid.scim2.common.types.UserResource; +import com.unboundid.scim2.common.utils.JsonUtils; +import tools.jackson.core.JacksonException; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.node.ArrayNode; +import tools.jackson.databind.node.ObjectNode; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + + +/** + * This class is used to simplify the process of obtaining SCIM resources that + * are nested within a JSON payload. + *

+ * + * 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: + *
+ *  {
+ *    "location": "/Users/fa1afe1",
+ *    "method": "POST",
+ *    "status": "200",
+ *    "response": {
+ *      "schemas": [ "urn:ietf:params:scim:schemas:core:2.0:User" ],
+ *      "id": "fa1afe1",
+ *      "userName": "Polaroid"
+ *    }
+ *  }
+ * 
+ *

+ * + * 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. This is + * because the data in the {@code response} field may correspond to: + * + *

+ * + * The definition of any SCIM resource type is available in the {@code schemas} + * attribute. However, fetching this data and conditionally converting it into + * different objects (with {@link JsonUtils#nodeToValue}) is extra work that the + * SCIM SDK aims to simplify. + *

+ * + * 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 processing, then add it with + * one of the following methods. + * + *

+ * + * 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 ScimResourceMapper +{ + /** + * The map that stores schema-to-class associations for this mapper class. + */ + // This does not need to be a concurrent map since it should only be + // configured/modified at application startup. + @NotNull + static final HashMap, Class> SCHEMAS_MAP = new HashMap<>(); + + static + { + initialize(); + } + + /** + * 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. + * + * @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 + { + Schema schema = clazz.getAnnotation(Schema.class); + if (schema == null) + { + throw new IllegalArgumentException("Requested schema for the " + + clazz.getName() + + " class, which does not have a valid @Schema annotation."); + } + + SCHEMAS_MAP.put(Set.of(schema.id()), clazz); + } + + /** + * 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. + * + * @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) + { + Objects.requireNonNull(schemas); + SCHEMAS_MAP.put(schemas, clazz); + } + + /** + * Clears all registered relationships between schemas and class types. + *

+ * + * This should generally not be called unless it is absolutely necessary to + * overwrite data in the map. + */ + public static void clear() + { + SCHEMAS_MAP.clear(); + } + + /** + * This utility method is the primary entrypoint to this class, and is + * responsible for converting JSON data into a ScimResource POJO. The subclass + * of ScimResource must be defined in the map contained within this class, or + * a {@link GenericScimResource} will be returned instead. + * + * @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 + { + if (json == null) + { + return null; + } + + // Attempt fetching the class using data from the "schemas" array. If there + // is not a mapping, a GenericScimResource will be used. + Class clazz = ScimResourceMapper.get(json.get("schemas")); + + try + { + return JsonUtils.nodeToValue(json, clazz); + } + catch (JacksonException e) + { + throw new IllegalArgumentException( + "Failed to convert SCIM data into a " + clazz.getName(), e); + } + } + + /** + * Fetches the class associated with the provided schema(s). If the schemas + * are not registered, a {@link GenericScimResource} is returned. + * + * @param s The {@code schemas} value of a SCIM resource. + * @param The returned Java type. + * + * @return The class type that is associated with the schemas, or a + * {@link GenericScimResource} if one is not defined. + */ + @NotNull + @SuppressWarnings("unchecked") + static Class get(@NotNull final Set s) + { + return (Class) SCHEMAS_MAP.getOrDefault(s, GenericScimResource.class); + } + + /** + * Fetches the class associated with the provided schema set. This is an + * alternative to {@link #get(Set)} which accepts a JsonNode. + * + * @param node The JsonNode representing the value of the {@code schemas} + * field on a SCIM resource. + * @param The returned Java type. + * + * @return The class type that is associated with the schemas, or a + * {@link GenericScimResource} if one is not defined. + */ + @NotNull + @SuppressWarnings("unchecked") + static Class get(@Nullable final JsonNode node) + { + if (!(node instanceof ArrayNode arrayNode)) + { + // 'schemas' should always be an array as defined by the SCIM standard. + return (Class) GenericScimResource.class; + } + + Set schemaSet = new HashSet<>(); + for (var value : arrayNode) + { + schemaSet.add(value.asString()); + } + + return get(schemaSet); + } + + /** + * Initializes this class by registering basic resource types that are + * generally applicable to SCIM clients. + */ + static void initialize() + { + clear(); + add(UserResource.class); + add(GroupResource.class); + add(ErrorResponse.class); + add(PatchRequest.class); + } +} 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/main/java/com/unboundid/scim2/common/bulk/BulkResourceMapper.java b/scim2-sdk-common/src/main/java/com/unboundid/scim2/common/bulk/BulkResourceMapper.java index 62352554..028f9d96 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/bulk/BulkResourceMapper.java @@ -32,103 +32,29 @@ package com.unboundid.scim2.common.bulk; -import com.unboundid.scim2.common.GenericScimResource; 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.annotations.Schema; -import com.unboundid.scim2.common.messages.ErrorResponse; -import com.unboundid.scim2.common.messages.PatchRequest; -import com.unboundid.scim2.common.types.GroupResource; -import com.unboundid.scim2.common.types.UserResource; -import com.unboundid.scim2.common.utils.JsonUtils; -import tools.jackson.core.JacksonException; -import tools.jackson.databind.JsonNode; -import tools.jackson.databind.node.ArrayNode; import tools.jackson.databind.node.ObjectNode; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Objects; import java.util.Set; /** - * This class is used to simplify the process of obtaining Java objects from - * bulk requests and responses. - *

+ * 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. * - * When dealing with bulk requests or responses, there is often embedded JSON - * data corresponding to a SCIM resource. For example, the value of the - * {@code response} field below represents a user that was just created: - *
- *  {
- *    "location": "/Users/fa1afe1",
- *    "method": "POST",
- *    "status": "200",
- *    "response": {
- *      "schemas": [ "urn:ietf:params:scim:schemas:core:2.0:User" ],
- *      "id": "fa1afe1",
- *      "userName": "Polaroid"
- *    }
- *  }
- * 
- *

- * - * 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: - *
    - *
  • A {@link UserResource}, representing a user. - *
  • A {@link GroupResource}, representing a group entity. - *
  • A {@link ErrorResponse}, representing an error that occurred. - *
  • Any other custom resource type defined by a SCIM service provider. - *
- *

- * - * The definition of any SCIM resource type is available in the {@code schemas} - * attribute. However, fetching this data and conditionally converting it into - * different objects (with {@link JsonUtils#nodeToValue}) is extra work that the - * SCIM SDK aims to simplify. - *

- * - * To solve this problem, this BulkResourceMapper 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 - * one of the following methods. - *
    - *
  • {@link #add(Class)}: If the class uses the {@link Schema} annotation. - *
  • {@link #put put()}: To define a direct relationship for any class. - *
- *

- * - * 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. + * @deprecated Use the ScimResourceMapper class instead, which contains all the + * same methods as this class. */ +@Deprecated(since = "6.1.0") public class BulkResourceMapper { /** - * The map that stores schema-to-class associations for this mapper class. - */ - // This does not need to be a concurrent map since it should only be - // configured/modified at application startup. - @NotNull - static final HashMap, Class> SCHEMAS_MAP = new HashMap<>(); - - static - { - initialize(); - } - - /** - * Updates the BulkResourceMapper 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. + * Wrapper for {@link ScimResourceMapper}. * * @param clazz The class to register. * @param The Java type, which must implement ScimResource. @@ -139,21 +65,11 @@ public static synchronized void add( @NotNull final Class clazz) throws IllegalArgumentException { - Schema schema = clazz.getAnnotation(Schema.class); - if (schema == null) - { - throw new IllegalArgumentException("Requested schema for the " - + clazz.getName() - + " class, which does not have a valid @Schema annotation."); - } - - SCHEMAS_MAP.put(Set.of(schema.id()), clazz); + ScimResourceMapper.add(clazz); } /** - * Updates the BulkResourceMapper 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. + * 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. @@ -163,27 +79,19 @@ public static synchronized void put( @NotNull final Set schemas, @NotNull final Class clazz) { - Objects.requireNonNull(schemas); - SCHEMAS_MAP.put(schemas, clazz); + ScimResourceMapper.put(schemas, clazz); } /** - * Clears all registered relationships between schemas and class types. - *

- * - * This should generally not be called unless it is absolutely necessary to - * overwrite data in the map. + * Wrapper for {@link ScimResourceMapper}. */ public static void clear() { - SCHEMAS_MAP.clear(); + ScimResourceMapper.clear(); } /** - * This utility method is the primary entrypoint to this class, and is - * responsible for converting JSON data into a ScimResource POJO. The subclass - * of ScimResource must be defined in the map contained within this class, or - * a {@link GenericScimResource} will be returned instead. + * Wrapper for {@link ScimResourceMapper}. * * @param json The JSON to convert. * @return A {@link ScimResource} subclass, or {@code null} if the JSON @@ -195,83 +103,6 @@ public static void clear() public static ScimResource asScimResource(@Nullable final ObjectNode json) throws IllegalArgumentException { - if (json == null) - { - return null; - } - - // 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")); - - try - { - return JsonUtils.nodeToValue(json, clazz); - } - catch (JacksonException e) - { - throw new IllegalArgumentException( - "Failed to convert bulk data into a " + clazz.getName(), e); - } - } - - /** - * Fetches the class associated with the provided schema(s). If the schemas - * are not registered, a {@link GenericScimResource} is returned. - * - * @param s The {@code schemas} value of a SCIM resource. - * @param The returned Java type. - * - * @return The class type that is associated with the schemas, or a - * {@link GenericScimResource} if one is not defined. - */ - @NotNull - @SuppressWarnings("unchecked") - static Class get(@NotNull final Set s) - { - return (Class) SCHEMAS_MAP.getOrDefault(s, GenericScimResource.class); - } - - /** - * Fetches the class associated with the provided schema set. This is an - * alternative to {@link #get(Set)} which accepts a JsonNode. - * - * @param node The JsonNode representing the value of the {@code schemas} - * field on a SCIM resource. - * @param The returned Java type. - * - * @return The class type that is associated with the schemas, or a - * {@link GenericScimResource} if one is not defined. - */ - @NotNull - @SuppressWarnings("unchecked") - static Class get(@Nullable final JsonNode node) - { - if (!(node instanceof ArrayNode arrayNode)) - { - // 'schemas' should always be an array as defined by the SCIM standard. - return (Class) GenericScimResource.class; - } - - Set schemaSet = new HashSet<>(); - for (var value : arrayNode) - { - schemaSet.add(value.asString()); - } - - return get(schemaSet); - } - - /** - * Initializes this class by registering basic resource types that are - * generally applicable to SCIM clients. - */ - static void initialize() - { - clear(); - add(UserResource.class); - add(GroupResource.class); - add(ErrorResponse.class); - add(PatchRequest.class); + return ScimResourceMapper.asScimResource(json); } } 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() { 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