Skip to content

Commit 7fcb568

Browse files
authored
HeadersPayloadExtractor should support case insensitive event headers (#4138)
When a custom event implementation is using case insensitive headers then our `HeadersPayloadExtractor` was not properly working due to the fact that we were checking if a header with a specific name is configured on our side. However, we should do the opposite. Check if there is a header with the configured name in the event headers.
1 parent 7f5dc7c commit 7fcb568

File tree

2 files changed

+94
-35
lines changed

2 files changed

+94
-35
lines changed

modules/flowable-engine/src/test/java/org/flowable/engine/test/eventregistry/BpmnHeaderEventRegistryConsumerTest.java

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.junit.jupiter.api.AfterEach;
3636
import org.junit.jupiter.api.BeforeEach;
3737
import org.junit.jupiter.api.Test;
38+
import org.springframework.util.LinkedCaseInsensitiveMap;
3839

3940
import com.fasterxml.jackson.core.JsonProcessingException;
4041
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -112,6 +113,58 @@ public void testProcessStartWithHeaders() {
112113
);
113114
}
114115

116+
@Test
117+
@Deployment(resources = "org/flowable/engine/test/eventregistry/BpmnHeaderEventRegistryConsumerTest.testProcessStartWithHeaders.bpmn20.xml")
118+
public void testProcessStartWithMissingHeaders() {
119+
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey("process").singleResult();
120+
assertThat(processDefinition).isNotNull();
121+
122+
EventSubscription eventSubscription = runtimeService.createEventSubscriptionQuery()
123+
.processDefinitionId(processDefinition.getId())
124+
.scopeType(ScopeTypes.BPMN)
125+
.singleResult();
126+
assertThat(eventSubscription).isNotNull();
127+
assertThat(eventSubscription.getEventType()).isEqualTo("myEvent");
128+
129+
assertThat(runtimeService.createProcessInstanceQuery().list()).isEmpty();
130+
131+
inboundEventChannelAdapter.triggerTestEventWithHeaders("payloadStartCustomer", null, null);
132+
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processDefinitionKey("process").singleResult();
133+
assertThat(runtimeService.getVariables(processInstance.getId()))
134+
.containsOnly(
135+
entry("customerIdVar", "payloadStartCustomer"),
136+
entry("payload1", "Hello World"),
137+
entry("myHeaderValue1", null),
138+
entry("myHeaderValue2", null)
139+
);
140+
}
141+
142+
@Test
143+
@Deployment(resources = "org/flowable/engine/test/eventregistry/BpmnHeaderEventRegistryConsumerTest.testProcessStartWithHeaders.bpmn20.xml")
144+
public void testProcessStartWithCaseInsensitiveEventHeaders() {
145+
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey("process").singleResult();
146+
assertThat(processDefinition).isNotNull();
147+
148+
EventSubscription eventSubscription = runtimeService.createEventSubscriptionQuery()
149+
.processDefinitionId(processDefinition.getId())
150+
.scopeType(ScopeTypes.BPMN)
151+
.singleResult();
152+
assertThat(eventSubscription).isNotNull();
153+
assertThat(eventSubscription.getEventType()).isEqualTo("myEvent");
154+
155+
assertThat(runtimeService.createProcessInstanceQuery().list()).isEmpty();
156+
157+
inboundEventChannelAdapter.triggerTestEventWithCaseInsensitiveHeaders("payloadStartCustomer", "testHeader", 1234);
158+
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processDefinitionKey("process").singleResult();
159+
assertThat(runtimeService.getVariables(processInstance.getId()))
160+
.containsOnly(
161+
entry("customerIdVar", "payloadStartCustomer"),
162+
entry("payload1", "Hello World"),
163+
entry("myHeaderValue1", "testHeader"),
164+
entry("myHeaderValue2", 1234)
165+
);
166+
}
167+
115168
private static class TestInboundEventChannelAdapter implements InboundEventChannelAdapter {
116169

117170
public InboundChannelModel inboundChannelModel;
@@ -135,8 +188,25 @@ public void setEventRegistry(EventRegistry eventRegistry) {
135188
public void triggerTestEventWithHeaders(String customerId, String headerValue1, Integer headerValue2) {
136189
ObjectNode eventNode = createTestEventNode(customerId, null);
137190
Map<String, Object> headers = new HashMap<>();
138-
headers.put("headerProperty1", headerValue1);
139-
headers.put("headerProperty2", headerValue2);
191+
if (headerValue1 != null) {
192+
headers.put("headerProperty1", headerValue1);
193+
}
194+
if (headerValue2 != null) {
195+
headers.put("headerProperty2", headerValue2);
196+
}
197+
try {
198+
String event = objectMapper.writeValueAsString(eventNode);
199+
eventRegistry.eventReceived(inboundChannelModel, new DefaultInboundEvent(event, headers));
200+
} catch (JsonProcessingException e) {
201+
throw new RuntimeException(e);
202+
}
203+
}
204+
205+
public void triggerTestEventWithCaseInsensitiveHeaders(String customerId, String headerValue1, Integer headerValue2) {
206+
ObjectNode eventNode = createTestEventNode(customerId, null);
207+
Map<String, Object> headers = new LinkedCaseInsensitiveMap<>();
208+
headers.put("HeaderProperty1", headerValue1);
209+
headers.put("HeaderProperty2", headerValue2);
140210
try {
141211
String event = objectMapper.writeValueAsString(eventNode);
142212
eventRegistry.eventReceived(inboundChannelModel, new DefaultInboundEvent(event, headers));

modules/flowable-event-registry/src/main/java/org/flowable/eventregistry/impl/payload/HeadersPayloadExtractor.java

Lines changed: 22 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,10 @@
1313
package org.flowable.eventregistry.impl.payload;
1414

1515
import java.nio.charset.StandardCharsets;
16+
import java.util.ArrayList;
1617
import java.util.Collection;
1718
import java.util.Collections;
18-
import java.util.HashMap;
1919
import java.util.Map;
20-
import java.util.stream.Collectors;
2120

2221
import org.flowable.eventregistry.api.FlowableEventInfo;
2322
import org.flowable.eventregistry.api.InboundEventInfoAwarePayloadExtractor;
@@ -35,52 +34,42 @@ public Collection<EventPayloadInstance> extractPayload(EventModel eventModel, Fl
3534
if (headers.isEmpty()) {
3635
return Collections.emptyList();
3736
}
38-
Map<String, Object> filteredHeaders = convertHeaderValues(event, eventModel);
39-
return headers.stream()
40-
.filter(headerDefinition -> filteredHeaders.containsKey(headerDefinition.getName()))
41-
.map(headerDefinition -> new EventPayloadInstanceImpl(headerDefinition, filteredHeaders.get(headerDefinition.getName())))
42-
.collect(Collectors.toList());
43-
}
44-
45-
protected Map<String, Object> convertHeaderValues(FlowableEventInfo<T> eventInfo, EventModel eventModel) {
46-
Map<String, Object> filteredHeaders = new HashMap<>();
47-
if (eventInfo.getHeaders() != null) {
48-
Map<String, Object> headers = eventInfo.getHeaders();
49-
for (String headerName : headers.keySet()) {
50-
EventPayload eventHeaderDef = eventModel.getPayload(headerName);
51-
if (eventHeaderDef != null && eventHeaderDef.isHeader()) {
52-
Object headerValueObject = headers.get(headerName);
53-
if (headerValueObject instanceof byte[]) {
54-
byte[] headerValue = (byte[]) headers.get(headerName);
55-
convertBytesHeaderValue(headerName, headerValue, filteredHeaders, eventHeaderDef);
56-
} else {
57-
filteredHeaders.put(headerName, headerValueObject);
58-
}
37+
Map<String, Object> eventHeaders = event.getHeaders();
38+
if (eventHeaders == null || eventHeaders.isEmpty()) {
39+
return Collections.emptyList();
40+
}
41+
Collection<EventPayloadInstance> eventPayloadHeaderInstances = new ArrayList<>(headers.size());
42+
for (EventPayload header : headers) {
43+
if (eventHeaders.containsKey(header.getName())) {
44+
Object headerValueObject = eventHeaders.get(header.getName());
45+
Object headerValue = headerValueObject;
46+
if (headerValueObject instanceof byte[] headerValueBytes) {
47+
headerValue = convertBytesHeaderValue(headerValueBytes, header);
5948
}
49+
eventPayloadHeaderInstances.add(new EventPayloadInstanceImpl(header, headerValue));
6050
}
6151
}
62-
63-
return filteredHeaders;
52+
return eventPayloadHeaderInstances;
6453
}
65-
66-
protected void convertBytesHeaderValue(String headerName, byte[] headerValue, Map<String, Object> filteredHeaders, EventPayload eventHeaderDef) {
54+
55+
protected Object convertBytesHeaderValue(byte[] headerValue, EventPayload eventHeaderDef) {
6756
if (EventPayloadTypes.STRING.equals(eventHeaderDef.getType())) {
68-
filteredHeaders.put(headerName, convertBytesToString(headerValue));
57+
return convertBytesToString(headerValue);
6958

7059
} else if (EventPayloadTypes.DOUBLE.equals(eventHeaderDef.getType())) {
71-
filteredHeaders.put(headerName, Double.valueOf(convertBytesToString(headerValue)));
60+
return Double.valueOf(convertBytesToString(headerValue));
7261

7362
} else if (EventPayloadTypes.INTEGER.equals(eventHeaderDef.getType())) {
74-
filteredHeaders.put(headerName, Integer.valueOf(convertBytesToString(headerValue)));
63+
return Integer.valueOf(convertBytesToString(headerValue));
7564

7665
} else if (EventPayloadTypes.LONG.equals(eventHeaderDef.getType())) {
77-
filteredHeaders.put(headerName, Long.valueOf(convertBytesToString(headerValue)));
66+
return Long.valueOf(convertBytesToString(headerValue));
7867

7968
} else if (EventPayloadTypes.BOOLEAN.equals(eventHeaderDef.getType())) {
80-
filteredHeaders.put(headerName, Boolean.valueOf(convertBytesToString(headerValue)));
69+
return Boolean.valueOf(convertBytesToString(headerValue));
8170

8271
} else {
83-
filteredHeaders.put(headerName, headerValue);
72+
return headerValue;
8473
}
8574
}
8675

0 commit comments

Comments
 (0)