Skip to content

Commit eb62d8a

Browse files
author
Henrik Adamski
authored
LIBS-781 - Add OptionalResponseSpringRabbitListener and remove requir… (#10)
* LIBS-781 - Add OptionalResponseSpringRabbitListener and remove requirement of correlationId in RequestResponseSpringRabbitListener * LIBS-781 - Fix build * LIBS-781 - Fix sonar * LIBS-781 - Fix sonar * LIBS-781 - PR * LIBS-781 - PR * LIBS-781 - PR
1 parent 584761f commit eb62d8a

File tree

7 files changed

+277
-30
lines changed

7 files changed

+277
-30
lines changed

.github/workflows/review.yml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,33 @@ jobs:
1818

1919
- name: checkout
2020
uses: actions/checkout@v2
21+
with:
22+
ref: ${{ github.head_ref }}
23+
token: ${{ secrets.GPR_TOKEN }}
24+
fetch-depth: ''
25+
26+
- name: post-checkout
27+
run: git fetch --prune --unshallow
28+
29+
- name: action-configuration-autoupdate
30+
id: actions_action_configuration_autoupdate
31+
uses: avides/actions-action-configuration-autoupdate@v1
32+
with:
33+
token: ${{ secrets.GPR_TOKEN }}
34+
actions-configuration-files: os-java-library/nightly.yml,os-java-library/release.yml,os-java-library/review.yml
35+
source-repository: ${{ secrets.ACTIONS_CONFIG_AUTOUPDATE_REPO }}
36+
37+
- uses: stefanzweifel/git-auto-commit-action@v4
38+
with:
39+
file_pattern: .github/workflows/*.yml
40+
commit_user_name: ${{ secrets.ACTIONS_CONFIG_AUTOUPDATE_USER }}
41+
commit_user_email: ${{ secrets.ACTIONS_CONFIG_AUTOUPDATE_EMAIL }}
42+
commit_author: ${{ secrets.ACTIONS_CONFIG_AUTOUPDATE_AUTHOR }}
43+
commit_message: Update GitHub Action configuration
44+
45+
- name: action-configuration-updated
46+
if: ${{ steps.actions_action_configuration_autoupdate.outputs.updated }}
47+
run: exit 1;
2148

2249
- name: project-version-check
2350
uses: avides/actions-project-version-check@v1

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
<dependency>
1313
<groupId>com.avides.spring</groupId>
1414
<artifactId>spring-rabbit</artifactId>
15-
<version>2.2.0</version>
15+
<version>2.3.0</version>
1616
</dependency>
1717
```
1818

pom.xml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
<groupId>com.avides.spring</groupId>
55
<artifactId>spring-rabbit</artifactId>
6-
<version>2.2.0</version>
6+
<version>2.3.0</version>
77

88
<name>spring-rabbit</name>
99
<description>Makes configuring RabbitMQ for Spring Boot applications more comfortable</description>
@@ -59,11 +59,11 @@
5959
<github-release-plugin.version>1.4.0</github-release-plugin.version>
6060
<nexus-staging-maven-plugin.version>1.6.8</nexus-staging-maven-plugin.version>
6161
<!-- Spring -->
62-
<spring.version>5.2.7.RELEASE</spring.version>
63-
<spring-boot.version>2.3.1.RELEASE</spring-boot.version>
62+
<spring.version>5.2.8.RELEASE</spring.version>
63+
<spring-boot.version>2.3.2.RELEASE</spring-boot.version>
6464
<http-client.version>3.7.0.RELEASE</http-client.version>
6565
<httpclient.version>4.5.12</httpclient.version>
66-
<micrometer-core.version>1.5.1</micrometer-core.version>
66+
<micrometer-core.version>1.5.3</micrometer-core.version>
6767
<!-- Validation -->
6868
<hibernate-validator.version>6.0.20.Final</hibernate-validator.version>
6969
<!-- Other -->
@@ -77,7 +77,7 @@
7777
<jacoco.version>0.8.5</jacoco.version>
7878
<powermock.version>2.0.7</powermock.version>
7979
<easymock.version>4.2</easymock.version>
80-
<spring-xom-unmarshaller.version>1.3.0</spring-xom-unmarshaller.version>
80+
<spring-xom-unmarshaller.version>1.4.0</spring-xom-unmarshaller.version>
8181
<javax.el.version>3.0.0</javax.el.version>
8282
<springtainer-rabbitmq.version>1.1.0</springtainer-rabbitmq.version>
8383
<logback-classic.version>1.2.3</logback-classic.version>
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package com.avides.spring.rabbit.listener;
2+
3+
import org.springframework.amqp.core.MessageProperties;
4+
import org.springframework.amqp.rabbit.core.RabbitTemplate;
5+
6+
/**
7+
* Abstract implementation of {@link SpringRabbitListener} with metrics to handle request messages and eventually produce corresponding response messages.
8+
*
9+
* @param <T> expected type of the incoming object
10+
*/
11+
public abstract class OptionalResponseSpringRabbitListener<T> extends AbstractSpringRabbitListener<T>
12+
{
13+
private RabbitTemplate responseRabbitTemplate;
14+
15+
/**
16+
* Constructs a new {@link OptionalResponseSpringRabbitListener} with the given {@link RabbitTemplate} for responses.
17+
*
18+
* @param responseRabbitTemplate used for response messages
19+
*/
20+
public OptionalResponseSpringRabbitListener(RabbitTemplate responseRabbitTemplate)
21+
{
22+
this.responseRabbitTemplate = responseRabbitTemplate;
23+
}
24+
25+
/**
26+
* Processes unmarshaled request message and returns the (not yet marshalled) response message.
27+
*
28+
* @param requestObject unmarshalled request message
29+
* @return response message (not yet marshalled)
30+
*/
31+
protected abstract Object processRequest(T requestObject);
32+
33+
/**
34+
* Handles given unmarshalled message with its properties and eventually (if response object and queue are given) sends a response message. Called by
35+
* {@link #handle(Object, MessageProperties)} which also collects some metrics. Calls {@link #processRequest(Object)} by default which must be overridden.
36+
*
37+
* @param requestObject the unmarshaled object
38+
* @param messageProperties the message properties
39+
*/
40+
@Override
41+
protected void handleEvent(T requestObject, MessageProperties messageProperties)
42+
{
43+
var replyQueueName = messageProperties.getReplyTo();
44+
45+
var responseObject = processRequest(requestObject);
46+
47+
if (responseObject != null && replyQueueName != null)
48+
{
49+
responseRabbitTemplate.convertAndSend("", replyQueueName, responseObject, message ->
50+
{
51+
message.getMessageProperties().setCorrelationId(messageProperties.getCorrelationId());
52+
return message;
53+
});
54+
}
55+
}
56+
}

src/main/java/com/avides/spring/rabbit/listener/RequestResponseSpringRabbitListener.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,19 +35,21 @@ public RequestResponseSpringRabbitListener(RabbitTemplate responseRabbitTemplate
3535
* Handles given unmarshaled message with its properties and sends a response message. Called by {@link #handle(Object, MessageProperties)} which also
3636
* collects some metrics. Calls {@link #processRequest(Object)} by default which must be overridden.
3737
*
38+
* You have to set "reply_to" in your {@link MessageProperties}. Additionally you can optional set a "correlation_id". These parameters are used for the
39+
* response message for better identification on request side.
40+
*
3841
* @param requestObject the unmarshaled object
3942
* @param messageProperties the message properties
4043
*/
4144
@Override
4245
protected void handleEvent(T requestObject, MessageProperties messageProperties)
4346
{
44-
String correlationId = messageProperties.getCorrelationId();
45-
String replyQueueName = messageProperties.getReplyTo();
47+
var correlationId = messageProperties.getCorrelationId();
48+
var replyQueueName = messageProperties.getReplyTo();
4649

47-
Assert.notNull(correlationId, "correlation_id must not be null");
4850
Assert.notNull(replyQueueName, "reply_to must not be null");
4951

50-
Object responseObject = processRequest(requestObject);
52+
var responseObject = processRequest(requestObject);
5153

5254
if (responseObject != null)
5355
{
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
package com.avides.spring.rabbit.listener;
2+
3+
import static org.easymock.EasyMock.eq;
4+
import static org.easymock.EasyMock.expect;
5+
import static org.easymock.EasyMock.mock;
6+
import static org.powermock.api.easymock.PowerMock.replayAll;
7+
import static org.powermock.api.easymock.PowerMock.verifyAll;
8+
9+
import org.easymock.EasyMock;
10+
import org.junit.Before;
11+
import org.junit.Test;
12+
import org.junit.runner.RunWith;
13+
import org.powermock.api.easymock.annotation.MockStrict;
14+
import org.powermock.core.classloader.annotations.PowerMockIgnore;
15+
import org.powermock.modules.junit4.PowerMockRunner;
16+
import org.powermock.reflect.Whitebox;
17+
import org.springframework.amqp.core.MessagePostProcessor;
18+
import org.springframework.amqp.core.MessageProperties;
19+
import org.springframework.amqp.rabbit.core.RabbitTemplate;
20+
21+
import io.micrometer.core.instrument.Counter;
22+
import io.micrometer.core.instrument.MeterRegistry;
23+
import io.micrometer.core.instrument.Tag;
24+
import io.micrometer.core.instrument.Tags;
25+
26+
@RunWith(PowerMockRunner.class)
27+
@PowerMockIgnore("javax.management.*")
28+
public class OptionalResponseSpringRabbitListenerTest
29+
{
30+
private OptionalResponseSpringRabbitListener<Object> successRabbitListener;
31+
32+
private OptionalResponseSpringRabbitListener<Object> failureRabbitListener;
33+
34+
@MockStrict
35+
private RabbitTemplate responseRabbitTemplate;
36+
37+
@MockStrict
38+
private MeterRegistry meterRegistry;
39+
40+
@Before
41+
public void setup()
42+
{
43+
successRabbitListener = new SuccessOptionalSpringRabbitListener();
44+
Whitebox.setInternalState(successRabbitListener, meterRegistry);
45+
46+
failureRabbitListener = new FailureOptionalSpringRabbitListener();
47+
Whitebox.setInternalState(failureRabbitListener, meterRegistry);
48+
}
49+
50+
@Test
51+
public void testHandleEventWithoutReplyTo()
52+
{
53+
var tags = Tags.of(Tag.of("listener", "SuccessOptionalSpringRabbitListener"), Tag.of("from", "sender-app"));
54+
55+
expect(meterRegistry.counter("rabbit.listener.event", tags)).andReturn(mock(Counter.class));
56+
expect(meterRegistry.counter("rabbit.listener.event.total.duration.milliseconds", tags)).andReturn(mock(Counter.class));
57+
58+
replayAll();
59+
var messageProperties = new MessageProperties();
60+
messageProperties.setAppId("sender-app");
61+
messageProperties.setCorrelationId("request1");
62+
successRabbitListener.handle("", messageProperties);
63+
verifyAll();
64+
}
65+
66+
@Test
67+
public void testHandleEventWithoutResponse()
68+
{
69+
var tags = Tags.of(Tag.of("listener", "FailureOptionalSpringRabbitListener"), Tag.of("from", "sender-app"));
70+
71+
expect(meterRegistry.counter("rabbit.listener.event", tags)).andReturn(mock(Counter.class));
72+
expect(meterRegistry.counter("rabbit.listener.event.total.duration.milliseconds", tags)).andReturn(mock(Counter.class));
73+
74+
replayAll();
75+
var messageProperties = new MessageProperties();
76+
messageProperties.setAppId("sender-app");
77+
messageProperties.setCorrelationId("request1");
78+
messageProperties.setReplyTo("response-queue");
79+
failureRabbitListener.handle("", messageProperties);
80+
verifyAll();
81+
}
82+
83+
@Test
84+
public void testHandleEventWithoutResponseAndAppId()
85+
{
86+
var tags = Tags.of(Tag.of("listener", "FailureOptionalSpringRabbitListener"), Tag.of("from", "UNKNOWN"));
87+
88+
expect(meterRegistry.counter("rabbit.listener.event", tags)).andReturn(mock(Counter.class));
89+
expect(meterRegistry.counter("rabbit.listener.event.total.duration.milliseconds", tags)).andReturn(mock(Counter.class));
90+
91+
replayAll();
92+
var messageProperties = new MessageProperties();
93+
messageProperties.setCorrelationId("request1");
94+
messageProperties.setReplyTo("response-queue");
95+
failureRabbitListener.handle("", messageProperties);
96+
verifyAll();
97+
}
98+
99+
@Test
100+
public void testHandleEvent()
101+
{
102+
var tags = Tags.of(Tag.of("listener", "SuccessOptionalSpringRabbitListener"), Tag.of("from", "sender-app"));
103+
104+
responseRabbitTemplate.convertAndSend(eq(""), eq("response-queue"), eq("response"), EasyMock.anyObject(MessagePostProcessor.class));
105+
expect(meterRegistry.counter("rabbit.listener.event", tags)).andReturn(mock(Counter.class));
106+
expect(meterRegistry.counter("rabbit.listener.event.total.duration.milliseconds", tags)).andReturn(mock(Counter.class));
107+
108+
replayAll();
109+
var messageProperties = new MessageProperties();
110+
messageProperties.setAppId("sender-app");
111+
messageProperties.setCorrelationId("request1");
112+
messageProperties.setReplyTo("response-queue");
113+
successRabbitListener.handle("", messageProperties);
114+
verifyAll();
115+
}
116+
117+
@Test
118+
public void testHandleEventWithoutAppId()
119+
{
120+
var tags = Tags.of(Tag.of("listener", "SuccessOptionalSpringRabbitListener"), Tag.of("from", "UNKNOWN"));
121+
122+
responseRabbitTemplate.convertAndSend(eq(""), eq("response-queue"), eq("response"), EasyMock.anyObject(MessagePostProcessor.class));
123+
expect(meterRegistry.counter("rabbit.listener.event", tags)).andReturn(mock(Counter.class));
124+
expect(meterRegistry.counter("rabbit.listener.event.total.duration.milliseconds", tags)).andReturn(mock(Counter.class));
125+
126+
replayAll();
127+
var messageProperties = new MessageProperties();
128+
messageProperties.setCorrelationId("request1");
129+
messageProperties.setReplyTo("response-queue");
130+
successRabbitListener.handle("", messageProperties);
131+
verifyAll();
132+
}
133+
134+
private class SuccessOptionalSpringRabbitListener extends OptionalResponseSpringRabbitListener<Object>
135+
{
136+
public SuccessOptionalSpringRabbitListener()
137+
{
138+
super(responseRabbitTemplate);
139+
}
140+
141+
@Override
142+
protected Object processRequest(Object requestObject)
143+
{
144+
return "response";
145+
}
146+
}
147+
148+
private class FailureOptionalSpringRabbitListener extends OptionalResponseSpringRabbitListener<Object>
149+
{
150+
public FailureOptionalSpringRabbitListener()
151+
{
152+
super(responseRabbitTemplate);
153+
}
154+
155+
@Override
156+
protected Object processRequest(Object requestObject)
157+
{
158+
return null;
159+
}
160+
}
161+
}

0 commit comments

Comments
 (0)