KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > james > core > MailImpl


1 /***********************************************************************
2  * Copyright (c) 2000-2004 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
22 import org.apache.james.util.RFC2822Headers;
23 import org.apache.mailet.Mail;
24 import org.apache.mailet.MailAddress;
25
26 import javax.mail.Address JavaDoc;
27 import javax.mail.Message JavaDoc;
28 import javax.mail.MessagingException JavaDoc;
29 import javax.mail.internet.InternetAddress JavaDoc;
30 import javax.mail.internet.MimeMessage JavaDoc;
31 import javax.mail.internet.ParseException JavaDoc;
32 import java.io.*;
33 import java.util.ArrayList JavaDoc;
34 import java.util.Collection JavaDoc;
35 import java.util.Date JavaDoc;
36 import java.util.Enumeration JavaDoc;
37 import java.util.HashSet JavaDoc;
38 import java.util.Iterator JavaDoc;
39 import java.util.HashMap JavaDoc;
40
41 /**
42  * <P>Wraps a MimeMessage adding routing information (from SMTP) and some simple
43  * API enhancements.</P>
44  * <P>From James version > 2.2.0a8 "mail attributes" have been added.
45  * Backward and forward compatibility is supported:
46  * messages stored in file repositories <I>without</I> attributes by James version <= 2.2.0a8
47  * will be processed by later versions as having an empty attributes hashmap;
48  * messages stored in file repositories <I>with</I> attributes by James version > 2.2.0a8
49  * will be processed by previous versions, ignoring the attributes.</P>
50  *
51  * @version CVS $Revision: 1.17.4.6 $ $Date: 2004/03/15 03:54:15 $
52  */

53 public class MailImpl implements Disposable, Mail {
54     /**
55      * We hardcode the serialVersionUID so that from James 1.2 on,
56      * MailImpl will be deserializable (so your mail doesn't get lost)
57      */

58     public static final long serialVersionUID = -4289663364703986260L;
59     /**
60      * The error message, if any, associated with this mail.
61      */

62     private String JavaDoc errorMessage;
63     /**
64      * The state of this mail, which determines how it is processed.
65      */

66     private String JavaDoc state;
67     /**
68      * The MimeMessage that holds the mail data.
69      */

70     private MimeMessage JavaDoc message;
71     /**
72      * The sender of this mail.
73      */

74     private MailAddress sender;
75     /**
76      * The collection of recipients to whom this mail was sent.
77      */

78     private Collection JavaDoc recipients;
79     /**
80      * The identifier for this mail message
81      */

82     private String JavaDoc name;
83     /**
84      * The remote host from which this mail was sent.
85      */

86     private String JavaDoc remoteHost = "localhost";
87     /**
88      * The remote address from which this mail was sent.
89      */

90     private String JavaDoc remoteAddr = "127.0.0.1";
91     /**
92      * The last time this message was updated.
93      */

94     private Date JavaDoc lastUpdated = new Date JavaDoc();
95     /**
96      * Attributes added to this MailImpl instance
97      */

98     private HashMap JavaDoc attributes;
99     /**
100      * A constructor that creates a new, uninitialized MailImpl
101      */

102     public MailImpl() {
103         setState(Mail.DEFAULT);
104         attributes = new HashMap JavaDoc();
105     }
106     /**
107      * A constructor that creates a MailImpl with the specified name,
108      * sender, and recipients.
109      *
110      * @param name the name of the MailImpl
111      * @param sender the sender for this MailImpl
112      * @param recipients the collection of recipients of this MailImpl
113      */

114     public MailImpl(String JavaDoc name, MailAddress sender, Collection JavaDoc recipients) {
115         this();
116         this.name = name;
117         this.sender = sender;
118         this.recipients = null;
119
120         // Copy the recipient list
121
if (recipients != null) {
122             Iterator JavaDoc theIterator = recipients.iterator();
123             this.recipients = new ArrayList JavaDoc();
124             while (theIterator.hasNext()) {
125                 this.recipients.add(theIterator.next());
126             }
127         }
128     }
129
130     /**
131      * A constructor that creates a MailImpl with the specified name,
132      * sender, recipients, and message data.
133      *
134      * @param name the name of the MailImpl
135      * @param sender the sender for this MailImpl
136      * @param recipients the collection of recipients of this MailImpl
137      * @param messageIn a stream containing the message source
138      */

139     public MailImpl(String JavaDoc name, MailAddress sender, Collection JavaDoc recipients, InputStream messageIn)
140         throws MessagingException JavaDoc {
141         this(name, sender, recipients);
142         MimeMessageSource source = new MimeMessageInputStreamSource(name, messageIn);
143         MimeMessageWrapper wrapper = new MimeMessageWrapper(source);
144         this.setMessage(wrapper);
145     }
146
147     /**
148      * A constructor that creates a MailImpl with the specified name,
149      * sender, recipients, and MimeMessage.
150      *
151      * @param name the name of the MailImpl
152      * @param sender the sender for this MailImpl
153      * @param recipients the collection of recipients of this MailImpl
154      * @param message the MimeMessage associated with this MailImpl
155      */

156     public MailImpl(String JavaDoc name, MailAddress sender, Collection JavaDoc recipients, MimeMessage JavaDoc message) {
157         this(name, sender, recipients);
158         this.setMessage(message);
159     }
160
161     /**
162      * A constructor which will attempt to obtain sender and recipients from the headers of the MimeMessage supplied.
163      * @param message - a MimeMessage from which to construct a Mail
164      */

165     public MailImpl(MimeMessage JavaDoc message) throws MessagingException JavaDoc {
166         this();
167         MailAddress sender = getReturnPath(message);
168         Collection JavaDoc recipients = null;
169         Address[] addresses = message.getRecipients(MimeMessage.RecipientType.TO);
170         if (addresses != null) {
171             recipients = new ArrayList JavaDoc();
172             for (int i = 0; i < addresses.length; i++) {
173                 try {
174                     recipients.add(new MailAddress(new InternetAddress JavaDoc(addresses[i].toString(), false)));
175                 } catch (ParseException JavaDoc pe) {
176                     // RFC 2822 section 3.4 allows To: fields without <>
177
// Let's give this one more try with <>.
178
try {
179                         recipients.add(new MailAddress("<" + new InternetAddress JavaDoc(addresses[i].toString()).toString() + ">"));
180                     } catch (ParseException JavaDoc _) {
181                         throw new MessagingException JavaDoc("Could not parse address: " + addresses[i].toString() + " from " + message.getHeader(RFC2822Headers.TO, ", "), pe);
182                     }
183                 }
184             }
185         }
186         this.name = message.toString();
187         this.sender = sender;
188         this.recipients = recipients;
189         this.setMessage(message);
190     }
191     /**
192      * Gets the MailAddress corresponding to the existing "Return-Path" of
193      * <I>message</I>.
194      * If missing or empty returns <CODE>null</CODE>,
195      */

196     private MailAddress getReturnPath(MimeMessage JavaDoc message) throws MessagingException JavaDoc {
197         MailAddress mailAddress = null;
198         String JavaDoc[] returnPathHeaders = message.getHeader(RFC2822Headers.RETURN_PATH);
199         String JavaDoc returnPathHeader = null;
200         if (returnPathHeaders != null) {
201             returnPathHeader = returnPathHeaders[0];
202             if (returnPathHeader != null) {
203                 returnPathHeader = returnPathHeader.trim();
204                 if (!returnPathHeader.equals("<>")) {
205                     try {
206                         mailAddress = new MailAddress(new InternetAddress JavaDoc(returnPathHeader, false));
207                     } catch (ParseException JavaDoc pe) {
208                         throw new MessagingException JavaDoc("Could not parse address: " + returnPathHeader + " from " + message.getHeader(RFC2822Headers.RETURN_PATH, ", "), pe);
209                     }
210                 }
211             }
212         }
213         return mailAddress;
214     }
215     /**
216      * Duplicate the MailImpl.
217      *
218      * @return a MailImpl that is a duplicate of this one
219      */

220     public Mail duplicate() {
221         return duplicate(name);
222     }
223     /**
224      * Duplicate the MailImpl, replacing the mail name with the one
225      * passed in as an argument.
226      *
227      * @param newName the name for the duplicated mail
228      *
229      * @return a MailImpl that is a duplicate of this one with a different name
230      */

231     public Mail duplicate(String JavaDoc newName) {
232         try {
233             MailImpl newMail = new MailImpl(newName, sender, recipients, getMessage());
234             newMail.setRemoteHost(remoteHost);
235             newMail.setRemoteAddr(remoteAddr);
236             newMail.setLastUpdated(lastUpdated);
237             newMail.setAttributesRaw((HashMap JavaDoc) attributes.clone());
238             return newMail;
239         } catch (MessagingException JavaDoc me) {
240             // Ignored. Return null in the case of an error.
241
}
242         return (Mail) null;
243     }
244     /**
245      * Get the error message associated with this MailImpl.
246      *
247      * @return the error message associated with this MailImpl
248      */

249     public String JavaDoc getErrorMessage() {
250         return errorMessage;
251     }
252     /**
253      * Get the MimeMessage associated with this MailImpl.
254      *
255      * @return the MimeMessage associated with this MailImpl
256      */

257     public MimeMessage JavaDoc getMessage() throws MessagingException JavaDoc {
258         return message;
259     }
260     /**
261      * Set the name of this MailImpl.
262      *
263      * @param name the name of this MailImpl
264      */

265     public void setName(String JavaDoc name) {
266         this.name = name;
267     }
268     /**
269      * Get the name of this MailImpl.
270      *
271      * @return the name of this MailImpl
272      */

273     public String JavaDoc getName() {
274         return name;
275     }
276     /**
277      * Get the recipients of this MailImpl.
278      *
279      * @return the recipients of this MailImpl
280      */

281     public Collection JavaDoc getRecipients() {
282         return recipients;
283     }
284     /**
285      * Get the sender of this MailImpl.
286      *
287      * @return the sender of this MailImpl
288      */

289     public MailAddress getSender() {
290         return sender;
291     }
292     /**
293      * Get the state of this MailImpl.
294      *
295      * @return the state of this MailImpl
296      */

297     public String JavaDoc getState() {
298         return state;
299     }
300     /**
301      * Get the remote host associated with this MailImpl.
302      *
303      * @return the remote host associated with this MailImpl
304      */

305     public String JavaDoc getRemoteHost() {
306         return remoteHost;
307     }
308     /**
309      * Get the remote address associated with this MailImpl.
310      *
311      * @return the remote address associated with this MailImpl
312      */

313     public String JavaDoc getRemoteAddr() {
314         return remoteAddr;
315     }
316     /**
317      * Get the last updated time for this MailImpl.
318      *
319      * @return the last updated time for this MailImpl
320      */

321     public Date JavaDoc getLastUpdated() {
322         return lastUpdated;
323     }
324     /**
325      * <p>Return the size of the message including its headers.
326      * MimeMessage.getSize() method only returns the size of the
327      * message body.</p>
328      *
329      * <p>Note: this size is not guaranteed to be accurate - see Sun's
330      * documentation of MimeMessage.getSize().</p>
331      *
332      * @return approximate size of full message including headers.
333      *
334      * @throws MessagingException if a problem occurs while computing the message size
335      */

336     public long getMessageSize() throws MessagingException JavaDoc {
337         //If we have a MimeMessageWrapper, then we can ask it for just the
338
// message size and skip calculating it
339
if (message instanceof MimeMessageWrapper) {
340             MimeMessageWrapper wrapper = (MimeMessageWrapper) message;
341             return wrapper.getMessageSize();
342         }
343         //SK: Should probably eventually store this as a locally
344
// maintained value (so we don't have to load and reparse
345
// messages each time).
346
long size = message.getSize();
347         Enumeration JavaDoc e = message.getAllHeaderLines();
348         while (e.hasMoreElements()) {
349             size += ((String JavaDoc) e.nextElement()).length();
350         }
351         return size;
352     }
353     /**
354      * Set the error message associated with this MailImpl.
355      *
356      * @param msg the new error message associated with this MailImpl
357      */

358     public void setErrorMessage(String JavaDoc msg) {
359         this.errorMessage = msg;
360     }
361     /**
362      * Set the MimeMessage associated with this MailImpl.
363      *
364      * @param message the new MimeMessage associated with this MailImpl
365      */

366     public void setMessage(MimeMessage JavaDoc message) {
367         this.message = message;
368     }
369     /**
370      * Set the recipients for this MailImpl.
371      *
372      * @param recipients the recipients for this MailImpl
373      */

374     public void setRecipients(Collection JavaDoc recipients) {
375         this.recipients = recipients;
376     }
377     /**
378      * Set the sender of this MailImpl.
379      *
380      * @param sender the sender of this MailImpl
381      */

382     public void setSender(MailAddress sender) {
383         this.sender = sender;
384     }
385     /**
386      * Set the state of this MailImpl.
387      *
388      * @param state the state of this MailImpl
389      */

390     public void setState(String JavaDoc state) {
391         this.state = state;
392     }
393     /**
394      * Set the remote address associated with this MailImpl.
395      *
396      * @param remoteHost the new remote host associated with this MailImpl
397      */

398     public void setRemoteHost(String JavaDoc remoteHost) {
399         this.remoteHost = remoteHost;
400     }
401     /**
402      * Set the remote address associated with this MailImpl.
403      *
404      * @param remoteAddr the new remote address associated with this MailImpl
405      */

406     public void setRemoteAddr(String JavaDoc remoteAddr) {
407         this.remoteAddr = remoteAddr;
408     }
409     /**
410      * Set the date this mail was last updated.
411      *
412      * @param lastUpdated the date the mail was last updated
413      */

414     public void setLastUpdated(Date JavaDoc lastUpdated) {
415         // Make a defensive copy to ensure that the date
416
// doesn't get changed external to the class
417
if (lastUpdated != null) {
418             lastUpdated = new Date JavaDoc(lastUpdated.getTime());
419         }
420         this.lastUpdated = lastUpdated;
421     }
422     /**
423      * Writes the message out to an OutputStream.
424      *
425      * @param out the OutputStream to which to write the content
426      *
427      * @throws MessagingException if the MimeMessage is not set for this MailImpl
428      * @throws IOException if an error occurs while reading or writing from the stream
429      */

430     public void writeMessageTo(OutputStream out) throws IOException, MessagingException JavaDoc {
431         if (message != null) {
432             message.writeTo(out);
433         } else {
434             throw new MessagingException JavaDoc("No message set for this MailImpl.");
435         }
436     }
437     /**
438      * Generates a bounce mail that is a bounce of the original message.
439      *
440      * @param bounceText the text to be prepended to the message to describe the bounce condition
441      *
442      * @return the bounce mail
443      *
444      * @throws MessagingException if the bounce mail could not be created
445      */

446     public Mail bounce(String JavaDoc bounceText) throws MessagingException JavaDoc {
447         //This sends a message to the james component that is a bounce of the sent message
448
MimeMessage JavaDoc original = getMessage();
449         MimeMessage JavaDoc reply = (MimeMessage JavaDoc) original.reply(false);
450         reply.setSubject("Re: " + original.getSubject());
451         Collection JavaDoc recipients = new HashSet JavaDoc();
452         recipients.add(getSender());
453         InternetAddress JavaDoc addr[] = { new InternetAddress JavaDoc(getSender().toString())};
454         reply.setRecipients(Message.RecipientType.TO, addr);
455         reply.setFrom(new InternetAddress JavaDoc(getRecipients().iterator().next().toString()));
456         reply.setText(bounceText);
457         reply.setHeader(RFC2822Headers.MESSAGE_ID, "replyTo-" + getName());
458         return new MailImpl(
459             "replyTo-" + getName(),
460             new MailAddress(getRecipients().iterator().next().toString()),
461             recipients,
462             reply);
463     }
464     /**
465      * Writes the content of the message, up to a total number of lines, out to
466      * an OutputStream.
467      *
468      * @param out the OutputStream to which to write the content
469      * @param lines the number of lines to write to the stream
470      *
471      * @throws MessagingException if the MimeMessage is not set for this MailImpl
472      * @throws IOException if an error occurs while reading or writing from the stream
473      */

474     public void writeContentTo(OutputStream out, int lines)
475         throws IOException, MessagingException JavaDoc {
476         String JavaDoc line;
477         BufferedReader br;
478         if (message != null) {
479             br = new BufferedReader(new InputStreamReader(message.getInputStream()));
480             while (lines-- > 0) {
481                 if ((line = br.readLine()) == null) {
482                     break;
483                 }
484                 line += "\r\n";
485                 out.write(line.getBytes());
486             }
487         } else {
488             throw new MessagingException JavaDoc("No message set for this MailImpl.");
489         }
490     }
491     // Serializable Methods
492
// TODO: These need some work. Currently very tightly coupled to
493
// the internal representation.
494
/**
495      * Read the MailImpl from an <code>ObjectInputStream</code>.
496      *
497      * @param in the ObjectInputStream from which the object is read
498      *
499      * @throws IOException if an error occurs while reading from the stream
500      * @throws ClassNotFoundException ?
501      * @throws ClassCastException if the serialized objects are not of the appropriate type
502      */

503     private void readObject(java.io.ObjectInputStream JavaDoc in)
504         throws IOException, ClassNotFoundException JavaDoc {
505         try {
506             Object JavaDoc obj = in.readObject();
507             if (obj == null) {
508                 sender = null;
509             } else if (obj instanceof String JavaDoc) {
510                 sender = new MailAddress((String JavaDoc) obj);
511             } else if (obj instanceof MailAddress) {
512                 sender = (MailAddress) obj;
513             }
514         } catch (ParseException JavaDoc pe) {
515             throw new IOException("Error parsing sender address: " + pe.getMessage());
516         }
517         recipients = (Collection JavaDoc) in.readObject();
518         state = (String JavaDoc) in.readObject();
519         errorMessage = (String JavaDoc) in.readObject();
520         name = (String JavaDoc) in.readObject();
521         remoteHost = (String JavaDoc) in.readObject();
522         remoteAddr = (String JavaDoc) in.readObject();
523         setLastUpdated((Date JavaDoc) in.readObject());
524         // the following is under try/catch to be backwards compatible
525
// with messages created with James version <= 2.2.0a8
526
try {
527             attributes = (HashMap JavaDoc) in.readObject();
528         } catch (OptionalDataException ode) {
529             if (ode.eof) {
530                 attributes = new HashMap JavaDoc();
531             } else {
532                 throw ode;
533             }
534         }
535     }
536     /**
537      * Write the MailImpl to an <code>ObjectOutputStream</code>.
538      *
539      * @param in the ObjectOutputStream to which the object is written
540      *
541      * @throws IOException if an error occurs while writing to the stream
542      */

543     private void writeObject(java.io.ObjectOutputStream JavaDoc out) throws IOException {
544         lastUpdated = new Date JavaDoc();
545         out.writeObject(sender);
546         out.writeObject(recipients);
547         out.writeObject(state);
548         out.writeObject(errorMessage);
549         out.writeObject(name);
550         out.writeObject(remoteHost);
551         out.writeObject(remoteAddr);
552         out.writeObject(lastUpdated);
553         out.writeObject(attributes);
554     }
555
556     /**
557      * @see org.apache.avalon.framework.activity.Disposable#dispose()
558      */

559     public void dispose() {
560         try {
561             MimeMessage JavaDoc wrapper = getMessage();
562             if (wrapper instanceof Disposable) {
563                 ((Disposable)wrapper).dispose();
564             }
565         } catch (MessagingException JavaDoc me) {
566             // Ignored
567
}
568     }
569
570     /**
571      * This method is necessary, when Mail repositories needs to deal
572      * explicitly with storing Mail attributes as a Serializable
573      * Note: This method is not exposed in the Mail interface,
574      * it is for internal use by James only.
575      * @return Serializable of the entire attributes collection
576      * @since 2.2.0
577      **/

578     public HashMap JavaDoc getAttributesRaw ()
579     {
580         return attributes;
581     }
582     
583     /**
584      * This method is necessary, when Mail repositories needs to deal
585      * explicitly with retriving Mail attributes as a Serializable
586      * Note: This method is not exposed in the Mail interface,
587      * it is for internal use by James only.
588      * @return Serializable of the entire attributes collection
589      * @since 2.2.0
590      **/

591     public void setAttributesRaw (HashMap JavaDoc attr)
592     {
593         this.attributes = (attr == null) ? new HashMap JavaDoc() : attr;
594     }
595
596     /**
597      * @see org.apache.mailet.Mail#getAttribute(String)
598      * @since 2.2.0
599      */

600     public Serializable getAttribute(String JavaDoc key) {
601         return (Serializable)attributes.get(key);
602     }
603     /**
604      * @see org.apache.mailet.Mail#setAttribute(String,Serializable)
605      * @since 2.2.0
606      */

607     public Serializable setAttribute(String JavaDoc key, Serializable object) {
608         return (Serializable)attributes.put(key, object);
609     }
610     /**
611      * @see org.apache.mailet.Mail#removeAttribute(String)
612      * @since 2.2.0
613      */

614     public Serializable removeAttribute(String JavaDoc key) {
615         return (Serializable)attributes.remove(key);
616     }
617     /**
618      * @see org.apache.mailet.Mail#removeAllAttributes()
619      * @since 2.2.0
620      */

621     public void removeAllAttributes() {
622         attributes.clear();
623     }
624     /**
625      * @see org.apache.mailet.Mail#getAttributeNames()
626      * @since 2.2.0
627      */

628     public Iterator JavaDoc getAttributeNames() {
629         return attributes.keySet().iterator();
630     }
631     /**
632      * @see org.apache.mailet.Mail#hasAttributes()
633      * @since 2.2.0
634      */

635     public boolean hasAttributes() {
636         return !attributes.isEmpty();
637     }
638 }
639
Popular Tags