1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.messaging.support;
18
19 import java.nio.charset.Charset;
20 import java.util.ArrayList;
21 import java.util.Arrays;
22 import java.util.HashMap;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.UUID;
26
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29
30 import org.springframework.messaging.Message;
31 import org.springframework.messaging.MessageChannel;
32 import org.springframework.messaging.MessageHeaders;
33 import org.springframework.util.Assert;
34 import org.springframework.util.IdGenerator;
35 import org.springframework.util.MimeType;
36 import org.springframework.util.MimeTypeUtils;
37 import org.springframework.util.ObjectUtils;
38 import org.springframework.util.PatternMatchUtils;
39 import org.springframework.util.StringUtils;
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116 public class MessageHeaderAccessor {
117
118 public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
119
120 private static final MimeType[] READABLE_MIME_TYPES = new MimeType[] {
121 MimeTypeUtils.APPLICATION_JSON, MimeTypeUtils.APPLICATION_XML,
122 new MimeType("text", "*"), new MimeType("application", "*+json"), new MimeType("application", "*+xml")
123 };
124
125
126 protected final Log logger = LogFactory.getLog(getClass());
127
128 private final MutableMessageHeaders headers;
129
130 private boolean leaveMutable = false;
131
132 private boolean modified = false;
133
134 private boolean enableTimestamp = false;
135
136 private IdGenerator idGenerator;
137
138
139
140
141
142 public MessageHeaderAccessor() {
143 this.headers = new MutableMessageHeaders();
144 }
145
146
147
148
149 public MessageHeaderAccessor(Message<?> message) {
150 if (message != null) {
151 this.headers = new MutableMessageHeaders(message.getHeaders());
152 }
153 else {
154 this.headers = new MutableMessageHeaders();
155 }
156 }
157
158
159
160
161
162
163
164 protected MessageHeaderAccessor createAccessor(Message<?> message) {
165 return new MessageHeaderAccessor(message);
166 }
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186 public void setLeaveMutable(boolean leaveMutable) {
187 Assert.state(this.headers.isMutable(), "Already immutable");
188 this.leaveMutable = leaveMutable;
189 }
190
191
192
193
194
195
196
197
198
199 public void setImmutable() {
200 this.headers.setIdAndTimestamp();
201 this.headers.setImmutable();
202 }
203
204
205
206
207
208 public boolean isMutable() {
209 return this.headers.isMutable();
210 }
211
212
213
214
215
216
217 protected void setModified(boolean modified) {
218 this.modified = modified;
219 }
220
221
222
223
224
225 public boolean isModified() {
226 return this.modified;
227 }
228
229
230
231
232
233
234
235 void setEnableTimestamp(boolean enableTimestamp) {
236 this.enableTimestamp = enableTimestamp;
237 }
238
239
240
241
242
243
244
245 void setIdGenerator(IdGenerator idGenerator) {
246 this.idGenerator = idGenerator;
247 }
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262 public MessageHeaders getMessageHeaders() {
263 if (!this.leaveMutable) {
264 setImmutable();
265 }
266 return this.headers;
267 }
268
269
270
271
272
273
274
275 public MessageHeaders toMessageHeaders() {
276 return new MessageHeaders(this.headers);
277 }
278
279
280
281
282
283
284 public Map<String, Object> toMap() {
285 return new HashMap<String, Object>(this.headers);
286 }
287
288
289
290
291
292
293
294
295
296 public Object getHeader(String headerName) {
297 return this.headers.get(headerName);
298 }
299
300
301
302
303
304 public void setHeader(String name, Object value) {
305 if (isReadOnly(name)) {
306 throw new IllegalArgumentException("'" + name + "' header is read-only");
307 }
308 verifyType(name, value);
309 if (!ObjectUtils.nullSafeEquals(value, getHeader(name))) {
310 this.modified = true;
311 if (value != null) {
312 this.headers.getRawHeaders().put(name, value);
313 }
314 else {
315 this.headers.getRawHeaders().remove(name);
316 }
317 }
318 }
319
320 protected void verifyType(String headerName, Object headerValue) {
321 if (headerName != null && headerValue != null) {
322 if (MessageHeaders.ERROR_CHANNEL.equals(headerName) || MessageHeaders.REPLY_CHANNEL.endsWith(headerName)) {
323 if (!(headerValue instanceof MessageChannel || headerValue instanceof String)) {
324 throw new IllegalArgumentException(
325 "'" + headerName + "' header value must be a MessageChannel or String");
326 }
327 }
328 }
329 }
330
331
332
333
334
335 public void setHeaderIfAbsent(String name, Object value) {
336 if (getHeader(name) == null) {
337 setHeader(name, value);
338 }
339 }
340
341
342
343
344 public void removeHeader(String headerName) {
345 if (StringUtils.hasLength(headerName) && !isReadOnly(headerName)) {
346 setHeader(headerName, null);
347 }
348 }
349
350
351
352
353
354
355 public void removeHeaders(String... headerPatterns) {
356 List<String> headersToRemove = new ArrayList<String>();
357 for (String pattern : headerPatterns) {
358 if (StringUtils.hasLength(pattern)){
359 if (pattern.contains("*")){
360 headersToRemove.addAll(getMatchingHeaderNames(pattern, this.headers));
361 }
362 else {
363 headersToRemove.add(pattern);
364 }
365 }
366 }
367 for (String headerToRemove : headersToRemove) {
368 removeHeader(headerToRemove);
369 }
370 }
371
372 private List<String> getMatchingHeaderNames(String pattern, Map<String, Object> headers) {
373 List<String> matchingHeaderNames = new ArrayList<String>();
374 if (headers != null) {
375 for (String key : headers.keySet()) {
376 if (PatternMatchUtils.simpleMatch(pattern, key)) {
377 matchingHeaderNames.add(key);
378 }
379 }
380 }
381 return matchingHeaderNames;
382 }
383
384
385
386
387
388
389 public void copyHeaders(Map<String, ?> headersToCopy) {
390 if (headersToCopy != null) {
391 for (Map.Entry<String, ?> entry : headersToCopy.entrySet()) {
392 if (!isReadOnly(entry.getKey())) {
393 setHeader(entry.getKey(), entry.getValue());
394 }
395 }
396 }
397 }
398
399
400
401
402
403 public void copyHeadersIfAbsent(Map<String, ?> headersToCopy) {
404 if (headersToCopy != null) {
405 for (Map.Entry<String, ?> entry : headersToCopy.entrySet()) {
406 if (!isReadOnly(entry.getKey())) {
407 setHeaderIfAbsent(entry.getKey(), entry.getValue());
408 }
409 }
410 }
411 }
412
413 protected boolean isReadOnly(String headerName) {
414 return (MessageHeaders.ID.equals(headerName) || MessageHeaders.TIMESTAMP.equals(headerName));
415 }
416
417
418
419
420 public UUID getId() {
421 Object value = getHeader(MessageHeaders.ID);
422 if (value == null) {
423 return null;
424 }
425 return (value instanceof UUID ? (UUID) value : UUID.fromString(value.toString()));
426 }
427
428 public Long getTimestamp() {
429 Object value = getHeader(MessageHeaders.TIMESTAMP);
430 if (value == null) {
431 return null;
432 }
433 return (value instanceof Long ? (Long) value : Long.parseLong(value.toString()));
434 }
435
436 public void setContentType(MimeType contentType) {
437 setHeader(MessageHeaders.CONTENT_TYPE, contentType);
438 }
439
440 public MimeType getContentType() {
441 Object value = getHeader(MessageHeaders.CONTENT_TYPE);
442 if (value == null) {
443 return null;
444 }
445 return (value instanceof MimeType ? (MimeType) value : MimeType.valueOf(value.toString()));
446 }
447
448 public void setReplyChannelName(String replyChannelName) {
449 setHeader(MessageHeaders.REPLY_CHANNEL, replyChannelName);
450 }
451
452 public void setReplyChannel(MessageChannel replyChannel) {
453 setHeader(MessageHeaders.REPLY_CHANNEL, replyChannel);
454 }
455
456 public Object getReplyChannel() {
457 return getHeader(MessageHeaders.REPLY_CHANNEL);
458 }
459
460 public void setErrorChannelName(String errorChannelName) {
461 setHeader(MessageHeaders.ERROR_CHANNEL, errorChannelName);
462 }
463
464 public void setErrorChannel(MessageChannel errorChannel) {
465 setHeader(MessageHeaders.ERROR_CHANNEL, errorChannel);
466 }
467
468 public Object getErrorChannel() {
469 return getHeader(MessageHeaders.ERROR_CHANNEL);
470 }
471
472
473
474
475
476
477
478
479
480 public String getShortLogMessage(Object payload) {
481 return "headers=" + this.headers.toString() + getShortPayloadLogMessage(payload);
482 }
483
484
485
486
487
488
489 public String getDetailedLogMessage(Object payload) {
490 return "headers=" + this.headers.toString() + getDetailedPayloadLogMessage(payload);
491 }
492
493 protected String getShortPayloadLogMessage(Object payload) {
494 if (payload instanceof String) {
495 String payloadText = (String) payload;
496 return (payloadText.length() < 80) ?
497 " payload=" + payloadText :
498 " payload=" + payloadText.substring(0, 80) + "...(truncated)";
499 }
500 else if (payload instanceof byte[]) {
501 byte[] bytes = (byte[]) payload;
502 if (isReadableContentType()) {
503 Charset charset = getContentType().getCharSet();
504 charset = (charset != null ? charset : DEFAULT_CHARSET);
505 return (bytes.length < 80) ?
506 " payload=" + new String(bytes, charset) :
507 " payload=" + new String(Arrays.copyOf(bytes, 80), charset) + "...(truncated)";
508 }
509 else {
510 return " payload=byte[" + bytes.length + "]";
511 }
512 }
513 else {
514 String payloadText = payload.toString();
515 return (payloadText.length() < 80) ?
516 " payload=" + payloadText :
517 " payload=" + ObjectUtils.identityToString(payload);
518 }
519 }
520
521 protected String getDetailedPayloadLogMessage(Object payload) {
522 if (payload instanceof String) {
523 return " payload=" + payload;
524 }
525 else if (payload instanceof byte[]) {
526 byte[] bytes = (byte[]) payload;
527 if (isReadableContentType()) {
528 Charset charset = getContentType().getCharSet();
529 charset = (charset != null ? charset : DEFAULT_CHARSET);
530 return " payload=" + new String(bytes, charset);
531 }
532 else {
533 return " payload=byte[" + bytes.length + "]";
534 }
535 }
536 else {
537 return " payload=" + payload;
538 }
539 }
540
541 protected boolean isReadableContentType() {
542 for (MimeType mimeType : READABLE_MIME_TYPES) {
543 if (mimeType.includes(getContentType())) {
544 return true;
545 }
546 }
547 return false;
548 }
549
550 @Override
551 public String toString() {
552 return getClass().getSimpleName() + " [headers=" + this.headers + "]";
553 }
554
555
556
557
558
559
560
561
562
563
564
565
566
567 public static <T extends MessageHeaderAccessor> T getAccessor(Message<?> message, Class<T> requiredType) {
568 return getAccessor(message.getHeaders(), requiredType);
569 }
570
571
572
573
574
575
576
577
578 @SuppressWarnings("unchecked")
579 public static <T extends MessageHeaderAccessor> T getAccessor(MessageHeaders messageHeaders, Class<T> requiredType) {
580 if (messageHeaders instanceof MutableMessageHeaders) {
581 MutableMessageHeaders mutableHeaders = (MutableMessageHeaders) messageHeaders;
582 MessageHeaderAccessor headerAccessor = mutableHeaders.getMessageHeaderAccessor();
583 if (requiredType.isAssignableFrom(headerAccessor.getClass())) {
584 return (T) headerAccessor;
585 }
586 }
587 return null;
588 }
589
590
591
592
593
594
595
596
597
598
599 public static MessageHeaderAccessor getMutableAccessor(Message<?> message) {
600 if (message.getHeaders() instanceof MutableMessageHeaders) {
601 MutableMessageHeaders mutableHeaders = (MutableMessageHeaders) message.getHeaders();
602 MessageHeaderAccessor accessor = mutableHeaders.getMessageHeaderAccessor();
603 if (accessor != null) {
604 return (accessor.isMutable() ? accessor : accessor.createAccessor(message));
605 }
606 }
607 return new MessageHeaderAccessor(message);
608 }
609
610
611 @SuppressWarnings("serial")
612 private class MutableMessageHeaders extends MessageHeaders {
613
614 private boolean immutable;
615
616 public MutableMessageHeaders() {
617 this(null);
618 }
619
620 public MutableMessageHeaders(Map<String, Object> headers) {
621 super(headers, MessageHeaders.ID_VALUE_NONE, -1L);
622 }
623
624 public MessageHeaderAccessor getMessageHeaderAccessor() {
625 return MessageHeaderAccessor.this;
626 }
627
628 @Override
629 public Map<String, Object> getRawHeaders() {
630 Assert.state(!this.immutable, "Already immutable");
631 return super.getRawHeaders();
632 }
633
634 public void setImmutable() {
635 this.immutable = true;
636 }
637
638 public boolean isMutable() {
639 return !this.immutable;
640 }
641
642 public void setIdAndTimestamp() {
643 if (!isMutable()) {
644 return;
645 }
646 if (getId() == null) {
647 IdGenerator idGenerator = (MessageHeaderAccessor.this.idGenerator != null ?
648 MessageHeaderAccessor.this.idGenerator : MessageHeaders.getIdGenerator());
649
650 UUID id = idGenerator.generateId();
651 if (id != null && id != MessageHeaders.ID_VALUE_NONE) {
652 getRawHeaders().put(ID, id);
653 }
654 }
655 if (getTimestamp() == null) {
656 if (MessageHeaderAccessor.this.enableTimestamp) {
657 getRawHeaders().put(TIMESTAMP, System.currentTimeMillis());
658 }
659 }
660 }
661 }
662
663 }