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.
This commit is contained in:
Chris Bono
2025-04-22 13:00:55 -05:00
committed by Chris Bono
parent a91dd8185f
commit 3ebb6cbbf3
4 changed files with 52 additions and 13 deletions

View File

@@ -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<Class<?>, Schema<?>> getCustomSchemaMappings() {
Map<Class<?>, 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;
}
}

View File

@@ -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<Class<?>, String> getCustomTopicMappings() {
Map<Class<?>, 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;
}
}

View File

@@ -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

View File

@@ -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) {