Browse Source

MessageBuilder avoids intermediate HashMap for MessageHeaders building

Issue: SPR-11468
pull/635/head
Juergen Hoeller 10 years ago
parent
commit
28a966f544
  1. 5
      spring-messaging/src/main/java/org/springframework/messaging/support/MessageBuilder.java
  2. 169
      spring-messaging/src/main/java/org/springframework/messaging/support/MessageHeaderAccessor.java

5
spring-messaging/src/main/java/org/springframework/messaging/support/MessageBuilder.java

@ -148,11 +148,12 @@ public final class MessageBuilder<T> {
if (this.originalMessage != null && !this.headerAccessor.isModified()) { if (this.originalMessage != null && !this.headerAccessor.isModified()) {
return this.originalMessage; return this.originalMessage;
} }
MessageHeaders headersToUse = this.headerAccessor.toMessageHeaders();
if (this.payload instanceof Throwable) { if (this.payload instanceof Throwable) {
return (Message<T>) new ErrorMessage((Throwable) this.payload, this.headerAccessor.toMap()); return (Message<T>) new ErrorMessage((Throwable) this.payload, headersToUse);
} }
else { else {
return new GenericMessage<T>(this.payload, this.headerAccessor.toMap()); return new GenericMessage<T>(this.payload, headersToUse);
} }
} }

169
spring-messaging/src/main/java/org/springframework/messaging/support/MessageHeaderAccessor.java

@ -51,7 +51,7 @@ import org.springframework.util.StringUtils;
* *
* <pre class="code"> * <pre class="code">
* MessageHeaderAccessor accessor = new MessageHeaderAccessor(); * MessageHeaderAccessor accessor = new MessageHeaderAccessor();
* accessor.set("foo", "bar"); * accessor.setHeader("foo", "bar");
* Message message = MessageBuilder.createMessage("payload", accessor.getMessageHeaders()); * Message message = MessageBuilder.createMessage("payload", accessor.getMessageHeaders());
* </pre> * </pre>
* *
@ -61,14 +61,14 @@ import org.springframework.util.StringUtils;
* *
* <pre class="code"> * <pre class="code">
* MessageHeaderAccessor accessor = new MessageHeaderAccessor(); * MessageHeaderAccessor accessor = new MessageHeaderAccessor();
* accessor.set("foo", "bar"); * accessor.setHeader("foo", "bar");
* accessor.setLeaveMutable(true); * accessor.setLeaveMutable(true);
* Message message = MessageBuilder.createMessage("payload", accessor.getMessageHeaders()); * Message message = MessageBuilder.createMessage("payload", accessor.getMessageHeaders());
* *
* // later on in the same thread... * // later on in the same thread...
* *
* MessageHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message); * MessageHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message);
* accessor.set("bar", "baz"); * accessor.setHeader("bar", "baz");
* accessor.setImmutable(); * accessor.setImmutable();
* </pre> * </pre>
* *
@ -111,6 +111,7 @@ import org.springframework.util.StringUtils;
* header accessors. The most likely usage however is through sub-classes. * header accessors. The most likely usage however is through sub-classes.
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @author Juergen Hoeller
* @since 4.0 * @since 4.0
*/ */
public class MessageHeaderAccessor { public class MessageHeaderAccessor {
@ -129,10 +130,10 @@ public class MessageHeaderAccessor {
private boolean leaveMutable = false; private boolean leaveMutable = false;
private boolean enableTimestamp = false;
private boolean modified = false; private boolean modified = false;
private boolean enableTimestamp = false;
private IdGenerator idGenerator; 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) { protected MessageHeaderAccessor createAccessor(Message<?> message) {
return new MessageHeaderAccessor(message); return new MessageHeaderAccessor(message);
} }
/**
* Return the underlying {@code MessageHeaders} instance.
* <p>Unless {@link #setLeaveMutable(boolean)} was set to {@code true}, after
* this call, the headers are immutable and this accessor can no longer
* modify them.
* <p>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;
}
/** // Configuration properties
* Return a copy of the underlying header values.
* <p>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<String, Object> toMap() {
return new HashMap<String, Object>(this.headers);
}
/** /**
* By default when {@link #getMessageHeaders()} is called, {@code "this"} * 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 * Mark the underlying message headers as modified.
* {@link org.springframework.messaging.MessageHeaders#TIMESTAMP} header. * @param modified typically {@code true}, or {@code false} to reset the flag
* <p>By default this property is set to false. * @since 4.1
* @see IdTimestampMessageHeaderInitializer
*/ */
void setEnableTimestamp(boolean enableTimestamp) { protected void setModified(boolean modified) {
this.enableTimestamp = enableTimestamp; 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() { public boolean isModified() {
return this.modified; 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.
* <p>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; this.idGenerator = idGenerator;
} }
// Accessors for the resulting MessageHeaders
/**
* Return the underlying {@code MessageHeaders} instance.
* <p>Unless {@link #setLeaveMutable(boolean)} was set to {@code true}, after
* this call, the headers are immutable and this accessor can no longer
* modify them.
* <p>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.
* <p>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.
* <p>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<String, Object> toMap() {
return new HashMap<String, Object>(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) { public Object getHeader(String headerName) {
return this.headers.get(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 * Set the value for the given header name only if the header name is not
* already associated with a value. * 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'. * Removes all headers provided via array of 'headerPatterns'.
* <p>As the name suggests, array may contain simple matching patterns for header * <p>As the name suggests, array may contain simple matching patterns for header
@ -337,15 +382,6 @@ public class MessageHeaderAccessor {
return matchingHeaderNames; 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. * Copy the name-value pairs from the provided Map.
* <p>This operation will overwrite any existing values. Use * <p>This operation will overwrite any existing values. Use
@ -370,13 +406,20 @@ public class MessageHeaderAccessor {
if (headersToCopy != null) { if (headersToCopy != null) {
Set<String> keys = headersToCopy.keySet(); Set<String> keys = headersToCopy.keySet();
for (String key : keys) { for (String key : keys) {
if (!this.isReadOnly(key)) { if (!isReadOnly(key)) {
setHeaderIfAbsent(key, headersToCopy.get(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() { public UUID getId() {
return (UUID) getHeader(MessageHeaders.ID); return (UUID) getHeader(MessageHeaders.ID);
} }
@ -385,6 +428,10 @@ public class MessageHeaderAccessor {
return (Long) getHeader(MessageHeaders.TIMESTAMP); return (Long) getHeader(MessageHeaders.TIMESTAMP);
} }
public void setReplyChannelName(String replyChannelName) {
setHeader(MessageHeaders.REPLY_CHANNEL, replyChannelName);
}
public void setReplyChannel(MessageChannel replyChannel) { public void setReplyChannel(MessageChannel replyChannel) {
setHeader(MessageHeaders.REPLY_CHANNEL, replyChannel); setHeader(MessageHeaders.REPLY_CHANNEL, replyChannel);
} }
@ -393,8 +440,8 @@ public class MessageHeaderAccessor {
return getHeader(MessageHeaders.REPLY_CHANNEL); return getHeader(MessageHeaders.REPLY_CHANNEL);
} }
public void setReplyChannelName(String replyChannelName) { public void setErrorChannelName(String errorChannelName) {
setHeader(MessageHeaders.REPLY_CHANNEL, replyChannelName); setHeader(MessageHeaders.ERROR_CHANNEL, errorChannelName);
} }
public void setErrorChannel(MessageChannel errorChannel) { public void setErrorChannel(MessageChannel errorChannel) {
@ -405,18 +452,16 @@ public class MessageHeaderAccessor {
return getHeader(MessageHeaders.ERROR_CHANNEL); 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) { public void setContentType(MimeType contentType) {
setHeader(MessageHeaders.CONTENT_TYPE, 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. * Return a concise message for logging purposes.
@ -495,10 +540,12 @@ public class MessageHeaderAccessor {
@Override @Override
public String toString() { 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 * 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 * of the given {@code Message}, or {@code null} if that's not available or if

Loading…
Cancel
Save