This is an automated email from the ASF dual-hosted git repository.

lhotari pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar.git


The following commit(s) were added to refs/heads/master by this push:
     new 334c3a541f0 [improve][misc] Follow up on ObjectMapper sharing changes 
(#19228)
334c3a541f0 is described below

commit 334c3a541f00c08aa2f378a6cc45f37cbeb54a5c
Author: Lari Hotari <[email protected]>
AuthorDate: Mon Jan 16 12:03:06 2023 +0200

    [improve][misc] Follow up on ObjectMapper sharing changes (#19228)
---
 .../pulsar/tests/SingletonCleanerListener.java     | 52 +++++++++++++---------
 .../pulsar/client/impl/schema/JSONSchema.java      | 29 ++++++++++--
 .../pulsar/common/util/ObjectMapperFactory.java    | 18 ++++----
 3 files changed, 65 insertions(+), 34 deletions(-)

diff --git 
a/buildtools/src/main/java/org/apache/pulsar/tests/SingletonCleanerListener.java
 
b/buildtools/src/main/java/org/apache/pulsar/tests/SingletonCleanerListener.java
index 257769a8d5b..532361d2bdb 100644
--- 
a/buildtools/src/main/java/org/apache/pulsar/tests/SingletonCleanerListener.java
+++ 
b/buildtools/src/main/java/org/apache/pulsar/tests/SingletonCleanerListener.java
@@ -30,8 +30,8 @@ import org.slf4j.LoggerFactory;
  */
 public class SingletonCleanerListener extends 
BetweenTestClassesListenerAdapter {
     private static final Logger LOG = 
LoggerFactory.getLogger(SingletonCleanerListener.class);
-    private static final Method OBJECTMAPPERFACTORY_CLEANCACHES_METHOD;
-    private static final Method OBJECTMAPPERFACTORY_REFRESH_METHOD;
+    private static final Method OBJECTMAPPERFACTORY_CLEARCACHES_METHOD;
+    private static final Method JSONSCHEMA_CLEARCACHES_METHOD;
 
     static {
         Class<?> objectMapperFactoryClazz =
@@ -50,49 +50,57 @@ public class SingletonCleanerListener extends 
BetweenTestClassesListenerAdapter
                                 .getMethod("clearCaches");
             }
         } catch (NoSuchMethodException e) {
-            LOG.warn("Cannot find method for cleaning singleton ObjectMapper 
caches", e);
+            LOG.warn("Cannot find method for clearing singleton ObjectMapper 
caches", e);
         }
-        OBJECTMAPPERFACTORY_CLEANCACHES_METHOD = clearCachesMethod;
+        OBJECTMAPPERFACTORY_CLEARCACHES_METHOD = clearCachesMethod;
 
-        Method refreshMethod = null;
+
+        Class<?> jsonSchemaClazz = null;
         try {
-            if (objectMapperFactoryClazz != null) {
-                refreshMethod =
-                        objectMapperFactoryClazz
-                                .getMethod("refresh");
+            jsonSchemaClazz = 
ClassUtils.getClass("org.apache.pulsar.client.impl.schema.JSONSchema");
+        } catch (ClassNotFoundException e) {
+            LOG.warn("Cannot find JSONSchema class", e);
+        }
+
+        Method jsonSchemaCleanCachesMethod = null;
+        try {
+            if (jsonSchemaClazz != null) {
+                jsonSchemaCleanCachesMethod =
+                        jsonSchemaClazz
+                                .getMethod("clearCaches");
             }
         } catch (NoSuchMethodException e) {
-            LOG.warn("Cannot find method for refreshing singleton ObjectMapper 
instances", e);
+            LOG.warn("Cannot find method for clearing singleton JSONSchema 
caches", e);
         }
-        OBJECTMAPPERFACTORY_REFRESH_METHOD = refreshMethod;
+        JSONSCHEMA_CLEARCACHES_METHOD = jsonSchemaCleanCachesMethod;
     }
 
     @Override
     protected void onBetweenTestClasses(Class<?> endedTestClass, Class<?> 
startedTestClass) {
-        cleanObjectMapperFactoryCaches();
-        refreshObjectMapperFactory();
+        objectMapperFactoryClearCaches();
+        jsonSchemaClearCaches();
     }
 
     // Call ObjectMapperFactory.clearCaches() using reflection to clear up 
classes held in
     // the singleton Jackson ObjectMapper instances
-    private static void cleanObjectMapperFactoryCaches() {
-        if (OBJECTMAPPERFACTORY_CLEANCACHES_METHOD != null) {
+    private static void objectMapperFactoryClearCaches() {
+        if (OBJECTMAPPERFACTORY_CLEARCACHES_METHOD != null) {
             try {
-                OBJECTMAPPERFACTORY_CLEANCACHES_METHOD.invoke(null);
+                OBJECTMAPPERFACTORY_CLEARCACHES_METHOD.invoke(null);
             } catch (IllegalAccessException | InvocationTargetException e) {
                 LOG.warn("Cannot clean singleton ObjectMapper caches", e);
             }
         }
     }
 
-    // Call ObjectMapperFactory.refresh() using reflection to release 
ObjectMapper instances
-    // that might be holding on classloaders and classes
-    private static void refreshObjectMapperFactory() {
-        if (OBJECTMAPPERFACTORY_REFRESH_METHOD != null) {
+    // Call JSONSchema.clearCaches() using reflection to clear up classes held 
in
+    // the singleton Jackson ObjectMapper instance of JSONSchema class
+    private static void jsonSchemaClearCaches() {
+        if (JSONSCHEMA_CLEARCACHES_METHOD != null) {
             try {
-                OBJECTMAPPERFACTORY_REFRESH_METHOD.invoke(null);
+                JSONSCHEMA_CLEARCACHES_METHOD.invoke(null);
             } catch (IllegalAccessException | InvocationTargetException e) {
-                LOG.warn("Cannot refresh ObjectMapper instances", e);
+                LOG.warn("Cannot clean singleton JSONSchema caches", e);
             }
         }
     }
diff --git 
a/pulsar-client/src/main/java/org/apache/pulsar/client/impl/schema/JSONSchema.java
 
b/pulsar-client/src/main/java/org/apache/pulsar/client/impl/schema/JSONSchema.java
index 39d842c1ed6..0048ea921e0 100644
--- 
a/pulsar-client/src/main/java/org/apache/pulsar/client/impl/schema/JSONSchema.java
+++ 
b/pulsar-client/src/main/java/org/apache/pulsar/client/impl/schema/JSONSchema.java
@@ -27,6 +27,7 @@ import com.fasterxml.jackson.databind.ObjectWriter;
 import com.fasterxml.jackson.module.jsonSchema.JsonSchema;
 import com.fasterxml.jackson.module.jsonSchema.JsonSchemaGenerator;
 import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.pulsar.client.api.schema.SchemaDefinition;
 import org.apache.pulsar.client.api.schema.SchemaReader;
@@ -42,15 +43,21 @@ import org.apache.pulsar.common.util.ObjectMapperFactory;
  */
 @Slf4j
 public class JSONSchema<T> extends AvroBaseStructSchema<T> {
-    private static final ObjectMapper JSON_MAPPER = createObjectMapper();
+    private static final AtomicReference<ObjectMapper> JSON_MAPPER = new 
AtomicReference<>(createObjectMapper());
 
     private static ObjectMapper createObjectMapper() {
-        ObjectMapper mapper = new ObjectMapper();
+        ObjectMapper mapper = ObjectMapperFactory.create();
         mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, 
false);
+        // keep backwards compatibility, don't accept unknown enum values
+        
mapper.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL, 
false);
         mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
         return mapper;
     }
 
+    private static ObjectMapper jsonMapper() {
+        return JSON_MAPPER.get();
+    }
+
     private final Class<T> pojo;
 
     private JSONSchema(SchemaInfo schemaInfo, Class<T> pojo, SchemaReader<T> 
reader, SchemaWriter<T> writer) {
@@ -87,9 +94,9 @@ public class JSONSchema<T> extends AvroBaseStructSchema<T> {
 
     public static <T> JSONSchema<T> of(SchemaDefinition<T> schemaDefinition) {
         SchemaReader<T> reader = schemaDefinition.getSchemaReaderOpt()
-                .orElseGet(() -> new JacksonJsonReader<>(JSON_MAPPER, 
schemaDefinition.getPojo()));
+                .orElseGet(() -> new JacksonJsonReader<>(jsonMapper(), 
schemaDefinition.getPojo()));
         SchemaWriter<T> writer = schemaDefinition.getSchemaWriterOpt()
-                .orElseGet(() -> new JacksonJsonWriter<>(JSON_MAPPER));
+                .orElseGet(() -> new JacksonJsonWriter<>(jsonMapper()));
         return new JSONSchema<>(parseSchemaInfo(schemaDefinition, 
SchemaType.JSON), schemaDefinition.getPojo(),
                 reader, writer);
     }
@@ -102,4 +109,18 @@ public class JSONSchema<T> extends AvroBaseStructSchema<T> 
{
         return 
JSONSchema.of(SchemaDefinition.<T>builder().withPojo(pojo).withProperties(properties).build());
     }
 
+    /**
+     * Clears the caches tied to the ObjectMapper instances and replaces the 
singleton ObjectMapper instance.
+     *
+     * This can be used in tests to ensure that classloaders and class 
references don't leak across tests.
+     */
+    public static void clearCaches() {
+        jsonMapper().getTypeFactory().clearCache();
+        replaceSingletonInstance();
+    }
+
+    private static void replaceSingletonInstance() {
+        // recycle the singleton instance to release remaining caches
+        JSON_MAPPER.set(createObjectMapper());
+    }
 }
diff --git 
a/pulsar-common/src/main/java/org/apache/pulsar/common/util/ObjectMapperFactory.java
 
b/pulsar-common/src/main/java/org/apache/pulsar/common/util/ObjectMapperFactory.java
index 32e83efbf42..2d0f9af5f23 100644
--- 
a/pulsar-common/src/main/java/org/apache/pulsar/common/util/ObjectMapperFactory.java
+++ 
b/pulsar-common/src/main/java/org/apache/pulsar/common/util/ObjectMapperFactory.java
@@ -266,24 +266,26 @@ public class ObjectMapperFactory {
     }
 
     /**
-     * Clears the caches tied to the ObjectMapper instances.
-     * This is used in tests to ensure that classloaders and class references 
don't leak between tests.
-     * Jackson's ObjectMapper doesn't expose a public API for clearing all 
caches so this solution is partial.
+     * Clears the caches tied to the ObjectMapper instances and replaces the 
singleton ObjectMapper instance.
+     *
+     * This can be used in tests to ensure that classloaders and class 
references don't leak across tests.
      */
     public static void clearCaches() {
-        clearCachesForObjectMapper(getMapper().getObjectMapper());
-        clearCachesForObjectMapper(getYamlMapper().getObjectMapper());
+        clearTypeFactoryCache(getMapper().getObjectMapper());
+        clearTypeFactoryCache(getYamlMapper().getObjectMapper());
+        clearTypeFactoryCache(getMapperWithIncludeAlways().getObjectMapper());
+        replaceSingletonInstances();
     }
 
-    private static void clearCachesForObjectMapper(ObjectMapper objectMapper) {
+    private static void clearTypeFactoryCache(ObjectMapper objectMapper) {
         objectMapper.getTypeFactory().clearCache();
     }
 
-    /**
+    /*
      * Replaces the existing singleton ObjectMapper instances with new 
instances.
      * This is used in tests to ensure that classloaders and class references 
don't leak between tests.
      */
-    public static void refresh() {
+    private static void replaceSingletonInstances() {
         MAPPER_REFERENCE.set(new 
MapperReference(createObjectMapperInstance()));
         INSTANCE_WITH_INCLUDE_ALWAYS.set(new 
MapperReference(createObjectMapperWithIncludeAlways()));
         YAML_MAPPER_REFERENCE.set(new MapperReference(createYamlInstance()));

Reply via email to