View Javadoc

1   /**
2    *
3    * Copyright 2003-2006 The Apache Software Foundation
4    *
5    *  Licensed under the Apache License, Version 2.0 (the "License");
6    *  you may not use this file except in compliance with the License.
7    *  You 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 implied.
14   *  See the License for the specific language governing permissions and
15   *  limitations under the License.
16   */
17  
18  package javax.mail;
19  
20  import java.util.ArrayList;
21  import java.util.HashMap;
22  import java.util.Iterator;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.Vector;
26  import javax.mail.event.TransportEvent;
27  import javax.mail.event.TransportListener;
28  
29  /**
30   * Abstract class modeling a message transport.
31   *
32   * @version $Rev: 421852 $ $Date: 2006-07-14 03:02:19 -0700 (Fri, 14 Jul 2006) $
33   */
34  public abstract class Transport extends Service {
35      /**
36       * Send a message to all recipient addresses it contains (as returned by {@link Message#getAllRecipients()})
37       * using message transports appropriate for each address. Message addresses are checked during submission,
38       * but there is no guarantee that the ultimate address is valid or that the message will ever be delivered.
39       * <p/>
40       * {@link Message#saveChanges()} will be called before the message is actually sent.
41       *
42       * @param message the message to send
43       * @throws MessagingException if there was a problem sending the message
44       */
45      public static void send(Message message) throws MessagingException {
46          send(message, message.getAllRecipients());
47      }
48  
49      /**
50       * Send a message to all addresses provided irrespective of any recipients contained in the message itself
51       * using message transports appropriate for each address. Message addresses are checked during submission,
52       * but there is no guarantee that the ultimate address is valid or that the message will ever be delivered.
53       * <p/>
54       * {@link Message#saveChanges()} will be called before the message is actually sent.
55       *
56       * @param message   the message to send
57       * @param addresses the addesses to send to
58       * @throws MessagingException if there was a problem sending the message
59       */
60      public static void send(Message message, Address[] addresses) throws MessagingException {
61          Session session = message.session;
62          Map msgsByTransport = new HashMap();
63          for (int i = 0; i < addresses.length; i++) {
64              Address address = addresses[i];
65              Transport transport = session.getTransport(address);
66              List addrs = (List) msgsByTransport.get(transport);
67              if (addrs == null) {
68                  addrs = new ArrayList();
69                  msgsByTransport.put(transport, addrs);
70              }
71              addrs.add(address);
72          }
73  
74          message.saveChanges();
75  
76          // Since we might be sending to multiple protocols, we need to catch and process each exception
77          // when we send and then throw a new SendFailedException when everything is done.  Unfortunately, this
78          // also means unwrapping the information in any SendFailedExceptions we receive and building
79          // composite failed list.
80          MessagingException chainedException = null;
81          ArrayList sentAddresses = new ArrayList();
82          ArrayList unsentAddresses = new ArrayList();
83          ArrayList invalidAddresses = new ArrayList();
84  
85  
86          for (Iterator i = msgsByTransport.entrySet().iterator(); i.hasNext();) {
87              Map.Entry entry = (Map.Entry) i.next();
88              Transport transport = (Transport) entry.getKey();
89              List addrs = (List) entry.getValue();
90              try {
91                  // we MUST connect to the transport before attempting to send.
92                  transport.connect();
93                  transport.sendMessage(message, (Address[]) addrs.toArray(new Address[addrs.size()]));
94                  // if we have to throw an exception because of another failure, these addresses need to
95                  // be in the valid list.  Since we succeeded here, we can add these now.
96                  sentAddresses.addAll(addrs);
97              } catch (SendFailedException e) {
98                  // a true send failure.  The exception contains a wealth of information about
99                  // the failures, including a potential chain of exceptions explaining what went wrong.  We're
100                 // going to send a new one of these, so we need to merge the information.
101 
102                 // add this to our exception chain
103                 if (chainedException == null) {
104                     chainedException = e;
105                 }
106                 else {
107                     chainedException.setNextException(e);
108                 }
109 
110                 // now extract each of the address categories from
111                 Address[] exAddrs = e.getValidSentAddresses();
112                 if (exAddrs != null) {
113                     for (int j = 0; j < exAddrs.length; j++) {
114                         sentAddresses.add(exAddrs[j]);
115                     }
116                 }
117 
118                 exAddrs = e.getValidUnsentAddresses();
119                 if (exAddrs != null) {
120                     for (int j = 0; j < exAddrs.length; j++) {
121                         unsentAddresses.add(exAddrs[j]);
122                     }
123                 }
124 
125                 exAddrs = e.getInvalidAddresses();
126                 if (exAddrs != null) {
127                     for (int j = 0; j < exAddrs.length; j++) {
128                         invalidAddresses.add(exAddrs[j]);
129                     }
130                 }
131 
132             } catch (MessagingException e) {
133                 // add this to our exception chain
134                 if (chainedException == null) {
135                     chainedException = e;
136                 }
137                 else {
138                     chainedException.setNextException(e);
139                 }
140             }
141             finally {
142                 transport.close();
143             }
144         }
145 
146         // if we have an exception chain then we need to throw a new exception giving the failure
147         // information.
148         if (chainedException != null) {
149             // if we're only sending to a single transport (common), and we received a SendFailedException
150             // as a result, then we have a fully formed exception already.  Rather than wrap this in another
151             // exception, we can just rethrow the one we have.
152             if (msgsByTransport.size() == 1 && chainedException instanceof SendFailedException) {
153                 throw chainedException;
154             }
155 
156             // create our lists for notification and exception reporting from this point on.
157             Address[] sent = (Address[])sentAddresses.toArray(new Address[0]);
158             Address[] unsent = (Address[])unsentAddresses.toArray(new Address[0]);
159             Address[] invalid = (Address[])invalidAddresses.toArray(new Address[0]);
160 
161             throw new SendFailedException("Send failure", chainedException, sent, unsent, invalid);
162         }
163     }
164 
165     /**
166      * Constructor taking Session and URLName parameters required for {@link Service#Service(Session, URLName)}.
167      *
168      * @param session the Session this transport is for
169      * @param name    the location this transport is for
170      */
171     public Transport(Session session, URLName name) {
172         super(session, name);
173     }
174 
175     /**
176      * Send a message to the supplied addresses using this transport; if any of the addresses are
177      * invalid then a {@link SendFailedException} is thrown. Whether the message is actually sent
178      * to any of the addresses is undefined.
179      * <p/>
180      * Unlike the static {@link #send(Message, Address[])} method, {@link Message#saveChanges()} is
181      * not called. A {@link TransportEvent} will be sent to registered listeners once the delivery
182      * attempt has been made.
183      *
184      * @param message   the message to send
185      * @param addresses list of addresses to send it to
186      * @throws SendFailedException if the send failed
187      * @throws MessagingException  if there was a problem sending the message
188      */
189     public abstract void sendMessage(Message message, Address[] addresses) throws MessagingException;
190 
191     private Vector transportListeners = new Vector();
192 
193     public void addTransportListener(TransportListener listener) {
194         transportListeners.add(listener);
195     }
196 
197     public void removeTransportListener(TransportListener listener) {
198         transportListeners.remove(listener);
199     }
200 
201     protected void notifyTransportListeners(int type, Address[] validSent, Address[] validUnsent, Address[] invalid, Message message) {
202         queueEvent(new TransportEvent(this, type, validSent, validUnsent, invalid, message), transportListeners);
203     }
204 }