Skip to content

Commit cc5a711

Browse files
artembilanspring-builds
authored andcommitted
GH-3174: Fix expiration property conversion for Stream module
Fixes: #3174 The Stream message comes with an `absoluteExpiryTime` according to the AMQP 1.0 message format. The AMQP 0.9.1 deals with a period instead of absolute time. * Fix `DefaultStreamMessageConverter` to calculate `expiration` from the difference between `absoluteExpiryTime` and `creationTime` (which is also absolute). * Use `System.currentTimeMillis()` if `creationTime` is not provided. * Fix an `absoluteExpiryTime` calculation based on the `creationTime` and `expiration` (cherry picked from commit a788393)
1 parent 58ac007 commit cc5a711

File tree

2 files changed

+38
-15
lines changed

2 files changed

+38
-15
lines changed

spring-rabbit-stream/src/main/java/org/springframework/rabbit/stream/support/converter/DefaultStreamMessageConverter.java

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import com.rabbitmq.stream.MessageBuilder.PropertiesBuilder;
2929
import com.rabbitmq.stream.Properties;
3030
import com.rabbitmq.stream.codec.WrapperMessageBuilder;
31+
import org.jspecify.annotations.Nullable;
3132

3233
import org.springframework.amqp.core.Message;
3334
import org.springframework.amqp.core.MessageProperties;
@@ -36,12 +37,15 @@
3637
import org.springframework.lang.Nullable;
3738
import org.springframework.rabbit.stream.support.StreamMessageProperties;
3839
import org.springframework.util.Assert;
40+
import org.springframework.util.StringUtils;
3941

4042
/**
4143
* Default {@link StreamMessageConverter}.
4244
*
4345
* @author Gary Russell
4446
* @author Ngoc Nhan
47+
* @author Artem Bilan
48+
*
4549
* @since 2.4
4650
*
4751
*/
@@ -92,30 +96,37 @@ public com.rabbitmq.stream.Message fromMessage(Message message) throws MessageCo
9296
Assert.isInstanceOf(StreamMessageProperties.class, props);
9397
StreamMessageProperties mProps = (StreamMessageProperties) props;
9498
JavaUtils.INSTANCE
95-
.acceptIfNotNull(mProps.getMessageId(), propsBuilder::messageId) // TODO different types
99+
.acceptIfNotNull(mProps.getMessageId(), propsBuilder::messageId)
96100
.acceptIfNotNull(mProps.getUserId(), usr -> propsBuilder.userId(usr.getBytes(this.charset)))
97101
.acceptIfNotNull(mProps.getTo(), propsBuilder::to)
98102
.acceptIfNotNull(mProps.getSubject(), propsBuilder::subject)
99103
.acceptIfNotNull(mProps.getReplyTo(), propsBuilder::replyTo)
100-
.acceptIfNotNull(mProps.getCorrelationId(), propsBuilder::correlationId) // TODO different types
104+
.acceptIfNotNull(mProps.getCorrelationId(), propsBuilder::correlationId)
101105
.acceptIfNotNull(mProps.getContentType(), propsBuilder::contentType)
102106
.acceptIfNotNull(mProps.getContentEncoding(), propsBuilder::contentEncoding)
103-
.acceptIfNotNull(mProps.getExpiration(), exp -> propsBuilder.absoluteExpiryTime(Long.parseLong(exp)))
104107
.acceptIfNotNull(mProps.getCreationTime(), propsBuilder::creationTime)
105108
.acceptIfNotNull(mProps.getGroupId(), propsBuilder::groupId)
106109
.acceptIfNotNull(mProps.getGroupSequence(), propsBuilder::groupSequence)
107110
.acceptIfNotNull(mProps.getReplyToGroupId(), propsBuilder::replyToGroupId);
108111
ApplicationPropertiesBuilder appPropsBuilder = builder.applicationProperties();
109-
if (!mProps.getHeaders().isEmpty()) {
110-
mProps.getHeaders().forEach((key, val) -> {
111-
mapProp(key, val, appPropsBuilder);
112-
});
112+
113+
long creationTime = mProps.getCreationTime();
114+
if (creationTime <= 0) {
115+
creationTime = System.currentTimeMillis();
116+
}
117+
propsBuilder.creationTime(creationTime);
118+
119+
String expiration = mProps.getExpiration();
120+
if (StringUtils.hasText(expiration)) {
121+
propsBuilder.absoluteExpiryTime(creationTime + Long.parseLong(expiration));
113122
}
123+
124+
mProps.getHeaders().forEach((key, val) -> mapProp(key, val, appPropsBuilder));
114125
builder.addData(message.getBody());
115126
return builder.build();
116127
}
117128

118-
private void mapProp(String key, Object val, ApplicationPropertiesBuilder builder) { // NOSONAR - complexity
129+
private void mapProp(String key, @Nullable Object val, ApplicationPropertiesBuilder builder) {
119130
if (val instanceof String string) {
120131
builder.entry(key, string);
121132
}
@@ -155,19 +166,28 @@ private void toMessageProperties(com.rabbitmq.stream.Message streamMessage,
155166
if (properties != null) {
156167
JavaUtils.INSTANCE
157168
.acceptIfNotNull(properties.getMessageIdAsString(), mProps::setMessageId)
158-
.acceptIfNotNull(properties.getUserId(), usr -> mProps.setUserId(new String(usr, this.charset)))
169+
.acceptIfNotNull(properties.getUserId(),
170+
usr -> mProps.setUserId(new String(usr, this.charset)))
159171
.acceptIfNotNull(properties.getTo(), mProps::setTo)
160172
.acceptIfNotNull(properties.getSubject(), mProps::setSubject)
161173
.acceptIfNotNull(properties.getReplyTo(), mProps::setReplyTo)
162174
.acceptIfNotNull(properties.getCorrelationIdAsString(), mProps::setCorrelationId)
163175
.acceptIfNotNull(properties.getContentType(), mProps::setContentType)
164176
.acceptIfNotNull(properties.getContentEncoding(), mProps::setContentEncoding)
165-
.acceptIfNotNull(properties.getAbsoluteExpiryTime(),
166-
exp -> mProps.setExpiration(Long.toString(exp)))
167-
.acceptIfNotNull(properties.getCreationTime(), mProps::setCreationTime)
168177
.acceptIfNotNull(properties.getGroupId(), mProps::setGroupId)
169178
.acceptIfNotNull(properties.getGroupSequence(), mProps::setGroupSequence)
170179
.acceptIfNotNull(properties.getReplyToGroupId(), mProps::setReplyToGroupId);
180+
181+
long creationTime = properties.getCreationTime();
182+
if (creationTime <= 0) {
183+
creationTime = System.currentTimeMillis();
184+
}
185+
mProps.setCreationTime(creationTime);
186+
187+
long absoluteExpiryTime = properties.getAbsoluteExpiryTime();
188+
if (absoluteExpiryTime > creationTime) {
189+
mProps.setExpiration(Long.toString(absoluteExpiryTime - creationTime));
190+
}
171191
}
172192
Map<String, Object> applicationProperties = streamMessage.getApplicationProperties();
173193
if (applicationProperties != null) {

spring-rabbit-stream/src/test/java/org/springframework/rabbit/stream/support/converter/DefaultStreamMessageConverterTests.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,13 @@
2424
import org.springframework.rabbit.stream.support.StreamMessageProperties;
2525

2626
import static org.assertj.core.api.Assertions.assertThat;
27+
import static org.assertj.core.api.Assertions.withinPercentage;
2728
import static org.mockito.Mockito.mock;
2829

2930
/**
3031
* @author Gary Russell
32+
* @author Artem Bilan
33+
*
3134
* @since 2.4
3235
*
3336
*/
@@ -45,7 +48,7 @@ void toAndFrom() {
4548
smp.setContentType("application/json");
4649
smp.setContentEncoding("UTF-8");
4750
smp.setExpiration("42");
48-
smp.setCreationTime(43L);
51+
smp.setCreationTime(System.currentTimeMillis());
4952
smp.setGroupId("groupId");
5053
smp.setGroupSequence(44L);
5154
smp.setReplyToGroupId("replyGroupId");
@@ -62,8 +65,8 @@ void toAndFrom() {
6265
assertThat(props.getCorrelationIdAsString()).isEqualTo("correlation");
6366
assertThat(props.getContentType()).isEqualTo("application/json");
6467
assertThat(props.getContentEncoding()).isEqualTo("UTF-8");
65-
assertThat(props.getAbsoluteExpiryTime()).isEqualTo(42L);
66-
assertThat(props.getCreationTime()).isEqualTo(43L);
68+
assertThat(props.getAbsoluteExpiryTime()).isCloseTo(System.currentTimeMillis() + 42, withinPercentage(5));
69+
assertThat(props.getCreationTime()).isCloseTo(System.currentTimeMillis(), withinPercentage(5));
6770
assertThat(props.getGroupId()).isEqualTo("groupId");
6871
assertThat(props.getGroupSequence()).isEqualTo(44L);
6972
assertThat(props.getReplyToGroupId()).isEqualTo("replyGroupId");

0 commit comments

Comments
 (0)