INT-3513: MutableMessage: Removes the use of DFA
JIRA: https://jira.spring.io/browse/INT-3513 Removing the `rawHeader` access by the `DFA` improves performance. Tests are added to verify that `MutableMessageBuilderFactory` does what it purports to, and that the `MutableMessageHeaders` is now applied, permitting updates to individual headers. change `@since` to correct/probable release version INT-3513: Polishing Change @since to 4.1.5
This commit is contained in:
committed by
Gary Russell
parent
5b395a8c5c
commit
8c24bf1dd9
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
* Copyright 2014-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -13,12 +13,12 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.support;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.integration.store.SimpleMessageStore;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
@@ -36,6 +36,7 @@ import org.springframework.util.ObjectUtils;
|
||||
*
|
||||
* @author Gary Russell
|
||||
* @author Artem Bilan
|
||||
* @author Stuart Williams
|
||||
* @since 4.0
|
||||
*
|
||||
*/
|
||||
@@ -43,33 +44,29 @@ class MutableMessage<T> implements Message<T>, Serializable {
|
||||
|
||||
private static final long serialVersionUID = -636635024258737500L;
|
||||
|
||||
private T payload;
|
||||
private final T payload;
|
||||
|
||||
private final MessageHeaders headers;
|
||||
|
||||
private final Map<String, Object> rawHeaders;
|
||||
private final MutableMessageHeaders headers;
|
||||
|
||||
MutableMessage(T payload) {
|
||||
this(payload, null);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
MutableMessage(T payload, Map<String, Object> headers) {
|
||||
Assert.notNull(payload, "payload must not be null");
|
||||
this.headers = new MessageHeaders(headers);
|
||||
this.payload = payload;
|
||||
// Needs SPR-11468 to avoid DFA and header manipulation
|
||||
rawHeaders = (Map<String, Object>) new DirectFieldAccessor(this.headers)
|
||||
.getPropertyValue("headers");
|
||||
|
||||
this.headers = new MutableMessageHeaders(headers);
|
||||
|
||||
if (headers != null) {
|
||||
this.rawHeaders.put(MessageHeaders.ID, headers.get(MessageHeaders.ID));
|
||||
this.rawHeaders.put(MessageHeaders.TIMESTAMP, headers.get(MessageHeaders.TIMESTAMP));
|
||||
this.headers.put(MessageHeaders.ID, headers.get(MessageHeaders.ID));
|
||||
this.headers.put(MessageHeaders.TIMESTAMP, headers.get(MessageHeaders.TIMESTAMP));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public MessageHeaders getHeaders() {
|
||||
public MutableMessageHeaders getHeaders() {
|
||||
return this.headers;
|
||||
}
|
||||
|
||||
@@ -78,13 +75,8 @@ class MutableMessage<T> implements Message<T>, Serializable {
|
||||
return this.payload;
|
||||
}
|
||||
|
||||
public void setPayload(T payload) {
|
||||
Assert.notNull(payload, "'payload' must not be null");
|
||||
this.payload = payload;
|
||||
}
|
||||
|
||||
public Map<String, Object> getRawHeaders() {
|
||||
return this.rawHeaders;
|
||||
Map<String, Object> getRawHeaders() {
|
||||
return this.headers.getRawHeaders();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
* Copyright 2014-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.support;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -34,7 +35,7 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
public class MutableMessageBuilder<T> extends AbstractIntegrationMessageBuilder<T> {
|
||||
|
||||
private MutableMessage<T> mutableMessage;
|
||||
private final MutableMessage<T> mutableMessage;
|
||||
|
||||
private final Map<String, Object> headers;
|
||||
|
||||
@@ -55,27 +56,23 @@ public class MutableMessageBuilder<T> extends AbstractIntegrationMessageBuilder<
|
||||
/**
|
||||
* Create a builder for a new {@link Message} instance pre-populated with all of the headers copied from the
|
||||
* provided message. The payload of the provided Message will also be used as the payload for the new message.
|
||||
*
|
||||
* @param message the Message from which the payload and all headers will be copied
|
||||
* @param <T> The type of the payload.
|
||||
* @return A MutableMessageBuilder.
|
||||
*/
|
||||
public static <T> MutableMessageBuilder<T> fromMessage(Message<T> message) {
|
||||
Assert.notNull(message, "message must not be null");
|
||||
MutableMessageBuilder<T> builder = new MutableMessageBuilder<T>(message);
|
||||
return builder;
|
||||
return new MutableMessageBuilder<T>(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a builder for a new {@link Message} instance with the provided payload.
|
||||
*
|
||||
* @param payload the payload for the new message
|
||||
* @param <T> The type of the payload.
|
||||
* @return A MessageBuilder.
|
||||
*/
|
||||
public static <T> MutableMessageBuilder<T> withPayload(T payload) {
|
||||
MutableMessageBuilder<T> builder = new MutableMessageBuilder<T>(new MutableMessage<T>(payload));
|
||||
return builder;
|
||||
return new MutableMessageBuilder<T>(new MutableMessage<T>(payload));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -102,8 +99,8 @@ public class MutableMessageBuilder<T> extends AbstractIntegrationMessageBuilder<
|
||||
public AbstractIntegrationMessageBuilder<T> removeHeaders(String... headerPatterns) {
|
||||
List<String> headersToRemove = new ArrayList<String>();
|
||||
for (String pattern : headerPatterns) {
|
||||
if (StringUtils.hasLength(pattern)){
|
||||
if (pattern.contains("*")){
|
||||
if (StringUtils.hasLength(pattern)) {
|
||||
if (pattern.contains("*")) {
|
||||
headersToRemove.addAll(getMatchingHeaderNames(pattern, this.headers));
|
||||
}
|
||||
else {
|
||||
@@ -120,8 +117,8 @@ public class MutableMessageBuilder<T> extends AbstractIntegrationMessageBuilder<
|
||||
private List<String> getMatchingHeaderNames(String pattern, Map<String, Object> headers) {
|
||||
List<String> matchingHeaderNames = new ArrayList<String>();
|
||||
if (headers != null) {
|
||||
for (Map.Entry<String, Object> header: headers.entrySet()) {
|
||||
if (PatternMatchUtils.simpleMatch(pattern, header.getKey())) {
|
||||
for (Map.Entry<String, Object> header : headers.entrySet()) {
|
||||
if (PatternMatchUtils.simpleMatch(pattern, header.getKey())) {
|
||||
matchingHeaderNames.add(header.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.support;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
|
||||
|
||||
/**
|
||||
* A MessageHeaders that permits direct access to and modification of the
|
||||
* header map.
|
||||
*
|
||||
* @author Stuart Williams
|
||||
* @since 4.1.5
|
||||
*/
|
||||
class MutableMessageHeaders extends MessageHeaders {
|
||||
|
||||
private static final long serialVersionUID = 3084692953798643018L;
|
||||
|
||||
MutableMessageHeaders(Map<String, Object> headers) {
|
||||
super(headers);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, Object> getRawHeaders() {
|
||||
return super.getRawHeaders();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(Map<? extends String, ? extends Object> map) {
|
||||
super.getRawHeaders().putAll(map);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object put(String key, Object value) {
|
||||
return super.getRawHeaders().put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
super.getRawHeaders().clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object remove(Object key) {
|
||||
return super.getRawHeaders().remove(key);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.support;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.integration.annotation.Filter;
|
||||
import org.springframework.integration.annotation.Gateway;
|
||||
import org.springframework.integration.annotation.IntegrationComponentScan;
|
||||
import org.springframework.integration.annotation.MessageEndpoint;
|
||||
import org.springframework.integration.annotation.MessagingGateway;
|
||||
import org.springframework.integration.annotation.ServiceActivator;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.integration.config.EnableIntegration;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.messaging.handler.annotation.Payload;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
|
||||
/**
|
||||
* @author Stuart Williams
|
||||
* @since 4.1.5
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration
|
||||
public class MutableMessageBuilderFactoryTests {
|
||||
|
||||
@Autowired
|
||||
ContextConfiguration.TestGateway gateway;
|
||||
|
||||
@Autowired
|
||||
CountDownLatch latch;
|
||||
|
||||
@Test
|
||||
public void test() throws InterruptedException {
|
||||
|
||||
this.gateway.input("hello!");
|
||||
|
||||
boolean result = this.latch.await(2L, TimeUnit.SECONDS);
|
||||
|
||||
assertTrue("A failure means that that MMBF wasn't used", result);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableIntegration
|
||||
@IntegrationComponentScan
|
||||
static class ContextConfiguration {
|
||||
|
||||
@Bean
|
||||
public MutableMessageBuilderFactory messageBuilderFactory() {
|
||||
return new MutableMessageBuilderFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DirectChannel input() {
|
||||
return new DirectChannel();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DirectChannel output() {
|
||||
return new DirectChannel();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CountDownLatch latch() {
|
||||
return new CountDownLatch(1);
|
||||
}
|
||||
|
||||
@MessagingGateway
|
||||
static interface TestGateway {
|
||||
|
||||
@Gateway(requestChannel = "input")
|
||||
void input(String payload);
|
||||
|
||||
}
|
||||
|
||||
@MessageEndpoint
|
||||
static class TestFilter {
|
||||
|
||||
@Filter(inputChannel = "input", outputChannel = "output")
|
||||
public boolean filter(MessageHeaders headers) {
|
||||
// headers are immutable, so if this passes without exception,
|
||||
// the MutableMessageBuilderFactory *was* used...
|
||||
try {
|
||||
headers.put("foo", "bar");
|
||||
return true;
|
||||
}
|
||||
catch (UnsupportedOperationException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@MessageEndpoint
|
||||
static class Counter {
|
||||
|
||||
@Autowired
|
||||
CountDownLatch latch;
|
||||
|
||||
@ServiceActivator(inputChannel = "output")
|
||||
public void count(@Payload String message) {
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.support;
|
||||
|
||||
import static org.hamcrest.Matchers.hasEntry;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
|
||||
|
||||
/**
|
||||
* @author Stuart Williams
|
||||
* @since 4.1.5
|
||||
*/
|
||||
public class MutableMessageTests {
|
||||
|
||||
@Test
|
||||
public void testMessageIdTimestampRemains() {
|
||||
|
||||
UUID uuid = UUID.randomUUID();
|
||||
long timestamp = System.currentTimeMillis();
|
||||
|
||||
Object payload = new Object();
|
||||
Map<String, Object> headerMap = new HashMap<>();
|
||||
|
||||
headerMap.put(MessageHeaders.ID, uuid);
|
||||
headerMap.put(MessageHeaders.TIMESTAMP, timestamp);
|
||||
|
||||
MutableMessage<Object> mutableMessage = new MutableMessage<>(payload, headerMap);
|
||||
MutableMessageHeaders headers = mutableMessage.getHeaders();
|
||||
|
||||
assertThat(headers.getRawHeaders(), hasEntry(MessageHeaders.ID, (Object) uuid));
|
||||
assertThat(headers.getRawHeaders(), hasEntry(MessageHeaders.TIMESTAMP, (Object) timestamp));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMessageHeaderIsSettable() {
|
||||
|
||||
Object payload = new Object();
|
||||
Map<String, Object> headerMap = new HashMap<>();
|
||||
Map<String, Object> additional = new HashMap<>();
|
||||
|
||||
MutableMessage<Object> mutableMessage = new MutableMessage<>(payload, headerMap);
|
||||
MutableMessageHeaders headers = mutableMessage.getHeaders();
|
||||
|
||||
// Should not throw an UnsupportedOperationException
|
||||
headers.put("foo", "bar");
|
||||
headers.put("eep", "bar");
|
||||
headers.remove("eep");
|
||||
headers.putAll(additional);
|
||||
|
||||
assertThat(headers.getRawHeaders(), hasEntry("foo", (Object) "bar"));
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user