io.projectreactor:reactor-test:[3.4.22]
+ com.fasterxml.jackson.dataformat:jackson-dataformat-xml:[2.13.3]
org.junit.jupiter:junit-jupiter-api:[5.8.2]
org.junit.jupiter:junit-jupiter-params:[5.8.2]
diff --git a/sdk/core/azure-core-test/src/main/java/module-info.java b/sdk/core/azure-core-test/src/main/java/module-info.java
index 340413c01205b..93f9d3b5a94d9 100644
--- a/sdk/core/azure-core-test/src/main/java/module-info.java
+++ b/sdk/core/azure-core-test/src/main/java/module-info.java
@@ -4,6 +4,7 @@
module com.azure.core.test {
requires transitive com.azure.core;
+ requires com.fasterxml.jackson.dataformat.xml;
requires org.junit.jupiter.api;
requires org.junit.jupiter.params;
requires reactor.test;
diff --git a/sdk/core/azure-core/src/main/java/com/azure/core/implementation/jackson/JacksonVersion.java b/sdk/core/azure-core/src/main/java/com/azure/core/implementation/jackson/JacksonVersion.java
index d28acffc23829..bbba42beaf787 100644
--- a/sdk/core/azure-core/src/main/java/com/azure/core/implementation/jackson/JacksonVersion.java
+++ b/sdk/core/azure-core/src/main/java/com/azure/core/implementation/jackson/JacksonVersion.java
@@ -39,14 +39,19 @@ final class JacksonVersion {
private final String helpString;
private JacksonVersion() {
- coreVersion = SemanticVersion.parse(
- new com.fasterxml.jackson.core.json.PackageVersion().version().toString());
- databindVersion = SemanticVersion.parse(
- new com.fasterxml.jackson.databind.cfg.PackageVersion().version().toString());
- xmlVersion = SemanticVersion.parse(
- new com.fasterxml.jackson.dataformat.xml.PackageVersion().version().toString());
- jsr310Version = SemanticVersion.parse(
- new com.fasterxml.jackson.datatype.jsr310.PackageVersion().version().toString());
+ coreVersion = SemanticVersion.parse(com.fasterxml.jackson.core.json.PackageVersion.VERSION.toString());
+ databindVersion = SemanticVersion.parse(com.fasterxml.jackson.databind.cfg.PackageVersion.VERSION.toString());
+ jsr310Version = SemanticVersion.parse(com.fasterxml.jackson.datatype.jsr310.PackageVersion.VERSION.toString());
+
+ SemanticVersion xmlVersion1;
+ try {
+ Class> xmlPackageVersion = Class.forName("com.fasterxml.jackson.dataformat.xml.PackageVersion");
+ xmlVersion1 = SemanticVersion.parse(xmlPackageVersion.getDeclaredField("VERSION").get(null).toString());
+ } catch (ReflectiveOperationException e) {
+ xmlVersion1 = SemanticVersion.createInvalid();
+ }
+ xmlVersion = xmlVersion1;
+
checkVersion(coreVersion, CORE_PACKAGE_NAME);
checkVersion(databindVersion, DATABIND_PACKAGE_NAME);
checkVersion(xmlVersion, XML_PACKAGE_NAME);
diff --git a/sdk/core/azure-core/src/main/java/com/azure/core/implementation/jackson/ObjectMapperFactory.java b/sdk/core/azure-core/src/main/java/com/azure/core/implementation/jackson/ObjectMapperFactory.java
index f44bc269f5e26..22a523238801c 100644
--- a/sdk/core/azure-core/src/main/java/com/azure/core/implementation/jackson/ObjectMapperFactory.java
+++ b/sdk/core/azure-core/src/main/java/com/azure/core/implementation/jackson/ObjectMapperFactory.java
@@ -13,15 +13,8 @@
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.cfg.MapperBuilder;
import com.fasterxml.jackson.databind.json.JsonMapper;
-import com.fasterxml.jackson.dataformat.xml.XmlMapper;
-import com.fasterxml.jackson.dataformat.xml.deser.FromXmlParser;
-import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.MethodType;
-
/**
* Constructs and configures {@link ObjectMapper} instances.
*/
@@ -29,48 +22,8 @@ final class ObjectMapperFactory {
// ObjectMapperFactory is a commonly used factory, use a static logger.
private static final ClientLogger LOGGER = new ClientLogger(ObjectMapperFactory.class);
- private static final String MUTABLE_COERCION_CONFIG = "com.fasterxml.jackson.databind.cfg.MutableCoercionConfig";
- private static final String COERCION_INPUT_SHAPE = "com.fasterxml.jackson.databind.cfg.CoercionInputShape";
- private static final String COERCION_ACTION = "com.fasterxml.jackson.databind.cfg.CoercionAction";
-
- private MethodHandle coercionConfigDefaults;
- private MethodHandle setCoercion;
- private Object coercionInputShapeEmptyString;
- private Object coercionActionAsNull;
- private boolean useReflectionToSetCoercion;
-
public static final ObjectMapperFactory INSTANCE = new ObjectMapperFactory();
- private ObjectMapperFactory() {
- MethodHandles.Lookup publicLookup = MethodHandles.publicLookup();
-
- try {
- Class> mutableCoercionConfig = Class.forName(MUTABLE_COERCION_CONFIG);
- Class> coercionInputShapeClass = Class.forName(COERCION_INPUT_SHAPE);
- Class> coercionActionClass = Class.forName(COERCION_ACTION);
-
- coercionConfigDefaults = publicLookup.findVirtual(ObjectMapper.class, "coercionConfigDefaults",
- MethodType.methodType(mutableCoercionConfig));
- setCoercion = publicLookup.findVirtual(mutableCoercionConfig, "setCoercion",
- MethodType.methodType(mutableCoercionConfig, coercionInputShapeClass, coercionActionClass));
- coercionInputShapeEmptyString = publicLookup.findStaticGetter(coercionInputShapeClass, "EmptyString",
- coercionInputShapeClass).invoke();
- coercionActionAsNull = publicLookup.findStaticGetter(coercionActionClass, "AsNull", coercionActionClass)
- .invoke();
- useReflectionToSetCoercion = true;
- } catch (Throwable ex) {
- // Throw the Error only if it isn't a LinkageError.
- // This initialization is attempting to use classes that may not exist.
- if (ex instanceof Error && !(ex instanceof LinkageError)) {
- throw (Error) ex;
- }
-
- LOGGER.verbose("Failed to retrieve MethodHandles used to set coercion configurations. "
- + "Setting coercion configurations will be skipped. "
- + "Please update your Jackson dependencies to at least version 2.12", ex);
- }
- }
-
public ObjectMapper createJsonMapper(ObjectMapper innerMapper) {
ObjectMapper flatteningMapper = initializeMapperBuilder(JsonMapper.builder())
.addModule(FlatteningSerializer.getModule(innerMapper))
@@ -87,32 +40,7 @@ public ObjectMapper createJsonMapper(ObjectMapper innerMapper) {
}
public ObjectMapper createXmlMapper() {
- ObjectMapper xmlMapper = initializeMapperBuilder(XmlMapper.builder())
- .defaultUseWrapper(false)
- .enable(ToXmlGenerator.Feature.WRITE_XML_DECLARATION)
- /*
- * In Jackson 2.12 the default value of this feature changed from true to false.
- * https://github.com/FasterXML/jackson/wiki/Jackson-Release-2.12#xml-module
- */
- .enable(FromXmlParser.Feature.EMPTY_ELEMENT_AS_NULL)
- .build();
-
- if (useReflectionToSetCoercion) {
- try {
- Object object = coercionConfigDefaults.invoke(xmlMapper);
- setCoercion.invoke(object, coercionInputShapeEmptyString, coercionActionAsNull);
- } catch (Throwable e) {
- if (e instanceof Error) {
- throw (Error) e;
- }
-
- LOGGER.verbose("Failed to set coercion actions.", e);
- }
- } else {
- LOGGER.verbose("Didn't set coercion defaults as it wasn't found on the classpath.");
- }
-
- return xmlMapper;
+ return XmlMapperFactory.INSTANCE.createXmlMapper();
}
public ObjectMapper createSimpleMapper() {
@@ -134,7 +62,7 @@ public ObjectMapper createHeaderMapper() {
}
@SuppressWarnings("deprecation")
- private > S initializeMapperBuilder(S mapper) {
+ static > S initializeMapperBuilder(S mapper) {
mapper.enable(SerializationFeature.WRITE_EMPTY_JSON_ARRAYS)
.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)
.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
diff --git a/sdk/core/azure-core/src/main/java/com/azure/core/implementation/jackson/XmlMapperFactory.java b/sdk/core/azure-core/src/main/java/com/azure/core/implementation/jackson/XmlMapperFactory.java
new file mode 100644
index 0000000000000..d67e2bf075bee
--- /dev/null
+++ b/sdk/core/azure-core/src/main/java/com/azure/core/implementation/jackson/XmlMapperFactory.java
@@ -0,0 +1,89 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.azure.core.implementation.jackson;
+
+import com.azure.core.util.logging.ClientLogger;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.xml.XmlMapper;
+import com.fasterxml.jackson.dataformat.xml.deser.FromXmlParser;
+import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+public final class XmlMapperFactory {
+ private static final ClientLogger LOGGER = new ClientLogger(XmlMapperFactory.class);
+
+ private static final String MUTABLE_COERCION_CONFIG = "com.fasterxml.jackson.databind.cfg.MutableCoercionConfig";
+ private static final String COERCION_INPUT_SHAPE = "com.fasterxml.jackson.databind.cfg.CoercionInputShape";
+ private static final String COERCION_ACTION = "com.fasterxml.jackson.databind.cfg.CoercionAction";
+
+ private MethodHandle coercionConfigDefaults;
+ private MethodHandle setCoercion;
+ private Object coercionInputShapeEmptyString;
+ private Object coercionActionAsNull;
+ private boolean useReflectionToSetCoercion;
+
+ public static final XmlMapperFactory INSTANCE = new XmlMapperFactory();
+
+ private XmlMapperFactory() {
+ MethodHandles.Lookup publicLookup = MethodHandles.publicLookup();
+
+ try {
+ Class> mutableCoercionConfig = Class.forName(MUTABLE_COERCION_CONFIG);
+ Class> coercionInputShapeClass = Class.forName(COERCION_INPUT_SHAPE);
+ Class> coercionActionClass = Class.forName(COERCION_ACTION);
+
+ coercionConfigDefaults = publicLookup.findVirtual(ObjectMapper.class, "coercionConfigDefaults",
+ MethodType.methodType(mutableCoercionConfig));
+ setCoercion = publicLookup.findVirtual(mutableCoercionConfig, "setCoercion",
+ MethodType.methodType(mutableCoercionConfig, coercionInputShapeClass, coercionActionClass));
+ coercionInputShapeEmptyString = publicLookup.findStaticGetter(coercionInputShapeClass, "EmptyString",
+ coercionInputShapeClass).invoke();
+ coercionActionAsNull = publicLookup.findStaticGetter(coercionActionClass, "AsNull", coercionActionClass)
+ .invoke();
+ useReflectionToSetCoercion = true;
+ } catch (Throwable ex) {
+ // Throw the Error only if it isn't a LinkageError.
+ // This initialization is attempting to use classes that may not exist.
+ if (ex instanceof Error && !(ex instanceof LinkageError)) {
+ throw (Error) ex;
+ }
+
+ LOGGER.verbose("Failed to retrieve MethodHandles used to set coercion configurations. "
+ + "Setting coercion configurations will be skipped. "
+ + "Please update your Jackson dependencies to at least version 2.12", ex);
+ }
+ }
+
+ public ObjectMapper createXmlMapper() {
+ ObjectMapper xmlMapper = ObjectMapperFactory.initializeMapperBuilder(XmlMapper.builder())
+ .defaultUseWrapper(false)
+ .enable(ToXmlGenerator.Feature.WRITE_XML_DECLARATION)
+ /*
+ * In Jackson 2.12 the default value of this feature changed from true to false.
+ * https://github.com/FasterXML/jackson/wiki/Jackson-Release-2.12#xml-module
+ */
+ .enable(FromXmlParser.Feature.EMPTY_ELEMENT_AS_NULL)
+ .build();
+
+ if (useReflectionToSetCoercion) {
+ try {
+ Object object = coercionConfigDefaults.invoke(xmlMapper);
+ setCoercion.invoke(object, coercionInputShapeEmptyString, coercionActionAsNull);
+ } catch (Throwable e) {
+ if (e instanceof Error) {
+ throw (Error) e;
+ }
+
+ LOGGER.verbose("Failed to set coercion actions.", e);
+ }
+ } else {
+ LOGGER.verbose("Didn't set coercion defaults as it wasn't found on the classpath.");
+ }
+
+ return xmlMapper;
+ }
+}
diff --git a/sdk/core/azure-core/src/main/java/com/azure/core/util/serializer/JacksonAdapter.java b/sdk/core/azure-core/src/main/java/com/azure/core/util/serializer/JacksonAdapter.java
index a6180a1c555ff..8558fd3ab16d9 100644
--- a/sdk/core/azure-core/src/main/java/com/azure/core/util/serializer/JacksonAdapter.java
+++ b/sdk/core/azure-core/src/main/java/com/azure/core/util/serializer/JacksonAdapter.java
@@ -41,11 +41,10 @@ public class JacksonAdapter implements SerializerAdapter {
* An instance of {@link ObjectMapperShim} to serialize/deserialize objects.
*/
private final ObjectMapperShim mapper;
-
- private final ObjectMapperShim xmlMapper;
-
private final ObjectMapperShim headerMapper;
+ private volatile ObjectMapperShim xmlMapper;
+
/**
* Raw mappers are needed only to support deprecated simpleMapper() and serializer().
*/
@@ -63,11 +62,11 @@ public JacksonAdapter() {
/**
* Creates a new JacksonAdapter instance with Azure Core mapper settings and applies additional configuration
* through {@code configureSerialization} callback.
- *
+ *
* {@code configureSerialization} callback provides outer and inner instances of {@link ObjectMapper}. Both of them
* are pre-configured for Azure serialization needs, but only outer mapper capable of flattening and populating
* additionalProperties. Outer mapper is used by {@code JacksonAdapter} for all serialization needs.
- *
+ *
* Register modules on the outer instance to add custom (de)serializers similar to {@code new JacksonAdapter((outer,
* inner) -> outer.registerModule(new MyModule()))}
*
@@ -79,7 +78,6 @@ public JacksonAdapter() {
public JacksonAdapter(BiConsumer configureSerialization) {
Objects.requireNonNull(configureSerialization, "'configureSerialization' cannot be null.");
this.headerMapper = ObjectMapperShim.createHeaderMapper();
- this.xmlMapper = ObjectMapperShim.createXmlMapper();
this.mapper = ObjectMapperShim.createJsonMapper(ObjectMapperShim.createSimpleMapper(),
(outerMapper, innerMapper) -> captureRawMappersAndConfigure(outerMapper, innerMapper, configureSerialization));
}
@@ -140,7 +138,7 @@ public String serialize(Object object, SerializerEncoding encoding) throws IOExc
}
return (String) useAccessHelper(() -> (encoding == SerializerEncoding.XML)
- ? xmlMapper.writeValueAsString(object)
+ ? getXmlMapper().writeValueAsString(object)
: mapper.writeValueAsString(object));
}
@@ -151,7 +149,7 @@ public byte[] serializeToBytes(Object object, SerializerEncoding encoding) throw
}
return (byte[]) useAccessHelper(() -> (encoding == SerializerEncoding.XML)
- ? xmlMapper.writeValueAsBytes(object)
+ ? getXmlMapper().writeValueAsBytes(object)
: mapper.writeValueAsBytes(object));
}
@@ -163,7 +161,7 @@ public void serialize(Object object, SerializerEncoding encoding, OutputStream o
useAccessHelper(() -> {
if (encoding == SerializerEncoding.XML) {
- xmlMapper.writeValue(outputStream, object);
+ getXmlMapper().writeValue(outputStream, object);
} else {
mapper.writeValue(outputStream, object);
}
@@ -209,7 +207,7 @@ public T deserialize(String value, Type type, SerializerEncoding encoding) t
}
return (T) useAccessHelper(() -> (encoding == SerializerEncoding.XML)
- ? xmlMapper.readValue(value, type)
+ ? getXmlMapper().readValue(value, type)
: mapper.readValue(value, type));
}
@@ -221,7 +219,7 @@ public T deserialize(byte[] bytes, Type type, SerializerEncoding encoding) t
}
return (T) useAccessHelper(() -> (encoding == SerializerEncoding.XML)
- ? xmlMapper.readValue(bytes, type)
+ ? getXmlMapper().readValue(bytes, type)
: mapper.readValue(bytes, type));
}
@@ -234,7 +232,7 @@ public T deserialize(InputStream inputStream, final Type type, SerializerEnc
}
return (T) useAccessHelper(() -> (encoding == SerializerEncoding.XML)
- ? xmlMapper.readValue(inputStream, type)
+ ? getXmlMapper().readValue(inputStream, type)
: mapper.readValue(inputStream, type));
}
@@ -250,6 +248,18 @@ public T deserializeHeader(Header header, Type type) throws IOException {
return (T) useAccessHelper(() -> headerMapper.readValue(header.getValue(), type));
}
+ private ObjectMapperShim getXmlMapper() {
+ if (xmlMapper == null) {
+ synchronized (mapper) {
+ if (xmlMapper == null) {
+ xmlMapper = ObjectMapperShim.createXmlMapper();
+ }
+ }
+ }
+
+ return xmlMapper;
+ }
+
@SuppressWarnings("removal")
private static Object useAccessHelper(IOExceptionCallable serializationCall) throws IOException {
if (useAccessHelper) {
diff --git a/sdk/core/azure-core/src/main/java/module-info.java b/sdk/core/azure-core/src/main/java/module-info.java
index 5d5ebcfeb9430..5c45b405ad37f 100644
--- a/sdk/core/azure-core/src/main/java/module-info.java
+++ b/sdk/core/azure-core/src/main/java/module-info.java
@@ -10,7 +10,7 @@
requires transitive com.fasterxml.jackson.core;
requires transitive com.fasterxml.jackson.databind;
- requires transitive com.fasterxml.jackson.dataformat.xml;
+ requires com.fasterxml.jackson.dataformat.xml;
requires transitive com.fasterxml.jackson.datatype.jsr310;
// public API surface area
diff --git a/sdk/parents/azure-client-sdk-parent/pom.xml b/sdk/parents/azure-client-sdk-parent/pom.xml
index 1a0d772d1fcc2..92f21e8086f5b 100644
--- a/sdk/parents/azure-client-sdk-parent/pom.xml
+++ b/sdk/parents/azure-client-sdk-parent/pom.xml
@@ -1100,24 +1100,6 @@
-
-
-
-
- base-modules-testCompile
-
- testCompile
-
-
- 11
- 11
-
- module-info.java
-
-
-
-
base-testCompile
@@ -1570,5 +1552,43 @@
+
+
+ test-module-base-compile
+
+
+ ${build.testSourceDirectory}/module-info.java
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.10.1
+
+
+
+
+
+ base-modules-testCompile
+
+ testCompile
+
+
+ 11
+ 11
+
+ module-info.java
+
+
+
+
+
+
+
+
diff --git a/sdk/servicebus/azure-messaging-servicebus/pom.xml b/sdk/servicebus/azure-messaging-servicebus/pom.xml
index 779905b1aafed..dfe45b86cce06 100644
--- a/sdk/servicebus/azure-messaging-servicebus/pom.xml
+++ b/sdk/servicebus/azure-messaging-servicebus/pom.xml
@@ -63,6 +63,12 @@
1.12.5