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