Skip to content

Commit ebf9fba

Browse files
NaccOllspring-builds
authored andcommitted
GH-9561: Make DelayedMessageWrapper JSON-serializable
Fixes: #9561 PR: #9561 The `DelayHandler.DelayedMessageWrapper` cannot be deserialized when using `RedisMessageStore` and JSON serialization: ``` org.springframework.data.redis.serializer.SerializationException: Could not read JSON:Cannot construct instance of `org.springframework.integration.handler.DelayHandler$DelayedMessageWrapper` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator) at [Source: UNKNOWN; byte offset: #UNKNOWN] at org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer.deserialize(GenericJackson2JsonRedisSerializer.java:311) ``` * More code clean up and refactoring in the test (cherry picked from commit 73bb813)
1 parent f45ea94 commit ebf9fba

File tree

3 files changed

+39
-59
lines changed

3 files changed

+39
-59
lines changed

Diff for: spring-integration-core/src/main/java/org/springframework/integration/handler/DelayHandler.java

+7-1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
import java.util.concurrent.locks.ReentrantLock;
3232
import java.util.stream.Stream;
3333

34+
import com.fasterxml.jackson.annotation.JsonCreator;
35+
import com.fasterxml.jackson.annotation.JsonProperty;
3436
import org.aopalliance.aop.Advice;
3537

3638
import org.springframework.aop.framework.ProxyFactory;
@@ -97,6 +99,7 @@
9799
* @author Artem Bilan
98100
* @author Gary Russell
99101
* @author Christian Tzolov
102+
* @author Youbin Wu
100103
*
101104
* @since 1.0.3
102105
*/
@@ -688,7 +691,10 @@ public static final class DelayedMessageWrapper implements Serializable {
688691

689692
private final Message<?> original;
690693

691-
DelayedMessageWrapper(Message<?> original, long requestDate) {
694+
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
695+
DelayedMessageWrapper(@JsonProperty("original") Message<?> original,
696+
@JsonProperty("requestDate") long requestDate) {
697+
692698
this.original = original;
693699
this.requestDate = requestDate;
694700
}

Diff for: spring-integration-core/src/main/java/org/springframework/integration/support/json/JacksonJsonUtils.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2022 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -46,6 +46,7 @@
4646
*
4747
* @author Artem Bilan
4848
* @author Gary Russell
49+
* @author Youbin Wu
4950
*
5051
* @since 3.0
5152
*
@@ -63,7 +64,8 @@ public final class JacksonJsonUtils {
6364
"org.springframework.integration.support",
6465
"org.springframework.integration.message",
6566
"org.springframework.integration.store",
66-
"org.springframework.integration.history"
67+
"org.springframework.integration.history",
68+
"org.springframework.integration.handler"
6769
);
6870

6971
private JacksonJsonUtils() {

Diff for: spring-integration-redis/src/test/java/org/springframework/integration/redis/store/RedisMessageGroupStoreTests.java

+28-56
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import java.util.Date;
2121
import java.util.Iterator;
2222
import java.util.List;
23-
import java.util.Objects;
2423
import java.util.Properties;
2524
import java.util.UUID;
2625
import java.util.concurrent.ExecutorService;
@@ -35,13 +34,16 @@
3534
import org.junit.jupiter.api.Disabled;
3635
import org.junit.jupiter.api.Test;
3736

37+
import org.springframework.beans.BeanUtils;
3838
import org.springframework.context.support.ClassPathXmlApplicationContext;
3939
import org.springframework.data.redis.connection.RedisConnectionFactory;
4040
import org.springframework.data.redis.core.StringRedisTemplate;
4141
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
42+
import org.springframework.data.redis.serializer.SerializationException;
4243
import org.springframework.integration.channel.DirectChannel;
4344
import org.springframework.integration.channel.NullChannel;
4445
import org.springframework.integration.channel.QueueChannel;
46+
import org.springframework.integration.handler.DelayHandler;
4547
import org.springframework.integration.history.MessageHistory;
4648
import org.springframework.integration.message.AdviceMessage;
4749
import org.springframework.integration.redis.RedisContainerTest;
@@ -56,14 +58,15 @@
5658
import org.springframework.messaging.support.GenericMessage;
5759

5860
import static org.assertj.core.api.Assertions.assertThat;
61+
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
5962
import static org.assertj.core.api.Assertions.assertThatNoException;
60-
import static org.assertj.core.api.Assertions.fail;
6163

6264
/**
6365
* @author Oleg Zhurakousky
6466
* @author Artem Bilan
6567
* @author Gary Russell
6668
* @author Artem Vozhdayenko
69+
* @author Youbin Wu
6770
*/
6871
class RedisMessageGroupStoreTests implements RedisContainerTest {
6972

@@ -317,7 +320,7 @@ void testConcurrentModifications() throws Exception {
317320
executor.execute(() -> {
318321
store2.removeMessagesFromGroup(this.groupId, message);
319322
MessageGroup group = store2.getMessageGroup(this.groupId);
320-
if (group.getMessages().size() != 0) {
323+
if (!group.getMessages().isEmpty()) {
321324
failures.add("REMOVE");
322325
throw new AssertionFailedError("Failed on Remove");
323326
}
@@ -401,11 +404,17 @@ void testJsonSerialization() {
401404
Message<?> mutableMessage = new MutableMessage<>(UUID.randomUUID());
402405
Message<?> adviceMessage = new AdviceMessage<>("foo", genericMessage);
403406
ErrorMessage errorMessage = new ErrorMessage(new RuntimeException("test exception"), mutableMessage);
407+
var delayedMessageWrapperConstructor =
408+
BeanUtils.getResolvableConstructor(DelayHandler.DelayedMessageWrapper.class);
409+
Message<?> delayMessage = new GenericMessage<>(
410+
BeanUtils.instantiateClass(delayedMessageWrapperConstructor, genericMessage,
411+
System.currentTimeMillis()));
404412

405-
store.addMessagesToGroup(this.groupId, genericMessage, mutableMessage, adviceMessage, errorMessage);
413+
store.addMessagesToGroup(this.groupId,
414+
genericMessage, mutableMessage, adviceMessage, errorMessage, delayMessage);
406415

407416
MessageGroup messageGroup = store.getMessageGroup(this.groupId);
408-
assertThat(messageGroup.size()).isEqualTo(4);
417+
assertThat(messageGroup.size()).isEqualTo(5);
409418
List<Message<?>> messages = new ArrayList<>(messageGroup.getMessages());
410419
assertThat(messages.get(0)).isEqualTo(genericMessage);
411420
assertThat(messages.get(0).getHeaders()).containsKeys(MessageHistory.HEADER_NAME);
@@ -418,22 +427,21 @@ void testJsonSerialization() {
418427
.isEqualTo(errorMessage.getOriginalMessage());
419428
assertThat(((ErrorMessage) errorMessageResult).getPayload().getMessage())
420429
.isEqualTo(errorMessage.getPayload().getMessage());
430+
assertThat(messages.get(4)).isEqualTo(delayMessage);
421431

422432
Message<Foo> fooMessage = new GenericMessage<>(new Foo("foo"));
423-
try {
424-
store.addMessageToGroup(this.groupId, fooMessage)
425-
.getMessages()
426-
.iterator()
427-
.next();
428-
fail("SerializationException expected");
429-
}
430-
catch (Exception e) {
431-
assertThat(e.getCause().getCause()).isInstanceOf(IllegalArgumentException.class);
432-
assertThat(e.getMessage()).contains("The class with " +
433-
"org.springframework.integration.redis.store.RedisMessageGroupStoreTests$Foo and name of " +
434-
"org.springframework.integration.redis.store.RedisMessageGroupStoreTests$Foo " +
435-
"is not in the trusted packages:");
436-
}
433+
434+
assertThatExceptionOfType(SerializationException.class)
435+
.isThrownBy(() ->
436+
store.addMessageToGroup(this.groupId, fooMessage)
437+
.getMessages()
438+
.iterator()
439+
.next())
440+
.withRootCauseInstanceOf(IllegalArgumentException.class)
441+
.withMessageContaining("The class with " +
442+
"org.springframework.integration.redis.store.RedisMessageGroupStoreTests$Foo and name of " +
443+
"org.springframework.integration.redis.store.RedisMessageGroupStoreTests$Foo " +
444+
"is not in the trusted packages:");
437445

438446
mapper = JacksonJsonUtils.messagingAwareMapper(getClass().getPackage().getName());
439447

@@ -486,43 +494,7 @@ public void removeMessagesFromGroupDontRemoveSameMessageInOtherGroup() {
486494
assertThat(store.messageGroupSize("2")).isEqualTo(1);
487495
}
488496

489-
private static class Foo {
490-
491-
private String foo;
492-
493-
Foo() {
494-
}
495-
496-
Foo(String foo) {
497-
this.foo = foo;
498-
}
499-
500-
public String getFoo() {
501-
return this.foo;
502-
}
503-
504-
public void setFoo(String foo) {
505-
this.foo = foo;
506-
}
507-
508-
@Override
509-
public boolean equals(Object o) {
510-
if (this == o) {
511-
return true;
512-
}
513-
if (o == null || getClass() != o.getClass()) {
514-
return false;
515-
}
516-
517-
Foo foo1 = (Foo) o;
518-
519-
return this.foo != null ? this.foo.equals(foo1.foo) : foo1.foo == null;
520-
}
521-
522-
@Override
523-
public int hashCode() {
524-
return Objects.hashCode(this.foo);
525-
}
497+
private record Foo(String foo) {
526498

527499
}
528500

0 commit comments

Comments
 (0)