All Jakarta JSON-P factory objects ({@link JsonProvider}, {@link JsonBuilderFactory},
+ * {@link JsonReaderFactory}, {@link JsonWriterFactory}) are resolved once at class-load time and
+ * cached as static fields. Callers should use the methods on this class rather than calling
+ * {@code Json.create*()} directly, both to avoid repeated factory-creation overhead and to
+ * ensure the correct {@link JsonProvider} is used in Maven plugin classloader contexts.
*/
public final class JsonObjects {
- private static final JsonProvider provider = JsonProvider.provider();
+ private static final JsonProvider provider = findProvider();
private static final JsonBuilderFactory jsonBuilderFactory = provider.createBuilderFactory(null);
private static final JsonReaderFactory jsonReaderFactory = provider.createReaderFactory(null);
private static final JsonWriterFactory jsonWriterFactory = provider.createWriterFactory(null);
@@ -47,6 +55,17 @@ public final class JsonObjects {
private static final String FIELD_IS_NOT_A_TYPE = "Field %s is not a %s";
+ private static JsonProvider findProvider() {
+ // Use the classloader that loaded JsonProvider itself (the plugin realm classloader in
+ // Maven plugin contexts) rather than the Thread Context ClassLoader used by
+ // JsonProvider.provider(). This avoids isAssignableFrom mismatches caused by Classworlds
+ // realm isolation, where the TCCL resolves the implementation through a different realm
+ // than the one that loaded the JsonProvider interface.
+ return ServiceLoader.load(JsonProvider.class, JsonProvider.class.getClassLoader())
+ .findFirst()
+ .orElseGet(JsonProvider::provider);
+ }
+
/**
* Private constructor to prevent misuse of utility class.
*/
@@ -54,204 +73,242 @@ private JsonObjects() {
}
/**
- * This is usage instead of Json.getProvider() to avoid re creation of Provider each time
+ * Returns the cached {@link JsonProvider}.
*
- * @return cached provider instance.
+ *
The provider is resolved once at class-load time using a classloader-aware
+ * {@link ServiceLoader} call and reused for all subsequent operations.
+ *
+ * @return the cached {@link JsonProvider} instance
*/
public static JsonProvider getProvider() {
return provider;
}
/**
- * This is usage instead of Json.createBuilderFactory(null) to avoid re creation of BuilderFactory each time
+ * Returns the cached {@link JsonBuilderFactory}.
+ *
+ *
Equivalent to {@code Json.createBuilderFactory(null)} but resolved once at
+ * class-load time and reused, avoiding repeated factory-creation overhead.
*
- * @return cached builder factory instance.
+ * @return the cached {@link JsonBuilderFactory} instance
*/
public static JsonBuilderFactory getJsonBuilderFactory() {
return jsonBuilderFactory;
}
/**
- * This is usage instead of Json.createReaderFactory(null) to avoid re creation of ReaderFactory each time
+ * Returns the cached {@link JsonReaderFactory}.
*
- * @return cached reader factory instance.
+ *
Equivalent to {@code Json.createReaderFactory(null)} but resolved once at
+ * class-load time and reused, avoiding repeated factory-creation overhead.
+ *
+ * @return the cached {@link JsonReaderFactory} instance
*/
public static JsonReaderFactory getJsonReaderFactory() {
return jsonReaderFactory;
}
/**
- * This is usage instead of Json.createWriterFactory(null) to avoid re creation of WriterFactory each time
+ * Returns the cached {@link JsonWriterFactory}.
+ *
+ *
Equivalent to {@code Json.createWriterFactory(null)} but resolved once at
+ * class-load time and reused, avoiding repeated factory-creation overhead.
*
- * @return cached writer factory instance.
+ * @return the cached {@link JsonWriterFactory} instance
*/
public static JsonWriterFactory getJsonWriterFactory() {
return jsonWriterFactory;
}
/**
- * This is usage instead of Json.createGeneratorFactory(null) to avoid re creation of Provider each time
+ * Creates a {@link JsonParser} that reads from the given {@link Reader}, using the
+ * cached {@link JsonProvider}.
*
- * @param reader i/o reader from which JSON is to be read
- * @return a JSON parser
+ * @param reader the character stream from which JSON is to be read
+ * @return a {@link JsonParser} positioned at the start of the JSON text
*/
public static JsonParser createParser(Reader reader) {
return getProvider().createParser(reader);
}
/**
- * This is usage instead of Json.createParserFactory(null) to avoid re creation of Provider each time
+ * Creates a {@link JsonParser} that reads from the given {@link InputStream}, using the
+ * cached {@link JsonProvider}. The character encoding is auto-detected per the JSON
+ * specification.
*
- * @param in i/o stream from which JSON is to be read
- * @throws JsonException if encoding cannot be determined
- * or i/o error (IOException would be cause of JsonException)
- * @return a JSON parser
+ * @param in the byte stream from which JSON is to be read
+ * @return a {@link JsonParser} positioned at the start of the JSON text
+ * @throws JsonException if the character encoding cannot be determined, or if an I/O error
+ * occurs (the causing {@link java.io.IOException} will be the cause)
*/
public static JsonParser createParser(InputStream in) {
return getProvider().createParser(in);
}
/**
- * This is usage instead of Json.createGeneratorFactory(null) to avoid re creation of Provider each time
+ * Creates a {@link JsonGenerator} that writes to the given {@link Writer}, using the
+ * cached {@link JsonProvider}.
*
- * @param writer a i/o writer to which JSON is written
- * @return a JSON generator
+ * @param writer the character stream to which JSON is written
+ * @return a new {@link JsonGenerator}
*/
public static JsonGenerator createGenerator(Writer writer) {
return getProvider().createGenerator(writer);
}
/**
- * This is usage instead of Json.createGeneratorFactory(null) to avoid re creation of Provider each time
+ * Creates a {@link JsonGenerator} that writes to the given {@link OutputStream}, using the
+ * cached {@link JsonProvider}.
*
- * @param out i/o stream to which JSON is written
- * @return a JSON generator
+ * @param out the byte stream to which JSON is written
+ * @return a new {@link JsonGenerator}
*/
public static JsonGenerator createGenerator(OutputStream out) {
return getProvider().createGenerator(out);
}
+
/**
- * This is usage instead of Json.createWriterFactory(null) to avoid re creation of WriterFactory each time
+ * Creates a {@link JsonWriter} that writes to the given {@link Writer}, using the
+ * cached {@link JsonWriterFactory}.
*
- * @param writer to which JSON object or array is written
- * @return a JSON writer
+ * @param writer the character stream to which a JSON object or array is written
+ * @return a new {@link JsonWriter}
*/
public static JsonWriter createWriter(Writer writer) {
return getJsonWriterFactory().createWriter(writer);
}
/**
- * This is usage instead of Json.createWriterFactory(null) to avoid re creation of WriterFactory each time
+ * Creates a {@link JsonWriter} that writes to the given {@link OutputStream}, using the
+ * cached {@link JsonWriterFactory}.
*
- * @param out to which JSON object or array is written
- * @return a JSON writer
+ * @param out the byte stream to which a JSON object or array is written
+ * @return a new {@link JsonWriter}
*/
public static JsonWriter createWriter(OutputStream out) {
return getJsonWriterFactory().createWriter(out);
}
/**
- * This is usage instead of Json.createReaderFactory(null) to avoid re creation of ReaderFactory each time
+ * Creates a {@link JsonReader} that reads from the given {@link Reader}, using the
+ * cached {@link JsonReaderFactory}.
*
- * @param reader a reader from which JSON is to be read
- * @return a JSON reader
+ * @param reader the character stream from which JSON is to be read
+ * @return a new {@link JsonReader}
*/
public static JsonReader createReader(Reader reader) {
return getJsonReaderFactory().createReader(reader);
}
/**
- * This is usage instead of Json.createReaderFactory(null) to avoid re creation of ReaderFactory each time
+ * Creates a {@link JsonReader} that reads from the given {@link InputStream}, using the
+ * cached {@link JsonReaderFactory}.
*
- * @param in a byte stream from which JSON is to be read
- * @return a JSON reader
+ * @param in the byte stream from which JSON is to be read
+ * @return a new {@link JsonReader}
*/
public static JsonReader createReader(InputStream in) {
return getJsonReaderFactory().createReader(in);
}
-
/**
- * This is usage instead of Json.createArrayBuilder() to avoid re creation of JsonBuilderFactory each time
+ * Creates a new {@link JsonArrayBuilder} using the cached {@link JsonBuilderFactory}.
*
- * @return a JSON array builder
+ * @return a new {@link JsonArrayBuilder}
*/
public static JsonArrayBuilder createArrayBuilder() {
return getJsonBuilderFactory().createArrayBuilder();
}
/**
- * This is usage instead of Json.createObjectBuilder() to avoid re creation of JsonBuilderFactory each time
+ * Creates a new empty {@link JsonObjectBuilder} using the cached {@link JsonBuilderFactory}.
*
- * @return a JSON object builder
+ * @return a new {@link JsonObjectBuilder}
*/
public static JsonObjectBuilder createObjectBuilder() {
return getJsonBuilderFactory().createObjectBuilder();
}
/**
- * Returns the (possibly nested) array value to which the specified name is mapped, if it
- * exists.
+ * Returns the (possibly nested) array value at the given field name path, if it exists and
+ * is not JSON null.
*
- * @param object the JsonObject from which to retrieve the value
- * @param names the field name path whose associated value is to be returned
- * @return the array value to which the specified name is mapped
- * @throws IllegalStateException if the value is not assignable to JsonArray type
+ * @param object the {@link JsonObject} from which to retrieve the value
+ * @param names one or more field names forming a path into nested objects
+ * @return an {@link Optional} containing the {@link JsonArray}, or {@link Optional#empty()}
+ * if the field is absent or its value is JSON null
+ * @throws IllegalArgumentException if {@code object} is null, {@code names} is empty, or
+ * the first name is null or empty
+ * @throws IllegalStateException if the field exists but its value is not a JSON array
*/
public static Optional getJsonArray(final JsonObject object, final String... names) {
return getJsonValue(object, ValueType.ARRAY, JsonObject::getJsonArray, names);
}
/**
- * Returns the (possibly nested) object value to which the specified name is mapped, if it
- * exists.
- *
- * @param object the JsonObject from which to retrieve the value
- * @param names the field name path whose associated value is to be returned
- * @return the object value to which the specified name is mapped, or {@code null} if this
- * object contains no mapping for the name
- * @throws IllegalStateException if the value is not assignable to JsonObject type
+ * Returns the (possibly nested) object value at the given field name path, if it exists and
+ * is not JSON null.
+ *
+ * @param object the {@link JsonObject} from which to retrieve the value
+ * @param names one or more field names forming a path into nested objects
+ * @return an {@link Optional} containing the {@link JsonObject}, or {@link Optional#empty()}
+ * if the field is absent or its value is JSON null
+ * @throws IllegalArgumentException if {@code object} is null, {@code names} is empty, or
+ * the first name is null or empty
+ * @throws IllegalStateException if the field exists but its value is not a JSON object
*/
public static Optional getJsonObject(final JsonObject object, final String... names) {
return getJsonValue(object, ValueType.OBJECT, JsonObject::getJsonObject, names);
}
/**
- * Returns the (possibly nested) number value to which the specified name is mapped, if it
- * exists.
+ * Returns the (possibly nested) number value at the given field name path, if it exists and
+ * is not JSON null.
*
- * @param object the JsonObject from which to retrieve the value
- * @param names the field name path whose associated value is to be returned
- * @return the number value to which the specified name is mapped
- * @throws IllegalStateException if the value is not assignable to JsonNumber type
+ * @param object the {@link JsonObject} from which to retrieve the value
+ * @param names one or more field names forming a path into nested objects
+ * @return an {@link Optional} containing the {@link JsonNumber}, or {@link Optional#empty()}
+ * if the field is absent or its value is JSON null
+ * @throws IllegalArgumentException if {@code object} is null, {@code names} is empty, or
+ * the first name is null or empty
+ * @throws IllegalStateException if the field exists but its value is not a JSON number
*/
public static Optional getJsonNumber(final JsonObject object, final String... names) {
return getJsonValue(object, ValueType.NUMBER, JsonObject::getJsonNumber, names);
}
/**
- * Returns the (possibly nested) string value to which the specified name is mapped, if it
- * exists.
+ * Returns the (possibly nested) string value at the given field name path, if it exists and
+ * is not JSON null.
*
- * @param object the JsonObject from which to retrieve the value
- * @param names the field name path whose associated value is to be returned
- * @return the string value to which the specified name is mapped
- * @throws IllegalStateException if the value is not assignable to JsonString type
+ * @param object the {@link JsonObject} from which to retrieve the value
+ * @param names one or more field names forming a path into nested objects
+ * @return an {@link Optional} containing the {@link JsonString}, or {@link Optional#empty()}
+ * if the field is absent or its value is JSON null
+ * @throws IllegalArgumentException if {@code object} is null, {@code names} is empty, or
+ * the first name is null or empty
+ * @throws IllegalStateException if the field exists but its value is not a JSON string
*/
public static Optional getJsonString(final JsonObject object, final String... names) {
return getJsonValue(object, ValueType.STRING, JsonObject::getJsonString, names);
}
/**
- * Returns a (possibly nested) value converted into a specified JsonValue type.
- *
- * @param object the JsonObject from which to retrieve the value
- * @param valueType the type of JsonValue we need to return
- * @param function the function to use to get the correct type of JsonValue from the
- * JsonObject
- * @param names the field name path whose associated value is to be returned
- * @param the type of JsonValue that will be returned
- * @return an optional value found at the specified location in the JsonObject
+ * Retrieves a typed JSON value at a (possibly nested) field path, applying type checking.
+ *
+ *
If {@code names} contains a single element the value is looked up directly in
+ * {@code object}. If it contains more than one element, the first name is resolved as an
+ * intermediate {@link JsonObject} and the lookup recurses into it with the remaining names.
+ *
+ * @param object the {@link JsonObject} from which to retrieve the value
+ * @param valueType the expected {@link ValueType} of the target field
+ * @param function extracts the correctly-typed value from a {@link JsonObject} by field name
+ * @param names one or more field names forming a path into nested objects
+ * @param the {@link JsonValue} subtype to return
+ * @return an {@link Optional} containing the value, or {@link Optional#empty()} if the field
+ * is absent at any level of the path, or its value is JSON null
+ * @throws IllegalStateException if the field exists but its {@link ValueType} does not match
+ * {@code valueType}
*/
private static Optional getJsonValue(final JsonObject object,
final ValueType valueType,
@@ -280,12 +337,16 @@ private static Optional getJsonValue(final JsonObject o
}
/**
- * A convenience method for {@code getJsonString(name).get().getString()}.
+ * Returns the (possibly nested) string value at the given field name path as a plain
+ * {@link String}, if it exists and is not JSON null.
*
- * @param object the JsonObject from which to retrieve the value
- * @param names whose associated value is to be returned as String
- * @return the String value to which the specified name is mapped
- * @throws IllegalStateException if the value is not assignable to JsonString type
+ * @param object the {@link JsonObject} from which to retrieve the value
+ * @param names one or more field names forming a path into nested objects
+ * @return an {@link Optional} containing the string value, or {@link Optional#empty()} if
+ * the field is absent or its value is JSON null
+ * @throws IllegalArgumentException if {@code object} is null, {@code names} is empty, or
+ * the first name is null or empty
+ * @throws IllegalStateException if the field exists but its value is not a JSON string
*/
public static Optional getString(final JsonObject object, final String... names) {
return getJsonValue(object, ValueType.STRING, JsonObject::getJsonString, names)
@@ -293,13 +354,17 @@ public static Optional getString(final JsonObject object, final String..
}
/**
- * A convenience method for {@code UUID.fromString(getJsonString(name).get().getString())}.
+ * Returns the (possibly nested) string value at the given field name path parsed as a
+ * {@link UUID}, if it exists and is not JSON null.
*
- * @param object the JsonObject from which to retrieve the value
- * @param names whose associated value is to be returned as String
- * @return the String value to which the specified name is mapped
- * @throws IllegalStateException if the value is not assignable to JsonString type
- * @throws IllegalArgumentException if the value is not assignable to a UUID
+ * @param object the {@link JsonObject} from which to retrieve the value
+ * @param names one or more field names forming a path into nested objects
+ * @return an {@link Optional} containing the {@link UUID}, or {@link Optional#empty()} if
+ * the field is absent or its value is JSON null
+ * @throws IllegalArgumentException if {@code object} is null, {@code names} is empty, or
+ * the first name is null or empty
+ * @throws IllegalStateException if the field exists but is not a JSON string, or if the
+ * string value cannot be parsed as a UUID
*/
public static Optional getUUID(final JsonObject object, final String... names) {
return getString(object, names)
@@ -313,13 +378,16 @@ public static Optional getUUID(final JsonObject object, final String... na
}
/**
- * A convenience method for {@code JsonNumber.longValue}.
+ * Returns the (possibly nested) number value at the given field name path as a {@link Long},
+ * if it exists and is not JSON null.
*
- * @param object the JsonObject from which to retrieve the value
- * @param names whose associated value is to be returned as Long
- * @return the Long value to which the specified name is mapped
- * @throws IllegalStateException if the value is not assignable to JsonNumber type
- * @throws IllegalArgumentException if the value is not assignable to a Long
+ * @param object the {@link JsonObject} from which to retrieve the value
+ * @param names one or more field names forming a path into nested objects
+ * @return an {@link Optional} containing the long value, or {@link Optional#empty()} if the
+ * field is absent or its value is JSON null
+ * @throws IllegalArgumentException if {@code object} is null, {@code names} is empty, or
+ * the first name is null or empty
+ * @throws IllegalStateException if the field exists but its value is not a JSON number
*/
public static Optional getLong(final JsonObject object, final String... names) {
return getJsonValue(object, ValueType.NUMBER, JsonObject::getJsonNumber, names)
@@ -327,12 +395,16 @@ public static Optional getLong(final JsonObject object, final String... na
}
/**
- * A convenience method to retrieve a Boolean value
+ * Returns the boolean value mapped to the given field name, if it exists.
+ *
+ *
Unlike the other getter methods this one does not support nested paths; it operates
+ * only on a single field name.
*
- * @param object the JsonObject from which to retrieve the value
- * @param name whose associated value is to be returned as Long
- * @return the Boolean value to which the specified name is mapped
- * @throws IllegalStateException if the value is not assignable to a Boolean
+ * @param object the {@link JsonObject} from which to retrieve the value
+ * @param name the field name whose associated value is to be returned
+ * @return an {@link Optional} containing the boolean value, or {@link Optional#empty()} if
+ * the field is absent
+ * @throws IllegalStateException if the field exists but its value is not a JSON boolean
*/
public static Optional getBoolean(final JsonObject object, final String name) {
try {
@@ -342,16 +414,19 @@ public static Optional getBoolean(final JsonObject object, final String
}
}
-
/**
- * A convenience method for getting a JsonArray as a List of a specific JsonValue type.
- *
- * @param object the JsonObject from which to retrieve the value
- * @param names whose associated value is to be returned
- * @param clazz the type of JsonValue that the returned list will contain
- * @return the Long value to which the specified name is mapped
- * @throws IllegalStateException if the value is not assignable to the specified type
- * @throws IllegalArgumentException if the value is not assignable to a Long
+ * Returns the (possibly nested) JSON array at the given field name path as a typed
+ * {@link List}, if it exists and is not JSON null.
+ *
+ * @param object the {@link JsonObject} from which to retrieve the value
+ * @param clazz the {@link JsonValue} subtype that each array element must be cast to
+ * @param names one or more field names forming a path into nested objects
+ * @param the {@link JsonValue} subtype of the list elements
+ * @return an {@link Optional} containing an immutable list of values, or
+ * {@link Optional#empty()} if the field is absent or its value is JSON null
+ * @throws IllegalArgumentException if {@code object} is null, {@code names} is empty, or
+ * the first name is null or empty
+ * @throws IllegalStateException if the field exists but its value is not a JSON array
*/
public static Optional> getList(final JsonObject object, final Class clazz, final String... names) {
return getJsonValue(object, ValueType.ARRAY, JsonObject::getJsonArray, names)
@@ -360,15 +435,23 @@ public static Optional> getList(final JsonObject o
}
/**
- * A convenience method for getting a list of a specific type.
- *
- * @param object the JsonObject from which to retrieve the value
- * @param jsonClazz the JsonValue type that the value is stored as
- * @param converter a function that can convert from the JsonValue class to the required type
- * @param names whose associated value is to be returned
- * @param the type of items in the return list
- * @param the JsonValue type that the value is stored as
- * @return an optional list of values found
+ * Returns the (possibly nested) JSON array at the given field name path as a {@link List} of
+ * converted values, if it exists and is not JSON null.
+ *
+ *
Each element in the JSON array is first cast to {@code jsonClazz} and then passed
+ * through {@code converter} to produce the final list element type.
+ *
+ * @param object the {@link JsonObject} from which to retrieve the value
+ * @param jsonClazz the {@link JsonValue} subtype that each array element is stored as
+ * @param converter a function that converts each {@link JsonValue} element to type {@code R}
+ * @param names one or more field names forming a path into nested objects
+ * @param the type of items in the returned list
+ * @param the {@link JsonValue} subtype of the array elements
+ * @return an {@link Optional} containing an immutable list of converted values, or
+ * {@link Optional#empty()} if the field is absent or its value is JSON null
+ * @throws IllegalArgumentException if {@code object} is null, {@code names} is empty, or
+ * the first name is null or empty
+ * @throws IllegalStateException if the field exists but its value is not a JSON array
*/
public static Optional> getList(final JsonObject object,
final Class jsonClazz,
@@ -382,11 +465,16 @@ public static Optional> getList(final JsonObjec
}
/**
- * Get a list of UUIDs from a JsonObject.
+ * Returns the (possibly nested) JSON array at the given field name path as a {@link List} of
+ * {@link UUID} values. Each array element must be a JSON string containing a valid UUID.
*
- * @param object object the JsonObject from which to retrieve the list
- * @param names the field name path whose associated value is to be returned
- * @return the list of UUIDs or an empty list if none were found
+ * @param object the {@link JsonObject} from which to retrieve the list
+ * @param names one or more field names forming a path into nested objects
+ * @return an immutable list of {@link UUID} values, or an empty list if the field is absent
+ * or its value is JSON null
+ * @throws IllegalArgumentException if {@code object} is null, {@code names} is empty, or
+ * the first name is null or empty
+ * @throws IllegalStateException if the field exists but its value is not a JSON array
*/
public static List getUUIDs(final JsonObject object, final String... names) {
return getList(object, JsonString.class, jsonString -> UUID.fromString(jsonString.getString()), names)
@@ -394,11 +482,13 @@ public static List getUUIDs(final JsonObject object, final String... names
}
/**
- * Create a {@link JsonObjectBuilder} from an existing {@link JsonObject} applying the filter.
- * Only copy the field names for which the filter returns true.
+ * Creates a {@link JsonObjectBuilder} initialised with all fields from {@code source} for
+ * which {@code filter} returns {@code true}.
*
- * @param source {@link JsonObject} to copy fields from
- * @return a {@link JsonObjectBuilder} initialised with the fields contained in the source
+ * @param source the {@link JsonObject} whose fields are to be selectively copied
+ * @param filter a predicate applied to each field name; only fields for which it returns
+ * {@code true} are included in the builder
+ * @return a {@link JsonObjectBuilder} containing the filtered fields
*/
public static JsonObjectBuilder createObjectBuilderWithFilter(final JsonObject source, Function filter) {
JsonObjectBuilder builder = getJsonBuilderFactory().createObjectBuilder();
@@ -407,21 +497,22 @@ public static JsonObjectBuilder createObjectBuilderWithFilter(final JsonObject s
}
/**
- * Create a {@link JsonObjectBuilder} from an existing {@link JsonObject}.
+ * Creates a {@link JsonObjectBuilder} initialised with all fields from {@code source}.
*
- * @param source {@link JsonObject} to copy fields from
- * @return a {@link JsonObjectBuilder} initialised with the fields contained in the source
+ * @param source the {@link JsonObject} whose fields are to be copied
+ * @return a {@link JsonObjectBuilder} containing all fields from {@code source}
*/
public static JsonObjectBuilder createObjectBuilder(final JsonObject source) {
return createObjectBuilderWithFilter(source, x -> true);
}
/**
- * Assert that the provided arguments are valid. The object must not be null, and the next field
- * name must be non-empty.
+ * Validates that the arguments passed to a getter method are well-formed.
*
- * @param object the JsonObject from which to retrieve the value
- * @param names the field names
+ * @param object the {@link JsonObject} being queried; must not be null
+ * @param names the field name path; must contain at least one non-null, non-empty name
+ * @throws IllegalArgumentException if {@code object} is null, {@code names} is empty, or
+ * {@code names[0]} is null or empty
*/
private static void checkArguments(final JsonObject object, final String... names) {
if (object == null) {
@@ -436,12 +527,13 @@ private static void checkArguments(final JsonObject object, final String... name
}
/**
- * Convert a collection of objects into a JsonArray
+ * Converts a collection of values into a {@link JsonArray} by applying {@code converter} to
+ * each element.
*
- * @param entries the collection to be converted
- * @param converter to convert each entry in the collection
+ * @param entries the collection of values to convert
+ * @param converter a function that maps each entry to a {@link JsonValue}
* @param the type of element in the collection
- * @return a JsonArray with the converted entries
+ * @return a {@link JsonArray} containing the converted entries in iteration order
*/
public static JsonArray toJsonArray(final Collection entries, final Function converter) {
final JsonArrayBuilder arrayBuilder = getJsonBuilderFactory().createArrayBuilder();
diff --git a/framework-utilities/utilities/utilities-core/src/main/resources/META-INF/beans.xml b/framework-utilities/utilities/utilities-core/src/main/resources/META-INF/beans.xml
index c543550fc..e35ee868a 100644
--- a/framework-utilities/utilities/utilities-core/src/main/resources/META-INF/beans.xml
+++ b/framework-utilities/utilities/utilities-core/src/main/resources/META-INF/beans.xml
@@ -1,9 +1,9 @@
diff --git a/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/cdi/InitialContextProducerTest.java b/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/cdi/InitialContextProducerTest.java
index 94c587fd7..105b138ae 100644
--- a/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/cdi/InitialContextProducerTest.java
+++ b/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/cdi/InitialContextProducerTest.java
@@ -7,8 +7,8 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
-import javax.enterprise.inject.InjectionException;
-import javax.enterprise.inject.spi.InjectionPoint;
+import jakarta.enterprise.inject.InjectionException;
+import jakarta.enterprise.inject.spi.InjectionPoint;
import javax.naming.InitialContext;
import javax.naming.NamingException;
diff --git a/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/cdi/LoggerProducerTest.java b/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/cdi/LoggerProducerTest.java
index 346f3da53..33d408ab7 100644
--- a/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/cdi/LoggerProducerTest.java
+++ b/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/cdi/LoggerProducerTest.java
@@ -8,7 +8,7 @@
import uk.gov.justice.services.common.util.Clock;
-import javax.enterprise.inject.spi.InjectionPoint;
+import jakarta.enterprise.inject.spi.InjectionPoint;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
diff --git a/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/cdi/QualifierAnnotationExtractorTest.java b/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/cdi/QualifierAnnotationExtractorTest.java
index e0e825a7d..fbe12f1e8 100644
--- a/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/cdi/QualifierAnnotationExtractorTest.java
+++ b/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/cdi/QualifierAnnotationExtractorTest.java
@@ -11,10 +11,10 @@
import java.util.HashSet;
import java.util.Set;
-import javax.enterprise.inject.InjectionException;
-import javax.enterprise.inject.spi.Bean;
-import javax.enterprise.inject.spi.InjectionPoint;
-import javax.inject.Named;
+import jakarta.enterprise.inject.InjectionException;
+import jakarta.enterprise.inject.spi.Bean;
+import jakarta.enterprise.inject.spi.InjectionPoint;
+import jakarta.inject.Named;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -56,7 +56,7 @@ public void shouldThrowARuntimeExceptionIfNoSubscriptionNameQualifierOnAnEventLi
qualifierAnnotationExtractor.getFrom(injectionPoint, Named.class);
fail();
} catch (final InjectionException expected) {
- assertThat(expected.getMessage(), is("Failed to find 'javax.inject.Named' annotation on bean 'uk.gov.justice.services.cdi.QualifierAnnotationExtractorTest$MayAnnotatedClass'"));
+ assertThat(expected.getMessage(), is("Failed to find 'jakarta.inject.Named' annotation on bean 'uk.gov.justice.services.cdi.QualifierAnnotationExtractorTest$MayAnnotatedClass'"));
}
}
diff --git a/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/configuration/GlobalValueProducerTest.java b/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/configuration/GlobalValueProducerTest.java
index ff1c96bad..4ec42687c 100644
--- a/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/configuration/GlobalValueProducerTest.java
+++ b/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/configuration/GlobalValueProducerTest.java
@@ -5,8 +5,8 @@
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.when;
-import javax.enterprise.inject.spi.Annotated;
-import javax.enterprise.inject.spi.InjectionPoint;
+import jakarta.enterprise.inject.spi.Annotated;
+import jakarta.enterprise.inject.spi.InjectionPoint;
import javax.naming.InitialContext;
import javax.naming.NameNotFoundException;
import javax.naming.NamingException;
diff --git a/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/configuration/ValueProducerTest.java b/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/configuration/ValueProducerTest.java
index 70a76ed61..297b4e5d2 100644
--- a/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/configuration/ValueProducerTest.java
+++ b/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/configuration/ValueProducerTest.java
@@ -6,8 +6,8 @@
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.when;
-import javax.enterprise.inject.spi.Annotated;
-import javax.enterprise.inject.spi.InjectionPoint;
+import jakarta.enterprise.inject.spi.Annotated;
+import jakarta.enterprise.inject.spi.InjectionPoint;
import javax.naming.InitialContext;
import javax.naming.NameNotFoundException;
import javax.naming.NamingException;
diff --git a/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/converter/JSONObjectValueObfuscatorTest.java b/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/converter/JSONObjectValueObfuscatorTest.java
index ac3bf3e64..e9636b84b 100644
--- a/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/converter/JSONObjectValueObfuscatorTest.java
+++ b/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/converter/JSONObjectValueObfuscatorTest.java
@@ -2,7 +2,7 @@
import static com.jayway.jsonassert.JsonAssert.with;
import static java.util.UUID.randomUUID;
-import static javax.json.JsonValue.NULL;
+import static jakarta.json.JsonValue.NULL;
import static org.hamcrest.Matchers.hasItems;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
@@ -11,8 +11,8 @@
import java.math.BigDecimal;
-import javax.json.JsonArray;
-import javax.json.JsonObject;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonObject;
import org.junit.jupiter.api.Test;
diff --git a/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/converter/JsonObjectToObjectConverterTest.java b/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/converter/JsonObjectToObjectConverterTest.java
index 2ecd8d831..70f2c4c05 100644
--- a/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/converter/JsonObjectToObjectConverterTest.java
+++ b/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/converter/JsonObjectToObjectConverterTest.java
@@ -14,12 +14,13 @@
import java.io.IOException;
import java.time.ZoneId;
+import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.UUID;
-import javax.json.JsonArray;
-import javax.json.JsonObject;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonObject;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
@@ -65,20 +66,20 @@ public void shouldConvertToPojoWithUTCDateTime() throws Exception {
assertThat(jsonObjectToObjectConverter
.convert(getJsonBuilderFactory().createObjectBuilder().add("dateTime", "2016-07-25T13:09:01.0+00:00").build(),
- PojoWithDateTime.class).getDateTime(),
- equalTo(ZonedDateTime.of(2016, 7, 25, 13, 9, 1, 0, ZoneId.of("UTC"))));
+ PojoWithDateTime.class).getDateTime().toInstant(),
+ equalTo(ZonedDateTime.of(2016, 7, 25, 13, 9, 1, 0, ZoneOffset.UTC).toInstant()));
assertThat(jsonObjectToObjectConverter
.convert(getJsonBuilderFactory().createObjectBuilder().add("dateTime", "2016-07-25T13:09:01.0Z").build(),
- PojoWithDateTime.class).getDateTime(),
- equalTo(ZonedDateTime.of(2016, 7, 25, 13, 9, 1, 0, ZoneId.of("UTC"))));
+ PojoWithDateTime.class).getDateTime().toInstant(),
+ equalTo(ZonedDateTime.of(2016, 7, 25, 13, 9, 1, 0, ZoneOffset.UTC).toInstant()));
assertThat(jsonObjectToObjectConverter
.convert(getJsonBuilderFactory().createObjectBuilder().add("dateTime", "2016-07-25T13:09:01Z").build(),
- PojoWithDateTime.class).getDateTime(),
- equalTo(ZonedDateTime.of(2016, 7, 25, 13, 9, 1, 0, ZoneId.of("UTC"))));
+ PojoWithDateTime.class).getDateTime().toInstant(),
+ equalTo(ZonedDateTime.of(2016, 7, 25, 13, 9, 1, 0, ZoneOffset.UTC).toInstant()));
assertThat(jsonObjectToObjectConverter
.convert(getJsonBuilderFactory().createObjectBuilder().add("dateTime", "2016-07-25T16:09:01.0+03:00").build(),
- PojoWithDateTime.class).getDateTime(),
- equalTo(ZonedDateTime.of(2016, 7, 25, 13, 9, 1, 0, ZoneId.of("UTC"))));
+ PojoWithDateTime.class).getDateTime().toInstant(),
+ equalTo(ZonedDateTime.of(2016, 7, 25, 13, 9, 1, 0, ZoneOffset.UTC).toInstant()));
}
diff --git a/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/converter/JsonValueFactoryTest.java b/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/converter/JsonValueFactoryTest.java
index 73d59fbdc..fad14b435 100644
--- a/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/converter/JsonValueFactoryTest.java
+++ b/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/converter/JsonValueFactoryTest.java
@@ -1,15 +1,15 @@
package uk.gov.justice.services.common.converter;
-import static javax.json.JsonValue.FALSE;
+import static jakarta.json.JsonValue.FALSE;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.math.BigDecimal;
-import javax.json.JsonNumber;
-import javax.json.JsonString;
-import javax.json.JsonValue;
+import jakarta.json.JsonNumber;
+import jakarta.json.JsonString;
+import jakarta.json.JsonValue;
import org.apache.commons.lang3.tuple.Pair;
import org.junit.jupiter.api.Test;
diff --git a/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/converter/ListToJsonArrayConverterTest.java b/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/converter/ListToJsonArrayConverterTest.java
index 5d0e749a1..1bbefc8e2 100644
--- a/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/converter/ListToJsonArrayConverterTest.java
+++ b/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/converter/ListToJsonArrayConverterTest.java
@@ -15,7 +15,7 @@
import java.util.UUID;
-import javax.json.JsonArray;
+import jakarta.json.JsonArray;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
diff --git a/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/converter/ObjectMapperProducerTest.java b/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/converter/ObjectMapperProducerTest.java
index ab341d073..4b343d670 100644
--- a/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/converter/ObjectMapperProducerTest.java
+++ b/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/converter/ObjectMapperProducerTest.java
@@ -23,8 +23,8 @@
import java.util.Map;
import java.util.Objects;
-import javax.json.JsonObject;
-import javax.json.JsonValue;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonValue;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.databind.ObjectMapper;
diff --git a/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/converter/ObjectToJsonObjectConverterTest.java b/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/converter/ObjectToJsonObjectConverterTest.java
index c7038bd1b..e90dbf109 100644
--- a/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/converter/ObjectToJsonObjectConverterTest.java
+++ b/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/converter/ObjectToJsonObjectConverterTest.java
@@ -16,8 +16,8 @@
import java.util.List;
import java.util.UUID;
-import javax.json.JsonArray;
-import javax.json.JsonObject;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
diff --git a/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/converter/ObjectToJsonValueConverterTest.java b/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/converter/ObjectToJsonValueConverterTest.java
index 123f432ae..d869321ab 100644
--- a/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/converter/ObjectToJsonValueConverterTest.java
+++ b/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/converter/ObjectToJsonValueConverterTest.java
@@ -1,6 +1,6 @@
package uk.gov.justice.services.common.converter;
-import static javax.json.JsonValue.NULL;
+import static jakarta.json.JsonValue.NULL;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
@@ -18,8 +18,8 @@
import java.util.List;
import java.util.UUID;
-import javax.json.JsonArray;
-import javax.json.JsonValue;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonValue;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
diff --git a/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/converter/StringToJsonObjectConverterTest.java b/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/converter/StringToJsonObjectConverterTest.java
index c00beb208..2a752a214 100644
--- a/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/converter/StringToJsonObjectConverterTest.java
+++ b/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/converter/StringToJsonObjectConverterTest.java
@@ -4,7 +4,7 @@
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
-import javax.json.JsonObject;
+import jakarta.json.JsonObject;
import org.junit.jupiter.api.Test;
diff --git a/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/converter/ZonedDateTimesTest.java b/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/converter/ZonedDateTimesTest.java
index 718f5313c..2ea0710f1 100644
--- a/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/converter/ZonedDateTimesTest.java
+++ b/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/converter/ZonedDateTimesTest.java
@@ -12,7 +12,7 @@
import java.time.ZoneId;
import java.time.ZonedDateTime;
-import javax.json.JsonString;
+import jakarta.json.JsonString;
import org.junit.jupiter.api.Test;
diff --git a/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/converter/jackson/integerenum/IntegerEnumDeserializerTest.java b/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/converter/jackson/integerenum/IntegerEnumDeserializerTest.java
index 3495c6d7f..1bb05ae5c 100644
--- a/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/converter/jackson/integerenum/IntegerEnumDeserializerTest.java
+++ b/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/converter/jackson/integerenum/IntegerEnumDeserializerTest.java
@@ -23,6 +23,9 @@ public class IntegerEnumDeserializerTest {
@Mock
private EnumResolver enumResolver;
+ @Mock
+ private EnumResolver toStringResolver;
+
@Mock
private EnumObjectUtil enumObjectUtil;
@@ -31,7 +34,7 @@ public class IntegerEnumDeserializerTest {
@BeforeEach
public void setup() {
when(enumResolver.getRawEnums()).thenReturn(Age.values());
- integerEnumDeserializer = new IntegerEnumDeserializer(enumResolver, enumObjectUtil);
+ integerEnumDeserializer = new IntegerEnumDeserializer(enumResolver, toStringResolver, enumObjectUtil);
}
@Test
diff --git a/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/converter/jackson/jsr353/JsonIncludesTest.java b/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/converter/jackson/jsr353/JsonIncludesTest.java
index 6d27d0af6..c4ebc0ed6 100644
--- a/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/converter/jackson/jsr353/JsonIncludesTest.java
+++ b/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/converter/jackson/jsr353/JsonIncludesTest.java
@@ -6,8 +6,8 @@
import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY;
import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL;
import static com.fasterxml.jackson.annotation.JsonInclude.Include.USE_DEFAULTS;
-import static javax.json.JsonValue.NULL;
-import static javax.json.JsonValue.TRUE;
+import static jakarta.json.JsonValue.NULL;
+import static jakarta.json.JsonValue.TRUE;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static uk.gov.justice.services.common.converter.jackson.jsr353.JsonIncludes.includeField;
diff --git a/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/polling/PollerTest.java b/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/polling/PollerTest.java
index 5710a6411..fd9762b70 100644
--- a/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/polling/PollerTest.java
+++ b/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/common/polling/PollerTest.java
@@ -11,7 +11,7 @@
import java.util.function.BooleanSupplier;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import org.junit.jupiter.api.Test;
diff --git a/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/ejb/timer/TimerCancelerTest.java b/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/ejb/timer/TimerCancelerTest.java
index 904e4cb92..ab815ab0d 100644
--- a/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/ejb/timer/TimerCancelerTest.java
+++ b/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/ejb/timer/TimerCancelerTest.java
@@ -6,8 +6,8 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import javax.ejb.Timer;
-import javax.ejb.TimerService;
+import jakarta.ejb.Timer;
+import jakarta.ejb.TimerService;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
diff --git a/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/ejb/timer/TimerConfigFactoryTest.java b/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/ejb/timer/TimerConfigFactoryTest.java
index 49fec8466..296608fdf 100644
--- a/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/ejb/timer/TimerConfigFactoryTest.java
+++ b/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/ejb/timer/TimerConfigFactoryTest.java
@@ -4,7 +4,7 @@
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
-import javax.ejb.TimerConfig;
+import jakarta.ejb.TimerConfig;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
diff --git a/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/ejb/timer/TimerServiceManagerTest.java b/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/ejb/timer/TimerServiceManagerTest.java
index 01e62670f..68678bb8e 100644
--- a/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/ejb/timer/TimerServiceManagerTest.java
+++ b/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/ejb/timer/TimerServiceManagerTest.java
@@ -6,9 +6,9 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
-import javax.ejb.Timer;
-import javax.ejb.TimerConfig;
-import javax.ejb.TimerService;
+import jakarta.ejb.Timer;
+import jakarta.ejb.TimerConfig;
+import jakarta.ejb.TimerService;
import org.apache.activemq.artemis.api.core.ICoreMessage;
import org.hamcrest.CoreMatchers;
diff --git a/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/messaging/JsonObjectsTest.java b/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/messaging/JsonObjectsTest.java
index 4abb273a1..3fc4f2fd1 100644
--- a/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/messaging/JsonObjectsTest.java
+++ b/framework-utilities/utilities/utilities-core/src/test/java/uk/gov/justice/services/messaging/JsonObjectsTest.java
@@ -2,7 +2,7 @@
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
-import static javax.json.JsonValue.NULL;
+import static jakarta.json.JsonValue.NULL;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -18,12 +18,12 @@
import java.util.UUID;
import java.util.function.Function;
-import javax.json.JsonArray;
-import javax.json.JsonNumber;
-import javax.json.JsonObject;
-import javax.json.JsonObjectBuilder;
-import javax.json.JsonString;
-import javax.json.JsonValue;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonNumber;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonObjectBuilder;
+import jakarta.json.JsonString;
+import jakarta.json.JsonValue;
import com.google.common.collect.ImmutableList;
import org.junit.jupiter.api.Test;
@@ -36,8 +36,8 @@ public class JsonObjectsTest {
private static final String UUID_A = "da45e8f6-d945-4f09-a115-1139a9dbb754";
private static final String UUID_B = "d04885b4-9652-4c2a-87c6-299bda0a87d4";
- private static final javax.json.JsonBuilderFactory jsonBuilderFactory = JsonObjects.getJsonBuilderFactory();
- private static final javax.json.JsonReaderFactory jsonReaderFactory = JsonObjects.getJsonReaderFactory();
+ private static final jakarta.json.JsonBuilderFactory jsonBuilderFactory = JsonObjects.getJsonBuilderFactory();
+ private static final jakarta.json.JsonReaderFactory jsonReaderFactory = JsonObjects.getJsonReaderFactory();
@Test
public void shouldReturnJsonArray() {
@@ -370,8 +370,8 @@ public void shouldJsonObjectsCacheProviders() {
@Test
public void shouldGetJsonReaderFactoryAndCacheIt() {
// when
- final javax.json.JsonReaderFactory first = JsonObjects.getJsonReaderFactory();
- final javax.json.JsonReaderFactory second = JsonObjects.getJsonReaderFactory();
+ final jakarta.json.JsonReaderFactory first = JsonObjects.getJsonReaderFactory();
+ final jakarta.json.JsonReaderFactory second = JsonObjects.getJsonReaderFactory();
// then
assertNotNull(first);
@@ -382,8 +382,8 @@ public void shouldGetJsonReaderFactoryAndCacheIt() {
@Test
public void shouldGetJsonWriterFactoryAndCacheIt() {
// when
- final javax.json.JsonWriterFactory first = JsonObjects.getJsonWriterFactory();
- final javax.json.JsonWriterFactory second = JsonObjects.getJsonWriterFactory();
+ final jakarta.json.JsonWriterFactory first = JsonObjects.getJsonWriterFactory();
+ final jakarta.json.JsonWriterFactory second = JsonObjects.getJsonWriterFactory();
// then
assertNotNull(first);
@@ -394,8 +394,8 @@ public void shouldGetJsonWriterFactoryAndCacheIt() {
@Test
public void shouldGetJsonBuilderFactoryAndCacheIt() {
// when
- final javax.json.JsonBuilderFactory first = JsonObjects.getJsonBuilderFactory();
- final javax.json.JsonBuilderFactory second = JsonObjects.getJsonBuilderFactory();
+ final jakarta.json.JsonBuilderFactory first = JsonObjects.getJsonBuilderFactory();
+ final jakarta.json.JsonBuilderFactory second = JsonObjects.getJsonBuilderFactory();
// then
assertNotNull(first);
@@ -406,8 +406,8 @@ public void shouldGetJsonBuilderFactoryAndCacheIt() {
@Test
public void shouldGetProviderAndCacheIt() {
// when
- final javax.json.spi.JsonProvider first = JsonObjects.getProvider();
- final javax.json.spi.JsonProvider second = JsonObjects.getProvider();
+ final jakarta.json.spi.JsonProvider first = JsonObjects.getProvider();
+ final jakarta.json.spi.JsonProvider second = JsonObjects.getProvider();
// then
assertNotNull(first);
@@ -420,15 +420,15 @@ public void shouldCreateParserFromReader() {
final String json = "{\"a\":1}";
// when
- final javax.json.stream.JsonParser parser = JsonObjects.createParser(new java.io.StringReader(json));
+ final jakarta.json.stream.JsonParser parser = JsonObjects.createParser(new java.io.StringReader(json));
boolean sawKey = false;
boolean sawValue = false;
while (parser.hasNext()) {
- final javax.json.stream.JsonParser.Event event = parser.next();
- if (event == javax.json.stream.JsonParser.Event.KEY_NAME) {
+ final jakarta.json.stream.JsonParser.Event event = parser.next();
+ if (event == jakarta.json.stream.JsonParser.Event.KEY_NAME) {
sawKey = true;
}
- if (event == javax.json.stream.JsonParser.Event.VALUE_NUMBER) {
+ if (event == jakarta.json.stream.JsonParser.Event.VALUE_NUMBER) {
sawValue = true;
}
}
@@ -445,15 +445,15 @@ public void shouldCreateParserFromInputStream() {
final java.io.InputStream in = new java.io.ByteArrayInputStream(json.getBytes(java.nio.charset.StandardCharsets.UTF_8));
// when
- final javax.json.stream.JsonParser parser = JsonObjects.createParser(in);
+ final jakarta.json.stream.JsonParser parser = JsonObjects.createParser(in);
boolean sawKey = false;
boolean sawValue = false;
while (parser.hasNext()) {
- final javax.json.stream.JsonParser.Event event = parser.next();
- if (event == javax.json.stream.JsonParser.Event.KEY_NAME) {
+ final jakarta.json.stream.JsonParser.Event event = parser.next();
+ if (event == jakarta.json.stream.JsonParser.Event.KEY_NAME) {
sawKey = true;
}
- if (event == javax.json.stream.JsonParser.Event.VALUE_NUMBER) {
+ if (event == jakarta.json.stream.JsonParser.Event.VALUE_NUMBER) {
sawValue = true;
}
}
@@ -469,13 +469,13 @@ public void shouldCreateGeneratorToWriter() {
final java.io.StringWriter writer = new java.io.StringWriter();
// when
- final javax.json.stream.JsonGenerator generator = JsonObjects.createGenerator(writer);
+ final jakarta.json.stream.JsonGenerator generator = JsonObjects.createGenerator(writer);
generator.writeStartObject().write("a", 1).writeEnd();
generator.close();
// then
final String json = writer.toString();
- final javax.json.JsonObject obj = JsonObjects.createReader(new java.io.StringReader(json)).readObject();
+ final jakarta.json.JsonObject obj = JsonObjects.createReader(new java.io.StringReader(json)).readObject();
assertThat(obj.getInt("a"), equalTo(1));
}
@@ -485,58 +485,107 @@ public void shouldCreateGeneratorToOutputStream() {
final java.io.ByteArrayOutputStream out = new java.io.ByteArrayOutputStream();
// when
- final javax.json.stream.JsonGenerator generator = JsonObjects.createGenerator(out);
+ final jakarta.json.stream.JsonGenerator generator = JsonObjects.createGenerator(out);
generator.writeStartObject().write("b", 2).writeEnd();
generator.close();
// then
final String json = new String(out.toByteArray(), java.nio.charset.StandardCharsets.UTF_8);
- final javax.json.JsonObject obj = JsonObjects.createReader(new java.io.StringReader(json)).readObject();
+ final jakarta.json.JsonObject obj = JsonObjects.createReader(new java.io.StringReader(json)).readObject();
assertThat(obj.getInt("b"), equalTo(2));
}
@Test
public void shouldCreateWriterAndReaderUsingWriter() {
// given
- final javax.json.JsonObject source = JsonObjects.createObjectBuilder().add("x", "y").build();
+ final jakarta.json.JsonObject source = JsonObjects.createObjectBuilder().add("x", "y").build();
// when
final java.io.StringWriter stringWriter = new java.io.StringWriter();
- try (final javax.json.JsonWriter jsonWriter = JsonObjects.createWriter(stringWriter)) {
+ try (final jakarta.json.JsonWriter jsonWriter = JsonObjects.createWriter(stringWriter)) {
jsonWriter.write(source);
}
// then
final String json = stringWriter.toString();
- final javax.json.JsonObject readBack = JsonObjects.createReader(new java.io.StringReader(json)).readObject();
+ final jakarta.json.JsonObject readBack = JsonObjects.createReader(new java.io.StringReader(json)).readObject();
assertThat(readBack.getString("x"), equalTo("y"));
}
@Test
public void shouldCreateWriterAndReaderUsingOutputStream() {
// given
- final javax.json.JsonObject source = JsonObjects.createObjectBuilder().add("p", true).build();
+ final jakarta.json.JsonObject source = JsonObjects.createObjectBuilder().add("p", true).build();
// when
final java.io.ByteArrayOutputStream out = new java.io.ByteArrayOutputStream();
- try (final javax.json.JsonWriter jsonWriter = JsonObjects.createWriter(out)) {
+ try (final jakarta.json.JsonWriter jsonWriter = JsonObjects.createWriter(out)) {
jsonWriter.write(source);
}
// then
final byte[] bytes = out.toByteArray();
- final javax.json.JsonObject readBack = JsonObjects.createReader(new java.io.ByteArrayInputStream(bytes)).readObject();
+ final jakarta.json.JsonObject readBack = JsonObjects.createReader(new java.io.ByteArrayInputStream(bytes)).readObject();
assertThat(readBack.getBoolean("p"), is(true));
}
@Test
public void shouldCreateArrayBuilder() {
// when
- final javax.json.JsonArray array = JsonObjects.createArrayBuilder().add("v1").add("v2").build();
+ final jakarta.json.JsonArray array = JsonObjects.createArrayBuilder().add("v1").add("v2").build();
// then
assertThat(array.size(), equalTo(2));
assertThat(array.getString(0), equalTo("v1"));
assertThat(array.getString(1), equalTo("v2"));
}
+
+ @Test
+ public void shouldThrowExceptionForEmptyFieldName() {
+ final JsonObject object = jsonBuilderFactory.createObjectBuilder()
+ .add("name", "test")
+ .build();
+ assertThrows(IllegalArgumentException.class, () -> getString(object, ""));
+ }
+
+ @Test
+ public void shouldReturnEmptyForNestedFieldWhenIntermediateObjectNotFound() {
+ final JsonObject object = jsonBuilderFactory.createObjectBuilder()
+ .add("other", "value")
+ .build();
+
+ final Optional result = getString(object, "missing", "child");
+
+ assertThat(result.isPresent(), is(false));
+ }
+
+ @Test
+ public void shouldReturnEmptyListForGetUUIDsWhenFieldNotFound() {
+ final JsonObject object = jsonBuilderFactory.createObjectBuilder()
+ .build();
+
+ final List result = getUUIDs(object, "missing");
+
+ assertThat(result, equalTo(java.util.Collections.emptyList()));
+ }
+
+ @Test
+ public void shouldReturnEmptyOptionalForGetListWhenFieldNotFound() {
+ final JsonObject object = jsonBuilderFactory.createObjectBuilder()
+ .build();
+
+ final Optional> result = getList(object, JsonString.class, "missing");
+
+ assertThat(result.isPresent(), is(false));
+ }
+
+ @Test
+ public void shouldReturnEmptyOptionalForGetListWithConverterWhenFieldNotFound() {
+ final JsonObject object = jsonBuilderFactory.createObjectBuilder()
+ .build();
+
+ final Optional> result = getList(object, JsonString.class, JsonString::getString, "missing");
+
+ assertThat(result.isPresent(), is(false));
+ }
}
diff --git a/generator-maven-plugin/files-for-testing-io/pom.xml b/generator-maven-plugin/files-for-testing-io/pom.xml
index 369911ca8..e5f522321 100644
--- a/generator-maven-plugin/files-for-testing-io/pom.xml
+++ b/generator-maven-plugin/files-for-testing-io/pom.xml
@@ -5,7 +5,7 @@
generator-maven-pluginuk.gov.justice.maven.generator
- 17.105.0-M3-SNAPSHOT
+ 21.0.0-SNAPSHOT4.0.0
diff --git a/generator-maven-plugin/generator-core/pom.xml b/generator-maven-plugin/generator-core/pom.xml
index f0aa6bf4a..af42efbd0 100644
--- a/generator-maven-plugin/generator-core/pom.xml
+++ b/generator-maven-plugin/generator-core/pom.xml
@@ -5,7 +5,7 @@
generator-maven-pluginuk.gov.justice.maven.generator
- 17.105.0-M3-SNAPSHOT
+ 21.0.0-SNAPSHOT4.0.0
@@ -27,5 +27,9 @@
Dependency JAR files — each JAR is inspected for a
+ * {@code META-INF/schema_catalog.json} catalog, and the schemas listed there
+ * are read directly from the JAR.
+ *
A local source directory — every {@code .json} file that contains an {@code "id"}
+ * field is included.
+ *
+ *
+ * The resulting map ({@code schemaId -> rawJsonString}) is used by {@link JsonSchemaInliner}
+ * to resolve external {@code $ref} values when building effective JSON schema documents.
+ */
+public class CatalogJsonSchemaLoader {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(CatalogJsonSchemaLoader.class);
+
+ private static final String SCHEMA_CATALOG_PATH = "META-INF/schema_catalog.json";
+
+ private final ObjectMapper objectMapper;
+
+ public CatalogJsonSchemaLoader(final ObjectMapper objectMapper) {
+ this.objectMapper = objectMapper;
+ }
+
+ /**
+ * Loads all known schemas from the given dependency JARs and the local source directory.
+ *
+ * @param dependencyJars JAR files from the project's compile classpath
+ * @param sourceDirectory the directory containing the current module's JSON schema files
+ * @return a map of schema ID to raw JSON string
+ */
+ public Map loadAllJsonSchemas(final List dependencyJars,
+ final Path sourceDirectory) {
+ final Map schemasById = new HashMap<>();
+ loadJsonSchemasFromJars(dependencyJars, schemasById);
+ loadJsonSchemasFromSourceDirectory(sourceDirectory, schemasById);
+ return schemasById;
+ }
+
+ private void loadJsonSchemasFromJars(final List jars,
+ final Map schemasById) {
+ for (final File jar : jars) {
+ if (jar != null && jar.exists()) {
+ loadJsonSchemasFromJar(jar, schemasById);
+ }
+ }
+ }
+
+ private void loadJsonSchemasFromJar(final File jar, final Map schemasById) {
+ try (final ZipFile zipFile = new ZipFile(jar)) {
+ final ZipEntry catalogEntry = zipFile.getEntry(SCHEMA_CATALOG_PATH);
+ if (catalogEntry != null) {
+ loadSchemasViaCatalog(zipFile, catalogEntry, jar, schemasById);
+ }
+ fallbackScanJsonEntriesInJar(zipFile, jar, schemasById);
+ } catch (final IOException e) {
+ throw new EffectiveJsonSchemaGenerationException(
+ format("Failed to load JSON schemas from JAR '%s'", jar.getAbsolutePath()), e);
+ }
+ }
+
+ private void loadSchemasViaCatalog(final ZipFile zipFile,
+ final ZipEntry catalogEntry,
+ final File jar,
+ final Map schemasById) throws IOException {
+ final String catalogJson;
+ try (final InputStream catalogStream = zipFile.getInputStream(catalogEntry)) {
+ catalogJson = IOUtils.toString(catalogStream, UTF_8);
+ }
+
+ final Catalog catalog = objectMapper.readValue(catalogJson, Catalog.class);
+
+ for (final Group group : catalog.getGroups()) {
+ final String baseLocation = group.getBaseLocation() != null ? group.getBaseLocation() : "";
+ for (final Schema schema : group.getSchemas()) {
+ final String schemaPath = baseLocation + schema.getLocation();
+ final ZipEntry schemaEntry = zipFile.getEntry(schemaPath);
+ if (schemaEntry != null) {
+ try (final InputStream schemaStream = zipFile.getInputStream(schemaEntry)) {
+ schemasById.put(schema.getId(), IOUtils.toString(schemaStream, UTF_8));
+ }
+ } else {
+ LOGGER.warn("Schema entry '{}' not found in JAR '{}'", schemaPath, jar.getName());
+ }
+ }
+ }
+ }
+
+ private void fallbackScanJsonEntriesInJar(final ZipFile zipFile,
+ final File jar,
+ final Map schemasById) {
+ zipFile.stream()
+ .filter(entry -> !entry.isDirectory())
+ .filter(entry -> entry.getName().endsWith(".json"))
+ .filter(entry -> !entry.getName().equals(SCHEMA_CATALOG_PATH))
+ .forEach(entry -> {
+ try (final InputStream stream = zipFile.getInputStream(entry)) {
+ final String content = IOUtils.toString(stream, UTF_8);
+ final var jsonNode = objectMapper.readTree(content);
+ if (jsonNode.has("id") && jsonNode.get("id").isTextual()) {
+ // Validate with org.json before caching — JsonSchemaInliner uses
+ // org.json later and is strict about duplicate keys
+ try {
+ new JSONObject(content);
+ schemasById.putIfAbsent(jsonNode.get("id").asText(), content);
+ } catch (final JSONException e) {
+ LOGGER.warn("Skipping '{}' in JAR '{}': rejected by JSON parser ({})",
+ entry.getName(), jar.getName(), e.getMessage());
+ }
+ }
+ } catch (final IOException e) {
+ LOGGER.warn("Skipping entry '{}' in JAR '{}': could not read as JSON ({})",
+ entry.getName(), jar.getName(), e.getMessage());
+ }
+ });
+ }
+
+ private void loadJsonSchemasFromSourceDirectory(final Path sourceDirectory,
+ final Map schemasById) {
+ if (!Files.isDirectory(sourceDirectory)) {
+ LOGGER.warn("Source directory '{}' does not exist or is not a directory", sourceDirectory);
+ return;
+ }
+
+ try {
+ Files.walk(sourceDirectory)
+ .filter(path -> path.toString().endsWith(".json"))
+ .forEach(path -> loadJsonSchemaFile(path, schemasById));
+ } catch (final IOException e) {
+ throw new EffectiveJsonSchemaGenerationException(
+ format("Failed to scan source directory '%s'", sourceDirectory), e);
+ }
+ }
+
+ private void loadJsonSchemaFile(final Path schemaFile, final Map schemasById) {
+ try {
+ final String content = Files.readString(schemaFile);
+ final var jsonNode = objectMapper.readTree(content);
+ if (jsonNode.has("id") && jsonNode.get("id").isTextual()) {
+ schemasById.put(jsonNode.get("id").asText(), content);
+ }
+ } catch (final IOException e) {
+ LOGGER.warn("Skipping '{}': could not parse as JSON ({})", schemaFile, e.getMessage());
+ }
+ }
+}
diff --git a/json-schema-catalog/catalog-effective-json-schema-generation/src/main/java/uk/gov/justice/schema/catalog/generation/effective/DefinitionNameFactory.java b/json-schema-catalog/catalog-effective-json-schema-generation/src/main/java/uk/gov/justice/schema/catalog/generation/effective/DefinitionNameFactory.java
new file mode 100644
index 000000000..20aa67526
--- /dev/null
+++ b/json-schema-catalog/catalog-effective-json-schema-generation/src/main/java/uk/gov/justice/schema/catalog/generation/effective/DefinitionNameFactory.java
@@ -0,0 +1,19 @@
+package uk.gov.justice.schema.catalog.generation.effective;
+
+/**
+ * Creates safe JSON Schema definition names from schema ID URLs, for use when
+ * inlining referenced schemas into an effective JSON schema document.
+ *
+ * Example: {@code http://justice.gov.uk/standards/complex_address.json}
+ * becomes {@code justice_gov_uk_standards_complex_address}
+ */
+public class DefinitionNameFactory {
+
+ public String createFor(final String schemaId) {
+ String name = schemaId.replaceFirst("^https?://", "");
+ name = name.replaceAll("[^a-zA-Z0-9]+", "_");
+ name = name.replaceAll("_json$", "");
+ name = name.replaceAll("^_+|_+$", "");
+ return name;
+ }
+}
diff --git a/json-schema-catalog/catalog-effective-json-schema-generation/src/main/java/uk/gov/justice/schema/catalog/generation/effective/EffectiveJsonSchemaGenerationException.java b/json-schema-catalog/catalog-effective-json-schema-generation/src/main/java/uk/gov/justice/schema/catalog/generation/effective/EffectiveJsonSchemaGenerationException.java
new file mode 100644
index 000000000..7d9a1f1d0
--- /dev/null
+++ b/json-schema-catalog/catalog-effective-json-schema-generation/src/main/java/uk/gov/justice/schema/catalog/generation/effective/EffectiveJsonSchemaGenerationException.java
@@ -0,0 +1,12 @@
+package uk.gov.justice.schema.catalog.generation.effective;
+
+public class EffectiveJsonSchemaGenerationException extends RuntimeException {
+
+ public EffectiveJsonSchemaGenerationException(final String message) {
+ super(message);
+ }
+
+ public EffectiveJsonSchemaGenerationException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/json-schema-catalog/catalog-effective-json-schema-generation/src/main/java/uk/gov/justice/schema/catalog/generation/effective/EffectiveJsonSchemaGenerator.java b/json-schema-catalog/catalog-effective-json-schema-generation/src/main/java/uk/gov/justice/schema/catalog/generation/effective/EffectiveJsonSchemaGenerator.java
new file mode 100644
index 000000000..eb45500c6
--- /dev/null
+++ b/json-schema-catalog/catalog-effective-json-schema-generation/src/main/java/uk/gov/justice/schema/catalog/generation/effective/EffectiveJsonSchemaGenerator.java
@@ -0,0 +1,76 @@
+package uk.gov.justice.schema.catalog.generation.effective;
+
+import static java.lang.String.format;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Map;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Generates effective JSON schema documents for a set of source schema files.
+ * For each schema file, all external {@code $ref} references are resolved and inlined
+ * into a single self-contained document, which is written to the output directory
+ * preserving the relative path structure of the source directory.
+ */
+public class EffectiveJsonSchemaGenerator {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(EffectiveJsonSchemaGenerator.class);
+
+ private final JsonSchemaInliner jsonSchemaInliner;
+ private final EffectiveJsonSchemaWriter effectiveJsonSchemaWriter;
+
+ public EffectiveJsonSchemaGenerator(final JsonSchemaInliner jsonSchemaInliner,
+ final EffectiveJsonSchemaWriter effectiveJsonSchemaWriter) {
+ this.jsonSchemaInliner = jsonSchemaInliner;
+ this.effectiveJsonSchemaWriter = effectiveJsonSchemaWriter;
+ }
+
+ /**
+ * Generates effective JSON schema documents for each of the given source schema files.
+ *
+ * @param schemaFiles the source schema files to process
+ * @param sourceBaseDirectory the root directory used to compute output relative paths
+ * @param allSchemasById a map of schema ID to raw JSON for all known schemas
+ * @param outputDirectory directory under which effective schemas are written
+ */
+ public void generate(final List schemaFiles,
+ final Path sourceBaseDirectory,
+ final Map allSchemasById,
+ final Path outputDirectory) {
+ for (final Path schemaFile : schemaFiles) {
+ generateEffectiveJsonSchema(schemaFile, sourceBaseDirectory, allSchemasById, outputDirectory);
+ }
+ }
+
+ private void generateEffectiveJsonSchema(final Path schemaFile,
+ final Path sourceBaseDirectory,
+ final Map allSchemasById,
+ final Path outputDirectory) {
+ try {
+ final String rawJson = Files.readString(schemaFile);
+ final JSONObject schema;
+ try {
+ schema = new JSONObject(rawJson);
+ } catch (final JSONException e) {
+ LOGGER.warn("Skipping '{}': could not parse as JSON schema ({})", schemaFile, e.getMessage());
+ return;
+ }
+ final JSONObject effectiveJsonSchema = jsonSchemaInliner.inline(schema, allSchemasById);
+
+ final Path relativePath = sourceBaseDirectory.relativize(schemaFile);
+ final Path outputPath = outputDirectory.resolve(relativePath);
+
+ effectiveJsonSchemaWriter.write(effectiveJsonSchema, outputPath);
+ } catch (final IOException e) {
+ throw new EffectiveJsonSchemaGenerationException(
+ format("Failed to generate effective JSON schema for '%s'", schemaFile), e);
+ }
+ }
+}
diff --git a/json-schema-catalog/catalog-effective-json-schema-generation/src/main/java/uk/gov/justice/schema/catalog/generation/effective/EffectiveJsonSchemaObjectFactory.java b/json-schema-catalog/catalog-effective-json-schema-generation/src/main/java/uk/gov/justice/schema/catalog/generation/effective/EffectiveJsonSchemaObjectFactory.java
new file mode 100644
index 000000000..47624c814
--- /dev/null
+++ b/json-schema-catalog/catalog-effective-json-schema-generation/src/main/java/uk/gov/justice/schema/catalog/generation/effective/EffectiveJsonSchemaObjectFactory.java
@@ -0,0 +1,30 @@
+package uk.gov.justice.schema.catalog.generation.effective;
+
+import uk.gov.justice.schema.catalog.CatalogObjectFactory;
+
+/**
+ * Factory for creating instances of the effective JSON schema generation components.
+ * Avoids the need for a dependency injection framework in library/plugin code.
+ */
+public class EffectiveJsonSchemaObjectFactory {
+
+ public DefinitionNameFactory definitionNameFactory() {
+ return new DefinitionNameFactory();
+ }
+
+ public JsonSchemaInliner jsonSchemaInliner() {
+ return new JsonSchemaInliner(definitionNameFactory());
+ }
+
+ public EffectiveJsonSchemaWriter effectiveJsonSchemaWriter() {
+ return new EffectiveJsonSchemaWriter();
+ }
+
+ public EffectiveJsonSchemaGenerator effectiveJsonSchemaGenerator() {
+ return new EffectiveJsonSchemaGenerator(jsonSchemaInliner(), effectiveJsonSchemaWriter());
+ }
+
+ public CatalogJsonSchemaLoader catalogJsonSchemaLoader() {
+ return new CatalogJsonSchemaLoader(new CatalogObjectFactory().objectMapper());
+ }
+}
diff --git a/json-schema-catalog/catalog-effective-json-schema-generation/src/main/java/uk/gov/justice/schema/catalog/generation/effective/EffectiveJsonSchemaWriter.java b/json-schema-catalog/catalog-effective-json-schema-generation/src/main/java/uk/gov/justice/schema/catalog/generation/effective/EffectiveJsonSchemaWriter.java
new file mode 100644
index 000000000..7b1624929
--- /dev/null
+++ b/json-schema-catalog/catalog-effective-json-schema-generation/src/main/java/uk/gov/justice/schema/catalog/generation/effective/EffectiveJsonSchemaWriter.java
@@ -0,0 +1,21 @@
+package uk.gov.justice.schema.catalog.generation.effective;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import org.json.JSONObject;
+
+/**
+ * Writes a generated effective JSON schema document to a file, creating any
+ * intermediate parent directories as needed.
+ */
+public class EffectiveJsonSchemaWriter {
+
+ public void write(final JSONObject effectiveJsonSchema, final Path outputPath) throws IOException {
+ Files.createDirectories(outputPath.getParent());
+ Files.writeString(outputPath, effectiveJsonSchema.toString(2), UTF_8);
+ }
+}
diff --git a/json-schema-catalog/catalog-effective-json-schema-generation/src/main/java/uk/gov/justice/schema/catalog/generation/effective/JsonSchemaInliner.java b/json-schema-catalog/catalog-effective-json-schema-generation/src/main/java/uk/gov/justice/schema/catalog/generation/effective/JsonSchemaInliner.java
new file mode 100644
index 000000000..8d9264bb1
--- /dev/null
+++ b/json-schema-catalog/catalog-effective-json-schema-generation/src/main/java/uk/gov/justice/schema/catalog/generation/effective/JsonSchemaInliner.java
@@ -0,0 +1,114 @@
+package uk.gov.justice.schema.catalog.generation.effective;
+
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+/**
+ * Inlines all external {@code $ref} references in a JSON schema into a single self-contained
+ * effective JSON schema document. Referenced schemas are collected in a top-level
+ * {@code definitions} block, and all external {@code $ref} values are rewritten to point to
+ * those local definitions.
+ *
+ *
Fragment references (e.g. {@code http://example.com/address.json#/definitions/postal})
+ * are preserved as a JSON Pointer path beneath the inlined definition entry.
+ *
+ *
Effective JSON schema files are written to {@code target/effective-json-schemas/} (or the
+ * configured {@code outputDirectory}), preserving the relative directory structure of the
+ * source directory.
+ *
+ *
This goal must run after the {@code generate-schema-catalog} goal so that the current
+ * module's own schemas are available. It is therefore bound to the {@code process-sources} phase
+ * by default.
+ */
+@Mojo(name = "generate-effective-json-schemas",
+ requiresDependencyResolution = COMPILE,
+ defaultPhase = PROCESS_SOURCES)
+public class EffectiveJsonSchemaMojo extends AbstractMojo {
+
+ @Parameter(required = true)
+ private File sourceDirectory;
+
+ @Parameter(defaultValue = "${project.build.directory}/effective-json-schemas")
+ private File outputDirectory;
+
+ @Parameter(defaultValue = "${project}", readonly = true, required = true)
+ private MavenProject project;
+
+ private final EffectiveJsonSchemaObjectFactory objectFactory;
+
+ public EffectiveJsonSchemaMojo() {
+ this.objectFactory = new EffectiveJsonSchemaObjectFactory();
+ }
+
+ EffectiveJsonSchemaMojo(final EffectiveJsonSchemaObjectFactory objectFactory,
+ final MavenProject project,
+ final File sourceDirectory,
+ final File outputDirectory) {
+ this.objectFactory = objectFactory;
+ this.project = project;
+ this.sourceDirectory = sourceDirectory;
+ this.outputDirectory = outputDirectory;
+ }
+
+ @Override
+ public void execute() throws MojoExecutionException {
+ if (!sourceDirectory.exists()) {
+ getLog().warn("Source directory '" + sourceDirectory + "' does not exist — skipping effective JSON schema generation");
+ return;
+ }
+
+ try {
+ final List dependencyJars = collectDependencyJars();
+ final CatalogJsonSchemaLoader loader = objectFactory.catalogJsonSchemaLoader();
+ final Map allSchemasById = loader.loadAllJsonSchemas(
+ dependencyJars, sourceDirectory.toPath());
+
+ final List schemaFiles = findJsonSchemaFiles(sourceDirectory.toPath());
+
+ if (schemaFiles.isEmpty()) {
+ getLog().info("No JSON schema files found in '" + sourceDirectory + "' — nothing to generate");
+ return;
+ }
+
+ final EffectiveJsonSchemaGenerator generator = objectFactory.effectiveJsonSchemaGenerator();
+ generator.generate(schemaFiles, sourceDirectory.toPath(), allSchemasById,
+ outputDirectory.toPath());
+
+ getLog().info("Generated " + schemaFiles.size() + " effective JSON schema(s) in '"
+ + outputDirectory.getAbsolutePath() + "'");
+
+ } catch (final EffectiveJsonSchemaGenerationException e) {
+ throw new MojoExecutionException("Effective JSON schema generation failed", e);
+ } catch (final IOException e) {
+ throw new MojoExecutionException("Failed to scan source directory '" + sourceDirectory + "'", e);
+ }
+ }
+
+ private List collectDependencyJars() {
+ return project.getArtifacts().stream()
+ .filter(artifact -> "jar".equals(artifact.getType()))
+ .map(Artifact::getFile)
+ .filter(Objects::nonNull)
+ .filter(File::exists)
+ .collect(toList());
+ }
+
+ private List findJsonSchemaFiles(final Path sourceDir) throws IOException {
+ return Files.walk(sourceDir)
+ .filter(path -> path.toString().endsWith(".json"))
+ .collect(toList());
+ }
+}
diff --git a/json-schema-catalog/effective-json-schema-maven-plugin/src/test/java/uk/gov/justice/schema/catalog/maven/EffectiveJsonSchemaMojoTest.java b/json-schema-catalog/effective-json-schema-maven-plugin/src/test/java/uk/gov/justice/schema/catalog/maven/EffectiveJsonSchemaMojoTest.java
new file mode 100644
index 000000000..87d64f539
--- /dev/null
+++ b/json-schema-catalog/effective-json-schema-maven-plugin/src/test/java/uk/gov/justice/schema/catalog/maven/EffectiveJsonSchemaMojoTest.java
@@ -0,0 +1,154 @@
+package uk.gov.justice.schema.catalog.maven;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyList;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import uk.gov.justice.schema.catalog.generation.effective.CatalogJsonSchemaLoader;
+import uk.gov.justice.schema.catalog.generation.effective.EffectiveJsonSchemaGenerationException;
+import uk.gov.justice.schema.catalog.generation.effective.EffectiveJsonSchemaGenerator;
+import uk.gov.justice.schema.catalog.generation.effective.EffectiveJsonSchemaObjectFactory;
+
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.project.MavenProject;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.io.TempDir;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+public class EffectiveJsonSchemaMojoTest {
+
+ @TempDir
+ Path tempDir;
+
+ @Mock
+ private EffectiveJsonSchemaObjectFactory objectFactory;
+
+ @Mock
+ private MavenProject project;
+
+ @Mock
+ private CatalogJsonSchemaLoader catalogJsonSchemaLoader;
+
+ @Mock
+ private EffectiveJsonSchemaGenerator effectiveJsonSchemaGenerator;
+
+ @Test
+ public void shouldSkipExecutionWhenSourceDirectoryDoesNotExist() throws Exception {
+ final File nonExistentDir = new File(tempDir.toFile(), "does-not-exist");
+ final File outputDir = tempDir.resolve("output").toFile();
+
+ final EffectiveJsonSchemaMojo mojo = new EffectiveJsonSchemaMojo(
+ objectFactory, project, nonExistentDir, outputDir);
+
+ mojo.execute();
+
+ verify(objectFactory, never()).catalogJsonSchemaLoader();
+ }
+
+ @Test
+ public void shouldSkipExecutionWhenSourceDirectoryContainsNoJsonFiles() throws Exception {
+ final Path sourceDir = tempDir.resolve("source");
+ Files.createDirectories(sourceDir);
+ Files.writeString(sourceDir.resolve("readme.txt"), "not a schema");
+
+ when(project.getArtifacts()).thenReturn(Set.of());
+ when(objectFactory.catalogJsonSchemaLoader()).thenReturn(catalogJsonSchemaLoader);
+ when(catalogJsonSchemaLoader.loadAllJsonSchemas(anyList(), any())).thenReturn(Map.of());
+
+ final EffectiveJsonSchemaMojo mojo = new EffectiveJsonSchemaMojo(
+ objectFactory, project, sourceDir.toFile(), tempDir.resolve("output").toFile());
+
+ mojo.execute();
+
+ verify(objectFactory, never()).effectiveJsonSchemaGenerator();
+ }
+
+ @Test
+ public void shouldCallGeneratorWithCollectedSchemasAndSchemaFiles() throws Exception {
+ final Path sourceDir = tempDir.resolve("source");
+ Files.createDirectories(sourceDir);
+ Files.writeString(sourceDir.resolve("person.json"),
+ "{ \"id\": \"http://example.com/person.json\" }");
+
+ final Map loadedSchemas = Map.of(
+ "http://example.com/person.json", "{ \"id\": \"http://example.com/person.json\" }");
+
+ when(project.getArtifacts()).thenReturn(Set.of());
+ when(objectFactory.catalogJsonSchemaLoader()).thenReturn(catalogJsonSchemaLoader);
+ when(catalogJsonSchemaLoader.loadAllJsonSchemas(anyList(), any())).thenReturn(loadedSchemas);
+ when(objectFactory.effectiveJsonSchemaGenerator()).thenReturn(effectiveJsonSchemaGenerator);
+
+ final File outputDir = tempDir.resolve("output").toFile();
+ final EffectiveJsonSchemaMojo mojo = new EffectiveJsonSchemaMojo(
+ objectFactory, project, sourceDir.toFile(), outputDir);
+
+ mojo.execute();
+
+ verify(effectiveJsonSchemaGenerator).generate(any(), any(), any(), any());
+ }
+
+ @Test
+ public void shouldCollectJarArtifactsFromProjectAndPassToLoader() throws Exception {
+ final Path sourceDir = tempDir.resolve("source");
+ Files.createDirectories(sourceDir);
+ Files.writeString(sourceDir.resolve("schema.json"), "{ \"id\": \"http://example.com/schema.json\" }");
+
+ final File jarFile = tempDir.resolve("dep.jar").toFile();
+ jarFile.createNewFile();
+
+ final Artifact artifact = org.mockito.Mockito.mock(Artifact.class);
+ when(artifact.getType()).thenReturn("jar");
+ when(artifact.getFile()).thenReturn(jarFile);
+
+ when(project.getArtifacts()).thenReturn(Set.of(artifact));
+ when(objectFactory.catalogJsonSchemaLoader()).thenReturn(catalogJsonSchemaLoader);
+ when(catalogJsonSchemaLoader.loadAllJsonSchemas(anyList(), any())).thenReturn(Map.of());
+ when(objectFactory.effectiveJsonSchemaGenerator()).thenReturn(effectiveJsonSchemaGenerator);
+
+ final EffectiveJsonSchemaMojo mojo = new EffectiveJsonSchemaMojo(
+ objectFactory, project, sourceDir.toFile(), tempDir.resolve("output").toFile());
+
+ mojo.execute();
+
+ verify(catalogJsonSchemaLoader).loadAllJsonSchemas(
+ org.mockito.ArgumentMatchers.argThat(list -> list.contains(jarFile)),
+ any());
+ }
+
+ @Test
+ public void shouldWrapEffectiveJsonSchemaGenerationExceptionInMojoExecutionException() throws Exception {
+ final Path sourceDir = tempDir.resolve("source");
+ Files.createDirectories(sourceDir);
+ Files.writeString(sourceDir.resolve("schema.json"), "{ \"id\": \"http://example.com/schema.json\" }");
+
+ when(project.getArtifacts()).thenReturn(Set.of());
+ when(objectFactory.catalogJsonSchemaLoader()).thenReturn(catalogJsonSchemaLoader);
+ when(catalogJsonSchemaLoader.loadAllJsonSchemas(anyList(), any())).thenReturn(Map.of());
+ when(objectFactory.effectiveJsonSchemaGenerator()).thenReturn(effectiveJsonSchemaGenerator);
+ org.mockito.Mockito.doThrow(new EffectiveJsonSchemaGenerationException("boom"))
+ .when(effectiveJsonSchemaGenerator).generate(any(), any(), any(), any());
+
+ final EffectiveJsonSchemaMojo mojo = new EffectiveJsonSchemaMojo(
+ objectFactory, project, sourceDir.toFile(), tempDir.resolve("output").toFile());
+
+ final MojoExecutionException exception = assertThrows(MojoExecutionException.class, mojo::execute);
+
+ assertThat(exception.getMessage().contains("Effective JSON schema generation failed"), is(true));
+ }
+}
diff --git a/json-schema-catalog/effective-json-schema-plugin-it/pom.xml b/json-schema-catalog/effective-json-schema-plugin-it/pom.xml
new file mode 100644
index 000000000..5963eb56b
--- /dev/null
+++ b/json-schema-catalog/effective-json-schema-plugin-it/pom.xml
@@ -0,0 +1,153 @@
+
+
+
+ json-schema-catalog
+ uk.gov.justice.schema
+ 21.0.0-SNAPSHOT
+
+ 4.0.0
+
+ effective-json-schema-plugin-it
+
+
+
+
+
+ uk.gov.justice.schema
+ catalog-generation-plugin
+ ${project.version}
+
+
+ generate-schema-catalog
+ generate-sources
+
+ generate-schema-catalog
+
+
+
+ uk.gov.justice.schema.catalog.generation.maven.MavenCatalogGeneratorFactory
+
+
+ uk.gov.justice.schema.catalog.generation.io.parser.ListOfUriParser
+
+ ${basedir}/src/raml/json/schema
+ ${project.build.directory}/generated-resources
+
+ **/*.json
+
+
+
+ ${project.artifactId}
+
+
+
+
+
+
+ uk.gov.justice.schema
+ catalog-generation
+ ${project.version}
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jakarta-jsonp
+ ${jackson-datatype-jakarta-jsonp.version}
+
+
+
+
+
+
+ uk.gov.justice.schema
+ effective-json-schema-maven-plugin
+ ${project.version}
+
+
+ generate-effective-json-schemas
+ process-sources
+
+ generate-effective-json-schemas
+
+
+ ${basedir}/src/raml/json/schema
+ ${project.build.directory}/effective-json-schemas
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-enforcer-plugin
+ ${plugins.maven.enforcer.version}
+
+
+ enforce-rules
+
+ enforce
+
+
+
+
+ ${enforcer.java.version.range}
+
+
+ ${enforcer.maven.version.range}
+
+
+ Please define plugin versions
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+ uk.gov.justice.schema
+ catalog-effective-json-schema-generation
+ ${project.version}
+ test
+
+
+ org.json
+ json
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ test
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+ org.mockito
+ mockito-junit-jupiter
+ test
+
+
+ org.hamcrest
+ hamcrest
+ test
+
+
+
diff --git a/json-schema-catalog/effective-json-schema-plugin-it/src/raml/json/schema/context/person.json b/json-schema-catalog/effective-json-schema-plugin-it/src/raml/json/schema/context/person.json
new file mode 100644
index 000000000..82e48199c
--- /dev/null
+++ b/json-schema-catalog/effective-json-schema-plugin-it/src/raml/json/schema/context/person.json
@@ -0,0 +1,25 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "id": "http://justice.gov.uk/context/person.json",
+ "type": "object",
+ "properties": {
+ "nino": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "home_address": {
+ "$ref": "http://justice.gov.uk/standards/address.json"
+ },
+ "correspondence_address": {
+ "$ref": "http://justice.gov.uk/standards/complex_address.json#/definitions/complex_address"
+ }
+ },
+ "required": [
+ "nino",
+ "name",
+ "home_address",
+ "correspondence_address"
+ ]
+}
diff --git a/json-schema-catalog/effective-json-schema-plugin-it/src/raml/json/schema/standards/address.json b/json-schema-catalog/effective-json-schema-plugin-it/src/raml/json/schema/standards/address.json
new file mode 100644
index 000000000..08abe3bfb
--- /dev/null
+++ b/json-schema-catalog/effective-json-schema-plugin-it/src/raml/json/schema/standards/address.json
@@ -0,0 +1,24 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "id": "http://justice.gov.uk/standards/address.json",
+ "type": "object",
+ "properties": {
+ "addressline1": {
+ "type": "string"
+ },
+ "addressline2": {
+ "type": "string"
+ },
+ "city": {
+ "type": "string"
+ },
+ "postcode": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "addressline1",
+ "city",
+ "postcode"
+ ]
+}
diff --git a/json-schema-catalog/effective-json-schema-plugin-it/src/raml/json/schema/standards/complex_address.json b/json-schema-catalog/effective-json-schema-plugin-it/src/raml/json/schema/standards/complex_address.json
new file mode 100644
index 000000000..5d71a55b7
--- /dev/null
+++ b/json-schema-catalog/effective-json-schema-plugin-it/src/raml/json/schema/standards/complex_address.json
@@ -0,0 +1,35 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "id": "http://justice.gov.uk/standards/complex_address.json",
+ "type": "object",
+ "definitions": {
+ "complex_address": {
+ "type": "object",
+ "properties": {
+ "addressline1": {
+ "type": "string"
+ },
+ "city": {
+ "type": "string"
+ },
+ "postcode": {
+ "type": "string"
+ },
+ "country": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "addressline1",
+ "city",
+ "postcode",
+ "country"
+ ]
+ }
+ },
+ "allOf": [
+ {
+ "$ref": "#/definitions/complex_address"
+ }
+ ]
+}
diff --git a/json-schema-catalog/effective-json-schema-plugin-it/src/test/java/uk/gov/justice/schema/catalog/generation/effective/it/EffectiveJsonSchemaGenerationIT.java b/json-schema-catalog/effective-json-schema-plugin-it/src/test/java/uk/gov/justice/schema/catalog/generation/effective/it/EffectiveJsonSchemaGenerationIT.java
new file mode 100644
index 000000000..d21b3ebf0
--- /dev/null
+++ b/json-schema-catalog/effective-json-schema-plugin-it/src/test/java/uk/gov/justice/schema/catalog/generation/effective/it/EffectiveJsonSchemaGenerationIT.java
@@ -0,0 +1,118 @@
+package uk.gov.justice.schema.catalog.generation.effective.it;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import org.json.JSONObject;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+/**
+ * Integration test that verifies the effective-json-schema-maven-plugin correctly generates
+ * effective JSON schema files with all external {@code $ref} values inlined.
+ *
+ * The plugin runs at {@code process-sources} phase and writes output to
+ * {@code target/effective-json-schemas/}. This test is deliberately placed in the
+ * test phase (after process-sources) so the generated files are already present.
+ */
+@ExtendWith(MockitoExtension.class)
+public class EffectiveJsonSchemaGenerationIT {
+
+ private static final Path EFFECTIVE_SCHEMAS_DIR =
+ Paths.get("target/effective-json-schemas");
+
+ @Test
+ public void shouldGenerateEffectiveJsonSchemaForSimpleSchemaWithNoExternalRefs() throws Exception {
+ final Path effectiveAddressSchema = EFFECTIVE_SCHEMAS_DIR.resolve("standards/address.json");
+
+ assertThat("Effective schema file should exist: " + effectiveAddressSchema,
+ Files.exists(effectiveAddressSchema), is(true));
+
+ final JSONObject schema = new JSONObject(Files.readString(effectiveAddressSchema));
+
+ assertThat(schema.getString("id"), is("http://justice.gov.uk/standards/address.json"));
+ assertThat(schema.getString("type"), is("object"));
+ assertThat(schema.getJSONObject("properties").has("city"), is(true));
+ }
+
+ @Test
+ public void shouldGenerateEffectiveJsonSchemaForPersonWithExternalRefsInlined() throws Exception {
+ final Path effectivePersonSchema = EFFECTIVE_SCHEMAS_DIR.resolve("context/person.json");
+
+ assertThat("Effective schema file should exist: " + effectivePersonSchema,
+ Files.exists(effectivePersonSchema), is(true));
+
+ final JSONObject schema = new JSONObject(Files.readString(effectivePersonSchema));
+
+ assertThat(schema.getString("id"), is("http://justice.gov.uk/context/person.json"));
+ assertThat(schema.getString("type"), is("object"));
+ }
+
+ @Test
+ public void shouldInlineAddressSchemaDefinitionIntoPersonEffectiveSchema() throws Exception {
+ final Path effectivePersonSchema = EFFECTIVE_SCHEMAS_DIR.resolve("context/person.json");
+ final JSONObject schema = new JSONObject(Files.readString(effectivePersonSchema));
+
+ final JSONObject definitions = schema.getJSONObject("definitions");
+
+ assertThat("Effective person schema should contain inlined address definition",
+ definitions.has("justice_gov_uk_standards_address"), is(true));
+
+ final JSONObject inlinedAddress = definitions.getJSONObject("justice_gov_uk_standards_address");
+ assertThat(inlinedAddress.getString("type"), is("object"));
+ assertThat(inlinedAddress.getJSONObject("properties").has("city"), is(true));
+ assertThat(inlinedAddress.getJSONObject("properties").has("postcode"), is(true));
+ }
+
+ @Test
+ public void shouldInlineComplexAddressSchemaDefinitionIntoPersonEffectiveSchema() throws Exception {
+ final Path effectivePersonSchema = EFFECTIVE_SCHEMAS_DIR.resolve("context/person.json");
+ final JSONObject schema = new JSONObject(Files.readString(effectivePersonSchema));
+
+ final JSONObject definitions = schema.getJSONObject("definitions");
+
+ assertThat("Effective person schema should contain inlined complex_address definition",
+ definitions.has("justice_gov_uk_standards_complex_address"), is(true));
+ }
+
+ @Test
+ public void shouldRewriteExternalRefToLocalDefinitionInPersonEffectiveSchema() throws Exception {
+ final Path effectivePersonSchema = EFFECTIVE_SCHEMAS_DIR.resolve("context/person.json");
+ final JSONObject schema = new JSONObject(Files.readString(effectivePersonSchema));
+
+ final String homeAddressRef = schema.getJSONObject("properties")
+ .getJSONObject("home_address")
+ .getString("$ref");
+
+ assertThat("home_address $ref should point to a local definition",
+ homeAddressRef.startsWith("#/definitions/"), is(true));
+ assertThat(homeAddressRef, is("#/definitions/justice_gov_uk_standards_address"));
+ }
+
+ @Test
+ public void shouldRewriteFragmentRefToLocalDefinitionInPersonEffectiveSchema() throws Exception {
+ final Path effectivePersonSchema = EFFECTIVE_SCHEMAS_DIR.resolve("context/person.json");
+ final JSONObject schema = new JSONObject(Files.readString(effectivePersonSchema));
+
+ final String correspondenceRef = schema.getJSONObject("properties")
+ .getJSONObject("correspondence_address")
+ .getString("$ref");
+
+ assertThat("correspondence_address $ref should point to a local definition with fragment path",
+ correspondenceRef.startsWith("#/definitions/"), is(true));
+ assertThat(correspondenceRef,
+ is("#/definitions/justice_gov_uk_standards_complex_address/definitions/complex_address"));
+ }
+
+ @Test
+ public void shouldPreserveRelativeDirectoryStructureOfOutputFiles() {
+ assertThat(Files.exists(EFFECTIVE_SCHEMAS_DIR.resolve("standards/address.json")), is(true));
+ assertThat(Files.exists(EFFECTIVE_SCHEMAS_DIR.resolve("standards/complex_address.json")), is(true));
+ assertThat(Files.exists(EFFECTIVE_SCHEMAS_DIR.resolve("context/person.json")), is(true));
+ }
+}
diff --git a/json-schema-catalog/effective-json-schema-plugin.md b/json-schema-catalog/effective-json-schema-plugin.md
new file mode 100644
index 000000000..eed6474dc
--- /dev/null
+++ b/json-schema-catalog/effective-json-schema-plugin.md
@@ -0,0 +1,417 @@
+# Effective JSON Schema Maven Plugin
+
+## Background
+
+JSON schemas in the CPP platform are designed to be composable. Shared definitions —
+UUID patterns, post code formats, common domain types — live in central library schemas
+(e.g. `http://justice.gov.uk/domain/core/common/definitions.json`) and are referenced
+from context-specific schemas using `$ref`:
+
+```json
+{
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "properties": {
+ "caseId": {
+ "$ref": "http://justice.gov.uk/domain/core/common/definitions.json#/definitions/uuid"
+ }
+ }
+}
+```
+
+This composition model keeps schemas DRY but creates a practical problem: to understand
+what a schema actually validates you must mentally (or programmatically) chase every `$ref`
+across multiple JARs. Validators, code generators, and documentation tools all need the
+complete schema in one place.
+
+The `effective-json-schema-maven-plugin` solves this in the same spirit as Maven's
+`help:effective-pom` — it produces a **single, self-contained document** with every
+external `$ref` resolved and inlined, written to `target/effective-json-schemas/` at
+build time.
+
+---
+
+## What "effective" means
+
+Given a source schema that references an external definition:
+
+**Source (`src/raml/json/schema/feature-permission.json`):**
+```json
+{
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type": "object",
+ "properties": {
+ "permissionId": {
+ "$ref": "http://justice.gov.uk/domain/core/common/definitions.json#/definitions/uuid"
+ }
+ }
+}
+```
+
+The plugin produces an effective schema where the referenced definitions file is inlined
+under a sanitised key in a top-level `definitions` block, and the `$ref` is rewritten to
+point at that local copy:
+
+**Output (`target/effective-json-schemas/feature-permission.json`):**
+```json
+{
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type": "object",
+ "definitions": {
+ "justice_gov_uk_domain_core_common_definitions": {
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "id": "http://justice.gov.uk/domain/core/common/definitions.json",
+ "definitions": {
+ "uuid": {
+ "pattern": "[a-fA-F0-9]{8}-...",
+ "type": "string"
+ }
+ }
+ }
+ },
+ "properties": {
+ "permissionId": {
+ "$ref": "#/definitions/justice_gov_uk_domain_core_common_definitions/definitions/uuid"
+ }
+ }
+}
+```
+
+The effective schema is valid JSON Schema draft-04. All `$ref` values point to `#/…`
+(local document) rather than external URLs, making the schema fully self-contained.
+
+---
+
+## Maven coordinates
+
+| Artifact | Group ID | Artifact ID |
+|---|---|---|
+| Maven plugin | `uk.gov.justice.schema` | `effective-json-schema-maven-plugin` |
+| Generation library | `uk.gov.justice.schema` | `catalog-effective-json-schema-generation` |
+
+Both are versioned as part of `cp-framework-libraries` (`json-schema-catalog` sub-project).
+
+---
+
+## How it activates automatically
+
+The plugin is configured in `cpp-platform-maven-service-parent-pom` so every context
+module with a `src/raml` directory gets it with zero additional pom changes.
+
+### In `pluginManagement`
+
+Default configuration is declared in `pluginManagement` (applies to all modules):
+
+```xml
+
+ uk.gov.justice.schema
+ effective-json-schema-maven-plugin
+ ${framework-libraries.version}
+
+
+ generate-effective-json-schemas
+
+ ${schema.generation.source.directory}
+ ${project.build.directory}/effective-json-schemas
+
+ generate-effective-json-schemas
+ process-sources
+
+
+
+```
+
+`schema.generation.source.directory` defaults to `src/raml/json/schema`.
+
+### In the `raml-catalog-generation` profile
+
+The profile activates automatically when a module contains a `src/raml` directory:
+
+```xml
+
+ raml-catalog-generation
+
+ src/raml
+
+
+
+
+
+ uk.gov.justice.schema
+ effective-json-schema-maven-plugin
+ ${framework-libraries.version}
+
+
+ generate-effective-json-schemas
+ generate-effective-json-schemas
+ process-sources
+
+
+
+
+
+
+```
+
+Any module with `src/raml` will have both the catalog generation plugin (phase:
+`generate-sources`) and the effective schema plugin (phase: `process-sources`) active.
+
+---
+
+## Build lifecycle
+
+```
+generate-sources → catalog-generation-plugin builds META-INF/schema_catalog.json
+ (indexes this module's own schemas)
+
+process-sources → effective-json-schema-maven-plugin
+ 1. Loads all schemas from dependency JARs (via their catalogs, then
+ via full classpath scan as fallback)
+ 2. Loads all schemas from the local source directory
+ 3. For each .json file in the source directory, resolves and inlines
+ every external $ref into a self-contained effective schema
+ 4. Writes results to target/effective-json-schemas/
+```
+
+The ordering matters: the catalog-generation plugin runs first so the local module's
+schemas are available before the inliner runs.
+
+---
+
+## Plugin parameters
+
+| Parameter | Default | Required | Description |
+|---|---|---|---|
+| `sourceDirectory` | `${schema.generation.source.directory}` | yes | Directory containing source JSON schema files to process |
+| `outputDirectory` | `${project.build.directory}/effective-json-schemas` | no | Directory where effective schemas are written |
+
+---
+
+## Schema discovery — how `$ref` targets are resolved
+
+The plugin builds a map of `{ schemaId → rawJson }` before inlining. It populates this
+map from three sources, in precedence order:
+
+### 1. Dependency JAR catalogs (highest precedence)
+
+Every dependency JAR that contains a `META-INF/schema_catalog.json` is opened. The
+catalog lists each schema's `id` and its path within the JAR. Those schemas are read
+and added to the map first.
+
+This is the primary mechanism for resolving refs to platform library schemas
+(e.g. `cpp-platform-core-domain`) and shared domain schemas.
+
+### 2. Full classpath scan (fallback)
+
+After catalog-based loading, every `.json` entry in every dependency JAR is scanned.
+If a file contains a top-level `"id"` field and can be parsed, it is added to the map
+via `putIfAbsent` — catalog-loaded schemas always take precedence.
+
+This fallback handles JARs that carry JSON schema files but have no
+`META-INF/schema_catalog.json` (e.g. schemas bundled by a context that does not
+publish a catalog).
+
+### 3. Local source directory
+
+Finally, every `.json` file in the configured `sourceDirectory` that contains an `"id"`
+field is added to the map (again using `put`, so local schemas override dependency
+schemas with the same ID).
+
+---
+
+## Fragment references
+
+A `$ref` that includes a JSON Pointer fragment:
+
+```json
+{ "$ref": "http://justice.gov.uk/domain/core/common/definitions.json#/definitions/uuid" }
+```
+
+is inlined so the full referenced schema is placed in `definitions` and the fragment
+path is preserved:
+
+```json
+{ "$ref": "#/definitions/justice_gov_uk_domain_core_common_definitions/definitions/uuid" }
+```
+
+The definition name is derived from the schema's base URL by stripping the protocol,
+replacing non-alphanumeric characters with underscores, and removing the `.json` suffix:
+
+| Schema URL | Definition name |
+|---|---|
+| `http://justice.gov.uk/domain/core/common/definitions.json` | `justice_gov_uk_domain_core_common_definitions` |
+| `http://justice.gov.uk/standards/complex_address.json` | `justice_gov_uk_standards_complex_address` |
+
+---
+
+## Edge cases
+
+### Circular references
+
+If schema A references schema B which references schema A, the plugin detects the cycle
+and leaves the back-reference pointing at the external URL rather than causing infinite
+recursion.
+
+### Unresolvable `$ref`
+
+If a `$ref` target cannot be found in any dependency JAR or the local source directory,
+the `$ref` is left unchanged in the output. A warning is not emitted — this preserves
+forward compatibility when a schema references a peer that is not yet on the classpath.
+
+### Schema file with invalid JSON or duplicate keys
+
+If a source schema file cannot be parsed (e.g. duplicate JSON keys), the file is
+skipped with a `WARN` log entry and all other schemas are still processed:
+
+```
+[WARNING] Skipping 'src/raml/json/schema/bad.json': could not parse as JSON schema (Duplicate key "name" ...)
+```
+
+Similarly, any classpath-scanned schema that fails the JSON parser is silently skipped
+and does not block other schemas from being resolved.
+
+---
+
+## Running manually
+
+To regenerate effective schemas for a single module without running the full build:
+
+```bash
+# In a context API module (e.g. usersgroups-query-api)
+mvn process-sources -Denforcer.skip=true
+```
+
+The output lands in `target/effective-json-schemas/` mirroring the relative path
+structure of the source directory:
+
+```
+target/effective-json-schemas/
+├── feature-permission.json
+├── group-details-schema.json
+├── user-details-schema.json
+└── ...
+```
+
+---
+
+## Example output walkthrough
+
+Source schema (`person.json`) references two external schemas:
+
+```json
+{
+ "id": "http://justice.gov.uk/context/person.json",
+ "type": "object",
+ "properties": {
+ "home_address": { "$ref": "http://justice.gov.uk/standards/address.json" },
+ "correspondence_address":{ "$ref": "http://justice.gov.uk/standards/complex_address.json#/definitions/complex_address" },
+ "name": { "type": "string" },
+ "nino": { "type": "string" }
+ },
+ "required": ["nino","name","home_address","correspondence_address"]
+}
+```
+
+Effective schema output:
+
+```json
+{
+ "id": "http://justice.gov.uk/context/person.json",
+ "type": "object",
+ "definitions": {
+ "justice_gov_uk_standards_address": {
+ "type": "object",
+ "properties": {
+ "addressline1": { "type": "string" },
+ "city": { "type": "string" },
+ "postcode": { "type": "string" },
+ "addressline2": { "type": "string" }
+ },
+ "required": ["addressline1","city","postcode"]
+ },
+ "justice_gov_uk_standards_complex_address": {
+ "type": "object",
+ "definitions": {
+ "complex_address": {
+ "type": "object",
+ "properties": {
+ "addressline1": { "type": "string" },
+ "city": { "type": "string" },
+ "postcode": { "type": "string" },
+ "country": { "type": "string" }
+ },
+ "required": ["addressline1","city","postcode","country"]
+ }
+ }
+ }
+ },
+ "properties": {
+ "home_address": { "$ref": "#/definitions/justice_gov_uk_standards_address" },
+ "correspondence_address": { "$ref": "#/definitions/justice_gov_uk_standards_complex_address/definitions/complex_address" },
+ "name": { "type": "string" },
+ "nino": { "type": "string" }
+ },
+ "required": ["nino","name","home_address","correspondence_address"]
+}
+```
+
+---
+
+## Module structure
+
+```
+json-schema-catalog/
+├── catalog-effective-json-schema-generation/ # Core library (no Maven API dependency)
+│ └── src/main/java/.../generation/effective/
+│ ├── CatalogJsonSchemaLoader.java # Loads schemas from JARs + source dir
+│ ├── DefinitionNameFactory.java # Converts schema URLs to definition keys
+│ ├── EffectiveJsonSchemaGenerator.java # Orchestrates inlining per schema file
+│ ├── EffectiveJsonSchemaObjectFactory.java# Wires components together (no DI framework)
+│ ├── EffectiveJsonSchemaWriter.java # Writes output files
+│ ├── EffectiveJsonSchemaGenerationException.java
+│ └── JsonSchemaInliner.java # Core $ref resolution and rewriting
+│
+├── effective-json-schema-maven-plugin/ # Thin Maven Mojo wrapper
+│ └── src/main/java/.../catalog/maven/
+│ └── EffectiveJsonSchemaMojo.java
+│
+└── effective-json-schema-plugin-it/ # Integration test (runs the plugin for real)
+ └── src/test/java/.../it/
+ └── EffectiveJsonSchemaGenerationIT.java
+```
+
+The core library (`catalog-effective-json-schema-generation`) has no dependency on the
+Maven plugin API and can be used directly in tests or tooling without a Maven runtime.
+
+---
+
+## Testing
+
+### Unit tests
+
+Every class has a dedicated JUnit 5 test using `@ExtendWith(MockitoExtension.class)`.
+Run from the library module:
+
+```bash
+cd catalog-effective-json-schema-generation
+mvn test -Denforcer.skip=true
+```
+
+### Integration test
+
+`effective-json-schema-plugin-it` is a self-contained Maven module that carries real
+JSON schema files (with cross-schema `$ref` relationships) and runs the plugin as part
+of its own build. The IT test class reads the generated files from
+`target/effective-json-schemas/` and asserts on their content.
+
+```bash
+cd effective-json-schema-plugin-it
+mvn clean verify -Denforcer.skip=true
+```
+
+The IT covers:
+
+- Schemas with no external refs are passed through unchanged
+- External refs are inlined into `definitions`
+- Fragment refs (`schema.json#/definitions/foo`) are rewritten correctly
+- The schema `"id"` field is preserved in the output
+- Nested definitions (schema referencing schema referencing schema) are all inlined
diff --git a/json-schema-catalog/pom.xml b/json-schema-catalog/pom.xml
index 638062519..d99c16c82 100644
--- a/json-schema-catalog/pom.xml
+++ b/json-schema-catalog/pom.xml
@@ -7,12 +7,12 @@
uk.gov.justice.framework.librariesframework-libraries
- 17.105.0-M3-SNAPSHOT
+ 21.0.0-SNAPSHOTuk.gov.justice.schemajson-schema-catalog
- 17.105.0-M3-SNAPSHOT
+ 21.0.0-SNAPSHOTpom
@@ -25,6 +25,9 @@
schema-example-contextcatalog-generation-pluginschema-service
+ catalog-effective-json-schema-generation
+ effective-json-schema-maven-plugin
+ effective-json-schema-plugin-it
@@ -34,7 +37,7 @@
org.apache.maven.pluginsmaven-surefire-plugin
- ${argLine} -Xmx64m
+ @{argLine} -Xmx64m -Dnet.bytebuddy.experimental=true
diff --git a/json-schema-catalog/schema-example-context/example-integration-test/pom.xml b/json-schema-catalog/schema-example-context/example-integration-test/pom.xml
index 784313b05..655f13fb6 100644
--- a/json-schema-catalog/schema-example-context/example-integration-test/pom.xml
+++ b/json-schema-catalog/schema-example-context/example-integration-test/pom.xml
@@ -5,7 +5,7 @@
schema-example-contextuk.gov.justice.schema
- 17.105.0-M3-SNAPSHOT
+ 21.0.0-SNAPSHOT4.0.0
diff --git a/json-schema-catalog/schema-example-context/pom.xml b/json-schema-catalog/schema-example-context/pom.xml
index 63d9677e9..7f59c7c9e 100644
--- a/json-schema-catalog/schema-example-context/pom.xml
+++ b/json-schema-catalog/schema-example-context/pom.xml
@@ -5,7 +5,7 @@
json-schema-cataloguk.gov.justice.schema
- 17.105.0-M3-SNAPSHOT
+ 21.0.0-SNAPSHOT4.0.0
diff --git a/json-schema-catalog/schema-example-context/schema-example-command-api/pom.xml b/json-schema-catalog/schema-example-context/schema-example-command-api/pom.xml
index 2dffdb576..9db60a432 100644
--- a/json-schema-catalog/schema-example-context/schema-example-command-api/pom.xml
+++ b/json-schema-catalog/schema-example-context/schema-example-command-api/pom.xml
@@ -5,7 +5,7 @@
schema-example-contextuk.gov.justice.schema
- 17.105.0-M3-SNAPSHOT
+ 21.0.0-SNAPSHOT4.0.0
diff --git a/json-schema-catalog/schema-example-context/schema-example-standards/pom.xml b/json-schema-catalog/schema-example-context/schema-example-standards/pom.xml
index ec485811a..d9373948e 100644
--- a/json-schema-catalog/schema-example-context/schema-example-standards/pom.xml
+++ b/json-schema-catalog/schema-example-context/schema-example-standards/pom.xml
@@ -5,7 +5,7 @@
schema-example-contextuk.gov.justice.schema
- 17.105.0-M3-SNAPSHOT
+ 21.0.0-SNAPSHOT4.0.0
diff --git a/json-schema-catalog/schema-service/pom.xml b/json-schema-catalog/schema-service/pom.xml
index 1f681c637..81569c3e6 100644
--- a/json-schema-catalog/schema-service/pom.xml
+++ b/json-schema-catalog/schema-service/pom.xml
@@ -5,7 +5,7 @@
json-schema-cataloguk.gov.justice.schema
- 17.105.0-M3-SNAPSHOT
+ 21.0.0-SNAPSHOT4.0.0
@@ -13,13 +13,13 @@
- javax
- javaee-api
+ jakarta.platform
+ jakarta.jakartaee-apiprovidedorg.glassfish
- javax.json
+ jakarta.jsonuk.gov.justice.schema
diff --git a/json-schema-catalog/schema-service/src/main/java/uk/gov/justice/schema/service/CatalogProducer.java b/json-schema-catalog/schema-service/src/main/java/uk/gov/justice/schema/service/CatalogProducer.java
index 7a2174672..a43401099 100644
--- a/json-schema-catalog/schema-service/src/main/java/uk/gov/justice/schema/service/CatalogProducer.java
+++ b/json-schema-catalog/schema-service/src/main/java/uk/gov/justice/schema/service/CatalogProducer.java
@@ -3,8 +3,8 @@
import uk.gov.justice.schema.catalog.Catalog;
import uk.gov.justice.schema.catalog.CatalogObjectFactory;
-import javax.enterprise.context.ApplicationScoped;
-import javax.enterprise.inject.Produces;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.enterprise.inject.Produces;
/**
* A Producer for the {@link Catalog} should you be running in a CDI container
diff --git a/json-schema-catalog/schema-service/src/main/java/uk/gov/justice/schema/service/SchemaCatalogResolverProducer.java b/json-schema-catalog/schema-service/src/main/java/uk/gov/justice/schema/service/SchemaCatalogResolverProducer.java
index 8a40c2a14..309a607c3 100644
--- a/json-schema-catalog/schema-service/src/main/java/uk/gov/justice/schema/service/SchemaCatalogResolverProducer.java
+++ b/json-schema-catalog/schema-service/src/main/java/uk/gov/justice/schema/service/SchemaCatalogResolverProducer.java
@@ -3,8 +3,8 @@
import uk.gov.justice.schema.catalog.CatalogObjectFactory;
import uk.gov.justice.schema.catalog.SchemaCatalogResolver;
-import javax.enterprise.context.ApplicationScoped;
-import javax.enterprise.inject.Produces;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.enterprise.inject.Produces;
/**
* A Producer for the {@link SchemaCatalogResolver} should you be running in a CDI container
diff --git a/json-schema-catalog/schema-service/src/main/java/uk/gov/justice/schema/service/SchemaCatalogService.java b/json-schema-catalog/schema-service/src/main/java/uk/gov/justice/schema/service/SchemaCatalogService.java
index bd9ae0b05..952c4638b 100644
--- a/json-schema-catalog/schema-service/src/main/java/uk/gov/justice/schema/service/SchemaCatalogService.java
+++ b/json-schema-catalog/schema-service/src/main/java/uk/gov/justice/schema/service/SchemaCatalogService.java
@@ -6,8 +6,8 @@
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
-import javax.enterprise.context.ApplicationScoped;
-import javax.inject.Inject;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
import org.everit.json.schema.Schema;
diff --git a/json-schema-catalog/schema-service/src/main/resources/META-INF/beans.xml b/json-schema-catalog/schema-service/src/main/resources/META-INF/beans.xml
index 794b8b5fb..b88648ba0 100644
--- a/json-schema-catalog/schema-service/src/main/resources/META-INF/beans.xml
+++ b/json-schema-catalog/schema-service/src/main/resources/META-INF/beans.xml
@@ -1,6 +1,6 @@
-
diff --git a/json-schema-catalog/schema-service/src/test/java/uk/gov/justice/schema/service/LoggerProducer.java b/json-schema-catalog/schema-service/src/test/java/uk/gov/justice/schema/service/LoggerProducer.java
index ce866cfdb..b5c494385 100644
--- a/json-schema-catalog/schema-service/src/test/java/uk/gov/justice/schema/service/LoggerProducer.java
+++ b/json-schema-catalog/schema-service/src/test/java/uk/gov/justice/schema/service/LoggerProducer.java
@@ -1,8 +1,8 @@
package uk.gov.justice.schema.service;
-import javax.enterprise.context.ApplicationScoped;
-import javax.enterprise.inject.Produces;
-import javax.enterprise.inject.spi.InjectionPoint;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.enterprise.inject.Produces;
+import jakarta.enterprise.inject.spi.InjectionPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
diff --git a/json-schema-catalog/schema-service/src/test/java/uk/gov/justice/schema/service/SchemaCatalogResolverIT.java b/json-schema-catalog/schema-service/src/test/java/uk/gov/justice/schema/service/SchemaCatalogResolverIT.java
index 3df1869a3..09676cab9 100644
--- a/json-schema-catalog/schema-service/src/test/java/uk/gov/justice/schema/service/SchemaCatalogResolverIT.java
+++ b/json-schema-catalog/schema-service/src/test/java/uk/gov/justice/schema/service/SchemaCatalogResolverIT.java
@@ -9,7 +9,7 @@
import java.io.InputStream;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import org.apache.openejb.jee.Application;
import org.apache.openejb.jee.WebApp;
diff --git a/json-schema-catalog/schema-service/src/test/java/uk/gov/justice/schema/service/SchemaServiceIT.java b/json-schema-catalog/schema-service/src/test/java/uk/gov/justice/schema/service/SchemaServiceIT.java
index 6c898213f..0a52fdd57 100644
--- a/json-schema-catalog/schema-service/src/test/java/uk/gov/justice/schema/service/SchemaServiceIT.java
+++ b/json-schema-catalog/schema-service/src/test/java/uk/gov/justice/schema/service/SchemaServiceIT.java
@@ -5,7 +5,7 @@
import java.util.Optional;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import org.apache.openejb.jee.Application;
import org.apache.openejb.jee.WebApp;
diff --git a/json-transformer/json-transformer-jolt/pom.xml b/json-transformer/json-transformer-jolt/pom.xml
index eece04cce..af69061e9 100644
--- a/json-transformer/json-transformer-jolt/pom.xml
+++ b/json-transformer/json-transformer-jolt/pom.xml
@@ -5,7 +5,7 @@
uk.gov.justice.jsonjson-transformer
- 17.105.0-M3-SNAPSHOT
+ 21.0.0-SNAPSHOT4.0.0
@@ -13,13 +13,13 @@
- javax
- javaee-api
+ jakarta.platform
+ jakarta.jakartaee-apiprovided
- javax.json
- javax.json-api
+ jakarta.json
+ jakarta.json-apiuk.gov.justice.utils
@@ -73,7 +73,7 @@
org.glassfish
- javax.json
+ jakarta.jsontest
diff --git a/json-transformer/json-transformer-jolt/src/main/java/uk/gov/justice/json/jolt/JoltTransformer.java b/json-transformer/json-transformer-jolt/src/main/java/uk/gov/justice/json/jolt/JoltTransformer.java
index 93af5ac91..5be1a1877 100644
--- a/json-transformer/json-transformer-jolt/src/main/java/uk/gov/justice/json/jolt/JoltTransformer.java
+++ b/json-transformer/json-transformer-jolt/src/main/java/uk/gov/justice/json/jolt/JoltTransformer.java
@@ -12,9 +12,9 @@
import java.util.List;
import java.util.Map;
-import javax.enterprise.context.ApplicationScoped;
-import javax.inject.Inject;
-import javax.json.JsonObject;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
+import jakarta.json.JsonObject;
import com.bazaarvoice.jolt.Chainr;
import com.bazaarvoice.jolt.JsonUtils;
diff --git a/json-transformer/json-transformer-jolt/src/main/resources/META-INF/beans.xml b/json-transformer/json-transformer-jolt/src/main/resources/META-INF/beans.xml
index a0aaf4421..ce634ddf2 100644
--- a/json-transformer/json-transformer-jolt/src/main/resources/META-INF/beans.xml
+++ b/json-transformer/json-transformer-jolt/src/main/resources/META-INF/beans.xml
@@ -1,8 +1,8 @@
+ https://jakarta.ee/xml/ns/jakartaee
+ https://jakarta.ee/xml/ns/jakartaee/beans_4_0.xsd" bean-discovery-mode="all">
diff --git a/json-transformer/json-transformer-jolt/src/test/java/uk/gov/justice/json/jolt/JoltTransformerTest.java b/json-transformer/json-transformer-jolt/src/test/java/uk/gov/justice/json/jolt/JoltTransformerTest.java
index 8abf278c9..dff8186c2 100644
--- a/json-transformer/json-transformer-jolt/src/test/java/uk/gov/justice/json/jolt/JoltTransformerTest.java
+++ b/json-transformer/json-transformer-jolt/src/test/java/uk/gov/justice/json/jolt/JoltTransformerTest.java
@@ -11,7 +11,7 @@
import java.io.FileNotFoundException;
-import javax.json.JsonObject;
+import jakarta.json.JsonObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import org.junit.jupiter.api.BeforeEach;
diff --git a/json-transformer/json-transformer-jolt/src/test/java/uk/gov/justice/json/jolt/JsonHelper.java b/json-transformer/json-transformer-jolt/src/test/java/uk/gov/justice/json/jolt/JsonHelper.java
index f2fd838be..68f178d78 100644
--- a/json-transformer/json-transformer-jolt/src/test/java/uk/gov/justice/json/jolt/JsonHelper.java
+++ b/json-transformer/json-transformer-jolt/src/test/java/uk/gov/justice/json/jolt/JsonHelper.java
@@ -6,8 +6,8 @@
import java.io.InputStream;
import java.io.UncheckedIOException;
-import javax.json.JsonObject;
-import javax.json.JsonReader;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonReader;
import static uk.gov.justice.services.messaging.JsonObjects.getJsonReaderFactory;
diff --git a/json-transformer/pom.xml b/json-transformer/pom.xml
index e35c1380b..a4ed6f1a9 100644
--- a/json-transformer/pom.xml
+++ b/json-transformer/pom.xml
@@ -7,7 +7,7 @@
uk.gov.justice.framework.librariesframework-libraries
- 17.105.0-M3-SNAPSHOT
+ 21.0.0-SNAPSHOTuk.gov.justice.json
@@ -27,7 +27,7 @@
org.apache.maven.pluginsmaven-surefire-plugin
- ${argLine} -Xmx64m
+ @{argLine} -Xmx64m -Dnet.bytebuddy.experimental=true
diff --git a/jsonschema-pojo-generator/example-pojo-schema/pom.xml b/jsonschema-pojo-generator/example-pojo-schema/pom.xml
index 5f4aa4558..18068d723 100644
--- a/jsonschema-pojo-generator/example-pojo-schema/pom.xml
+++ b/jsonschema-pojo-generator/example-pojo-schema/pom.xml
@@ -5,7 +5,7 @@
jsonschema-pojo-generatoruk.gov.justice.generators
- 17.105.0-M3-SNAPSHOT
+ 21.0.0-SNAPSHOT4.0.0
diff --git a/jsonschema-pojo-generator/example-schema-to-pojo-gen-test/pom.xml b/jsonschema-pojo-generator/example-schema-to-pojo-gen-test/pom.xml
index 6dc8c6f64..87dca9ca9 100644
--- a/jsonschema-pojo-generator/example-schema-to-pojo-gen-test/pom.xml
+++ b/jsonschema-pojo-generator/example-schema-to-pojo-gen-test/pom.xml
@@ -5,7 +5,7 @@
jsonschema-pojo-generatoruk.gov.justice.generators
- 17.105.0-M3-SNAPSHOT
+ 21.0.0-SNAPSHOT../pom.xml4.0.0
@@ -21,8 +21,8 @@
- javax.json
- javax.json-api
+ jakarta.json
+ jakarta.json-apiprovided
diff --git a/jsonschema-pojo-generator/pojo-event-annotation-plugin/pom.xml b/jsonschema-pojo-generator/pojo-event-annotation-plugin/pom.xml
index f54a6038f..061cb66dc 100644
--- a/jsonschema-pojo-generator/pojo-event-annotation-plugin/pom.xml
+++ b/jsonschema-pojo-generator/pojo-event-annotation-plugin/pom.xml
@@ -5,7 +5,7 @@
jsonschema-pojo-generatoruk.gov.justice.generators
- 17.105.0-M3-SNAPSHOT
+ 21.0.0-SNAPSHOT4.0.0
diff --git a/jsonschema-pojo-generator/pojo-generation-core/pom.xml b/jsonschema-pojo-generator/pojo-generation-core/pom.xml
index a064e03d6..9ae87ee9e 100644
--- a/jsonschema-pojo-generator/pojo-generation-core/pom.xml
+++ b/jsonschema-pojo-generator/pojo-generation-core/pom.xml
@@ -5,7 +5,7 @@
jsonschema-pojo-generatoruk.gov.justice.generators
- 17.105.0-M3-SNAPSHOT
+ 21.0.0-SNAPSHOT4.0.0
diff --git a/jsonschema-pojo-generator/pojo-generation-core/src/test/java/uk/gov/justice/generation/pojo/plugin/classmodifying/AddHashcodeAndEqualsPluginTest.java b/jsonschema-pojo-generator/pojo-generation-core/src/test/java/uk/gov/justice/generation/pojo/plugin/classmodifying/AddHashcodeAndEqualsPluginTest.java
index 9e20ae7ee..bdd915a82 100644
--- a/jsonschema-pojo-generator/pojo-generation-core/src/test/java/uk/gov/justice/generation/pojo/plugin/classmodifying/AddHashcodeAndEqualsPluginTest.java
+++ b/jsonschema-pojo-generator/pojo-generation-core/src/test/java/uk/gov/justice/generation/pojo/plugin/classmodifying/AddHashcodeAndEqualsPluginTest.java
@@ -59,7 +59,7 @@ public void shouldGenerateEqualsAndHashCodeMethodsUsingTheClassesFields() throws
final String expectedHashCodeMethod =
"@java.lang.Override\n" +
"public int hashCode() {\n " +
- "return java.util.Objects.hash(firstName, lastName, age);" +
+ "return java.util.Objects.hash(firstName, lastName, age);\n" +
"}\n";
final TypeSpec.Builder classBuilder = classBuilder("MyClass").addModifiers(PUBLIC);
@@ -123,7 +123,7 @@ public void shouldAddAdditionalPropertiesToHashcodeAndEquals() throws Exception
final String expectedHashCodeMethod =
"@java.lang.Override\n" +
"public int hashCode() {\n " +
- "return java.util.Objects.hash(firstName, lastName, age, additionalProperties);" +
+ "return java.util.Objects.hash(firstName, lastName, age, additionalProperties);\n" +
"}\n";
final TypeSpec.Builder classBuilder = classBuilder("MyClass").addModifiers(PUBLIC);
diff --git a/jsonschema-pojo-generator/pojo-generation-core/src/test/java/uk/gov/justice/generation/pojo/plugin/classmodifying/builder/BuilderMethodFactoryTest.java b/jsonschema-pojo-generator/pojo-generation-core/src/test/java/uk/gov/justice/generation/pojo/plugin/classmodifying/builder/BuilderMethodFactoryTest.java
index 4ed386ab4..5bbe536b9 100644
--- a/jsonschema-pojo-generator/pojo-generation-core/src/test/java/uk/gov/justice/generation/pojo/plugin/classmodifying/builder/BuilderMethodFactoryTest.java
+++ b/jsonschema-pojo-generator/pojo-generation-core/src/test/java/uk/gov/justice/generation/pojo/plugin/classmodifying/builder/BuilderMethodFactoryTest.java
@@ -203,9 +203,8 @@ public void shouldCreateMethodsWithAdditionalProperties() throws Exception {
final String expectedAdditionalPropertiesWithMethod =
- "public org.bloggs.fred.AlcubierreDrive.Builder withAdditionalProperty(" +
- "final java.lang.String name, " +
- "final java.lang.Object value) {\n " +
+ "public org.bloggs.fred.AlcubierreDrive.Builder withAdditionalProperty(final java.lang.String name,\n" +
+ " final java.lang.Object value) {\n " +
"additionalProperties.put(name, value);\n " +
"return this;\n" +
"}\n";
diff --git a/jsonschema-pojo-generator/pojo-generation-core/src/test/java/uk/gov/justice/generation/pojo/plugin/classmodifying/builder/WithMethodGeneratorTest.java b/jsonschema-pojo-generator/pojo-generation-core/src/test/java/uk/gov/justice/generation/pojo/plugin/classmodifying/builder/WithMethodGeneratorTest.java
index 019b7aca0..168681647 100644
--- a/jsonschema-pojo-generator/pojo-generation-core/src/test/java/uk/gov/justice/generation/pojo/plugin/classmodifying/builder/WithMethodGeneratorTest.java
+++ b/jsonschema-pojo-generator/pojo-generation-core/src/test/java/uk/gov/justice/generation/pojo/plugin/classmodifying/builder/WithMethodGeneratorTest.java
@@ -109,11 +109,13 @@ public void shouldOverloadTheWithMethodIfTheFieldIsOptional() throws Exception {
"}\n";
final String overloadedBuildMethod =
- "public org.bloggs.fred.AlcubierreDriveBuilder withFirstName(final java.util.Optional firstName) {" +
- "\n if (firstName != null) {\n" +
+ "public org.bloggs.fred.AlcubierreDriveBuilder withFirstName(\n" +
+ " final java.util.Optional firstName) {\n" +
+ " if (firstName != null) {\n" +
" this.firstName = firstName.orElse(null);\n" +
" }\n" +
- " return this;\n}\n";
+ " return this;\n" +
+ "}\n";
assertThat(methodSpecs.get(0).toString(), is(expectedWithMethod));
assertThat(methodSpecs.get(1).toString(), is(overloadedBuildMethod));
@@ -162,7 +164,8 @@ public void shouldGenerateTheBuildersWithValuesFromMethod() throws Exception {
pluginContext);
final String expectedGeneratedMethod =
- "public org.bloggs.fred.AlcubierreDriveBuilder withValuesFrom(final org.bloggs.fred.AlcubierreDrive alcubierreDrive) {\n" +
+ "public org.bloggs.fred.AlcubierreDriveBuilder withValuesFrom(\n" +
+ " final org.bloggs.fred.AlcubierreDrive alcubierreDrive) {\n" +
" this.name = alcubierreDrive.getName();\n" +
" this.age = alcubierreDrive.getAge();\n" +
" this.haircut = alcubierreDrive.getHaircut();\n" +
diff --git a/jsonschema-pojo-generator/pojo-generation-plugin/pom.xml b/jsonschema-pojo-generator/pojo-generation-plugin/pom.xml
index ea4871c91..3606fe06c 100644
--- a/jsonschema-pojo-generator/pojo-generation-plugin/pom.xml
+++ b/jsonschema-pojo-generator/pojo-generation-plugin/pom.xml
@@ -5,7 +5,7 @@
jsonschema-pojo-generatoruk.gov.justice.generators
- 17.105.0-M3-SNAPSHOT
+ 21.0.0-SNAPSHOT4.0.0
@@ -49,6 +49,16 @@
+
+ org.apache.maven
+ maven-plugin-api
+ provided
+
+
+ org.apache.maven
+ maven-core
+ provided
+
diff --git a/jsonschema-pojo-generator/pojo-integration-test/pom.xml b/jsonschema-pojo-generator/pojo-integration-test/pom.xml
index 2070914ca..a66bdde43 100644
--- a/jsonschema-pojo-generator/pojo-integration-test/pom.xml
+++ b/jsonschema-pojo-generator/pojo-integration-test/pom.xml
@@ -5,7 +5,7 @@
jsonschema-pojo-generatoruk.gov.justice.generators
- 17.105.0-M3-SNAPSHOT
+ 21.0.0-SNAPSHOT4.0.0
@@ -16,7 +16,7 @@
org.glassfish
- javax.json
+ jakarta.jsonorg.junit.jupiter
diff --git a/jsonschema-pojo-generator/pojo-plugin-it/pom.xml b/jsonschema-pojo-generator/pojo-plugin-it/pom.xml
index b88efcf0f..b8210eb0f 100644
--- a/jsonschema-pojo-generator/pojo-plugin-it/pom.xml
+++ b/jsonschema-pojo-generator/pojo-plugin-it/pom.xml
@@ -5,7 +5,7 @@
jsonschema-pojo-generatoruk.gov.justice.generators
- 17.105.0-M3-SNAPSHOT
+ 21.0.0-SNAPSHOT4.0.0
@@ -25,7 +25,7 @@
org.glassfish
- javax.json
+ jakarta.jsontest
diff --git a/jsonschema-pojo-generator/pom.xml b/jsonschema-pojo-generator/pom.xml
index 7b92b45ce..d3bc3cc7e 100644
--- a/jsonschema-pojo-generator/pom.xml
+++ b/jsonschema-pojo-generator/pom.xml
@@ -7,7 +7,7 @@
uk.gov.justice.framework.librariesframework-libraries
- 17.105.0-M3-SNAPSHOT
+ 21.0.0-SNAPSHOTuk.gov.justice.generators
@@ -34,7 +34,7 @@
org.apache.maven.pluginsmaven-surefire-plugin
- ${argLine} -Xmx64m
+ @{argLine} -Xmx64m -Dnet.bytebuddy.experimental=true
diff --git a/jsonschema-pojo-generator/standard-test-catalog/pom.xml b/jsonschema-pojo-generator/standard-test-catalog/pom.xml
index 798848a1c..e540f5b34 100644
--- a/jsonschema-pojo-generator/standard-test-catalog/pom.xml
+++ b/jsonschema-pojo-generator/standard-test-catalog/pom.xml
@@ -5,7 +5,7 @@
jsonschema-pojo-generatoruk.gov.justice.generators
- 17.105.0-M3-SNAPSHOT
+ 21.0.0-SNAPSHOT4.0.0
diff --git a/pom.xml b/pom.xml
index 648b58773..76fbb6366 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,13 +6,13 @@
uk.gov.justicemaven-framework-parent-pom
- 17.103.0
+ 21.0.0-SNAPSHOT4.0.0uk.gov.justice.framework.librariesframework-libraries
- 17.105.0-M3-SNAPSHOT
+ 21.0.0-SNAPSHOTpomFramework Common Libraries
@@ -21,7 +21,6 @@
annotation-validatordomain-test-dsl
- embedded-artemisframework-apiframework-libraries-bomframework-utilities
@@ -37,16 +36,13 @@
framework-libraries
- 17.104.0
+ 21.0.0-SNAPSHOT2.0.0
- 17.0.1
- 17.104.0
-
-
- 8.0.13
+ 21.0.0-SNAPSHOT
+ 21.0.0-SNAPSHOT
- 3.7.1
+ 3.9.0
@@ -92,6 +88,14 @@
+
+
+ org.mockito
+ mockito-junit-jupiter
+ test
+
+
+
${cpp.scm.connection}${cpp.scm.developerConnection}
@@ -109,8 +113,8 @@
- javax.xml.bind
- jaxb-api
+ jakarta.xml.bind
+ jakarta.xml.bind-api${jaxb.version}
diff --git a/raml-maven/lint-checker-core/pom.xml b/raml-maven/lint-checker-core/pom.xml
index 3021ebb1f..207e31bb5 100644
--- a/raml-maven/lint-checker-core/pom.xml
+++ b/raml-maven/lint-checker-core/pom.xml
@@ -5,7 +5,7 @@
raml-mavenuk.gov.justice.maven
- 17.105.0-M3-SNAPSHOT
+ 21.0.0-SNAPSHOT4.0.0
diff --git a/raml-maven/pom.xml b/raml-maven/pom.xml
index 00c0e0b3b..b20dd878d 100644
--- a/raml-maven/pom.xml
+++ b/raml-maven/pom.xml
@@ -5,7 +5,7 @@
uk.gov.justice.framework.librariesframework-libraries
- 17.105.0-M3-SNAPSHOT
+ 21.0.0-SNAPSHOTuk.gov.justice.maven
diff --git a/raml-maven/raml-for-testing-io/pom.xml b/raml-maven/raml-for-testing-io/pom.xml
index a3300d5a1..f4bbe2c01 100644
--- a/raml-maven/raml-for-testing-io/pom.xml
+++ b/raml-maven/raml-for-testing-io/pom.xml
@@ -5,7 +5,7 @@
raml-mavenuk.gov.justice.maven
- 17.105.0-M3-SNAPSHOT
+ 21.0.0-SNAPSHOT4.0.0
diff --git a/raml-maven/raml-generator-core/pom.xml b/raml-maven/raml-generator-core/pom.xml
index 587bdaf87..f72581c11 100644
--- a/raml-maven/raml-generator-core/pom.xml
+++ b/raml-maven/raml-generator-core/pom.xml
@@ -5,7 +5,7 @@
raml-mavenuk.gov.justice.maven
- 17.105.0-M3-SNAPSHOT
+ 21.0.0-SNAPSHOT4.0.0
@@ -27,5 +27,9 @@
+
+ javax.xml.bind
+ jaxb-api
+
diff --git a/raml-maven/raml-generator-for-testing/pom.xml b/raml-maven/raml-generator-for-testing/pom.xml
index 2585d56a6..ba9cebabd 100644
--- a/raml-maven/raml-generator-for-testing/pom.xml
+++ b/raml-maven/raml-generator-for-testing/pom.xml
@@ -5,7 +5,7 @@
raml-mavenuk.gov.justice.maven
- 17.105.0-M3-SNAPSHOT
+ 21.0.0-SNAPSHOT4.0.0
diff --git a/raml-maven/raml-maven-io-utils/pom.xml b/raml-maven/raml-maven-io-utils/pom.xml
index fb403597a..460fa40ac 100644
--- a/raml-maven/raml-maven-io-utils/pom.xml
+++ b/raml-maven/raml-maven-io-utils/pom.xml
@@ -5,7 +5,7 @@
raml-mavenuk.gov.justice.maven
- 17.105.0-M3-SNAPSHOT
+ 21.0.0-SNAPSHOT4.0.0
diff --git a/raml-maven/raml-maven-plugin-it/pom.xml b/raml-maven/raml-maven-plugin-it/pom.xml
index f237ec620..961e9e325 100644
--- a/raml-maven/raml-maven-plugin-it/pom.xml
+++ b/raml-maven/raml-maven-plugin-it/pom.xml
@@ -6,7 +6,7 @@
uk.gov.justice.mavenraml-maven
- 17.105.0-M3-SNAPSHOT
+ 21.0.0-SNAPSHOT4.0.0
@@ -109,8 +109,8 @@
${project.version}
- javax.xml.bind
- jaxb-api
+ jakarta.xml.bind
+ jakarta.xml.bind-api${jaxb.version}
diff --git a/raml-maven/raml-maven-plugin/pom.xml b/raml-maven/raml-maven-plugin/pom.xml
index 2f5a450e3..61b193acb 100644
--- a/raml-maven/raml-maven-plugin/pom.xml
+++ b/raml-maven/raml-maven-plugin/pom.xml
@@ -6,7 +6,7 @@
uk.gov.justice.mavenraml-maven
- 17.105.0-M3-SNAPSHOT
+ 21.0.0-SNAPSHOTraml-maven-plugin
@@ -67,21 +67,25 @@
org.apache.mavenmaven-plugin-api
+ providedorg.apache.mavenmaven-compat
+ providedorg.apache.mavenmaven-core
+ providedorg.apache.mavenmaven-model
+ provided
@@ -138,7 +142,7 @@
org.glassfish
- javax.json
+ jakarta.jsontest
@@ -159,8 +163,8 @@
test
- javax.xml.bind
- jaxb-api
+ jakarta.xml.bind
+ jakarta.xml.bind-apitest
diff --git a/raml-maven/raml-parser/pom.xml b/raml-maven/raml-parser/pom.xml
index b1e63812f..b75522932 100644
--- a/raml-maven/raml-parser/pom.xml
+++ b/raml-maven/raml-parser/pom.xml
@@ -7,7 +7,7 @@
raml-mavenuk.gov.justice.maven
- 17.105.0-M3-SNAPSHOT
+ 21.0.0-SNAPSHOT
@@ -34,6 +34,10 @@
javax.xml.bindjaxb-api
+
+
+ jakarta.xml.bind
+ jakarta.xml.bind-apitest
diff --git a/security-updates.md b/security-updates.md
new file mode 100644
index 000000000..6a0d4bd84
--- /dev/null
+++ b/security-updates.md
@@ -0,0 +1,52 @@
+# Framework Libraries — Security Updates (Java 21 / Jakarta EE 10 Baseline)
+
+The Java 21 / Jakarta EE 10 release (`21.0.0-SNAPSHOT`) does not itself introduce new CVE-tagged fixes, but it is built on the full `17.x` release history. The security vulnerabilities below were all resolved in the `17.x` series and are included in the Java 21 baseline. The `21.0.0-SNAPSHOT` upgrade also transitively eliminates the javax namespace surface area (replacing it with Jakarta EE 10 APIs), which removes entire categories of vulnerability exposure in the older `javax.*` ecosystem.
+
+---
+
+## CVE-Referenced Security Fixes Included in the Java 21 Baseline
+
+### Fixed in [17.104.0] — 2025-12-16
+
+| CVE | Component | Fixed Version | Detail |
+|-----|-----------|---------------|--------|
+| CVE-2025-48734 | `commons-beanutils` | 1.11.0 | https://cwe.mitre.org/data/definitions/284.html |
+| CVE-2023-0482 | `resteasy` | 3.15.5.Final | https://cwe.mitre.org/data/definitions/378.html |
+| CVE-2021-47621 | `classgraph` | 4.8.112 | https://cwe.mitre.org/data/definitions/611.html |
+| CVE-2025-48924 | `commons-lang` | 3.18.0 | https://cwe.mitre.org/data/definitions/674.html |
+
+### Fixed in [17.101.2] — 2025-01-09
+
+| CVE | Component | Fixed Version | Detail |
+|-----|-----------|---------------|--------|
+| CWE-787 | `com.jayway.json-path` | 2.9.0 | https://cwe.mitre.org/data/definitions/787.html |
+| CVE-2024-47554 | `commons-io` | 2.18.0 | https://nvd.nist.gov/vuln/detail/CVE-2024-47554 |
+
+### Fixed in [17.0.2] — 2023-06-14
+
+| CVE | Component | Fixed Version | Detail |
+|-----|-----------|---------------|--------|
+| CVE-2022-45688 | `org.json` | 20230227 | https://nvd.nist.gov/vuln/detail/CVE-2022-45688 |
+
+---
+
+## Additional Security Updates (No CVE Assigned)
+
+These versions applied security-related dependency upgrades that were not associated with a specific CVE number in the changelog.
+
+| Version | Components Updated |
+|---------|--------------------|
+| [17.2.0] — 2023-11-03 | `org.json`, `plexus-codehaus`, `apache-tika`, `google-guava` (via `maven-common-bom 17.2.0`) |
+| [7.1.3] — 2020-10-14 | `apache-tika`, `commons-beanutils`, `commons-guava`, `junit` (via `maven-common-bom 7.1.1`) |
+
+---
+
+## Jakarta EE 10 Namespace Migration (21.0.0-SNAPSHOT)
+
+The `21.0.0-SNAPSHOT` upgrade replaces all `javax.*` APIs with their `jakarta.*` equivalents. While not assigned individual CVEs, this migration removes exposure to vulnerabilities in the unmaintained `javax` namespace and aligns the platform with actively maintained Jakarta EE 10 security patches.
+
+Key changes in `21.0.0-SNAPSHOT`:
+
+- Migrated `javax.jms.*` → `jakarta.jms.*`
+- Replaced `javax.xml.bind:jaxb-api` → `jakarta.xml.bind:jakarta.xml.bind-api`
+- Upgraded OpenEJB `8.0.13` → `10.0.0` (Jakarta EE 10 compatible)