Skip to content

Commit e79954e

Browse files
committed
Stop using Class as map key
This commit updates the DefaultTopicResolver and DefaulSchemaResolver to use String class names rather than Class instances as map keys for their custom mappings. Resolves #1078
1 parent 8120076 commit e79954e

File tree

4 files changed

+85
-26
lines changed

4 files changed

+85
-26
lines changed

spring-pulsar/src/main/java/org/springframework/pulsar/core/DefaultSchemaResolver.java

+36-8
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,12 @@
2323
import java.time.LocalDate;
2424
import java.time.LocalDateTime;
2525
import java.time.LocalTime;
26-
import java.util.Collections;
2726
import java.util.Date;
2827
import java.util.HashMap;
2928
import java.util.LinkedHashMap;
3029
import java.util.Map;
3130
import java.util.Objects;
31+
import java.util.Optional;
3232

3333
import org.apache.pulsar.client.api.Schema;
3434
import org.apache.pulsar.client.impl.schema.AvroSchema;
@@ -92,7 +92,7 @@ public class DefaultSchemaResolver implements SchemaResolver {
9292
BASE_SCHEMA_MAPPINGS.put(LocalTime.class, Schema.LOCAL_TIME);
9393
}
9494

95-
private final Map<Class<?>, Schema<?>> customSchemaMappings = new LinkedHashMap<>();
95+
private final Map<String, Schema<?>> customSchemaMappings = new LinkedHashMap<>();
9696

9797
private final PulsarMessageAnnotationRegistry pulsarMessageAnnotationRegistry = new PulsarMessageAnnotationRegistry();
9898

@@ -122,7 +122,7 @@ public void usePulsarMessageAnnotations(boolean usePulsarMessageAnnotations) {
122122
*/
123123
@Nullable
124124
public Schema<?> addCustomSchemaMapping(Class<?> messageType, Schema<?> schema) {
125-
return this.customSchemaMappings.put(messageType, schema);
125+
return this.customSchemaMappings.put(this.toMessageTypeMapKey(messageType), schema);
126126
}
127127

128128
/**
@@ -133,15 +133,30 @@ public Schema<?> addCustomSchemaMapping(Class<?> messageType, Schema<?> schema)
133133
*/
134134
@Nullable
135135
public Schema<?> removeCustomMapping(Class<?> messageType) {
136-
return this.customSchemaMappings.remove(messageType);
136+
return this.customSchemaMappings.remove(this.toMessageTypeMapKey(messageType));
137137
}
138138

139139
/**
140-
* Gets the currently registered custom mappings from message type to schema.
141-
* @return unmodifiable map of custom mappings
140+
* Gets the currently registered custom mapping for the specified message type.
141+
* @return optional custom topic registered for the message type
142+
* @deprecated deprecated in favor of {@link #getCustomSchemaMapping(Class)} (Class)}
142143
*/
144+
@Deprecated(since = "1.2.5", forRemoval = true)
143145
public Map<Class<?>, Schema<?>> getCustomSchemaMappings() {
144-
return Collections.unmodifiableMap(this.customSchemaMappings);
146+
Map<Class<?>, Schema<?>> copyOfMappings = new HashMap<>();
147+
this.customSchemaMappings.entrySet()
148+
.stream()
149+
.map((e) -> copyOfMappings.put(this.fromMessageTypeMapKey(e.getKey()), e.getValue()));
150+
return copyOfMappings;
151+
}
152+
153+
/**
154+
* Gets the currently registered custom mapping for the specified message type.
155+
* @param messageType the message type
156+
* @return optional custom topic registered for the message type
157+
*/
158+
public Optional<Schema<?>> getCustomSchemaMapping(Class<?> messageType) {
159+
return Optional.ofNullable(this.customSchemaMappings.get(this.toMessageTypeMapKey(messageType)));
145160
}
146161

147162
@Override
@@ -162,7 +177,7 @@ public <T> Resolved<Schema<T>> resolveSchema(@Nullable Class<?> messageClass, bo
162177
@Nullable
163178
protected Schema<?> getCustomSchemaOrMaybeDefault(@Nullable Class<?> messageClass, boolean returnDefault) {
164179
// Check for custom schema mapping
165-
Schema<?> schema = this.customSchemaMappings.get(messageClass);
180+
var schema = this.getCustomSchemaMapping(messageClass).orElse(null);
166181

167182
// If no custom schema mapping found, look for @PulsarMessage (if enabled)
168183
if (this.usePulsarMessageAnnotations && schema == null && messageClass != null) {
@@ -289,4 +304,17 @@ private <X> Schema<X> castToType(Schema<?> rawSchema) {
289304
return (Schema<X>) rawSchema;
290305
}
291306

307+
private Class<?> fromMessageTypeMapKey(String messageTypeKey) {
308+
try {
309+
return Class.forName(messageTypeKey);
310+
}
311+
catch (ClassNotFoundException e) {
312+
throw new RuntimeException(e);
313+
}
314+
}
315+
316+
private String toMessageTypeMapKey(Class<?> messageType) {
317+
return messageType.getName();
318+
}
319+
292320
}

spring-pulsar/src/main/java/org/springframework/pulsar/core/DefaultTopicResolver.java

+37-7
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@
1616

1717
package org.springframework.pulsar.core;
1818

19-
import java.util.Collections;
19+
import java.util.HashMap;
2020
import java.util.LinkedHashMap;
2121
import java.util.Map;
22+
import java.util.Optional;
2223
import java.util.function.Supplier;
2324

2425
import org.springframework.beans.BeansException;
@@ -45,7 +46,7 @@ public class DefaultTopicResolver implements TopicResolver, BeanFactoryAware {
4546

4647
private final LogAccessor logger = new LogAccessor(this.getClass());
4748

48-
private final Map<Class<?>, String> customTopicMappings = new LinkedHashMap<>();
49+
private final Map<String, String> customTopicMappings = new LinkedHashMap<>();
4950

5051
private final PulsarMessageAnnotationRegistry pulsarMessageAnnotationRegistry = new PulsarMessageAnnotationRegistry();
5152

@@ -86,7 +87,7 @@ public void usePulsarMessageAnnotations(boolean usePulsarMessageAnnotations) {
8687
*/
8788
@Nullable
8889
public String addCustomTopicMapping(Class<?> messageType, String topic) {
89-
return this.customTopicMappings.put(messageType, topic);
90+
return this.customTopicMappings.put(this.toMessageTypeMapKey(messageType), topic);
9091
}
9192

9293
/**
@@ -97,15 +98,31 @@ public String addCustomTopicMapping(Class<?> messageType, String topic) {
9798
*/
9899
@Nullable
99100
public String removeCustomMapping(Class<?> messageType) {
100-
return this.customTopicMappings.remove(messageType);
101+
return this.customTopicMappings.remove(this.toMessageTypeMapKey(messageType));
101102
}
102103

103104
/**
104-
* Gets the currently registered custom mappings from message type to topic.
105+
* Gets the currently registered custom mappings from message type class name to
106+
* topic.
105107
* @return unmodifiable map of custom mappings
108+
* @deprecated deprecated in favor of {@link #getCustomTopicMapping(Class)}
106109
*/
110+
@Deprecated(since = "1.2.5", forRemoval = true)
107111
public Map<Class<?>, String> getCustomTopicMappings() {
108-
return Collections.unmodifiableMap(this.customTopicMappings);
112+
Map<Class<?>, String> copyOfMappings = new HashMap<>();
113+
this.customTopicMappings.entrySet()
114+
.stream()
115+
.map((e) -> copyOfMappings.put(this.fromMessageTypeMapKey(e.getKey()), e.getValue()));
116+
return copyOfMappings;
117+
}
118+
119+
/**
120+
* Gets the currently registered custom mapping for the specified message type.
121+
* @param messageType the message type
122+
* @return optional custom topic registered for the message type
123+
*/
124+
public Optional<String> getCustomTopicMapping(Class<?> messageType) {
125+
return Optional.ofNullable(this.customTopicMappings.get(this.toMessageTypeMapKey(messageType)));
109126
}
110127

111128
@Override
@@ -141,7 +158,7 @@ protected Resolved<String> doResolveTopic(@Nullable String userSpecifiedTopic, @
141158
return Resolved.failed("Topic must be specified when the message is null");
142159
}
143160
// Check for custom topic mapping
144-
String topic = this.customTopicMappings.get(messageType);
161+
String topic = this.customTopicMappings.get(this.toMessageTypeMapKey(messageType));
145162

146163
// If no custom topic mapping found, look for @PulsarMessage (if enabled)
147164
if (this.usePulsarMessageAnnotations && topic == null) {
@@ -174,6 +191,19 @@ private String resolveExpression(String v) {
174191
.orElseThrow(() -> "Failed to resolve topic expression: %s".formatted(v));
175192
}
176193

194+
private Class<?> fromMessageTypeMapKey(String messageTypeKey) {
195+
try {
196+
return Class.forName(messageTypeKey);
197+
}
198+
catch (ClassNotFoundException e) {
199+
throw new RuntimeException(e);
200+
}
201+
}
202+
203+
private String toMessageTypeMapKey(Class<?> messageType) {
204+
return messageType.getName();
205+
}
206+
177207
@Override
178208
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
179209
if (beanFactory instanceof ConfigurableBeanFactory configurableBeanFactory) {

spring-pulsar/src/test/java/org/springframework/pulsar/core/DefaultSchemaResolverTests.java

+7-6
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ private static String sanitizedClassName(Class<?> clazz) {
7979
@Nested
8080
class CustomSchemaMappingsAPI {
8181

82+
@SuppressWarnings("removal")
8283
@Test
8384
void noMappingsByDefault() {
8485
assertThat(resolver.getCustomSchemaMappings()).asInstanceOf(InstanceOfAssertFactories.MAP).isEmpty();
@@ -88,14 +89,13 @@ void noMappingsByDefault() {
8889
void addMappings() {
8990
Schema<?> previouslyMappedSchema = resolver.addCustomSchemaMapping(Foo.class, Schema.STRING);
9091
assertThat(previouslyMappedSchema).isNull();
91-
assertThat(resolver.getCustomSchemaMappings()).asInstanceOf(InstanceOfAssertFactories.MAP)
92-
.containsEntry(Foo.class, Schema.STRING);
92+
assertThat(resolver.getCustomSchemaMapping(Foo.class)).hasValue(Schema.STRING);
9393
previouslyMappedSchema = resolver.addCustomSchemaMapping(Foo.class, Schema.BOOL);
9494
assertThat(previouslyMappedSchema).isEqualTo(Schema.STRING);
95-
assertThat(resolver.getCustomSchemaMappings()).asInstanceOf(InstanceOfAssertFactories.MAP)
96-
.containsEntry(Foo.class, Schema.BOOL);
95+
assertThat(resolver.getCustomSchemaMapping(Foo.class)).hasValue(Schema.BOOL);
9796
}
9897

98+
@SuppressWarnings("removal")
9999
@Test
100100
void removeMappings() {
101101
Schema<?> previouslyMappedSchema = resolver.removeCustomMapping(Foo.class);
@@ -423,10 +423,11 @@ void annotatedMessageType() {
423423
.endsWith(JsonMsgType.class.getSimpleName());
424424

425425
// verify added to custom mappings
426-
assertThat(resolver.getCustomSchemaMappings().get(JsonMsgType.class)).isSameAs(resolvedSchema);
426+
assertThat(resolver.getCustomSchemaMapping(JsonMsgType.class))
427+
.hasValueSatisfying((v) -> assertThat(v).isSameAs(resolvedSchema));
427428

428429
// verify subsequent calls skip resolution again
429-
assertThat(resolver.resolveSchema(JsonMsgType.class, false).orElseThrow()).isSameAs(resolvedSchema);
430+
assertThat(resolver.resolveSchema(JsonMsgType.class, false).orElseThrow()).isEqualTo(resolvedSchema);
430431
verify(resolver, times(1)).getAnnotatedSchemaType(JsonMsgType.class);
431432
}
432433

spring-pulsar/src/test/java/org/springframework/pulsar/core/DefaultTopicResolverTests.java

+5-5
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ void annotatedMessageTypeWithTopicInfo() {
166166
assertThat(resolver.resolveTopic(null, Baz.class, () -> defaultTopic).value().orElse(null))
167167
.isEqualTo(bazTopic);
168168
// verify added to custom mappings
169-
assertThat(resolver.getCustomTopicMappings().get(Baz.class)).isEqualTo(bazTopic);
169+
assertThat(resolver.getCustomTopicMapping(Baz.class)).hasValue(bazTopic);
170170
// verify subsequent calls skip resolution again
171171
assertThat(resolver.resolveTopic(null, Baz.class, () -> defaultTopic).value().orElse(null))
172172
.isEqualTo(bazTopic);
@@ -242,6 +242,7 @@ void resetResolver() {
242242
resolver = new DefaultTopicResolver();
243243
}
244244

245+
@SuppressWarnings("removal")
245246
@Test
246247
void noMappingsByDefault() {
247248
resolver = new DefaultTopicResolver();
@@ -254,14 +255,13 @@ void addMappings() {
254255
String topic2 = "bar-topic";
255256
String previouslyMappedTopic = resolver.addCustomTopicMapping(Foo.class, topic1);
256257
assertThat(previouslyMappedTopic).isNull();
257-
assertThat(resolver.getCustomTopicMappings()).asInstanceOf(InstanceOfAssertFactories.MAP)
258-
.containsEntry(Foo.class, topic1);
258+
assertThat(resolver.getCustomTopicMapping(Foo.class)).hasValue(topic1);
259259
previouslyMappedTopic = resolver.addCustomTopicMapping(Foo.class, topic2);
260260
assertThat(previouslyMappedTopic).isEqualTo(topic1);
261-
assertThat(resolver.getCustomTopicMappings()).asInstanceOf(InstanceOfAssertFactories.MAP)
262-
.containsEntry(Foo.class, topic2);
261+
assertThat(resolver.getCustomTopicMapping(Foo.class)).hasValue(topic2);
263262
}
264263

264+
@SuppressWarnings("removal")
265265
@Test
266266
void removeMappings() {
267267
String previouslyMappedTopic = resolver.removeCustomMapping(Foo.class);

0 commit comments

Comments
 (0)