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
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
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