1
2
3
4
5
6
7
8
9
10
11 package org.mule.transport;
12
13 import org.mule.api.ExceptionPayload;
14 import org.mule.api.MuleRuntimeException;
15 import org.mule.api.ThreadSafeAccess;
16 import org.mule.api.config.MuleProperties;
17 import org.mule.api.transport.MessageAdapter;
18 import org.mule.api.transport.PropertyScope;
19 import org.mule.api.transport.UniqueIdNotSupportedException;
20 import org.mule.config.MuleManifest;
21 import org.mule.config.i18n.CoreMessages;
22 import org.mule.util.FileUtils;
23 import org.mule.util.IOUtils;
24 import org.mule.util.StringUtils;
25 import org.mule.util.UUID;
26
27 import java.io.InputStream;
28 import java.util.Collections;
29 import java.util.Iterator;
30 import java.util.Map;
31 import java.util.Set;
32
33 import javax.activation.DataHandler;
34
35 import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap;
36 import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentMap;
37 import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicBoolean;
38 import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicReference;
39
40 import org.apache.commons.logging.Log;
41 import org.apache.commons.logging.LogFactory;
42
43
44
45
46
47
48 public abstract class AbstractMessageAdapter implements MessageAdapter, ThreadSafeAccess
49 {
50
51 protected static transient Log logger;
52
53
54 protected MessagePropertiesContext properties = new MessagePropertiesContext();
55
56
57 protected ConcurrentMap attachments = new ConcurrentHashMap();
58
59
60 protected String encoding = FileUtils.DEFAULT_ENCODING;
61
62
63 protected ExceptionPayload exceptionPayload;
64
65
66 protected String id = UUID.getUUID();
67
68
69
70 private transient AtomicReference ownerThread = null;
71
72 private transient AtomicBoolean mutable = null;
73
74 protected AbstractMessageAdapter()
75 {
76
77 logger = LogFactory.getLog(getClass());
78 }
79
80
81
82
83
84 protected AbstractMessageAdapter(MessageAdapter template)
85 {
86 logger = LogFactory.getLog(getClass());
87 if (null != template)
88 {
89 Iterator propertyNames = template.getPropertyNames().iterator();
90 while (propertyNames.hasNext())
91 {
92 String key = (String) propertyNames.next();
93 try
94 {
95 setProperty(key, template.getProperty(key));
96 }
97 catch (Exception e)
98 {
99 throw new MuleRuntimeException(CoreMessages.failedToReadPayload(), e);
100 }
101 }
102 Iterator attachmentNames = template.getAttachmentNames().iterator();
103 while (attachmentNames.hasNext())
104 {
105 String key = (String) attachmentNames.next();
106 try
107 {
108 addAttachment(key, template.getAttachment(key));
109 }
110 catch (Exception e)
111 {
112 throw new MuleRuntimeException(CoreMessages.failedToReadPayload(), e);
113 }
114 }
115 encoding = template.getEncoding();
116 exceptionPayload = template.getExceptionPayload();
117
118 try
119 {
120 id = template.getUniqueId();
121 }
122 catch (UniqueIdNotSupportedException e)
123 {
124
125 }
126 }
127 }
128
129
130 public String toString()
131 {
132 assertAccess(READ);
133 StringBuffer buf = new StringBuffer(120);
134 buf.append(getClass().getName());
135 buf.append("/" + super.toString());
136 buf.append('{');
137 buf.append("id=").append(getUniqueId());
138 buf.append(", payload=").append(getPayload().getClass().getName());
139 buf.append(", correlationId=").append(getCorrelationId());
140 buf.append(", correlationGroup=").append(getCorrelationGroupSize());
141 buf.append(", correlationSeq=").append(getCorrelationSequence());
142 buf.append(", encoding=").append(getEncoding());
143 buf.append(", exceptionPayload=").append(exceptionPayload);
144 if (logger.isDebugEnabled())
145 {
146 buf.append(", properties=").append(properties);
147 }
148 buf.append('}');
149 return buf.toString();
150 }
151
152
153 public void addProperties(Map props)
154 {
155 addProperties(props, properties.getDefaultScope());
156 assertAccess(WRITE);
157 if (props != null)
158 {
159 synchronized (props)
160 {
161 for (Iterator iter = props.entrySet().iterator(); iter.hasNext();)
162 {
163 Map.Entry entry = (Map.Entry) iter.next();
164 setProperty((String) entry.getKey(), entry.getValue());
165 }
166 }
167 }
168 }
169
170
171 public void addProperties(Map props, PropertyScope scope)
172 {
173 assertAccess(WRITE);
174 if (props != null)
175 {
176 synchronized (props)
177 {
178 for (Iterator iter = props.entrySet().iterator(); iter.hasNext();)
179 {
180 Map.Entry entry = (Map.Entry) iter.next();
181 setProperty((String) entry.getKey(), entry.getValue(), scope);
182 }
183 }
184 }
185 }
186
187
188
189
190
191
192
193 protected void addInboundProperties(Map props)
194 {
195 properties.addInboundProperties(props);
196 }
197
198
199 public void clearProperties()
200 {
201 assertAccess(WRITE);
202 properties.clearProperties();
203 }
204
205
206 public Object removeProperty(String key)
207 {
208 assertAccess(WRITE);
209 return properties.removeProperty(key);
210 }
211
212
213 public Object getProperty(String key)
214 {
215 assertAccess(READ);
216 return properties.getProperty(key);
217 }
218
219
220 public Set getPropertyNames()
221 {
222 assertAccess(READ);
223 return properties.getPropertyNames();
224 }
225
226
227 public void setProperty(String key, Object value)
228 {
229 assertAccess(WRITE);
230 if (key != null)
231 {
232 if (value != null)
233 {
234 properties.setProperty(key, value);
235 }
236 else
237 {
238 logger.warn("setProperty(key, value) called with null value; removing key: " + key
239 + "; please report the following stack trace to " + MuleManifest.getDevListEmail(),
240 new Throwable());
241 properties.removeProperty(key);
242 }
243 }
244 else
245 {
246 logger.warn("setProperty(key, value) ignored because of null key for object: " + value
247 + "; please report the following stack trace to " + MuleManifest.getDevListEmail(),
248 new Throwable());
249 }
250 }
251
252
253 public void setProperty(String key, Object value, PropertyScope scope)
254 {
255 assertAccess(WRITE);
256 if (key != null)
257 {
258 if (value != null)
259 {
260 properties.setProperty(key, value, scope);
261 }
262 else
263 {
264 logger.warn("setProperty(key, value) called with null value; removing key: " + key
265 + "; please report the following stack trace to " + MuleManifest.getDevListEmail(),
266 new Throwable());
267 properties.removeProperty(key);
268 }
269 }
270 else
271 {
272 logger.warn("setProperty(key, value) ignored because of null key for object: " + value
273 + "; please report the following stack trace to " + MuleManifest.getDevListEmail(),
274 new Throwable());
275 }
276 }
277
278
279 public String getUniqueId()
280 {
281 assertAccess(READ);
282 return id;
283 }
284
285
286 public Object getProperty(String name, Object defaultValue)
287 {
288 assertAccess(READ);
289 return properties.getProperty(name, defaultValue);
290 }
291
292
293 public int getIntProperty(String name, int defaultValue)
294 {
295 assertAccess(READ);
296 return properties.getIntProperty(name, defaultValue);
297 }
298
299
300 public long getLongProperty(String name, long defaultValue)
301 {
302 assertAccess(READ);
303 return properties.getLongProperty(name, defaultValue);
304 }
305
306
307 public double getDoubleProperty(String name, double defaultValue)
308 {
309 assertAccess(READ);
310 return properties.getDoubleProperty(name, defaultValue);
311 }
312
313
314 public boolean getBooleanProperty(String name, boolean defaultValue)
315 {
316 assertAccess(READ);
317 return properties.getBooleanProperty(name, defaultValue);
318 }
319
320
321 public String getStringProperty(String name, String defaultValue)
322 {
323 assertAccess(READ);
324 return properties.getStringProperty(name, defaultValue);
325 }
326
327
328 public void setBooleanProperty(String name, boolean value)
329 {
330 assertAccess(WRITE);
331 setProperty(name, Boolean.valueOf(value));
332 }
333
334
335 public void setIntProperty(String name, int value)
336 {
337 assertAccess(WRITE);
338 setProperty(name, new Integer(value));
339 }
340
341 public void setLongProperty(String name, long value)
342 {
343 assertAccess(WRITE);
344 setProperty(name, new Long(value));
345 }
346
347 public void setDoubleProperty(String name, double value)
348 {
349 assertAccess(WRITE);
350 setProperty(name, new Double(value));
351 }
352
353
354 public void setStringProperty(String name, String value)
355 {
356 assertAccess(WRITE);
357 setProperty(name, value);
358 }
359
360
361 public Object getReplyTo()
362 {
363 assertAccess(READ);
364 return getProperty(MuleProperties.MULE_REPLY_TO_PROPERTY);
365 }
366
367
368 public void setReplyTo(Object replyTo)
369 {
370 assertAccess(WRITE);
371 if (replyTo != null)
372 {
373 setProperty(MuleProperties.MULE_REPLY_TO_PROPERTY, replyTo);
374 }
375 else
376 {
377 removeProperty(MuleProperties.MULE_REPLY_TO_PROPERTY);
378 }
379 }
380
381
382 public String getCorrelationId()
383 {
384 assertAccess(READ);
385 return (String) getProperty(MuleProperties.MULE_CORRELATION_ID_PROPERTY);
386 }
387
388
389
390 public void setCorrelationId(String correlationId)
391 {
392 assertAccess(WRITE);
393 if (StringUtils.isNotBlank(correlationId))
394 {
395 setProperty(MuleProperties.MULE_CORRELATION_ID_PROPERTY, correlationId);
396 }
397 else
398 {
399 removeProperty(MuleProperties.MULE_CORRELATION_ID_PROPERTY);
400 }
401 }
402
403
404 public int getCorrelationSequence()
405 {
406 assertAccess(READ);
407 return getIntProperty(MuleProperties.MULE_CORRELATION_SEQUENCE_PROPERTY, -1);
408 }
409
410
411 public void setCorrelationSequence(int sequence)
412 {
413 assertAccess(WRITE);
414 setIntProperty(MuleProperties.MULE_CORRELATION_SEQUENCE_PROPERTY, sequence);
415 }
416
417
418 public int getCorrelationGroupSize()
419 {
420 assertAccess(READ);
421 return getIntProperty(MuleProperties.MULE_CORRELATION_GROUP_SIZE_PROPERTY, -1);
422 }
423
424
425 public void setCorrelationGroupSize(int size)
426 {
427 assertAccess(WRITE);
428 setIntProperty(MuleProperties.MULE_CORRELATION_GROUP_SIZE_PROPERTY, size);
429 }
430
431 public ExceptionPayload getExceptionPayload()
432 {
433 assertAccess(READ);
434 return exceptionPayload;
435 }
436
437
438 public void setExceptionPayload(ExceptionPayload payload)
439 {
440 assertAccess(WRITE);
441 exceptionPayload = payload;
442 }
443
444
445 public void addAttachment(String name, DataHandler dataHandler) throws Exception
446 {
447 assertAccess(WRITE);
448 attachments.put(name, dataHandler);
449 }
450
451
452 public void removeAttachment(String name) throws Exception
453 {
454 assertAccess(WRITE);
455 attachments.remove(name);
456 }
457
458
459 public DataHandler getAttachment(String name)
460 {
461 assertAccess(READ);
462 return (DataHandler) attachments.get(name);
463 }
464
465
466 public Set getAttachmentNames()
467 {
468 assertAccess(READ);
469 return Collections.unmodifiableSet(attachments.keySet());
470 }
471
472
473 public String getEncoding()
474 {
475 assertAccess(READ);
476 return encoding;
477 }
478
479
480 public void setEncoding(String encoding)
481 {
482 assertAccess(WRITE);
483 this.encoding = encoding;
484 }
485
486
487 public void release()
488 {
489
490 if(getPayload() instanceof InputStream)
491 {
492 IOUtils.closeQuietly((InputStream)getPayload());
493 }
494 properties.clearProperties();
495 attachments.clear();
496 }
497
498
499
500
501 public void assertAccess(boolean write)
502 {
503 if (AccessControl.isAssertMessageAccess())
504 {
505 initAccessControl();
506 setOwner();
507 checkMutable(write);
508 }
509 }
510
511 private void setOwner()
512 {
513 if (null == ownerThread.get())
514 {
515 ownerThread.compareAndSet(null, Thread.currentThread());
516 }
517 }
518
519 private void checkMutable(boolean write)
520 {
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541 Thread currentThread = Thread.currentThread();
542 if (currentThread.equals(ownerThread.get()))
543 {
544 if (write && !mutable.get())
545 {
546 if (isDisabled())
547 {
548 logger.warn("Writing to immutable message (exception disabled)");
549 }
550 else
551 {
552 throw newException("Cannot write to immutable message");
553 }
554 }
555 }
556 else
557 {
558 if (write)
559 {
560 if (isDisabled())
561 {
562 logger.warn("Non-owner writing to message (exception disabled)");
563 }
564 else
565 {
566 throw newException("Only owner thread can write to message: "
567 + ownerThread.get() + "/" + Thread.currentThread());
568 }
569 }
570 else
571 {
572
573 mutable.set(false);
574 }
575 }
576 }
577
578 protected IllegalStateException newException(String message)
579 {
580 IllegalStateException exception = new IllegalStateException(message);
581 logger.warn("Message access violation", exception);
582 return exception;
583 }
584
585 protected boolean isDisabled()
586 {
587 return !AccessControl.isFailOnMessageScribbling();
588 }
589
590 private synchronized void initAccessControl()
591 {
592 if (null == ownerThread)
593 {
594 ownerThread = new AtomicReference();
595 }
596 if (null == mutable)
597 {
598 mutable = new AtomicBoolean(true);
599 }
600 }
601
602
603 public synchronized void resetAccessControl()
604 {
605
606
607 if (ownerThread != null)
608 {
609 ownerThread.set(null);
610 }
611 if (mutable != null)
612 {
613 mutable.set(true);
614 }
615 }
616
617
618 public ThreadSafeAccess newThreadCopy()
619 {
620 if (logger.isInfoEnabled())
621 {
622 logger.info("The newThreadCopy method in AbstractMessageAdapter is being used directly. "
623 + "This code may be susceptible to 'scribbling' issues with messages. "
624 + "Please consider implementing the ThreadSafeAccess interface in the message adapter.");
625 }
626 return this;
627 }
628
629 }