View Javadoc

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