1 /************************************************************************
2 * Copyright (c) 2000-2006 The Apache Software Foundation. *
3 * All rights reserved. *
4 * ------------------------------------------------------------------- *
5 * Licensed under the Apache License, Version 2.0 (the "License"); you *
6 * may not use this file except in compliance with the License. You *
7 * may obtain a copy of the License at: *
8 * *
9 * http://www.apache.org/licenses/LICENSE-2.0 *
10 * *
11 * Unless required by applicable law or agreed to in writing, software *
12 * distributed under the License is distributed on an "AS IS" BASIS, *
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or *
14 * implied. See the License for the specific language governing *
15 * permissions and limitations under the License. *
16 ***********************************************************************/
17
18 package org.apache.james.core;
19
20 import org.apache.avalon.framework.activity.Disposable;
21 import org.apache.avalon.framework.container.ContainerUtil;
22
23 import javax.activation.DataHandler;
24 import javax.mail.Address;
25 import javax.mail.Flags;
26 import javax.mail.Folder;
27 import javax.mail.Message;
28 import javax.mail.MessagingException;
29 import javax.mail.Multipart;
30 import javax.mail.Session;
31 import javax.mail.Flags.Flag;
32 import javax.mail.internet.MimeMessage;
33 import javax.mail.search.SearchTerm;
34
35 import java.io.IOException;
36 import java.io.InputStream;
37 import java.io.OutputStream;
38 import java.util.Date;
39 import java.util.Enumeration;
40
41 /***
42 * This object wraps a "possibly shared" MimeMessage tracking copies and
43 * automatically cloning it (if shared) when a write operation is invoked.
44 */
45 public class MimeMessageCopyOnWriteProxy extends MimeMessage implements
46 Disposable {
47
48 /***
49 * Used internally to track the reference count
50 * It is important that this is static otherwise it will keep a reference to
51 * the parent object.
52 */
53 protected static class MessageReferenceTracker {
54
55 /***
56 * reference counter
57 */
58 private int referenceCount = 1;
59
60 /***
61 * The mime message in memory
62 */
63 private MimeMessage wrapped = null;
64
65 public MessageReferenceTracker(MimeMessage ref) {
66 wrapped = ref;
67 }
68
69 protected synchronized void incrementReferenceCount() {
70 referenceCount++;
71 }
72
73 protected synchronized void decrementReferenceCount() {
74 referenceCount--;
75 if (referenceCount<=0) {
76 ContainerUtil.dispose(wrapped);
77 wrapped = null;
78 }
79 }
80
81 protected synchronized int getReferenceCount() {
82 return referenceCount;
83 }
84
85 public MimeMessage getWrapped() {
86 return wrapped;
87 }
88
89 }
90
91 protected MessageReferenceTracker refCount;
92
93 /***
94 * @param original
95 * MimeMessageWrapper
96 * @throws MessagingException
97 */
98 public MimeMessageCopyOnWriteProxy(MimeMessage original)
99 throws MessagingException {
100 this(original, false);
101 }
102
103 /***
104 * @param original
105 * MimeMessageSource
106 * @throws MessagingException
107 */
108 public MimeMessageCopyOnWriteProxy(MimeMessageSource original)
109 throws MessagingException {
110 this(new MimeMessageWrapper(original), true);
111 }
112
113 /***
114 * Private constructor providing an external reference counter.
115 *
116 * @param original
117 * @param refCount
118 * @throws MessagingException
119 */
120 private MimeMessageCopyOnWriteProxy(MimeMessage original,
121 boolean writeable)
122 throws MessagingException {
123 super(Session.getDefaultInstance(System.getProperties(), null));
124
125 if (original instanceof MimeMessageCopyOnWriteProxy) {
126 refCount = ((MimeMessageCopyOnWriteProxy) original).refCount;
127 } else {
128 refCount = new MessageReferenceTracker(original);
129 }
130
131 if (!writeable) {
132 refCount.incrementReferenceCount();
133 }
134 }
135
136 /***
137 * Check the number of references over the MimeMessage and clone it if
138 * needed before returning the reference
139 *
140 * @throws MessagingException
141 * exception
142 */
143 protected MimeMessage getWrappedMessageForWriting() throws MessagingException {
144 synchronized (refCount) {
145 if (refCount.getReferenceCount() > 1) {
146 refCount.decrementReferenceCount();
147 refCount = new MessageReferenceTracker(new MimeMessageWrapper(refCount.getWrapped()));
148 }
149 }
150 return refCount.getWrapped();
151 }
152
153 /***
154 * @return
155 */
156 public MimeMessage getWrappedMessage() {
157 return refCount.getWrapped();
158 }
159
160 /***
161 * @see org.apache.avalon.framework.activity.Disposable#dispose()
162 */
163 public synchronized void dispose() {
164 if (refCount != null) {
165 refCount.decrementReferenceCount();
166 refCount = null;
167 }
168 }
169
170 /***
171 * Rewritten for optimization purposes
172 */
173 public void writeTo(OutputStream os) throws IOException, MessagingException {
174 getWrappedMessage().writeTo(os);
175 }
176
177 /***
178 * Rewritten for optimization purposes
179 */
180 public void writeTo(OutputStream os, String[] ignoreList)
181 throws IOException, MessagingException {
182 getWrappedMessage().writeTo(os, ignoreList);
183 }
184
185 /***
186 * Various reader methods
187 */
188
189 /***
190 * @see javax.mail.Message#getFrom()
191 */
192 public Address[] getFrom() throws MessagingException {
193 return getWrappedMessage().getFrom();
194 }
195
196 /***
197 * @see javax.mail.Message#getRecipients(javax.mail.Message.RecipientType)
198 */
199 public Address[] getRecipients(Message.RecipientType type)
200 throws MessagingException {
201 return getWrappedMessage().getRecipients(type);
202 }
203
204 /***
205 * @see javax.mail.Message#getAllRecipients()
206 */
207 public Address[] getAllRecipients() throws MessagingException {
208 return getWrappedMessage().getAllRecipients();
209 }
210
211 /***
212 * @see javax.mail.Message#getReplyTo()
213 */
214 public Address[] getReplyTo() throws MessagingException {
215 return getWrappedMessage().getReplyTo();
216 }
217
218 /***
219 * @see javax.mail.Message#getSubject()
220 */
221 public String getSubject() throws MessagingException {
222 return getWrappedMessage().getSubject();
223 }
224
225 /***
226 * @see javax.mail.Message#getSentDate()
227 */
228 public Date getSentDate() throws MessagingException {
229 return getWrappedMessage().getSentDate();
230 }
231
232 /***
233 * @see javax.mail.Message#getReceivedDate()
234 */
235 public Date getReceivedDate() throws MessagingException {
236 return getWrappedMessage().getReceivedDate();
237 }
238
239 /***
240 * @see javax.mail.Part#getSize()
241 */
242 public int getSize() throws MessagingException {
243 return getWrappedMessage().getSize();
244 }
245
246 /***
247 * @see javax.mail.Part#getLineCount()
248 */
249 public int getLineCount() throws MessagingException {
250 return getWrappedMessage().getLineCount();
251 }
252
253 /***
254 * @see javax.mail.Part#getContentType()
255 */
256 public String getContentType() throws MessagingException {
257 return getWrappedMessage().getContentType();
258 }
259
260 /***
261 * @see javax.mail.Part#isMimeType(java.lang.String)
262 */
263 public boolean isMimeType(String mimeType) throws MessagingException {
264 return getWrappedMessage().isMimeType(mimeType);
265 }
266
267 /***
268 * @see javax.mail.Part#getDisposition()
269 */
270 public String getDisposition() throws MessagingException {
271 return getWrappedMessage().getDisposition();
272 }
273
274 /***
275 * @see javax.mail.internet.MimePart#getEncoding()
276 */
277 public String getEncoding() throws MessagingException {
278 return getWrappedMessage().getEncoding();
279 }
280
281 /***
282 * @see javax.mail.internet.MimePart#getContentID()
283 */
284 public String getContentID() throws MessagingException {
285 return getWrappedMessage().getContentID();
286 }
287
288 /***
289 * @see javax.mail.internet.MimePart#getContentMD5()
290 */
291 public String getContentMD5() throws MessagingException {
292 return getWrappedMessage().getContentMD5();
293 }
294
295 /***
296 * @see javax.mail.Part#getDescription()
297 */
298 public String getDescription() throws MessagingException {
299 return getWrappedMessage().getDescription();
300 }
301
302 /***
303 * @see javax.mail.internet.MimePart#getContentLanguage()
304 */
305 public String[] getContentLanguage() throws MessagingException {
306 return getWrappedMessage().getContentLanguage();
307 }
308
309 /***
310 * @see javax.mail.internet.MimeMessage#getMessageID()
311 */
312 public String getMessageID() throws MessagingException {
313 return getWrappedMessage().getMessageID();
314 }
315
316 /***
317 * @see javax.mail.Part#getFileName()
318 */
319 public String getFileName() throws MessagingException {
320 return getWrappedMessage().getFileName();
321 }
322
323 /***
324 * @see javax.mail.Part#getInputStream()
325 */
326 public InputStream getInputStream() throws IOException, MessagingException {
327 return getWrappedMessage().getInputStream();
328 }
329
330 /***
331 * @see javax.mail.Part#getDataHandler()
332 */
333 public DataHandler getDataHandler() throws MessagingException {
334 return getWrappedMessage().getDataHandler();
335 }
336
337 /***
338 * @see javax.mail.Part#getContent()
339 */
340 public Object getContent() throws IOException, MessagingException {
341 return getWrappedMessage().getContent();
342 }
343
344 /***
345 * @see javax.mail.Part#getHeader(java.lang.String)
346 */
347 public String[] getHeader(String name) throws MessagingException {
348 return getWrappedMessage().getHeader(name);
349 }
350
351 /***
352 * @see javax.mail.internet.MimePart#getHeader(java.lang.String, java.lang.String)
353 */
354 public String getHeader(String name, String delimiter)
355 throws MessagingException {
356 return getWrappedMessage().getHeader(name, delimiter);
357 }
358
359 /***
360 * @see javax.mail.Part#getAllHeaders()
361 */
362 public Enumeration getAllHeaders() throws MessagingException {
363 return getWrappedMessage().getAllHeaders();
364 }
365
366 /***
367 * @see javax.mail.Part#getMatchingHeaders(java.lang.String[])
368 */
369 public Enumeration getMatchingHeaders(String[] names)
370 throws MessagingException {
371 return getWrappedMessage().getMatchingHeaders(names);
372 }
373
374 /***
375 * @see javax.mail.Part#getNonMatchingHeaders(java.lang.String[])
376 */
377 public Enumeration getNonMatchingHeaders(String[] names)
378 throws MessagingException {
379 return getWrappedMessage().getNonMatchingHeaders(names);
380 }
381
382 /***
383 * @see javax.mail.internet.MimePart#getAllHeaderLines()
384 */
385 public Enumeration getAllHeaderLines() throws MessagingException {
386 return getWrappedMessage().getAllHeaderLines();
387 }
388
389 /***
390 * @see javax.mail.internet.MimePart#getMatchingHeaderLines(java.lang.String[])
391 */
392 public Enumeration getMatchingHeaderLines(String[] names)
393 throws MessagingException {
394 return getWrappedMessage().getMatchingHeaderLines(names);
395 }
396
397 /***
398 * @see javax.mail.internet.MimePart#getNonMatchingHeaderLines(java.lang.String[])
399 */
400 public Enumeration getNonMatchingHeaderLines(String[] names)
401 throws MessagingException {
402 return getWrappedMessage().getNonMatchingHeaderLines(names);
403 }
404
405 /***
406 * @see javax.mail.Message#getFlags()
407 */
408 public Flags getFlags() throws MessagingException {
409 return getWrappedMessage().getFlags();
410 }
411
412 /***
413 * @see javax.mail.Message#isSet(javax.mail.Flags.Flag)
414 */
415 public boolean isSet(Flags.Flag flag) throws MessagingException {
416 return getWrappedMessage().isSet(flag);
417 }
418
419 /***
420 * @see javax.mail.internet.MimeMessage#getSender()
421 */
422 public Address getSender() throws MessagingException {
423 return getWrappedMessage().getSender();
424 }
425
426 /***
427 * @see javax.mail.Message#match(javax.mail.search.SearchTerm)
428 */
429 public boolean match(SearchTerm arg0) throws MessagingException {
430 return getWrappedMessage().match(arg0);
431 }
432
433 /***
434 * @see javax.mail.internet.MimeMessage#getRawInputStream()
435 */
436 public InputStream getRawInputStream() throws MessagingException {
437 return getWrappedMessage().getRawInputStream();
438 }
439
440 /***
441 * @see javax.mail.Message#getFolder()
442 */
443 public Folder getFolder() {
444 return getWrappedMessage().getFolder();
445 }
446
447 /***
448 * @see javax.mail.Message#getMessageNumber()
449 */
450 public int getMessageNumber() {
451 return getWrappedMessage().getMessageNumber();
452 }
453
454 /***
455 * @see javax.mail.Message#isExpunged()
456 */
457 public boolean isExpunged() {
458 return getWrappedMessage().isExpunged();
459 }
460
461 /***
462 * @see java.lang.Object#equals(java.lang.Object)
463 */
464 public boolean equals(Object arg0) {
465 return getWrappedMessage().equals(arg0);
466 }
467
468 /***
469 * @see java.lang.Object#hashCode()
470 */
471 public int hashCode() {
472 return getWrappedMessage().hashCode();
473 }
474
475 /***
476 * @see java.lang.Object#toString()
477 */
478 public String toString() {
479 return getWrappedMessage().toString();
480 }
481
482
483
484
485
486 /***
487 * @see javax.mail.Message#setFrom(javax.mail.Address)
488 */
489 public void setFrom(Address address) throws MessagingException {
490 getWrappedMessageForWriting().setFrom(address);
491 }
492
493 /***
494 * @see javax.mail.Message#setFrom()
495 */
496 public void setFrom() throws MessagingException {
497 getWrappedMessageForWriting().setFrom();
498 }
499
500 /***
501 * @see javax.mail.Message#addFrom(javax.mail.Address[])
502 */
503 public void addFrom(Address[] addresses) throws MessagingException {
504 getWrappedMessageForWriting().addFrom(addresses);
505 }
506
507 /***
508 * @see javax.mail.Message#setRecipients(javax.mail.Message.RecipientType, javax.mail.Address[])
509 */
510 public void setRecipients(Message.RecipientType type, Address[] addresses)
511 throws MessagingException {
512 getWrappedMessageForWriting().setRecipients(type, addresses);
513 }
514
515 /***
516 * @see javax.mail.Message#addRecipients(javax.mail.Message.RecipientType, javax.mail.Address[])
517 */
518 public void addRecipients(Message.RecipientType type, Address[] addresses)
519 throws MessagingException {
520 getWrappedMessageForWriting().addRecipients(type, addresses);
521 }
522
523 /***
524 * @see javax.mail.Message#setReplyTo(javax.mail.Address[])
525 */
526 public void setReplyTo(Address[] addresses) throws MessagingException {
527 getWrappedMessageForWriting().setReplyTo(addresses);
528 }
529
530 /***
531 * @see javax.mail.Message#setSubject(java.lang.String)
532 */
533 public void setSubject(String subject) throws MessagingException {
534 getWrappedMessageForWriting().setSubject(subject);
535 }
536
537 /***
538 * @see javax.mail.internet.MimeMessage#setSubject(java.lang.String, java.lang.String)
539 */
540 public void setSubject(String subject, String charset)
541 throws MessagingException {
542 getWrappedMessageForWriting().setSubject(subject, charset);
543 }
544
545 /***
546 * @see javax.mail.Message#setSentDate(java.util.Date)
547 */
548 public void setSentDate(Date d) throws MessagingException {
549 getWrappedMessageForWriting().setSentDate(d);
550 }
551
552 /***
553 * @see javax.mail.Part#setDisposition(java.lang.String)
554 */
555 public void setDisposition(String disposition) throws MessagingException {
556 getWrappedMessageForWriting().setDisposition(disposition);
557 }
558
559 /***
560 * @see javax.mail.internet.MimeMessage#setContentID(java.lang.String)
561 */
562 public void setContentID(String cid) throws MessagingException {
563 getWrappedMessageForWriting().setContentID(cid);
564 }
565
566 /***
567 * @see javax.mail.internet.MimePart#setContentMD5(java.lang.String)
568 */
569 public void setContentMD5(String md5) throws MessagingException {
570 getWrappedMessageForWriting().setContentMD5(md5);
571 }
572
573 /***
574 * @see javax.mail.Part#setDescription(java.lang.String)
575 */
576 public void setDescription(String description) throws MessagingException {
577 getWrappedMessageForWriting().setDescription(description);
578 }
579
580 /***
581 * @see javax.mail.internet.MimeMessage#setDescription(java.lang.String, java.lang.String)
582 */
583 public void setDescription(String description, String charset)
584 throws MessagingException {
585 getWrappedMessageForWriting().setDescription(description, charset);
586 }
587
588 /***
589 * @see javax.mail.internet.MimePart#setContentLanguage(java.lang.String[])
590 */
591 public void setContentLanguage(String[] languages)
592 throws MessagingException {
593 getWrappedMessageForWriting().setContentLanguage(languages);
594 }
595
596 /***
597 * @see javax.mail.Part#setFileName(java.lang.String)
598 */
599 public void setFileName(String filename) throws MessagingException {
600 getWrappedMessageForWriting().setFileName(filename);
601 }
602
603 /***
604 * @see javax.mail.Part#setDataHandler(javax.activation.DataHandler)
605 */
606 public void setDataHandler(DataHandler dh) throws MessagingException {
607 getWrappedMessageForWriting().setDataHandler(dh);
608 }
609
610 /***
611 * @see javax.mail.Part#setContent(java.lang.Object, java.lang.String)
612 */
613 public void setContent(Object o, String type) throws MessagingException {
614 getWrappedMessageForWriting().setContent(o, type);
615 }
616
617 /***
618 * @see javax.mail.Part#setText(java.lang.String)
619 */
620 public void setText(String text) throws MessagingException {
621 getWrappedMessageForWriting().setText(text);
622 }
623
624 /***
625 * @see javax.mail.internet.MimePart#setText(java.lang.String, java.lang.String)
626 */
627 public void setText(String text, String charset) throws MessagingException {
628 getWrappedMessageForWriting().setText(text, charset);
629 }
630
631 /***
632 * @see javax.mail.Part#setContent(javax.mail.Multipart)
633 */
634 public void setContent(Multipart mp) throws MessagingException {
635 getWrappedMessageForWriting().setContent(mp);
636 }
637
638 /***
639 * This does not need a writable message
640 * @see javax.mail.Message#reply(boolean)
641 */
642 public Message reply(boolean replyToAll) throws MessagingException {
643 return getWrappedMessage().reply(replyToAll);
644 }
645
646 /***
647 * @see javax.mail.Part#setHeader(java.lang.String, java.lang.String)
648 */
649 public void setHeader(String name, String value) throws MessagingException {
650 getWrappedMessageForWriting().setHeader(name, value);
651 }
652
653 /***
654 * @see javax.mail.Part#addHeader(java.lang.String, java.lang.String)
655 */
656 public void addHeader(String name, String value) throws MessagingException {
657 getWrappedMessageForWriting().addHeader(name, value);
658 }
659
660 /***
661 * @see javax.mail.Part#removeHeader(java.lang.String)
662 */
663 public void removeHeader(String name) throws MessagingException {
664 getWrappedMessageForWriting().removeHeader(name);
665 }
666
667 /***
668 * @see javax.mail.internet.MimePart#addHeaderLine(java.lang.String)
669 */
670 public void addHeaderLine(String line) throws MessagingException {
671 getWrappedMessageForWriting().addHeaderLine(line);
672 }
673
674 /***
675 * @see javax.mail.Message#setFlags(javax.mail.Flags, boolean)
676 */
677 public void setFlags(Flags flag, boolean set) throws MessagingException {
678 getWrappedMessageForWriting().setFlags(flag, set);
679 }
680
681 /***
682 * @see javax.mail.Message#saveChanges()
683 */
684 public void saveChanges() throws MessagingException {
685 getWrappedMessageForWriting().saveChanges();
686 }
687
688
689
690
691
692 /***
693 * @see javax.mail.internet.MimeMessage#addRecipients(javax.mail.Message.RecipientType, java.lang.String)
694 */
695 public void addRecipients(Message.RecipientType type, String addresses)
696 throws MessagingException {
697 getWrappedMessageForWriting().addRecipients(type, addresses);
698 }
699
700 /***
701 * @see javax.mail.internet.MimeMessage#setRecipients(javax.mail.Message.RecipientType, java.lang.String)
702 */
703 public void setRecipients(Message.RecipientType type, String addresses)
704 throws MessagingException {
705 getWrappedMessageForWriting().setRecipients(type, addresses);
706 }
707
708 /***
709 * @see javax.mail.internet.MimeMessage#setSender(javax.mail.Address)
710 */
711 public void setSender(Address arg0) throws MessagingException {
712 getWrappedMessageForWriting().setSender(arg0);
713 }
714
715 /***
716 * @see javax.mail.Message#addRecipient(javax.mail.Message.RecipientType, javax.mail.Address)
717 */
718 public void addRecipient(RecipientType arg0, Address arg1)
719 throws MessagingException {
720 getWrappedMessageForWriting().addRecipient(arg0, arg1);
721 }
722
723 /***
724 * @see javax.mail.Message#setFlag(javax.mail.Flags.Flag, boolean)
725 */
726 public void setFlag(Flag arg0, boolean arg1) throws MessagingException {
727 getWrappedMessageForWriting().setFlag(arg0, arg1);
728 }
729
730 /***
731 * @see java.lang.Object#finalize()
732 */
733 protected void finalize() throws Throwable {
734 dispose();
735 super.finalize();
736 }
737
738 /***
739 * @return the message size
740 * @throws MessagingException
741 */
742 public long getMessageSize() throws MessagingException {
743 return MimeMessageUtil.getMessageSize(getWrappedMessage());
744 }
745
746 }
747