View Javadoc

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      * Various writer methods
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      * Since JavaMail 1.2
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