From 28a966f544468d53ebff74015133e5cb91a6a06f Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 22 Aug 2014 22:55:48 +0200 Subject: [PATCH] MessageBuilder avoids intermediate HashMap for MessageHeaders building Issue: SPR-11468 --- .../messaging/support/MessageBuilder.java | 5 +- .../support/MessageHeaderAccessor.java | 169 +++++++++++------- 2 files changed, 111 insertions(+), 63 deletions(-) diff --git a/spring-messaging/src/main/java/org/springframework/messaging/support/MessageBuilder.java b/spring-messaging/src/main/java/org/springframework/messaging/support/MessageBuilder.java index a7dcbf8b9e..22cc4bec71 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/support/MessageBuilder.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/support/MessageBuilder.java @@ -148,11 +148,12 @@ public final class MessageBuilder { if (this.originalMessage != null && !this.headerAccessor.isModified()) { return this.originalMessage; } + MessageHeaders headersToUse = this.headerAccessor.toMessageHeaders(); if (this.payload instanceof Throwable) { - return (Message) new ErrorMessage((Throwable) this.payload, this.headerAccessor.toMap()); + return (Message) new ErrorMessage((Throwable) this.payload, headersToUse); } else { - return new GenericMessage(this.payload, this.headerAccessor.toMap()); + return new GenericMessage(this.payload, headersToUse); } } diff --git a/spring-messaging/src/main/java/org/springframework/messaging/support/MessageHeaderAccessor.java b/spring-messaging/src/main/java/org/springframework/messaging/support/MessageHeaderAccessor.java index 4aec0b5596..7209177830 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/support/MessageHeaderAccessor.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/support/MessageHeaderAccessor.java @@ -51,7 +51,7 @@ import org.springframework.util.StringUtils; * *
  * MessageHeaderAccessor accessor = new MessageHeaderAccessor();
- * accessor.set("foo", "bar");
+ * accessor.setHeader("foo", "bar");
  * Message message = MessageBuilder.createMessage("payload", accessor.getMessageHeaders());
  * 
* @@ -61,14 +61,14 @@ import org.springframework.util.StringUtils; * *
  * MessageHeaderAccessor accessor = new MessageHeaderAccessor();
- * accessor.set("foo", "bar");
+ * accessor.setHeader("foo", "bar");
  * accessor.setLeaveMutable(true);
  * Message message = MessageBuilder.createMessage("payload", accessor.getMessageHeaders());
  *
  * // later on in the same thread...
  *
  * MessageHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message);
- * accessor.set("bar", "baz");
+ * accessor.setHeader("bar", "baz");
  * accessor.setImmutable();
  * 
* @@ -111,6 +111,7 @@ import org.springframework.util.StringUtils; * header accessors. The most likely usage however is through sub-classes. * * @author Rossen Stoyanchev + * @author Juergen Hoeller * @since 4.0 */ public class MessageHeaderAccessor { @@ -129,10 +130,10 @@ public class MessageHeaderAccessor { private boolean leaveMutable = false; - private boolean enableTimestamp = false; - private boolean modified = false; + private boolean enableTimestamp = false; + private IdGenerator idGenerator; @@ -156,34 +157,17 @@ public class MessageHeaderAccessor { } + /** + * Build a 'nested' accessor for the given message. + * @param message the message to build a new accessor for + * @return the nested accessor (typically a specific subclass) + */ protected MessageHeaderAccessor createAccessor(Message message) { return new MessageHeaderAccessor(message); } - /** - * Return the underlying {@code MessageHeaders} instance. - *

Unless {@link #setLeaveMutable(boolean)} was set to {@code true}, after - * this call, the headers are immutable and this accessor can no longer - * modify them. - *

This method always returns the same {@code MessageHeaders} instance if - * invoked multiples times. To obtain a copy of the underlying headers instead - * use {@link #toMap()}. - */ - public MessageHeaders getMessageHeaders() { - if (!this.leaveMutable) { - setImmutable(); - } - return this.headers; - } - /** - * Return a copy of the underlying header values. - *

This method can be invoked many times, with modifications in between - * where each new call returns a fresh copy of the current header values. - */ - public Map toMap() { - return new HashMap(this.headers); - } + // Configuration properties /** * By default when {@link #getMessageHeaders()} is called, {@code "this"} @@ -227,21 +211,30 @@ public class MessageHeaderAccessor { } /** - * A package private mechanism to enables the automatic addition of the - * {@link org.springframework.messaging.MessageHeaders#TIMESTAMP} header. - *

By default this property is set to false. - * @see IdTimestampMessageHeaderInitializer + * Mark the underlying message headers as modified. + * @param modified typically {@code true}, or {@code false} to reset the flag + * @since 4.1 */ - void setEnableTimestamp(boolean enableTimestamp) { - this.enableTimestamp = enableTimestamp; + protected void setModified(boolean modified) { + this.modified = modified; } + /** + * Check whether the underlying message headers have been marked as modified. + * @return {@code true} if the flag has been set, {@code false} otherwise + */ public boolean isModified() { return this.modified; } - protected void setModified(boolean modified) { - this.modified = modified; + /** + * A package private mechanism to enables the automatic addition of the + * {@link org.springframework.messaging.MessageHeaders#TIMESTAMP} header. + *

By default, this property is set to {@code false}. + * @see IdTimestampMessageHeaderInitializer + */ + void setEnableTimestamp(boolean enableTimestamp) { + this.enableTimestamp = enableTimestamp; } /** @@ -254,6 +247,53 @@ public class MessageHeaderAccessor { this.idGenerator = idGenerator; } + + // Accessors for the resulting MessageHeaders + + /** + * Return the underlying {@code MessageHeaders} instance. + *

Unless {@link #setLeaveMutable(boolean)} was set to {@code true}, after + * this call, the headers are immutable and this accessor can no longer + * modify them. + *

This method always returns the same {@code MessageHeaders} instance if + * invoked multiples times. To obtain a copy of the underlying headers, use + * {@link #toMessageHeaders()} or {@link #toMap()} instead. + * @since 4.1 + */ + public MessageHeaders getMessageHeaders() { + if (!this.leaveMutable) { + setImmutable(); + } + return this.headers; + } + + /** + * Return a copy of the underlying header values as a {@link MessageHeaders} object. + *

This method can be invoked many times, with modifications in between + * where each new call returns a fresh copy of the current header values. + * @since 4.1 + */ + public MessageHeaders toMessageHeaders() { + return new MessageHeaders(this.headers); + } + + /** + * Return a copy of the underlying header values as a plain {@link Map} object. + *

This method can be invoked many times, with modifications in between + * where each new call returns a fresh copy of the current header values. + */ + public Map toMap() { + return new HashMap(this.headers); + } + + + // Generic header accessors + + /** + * Retrieve the value for the header with the given name. + * @param headerName the name of the header + * @return the associated value, or {@code null} if none found + */ public Object getHeader(String headerName) { return this.headers.get(headerName); } @@ -289,10 +329,6 @@ public class MessageHeaderAccessor { } } - protected boolean isReadOnly(String headerName) { - return MessageHeaders.ID.equals(headerName) || MessageHeaders.TIMESTAMP.equals(headerName); - } - /** * Set the value for the given header name only if the header name is not * already associated with a value. @@ -303,6 +339,15 @@ public class MessageHeaderAccessor { } } + /** + * Remove the value for the given header name. + */ + public void removeHeader(String headerName) { + if (StringUtils.hasLength(headerName) && !isReadOnly(headerName)) { + setHeader(headerName, null); + } + } + /** * Removes all headers provided via array of 'headerPatterns'. *

As the name suggests, array may contain simple matching patterns for header @@ -337,15 +382,6 @@ public class MessageHeaderAccessor { return matchingHeaderNames; } - /** - * Remove the value for the given header name. - */ - public void removeHeader(String headerName) { - if (StringUtils.hasLength(headerName) && !isReadOnly(headerName)) { - setHeader(headerName, null); - } - } - /** * Copy the name-value pairs from the provided Map. *

This operation will overwrite any existing values. Use @@ -370,13 +406,20 @@ public class MessageHeaderAccessor { if (headersToCopy != null) { Set keys = headersToCopy.keySet(); for (String key : keys) { - if (!this.isReadOnly(key)) { + if (!isReadOnly(key)) { setHeaderIfAbsent(key, headersToCopy.get(key)); } } } } + protected boolean isReadOnly(String headerName) { + return (MessageHeaders.ID.equals(headerName) || MessageHeaders.TIMESTAMP.equals(headerName)); + } + + + // Specific header accessors + public UUID getId() { return (UUID) getHeader(MessageHeaders.ID); } @@ -385,6 +428,10 @@ public class MessageHeaderAccessor { return (Long) getHeader(MessageHeaders.TIMESTAMP); } + public void setReplyChannelName(String replyChannelName) { + setHeader(MessageHeaders.REPLY_CHANNEL, replyChannelName); + } + public void setReplyChannel(MessageChannel replyChannel) { setHeader(MessageHeaders.REPLY_CHANNEL, replyChannel); } @@ -393,8 +440,8 @@ public class MessageHeaderAccessor { return getHeader(MessageHeaders.REPLY_CHANNEL); } - public void setReplyChannelName(String replyChannelName) { - setHeader(MessageHeaders.REPLY_CHANNEL, replyChannelName); + public void setErrorChannelName(String errorChannelName) { + setHeader(MessageHeaders.ERROR_CHANNEL, errorChannelName); } public void setErrorChannel(MessageChannel errorChannel) { @@ -405,18 +452,16 @@ public class MessageHeaderAccessor { return getHeader(MessageHeaders.ERROR_CHANNEL); } - public void setErrorChannelName(String errorChannelName) { - setHeader(MessageHeaders.ERROR_CHANNEL, errorChannelName); - } - - public MimeType getContentType() { - return (MimeType) getHeader(MessageHeaders.CONTENT_TYPE); - } - public void setContentType(MimeType contentType) { setHeader(MessageHeaders.CONTENT_TYPE, contentType); } + public MimeType getContentType() { + return (MimeType) getHeader(MessageHeaders.CONTENT_TYPE); + } + + + // Log message stuff /** * Return a concise message for logging purposes. @@ -495,10 +540,12 @@ public class MessageHeaderAccessor { @Override public String toString() { - return getClass().getSimpleName() + " [headers=" + this.headers + "]"; + return getClass().getSimpleName() + "[headers=" + this.headers + "]"; } + // Static factory methods + /** * Return the original {@code MessageHeaderAccessor} used to create the headers * of the given {@code Message}, or {@code null} if that's not available or if