From 3ebb6cbbf3ea554f53f14d8dc5bf05d0fe15167c Mon Sep 17 00:00:00 2001 From: Chris Bono Date: Tue, 22 Apr 2025 13:00:55 -0500 Subject: [PATCH] Fix getCustomMappings API for resolvers This fixes the DefaultTopicResolver.getCustomTopicMappings and DefaultSchemaResolver.getCustomSchemaMappings APIs to actually return the mappings and also use ClassUtils.forName instead of Class.forName to safely load the class from string key. --- .../pulsar/core/DefaultSchemaResolver.java | 22 +++++++++++++----- .../pulsar/core/DefaultTopicResolver.java | 23 +++++++++++++------ .../core/DefaultSchemaResolverTests.java | 10 ++++++++ .../core/DefaultTopicResolverTests.java | 10 ++++++++ 4 files changed, 52 insertions(+), 13 deletions(-) diff --git a/spring-pulsar/src/main/java/org/springframework/pulsar/core/DefaultSchemaResolver.java b/spring-pulsar/src/main/java/org/springframework/pulsar/core/DefaultSchemaResolver.java index 3ccc5a91..980cf69e 100644 --- a/spring-pulsar/src/main/java/org/springframework/pulsar/core/DefaultSchemaResolver.java +++ b/spring-pulsar/src/main/java/org/springframework/pulsar/core/DefaultSchemaResolver.java @@ -27,8 +27,10 @@ import java.util.Date; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; +import java.util.Map.Entry; import java.util.Objects; import java.util.Optional; +import java.util.stream.Collectors; import org.apache.pulsar.client.api.Schema; import org.apache.pulsar.client.impl.schema.AvroSchema; @@ -38,11 +40,13 @@ import org.apache.pulsar.common.schema.KeyValue; import org.apache.pulsar.common.schema.KeyValueEncodingType; import org.apache.pulsar.common.schema.SchemaType; +import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.core.ResolvableType; import org.springframework.core.log.LogAccessor; import org.springframework.lang.Nullable; import org.springframework.pulsar.annotation.PulsarMessage; import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; import com.fasterxml.jackson.databind.ObjectMapper; @@ -58,7 +62,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; * @author Chris Bono * @author Aleksei Arsenev */ -public class DefaultSchemaResolver implements SchemaResolver { +public class DefaultSchemaResolver implements SchemaResolver, BeanClassLoaderAware { private final LogAccessor logger = new LogAccessor(this.getClass()); @@ -100,6 +104,9 @@ public class DefaultSchemaResolver implements SchemaResolver { private ObjectMapper objectMapper; + @Nullable + private ClassLoader classLoader; + public void setObjectMapper(ObjectMapper objectMapper) { this.objectMapper = objectMapper; } @@ -143,11 +150,9 @@ public class DefaultSchemaResolver implements SchemaResolver { */ @Deprecated(since = "1.2.5", forRemoval = true) public Map, Schema> getCustomSchemaMappings() { - Map, Schema> copyOfMappings = new HashMap<>(); - this.customSchemaMappings.entrySet() + return this.customSchemaMappings.entrySet() .stream() - .map((e) -> copyOfMappings.put(this.fromMessageTypeMapKey(e.getKey()), e.getValue())); - return copyOfMappings; + .collect(Collectors.toMap((e) -> this.fromMessageTypeMapKey(e.getKey()), Entry::getValue)); } /** @@ -306,7 +311,7 @@ public class DefaultSchemaResolver implements SchemaResolver { private Class fromMessageTypeMapKey(String messageTypeKey) { try { - return Class.forName(messageTypeKey); + return ClassUtils.forName(messageTypeKey, this.classLoader); } catch (ClassNotFoundException e) { throw new RuntimeException(e); @@ -317,4 +322,9 @@ public class DefaultSchemaResolver implements SchemaResolver { return messageType.getName(); } + @Override + public void setBeanClassLoader(ClassLoader classLoader) { + this.classLoader = classLoader; + } + } diff --git a/spring-pulsar/src/main/java/org/springframework/pulsar/core/DefaultTopicResolver.java b/spring-pulsar/src/main/java/org/springframework/pulsar/core/DefaultTopicResolver.java index 3c7b35b2..db755cb1 100644 --- a/spring-pulsar/src/main/java/org/springframework/pulsar/core/DefaultTopicResolver.java +++ b/spring-pulsar/src/main/java/org/springframework/pulsar/core/DefaultTopicResolver.java @@ -16,19 +16,22 @@ package org.springframework.pulsar.core; -import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; +import java.util.Map.Entry; import java.util.Optional; import java.util.function.Supplier; +import java.util.stream.Collectors; import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.core.log.LogAccessor; import org.springframework.lang.Nullable; import org.springframework.pulsar.annotation.PulsarMessage; +import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; /** @@ -42,7 +45,7 @@ import org.springframework.util.StringUtils; * @author Aleksei Arsenev * @author Jonas Geiregat */ -public class DefaultTopicResolver implements TopicResolver, BeanFactoryAware { +public class DefaultTopicResolver implements TopicResolver, BeanFactoryAware, BeanClassLoaderAware { private final LogAccessor logger = new LogAccessor(this.getClass()); @@ -55,6 +58,9 @@ public class DefaultTopicResolver implements TopicResolver, BeanFactoryAware { @Nullable private ExpressionResolver expressionResolver; + @Nullable + private ClassLoader classLoader; + /** * Constructs a new DefaultTopicResolver with the given expression resolver. * @param expressionResolver the expression resolver to use for resolving topic @@ -109,11 +115,9 @@ public class DefaultTopicResolver implements TopicResolver, BeanFactoryAware { */ @Deprecated(since = "1.2.5", forRemoval = true) public Map, String> getCustomTopicMappings() { - Map, String> copyOfMappings = new HashMap<>(); - this.customTopicMappings.entrySet() + return this.customTopicMappings.entrySet() .stream() - .map((e) -> copyOfMappings.put(this.fromMessageTypeMapKey(e.getKey()), e.getValue())); - return copyOfMappings; + .collect(Collectors.toMap((e) -> this.fromMessageTypeMapKey(e.getKey()), Entry::getValue)); } /** @@ -193,7 +197,7 @@ public class DefaultTopicResolver implements TopicResolver, BeanFactoryAware { private Class fromMessageTypeMapKey(String messageTypeKey) { try { - return Class.forName(messageTypeKey); + return ClassUtils.forName(messageTypeKey, this.classLoader); } catch (ClassNotFoundException e) { throw new RuntimeException(e); @@ -217,4 +221,9 @@ public class DefaultTopicResolver implements TopicResolver, BeanFactoryAware { } } + @Override + public void setBeanClassLoader(ClassLoader classLoader) { + this.classLoader = classLoader; + } + } diff --git a/spring-pulsar/src/test/java/org/springframework/pulsar/core/DefaultSchemaResolverTests.java b/spring-pulsar/src/test/java/org/springframework/pulsar/core/DefaultSchemaResolverTests.java index 794293b5..a2674e86 100644 --- a/spring-pulsar/src/test/java/org/springframework/pulsar/core/DefaultSchemaResolverTests.java +++ b/spring-pulsar/src/test/java/org/springframework/pulsar/core/DefaultSchemaResolverTests.java @@ -16,6 +16,7 @@ package org.springframework.pulsar.core; +import static java.util.Map.entry; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; import static org.assertj.core.api.Assertions.assertThatIllegalStateException; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; @@ -106,6 +107,15 @@ class DefaultSchemaResolverTests { assertThat(resolver.getCustomSchemaMappings()).asInstanceOf(InstanceOfAssertFactories.MAP).isEmpty(); } + @SuppressWarnings("removal") + @Test + void getCustomMappingsReturnsMapping() { + assertThat(resolver.getCustomSchemaMappings()).asInstanceOf(InstanceOfAssertFactories.MAP).isEmpty(); + resolver.addCustomSchemaMapping(Foo.class, Schema.STRING); + assertThat(resolver.getCustomSchemaMappings()).asInstanceOf(InstanceOfAssertFactories.MAP) + .containsExactly(entry(Foo.class, Schema.STRING)); + } + } @Nested diff --git a/spring-pulsar/src/test/java/org/springframework/pulsar/core/DefaultTopicResolverTests.java b/spring-pulsar/src/test/java/org/springframework/pulsar/core/DefaultTopicResolverTests.java index 704204a9..77444484 100644 --- a/spring-pulsar/src/test/java/org/springframework/pulsar/core/DefaultTopicResolverTests.java +++ b/spring-pulsar/src/test/java/org/springframework/pulsar/core/DefaultTopicResolverTests.java @@ -16,6 +16,7 @@ package org.springframework.pulsar.core; +import static java.util.Map.entry; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.junit.jupiter.params.provider.Arguments.arguments; import static org.mockito.Mockito.spy; @@ -272,6 +273,15 @@ class DefaultTopicResolverTests { assertThat(resolver.getCustomTopicMappings()).asInstanceOf(InstanceOfAssertFactories.MAP).isEmpty(); } + @SuppressWarnings("removal") + @Test + void getCustomMappingsReturnsMapping() { + assertThat(resolver.getCustomTopicMappings()).asInstanceOf(InstanceOfAssertFactories.MAP).isEmpty(); + resolver.addCustomTopicMapping(Foo.class, "fooTopic"); + assertThat(resolver.getCustomTopicMappings()).asInstanceOf(InstanceOfAssertFactories.MAP) + .containsExactly(entry(Foo.class, "fooTopic")); + } + } record Foo(String value) {