1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 *
4 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
5 *
6 * The contents of this file are subject to the terms of either the GNU
7 * General Public License Version 2 only ("GPL") or the Common Development
8 * and Distribution License("CDDL") (collectively, the "License"). You
9 * may not use this file except in compliance with the License. You can obtain
10 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
11 * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
12 * language governing permissions and limitations under the License.
13 *
14 * When distributing the software, include this License Header Notice in each
15 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
16 * Sun designates this particular file as subject to the "Classpath" exception
17 * as provided by Sun in the GPL Version 2 section of the License file that
18 * accompanied this code. If applicable, add the following below the License
19 * Header, with the fields enclosed by brackets [] replaced by your own
20 * identifying information: "Portions Copyrighted [year]
21 * [name of copyright owner]"
22 *
23 * Contributor(s):
24 *
25 * If you wish your version of this file to be governed by only the CDDL or
26 * only the GPL Version 2, indicate your decision by adding "[Contributor]
27 * elects to include this software in this distribution under the [CDDL or GPL
28 * Version 2] license." If you don't indicate a single choice of license, a
29 * recipient has the option to distribute your version of this file under
30 * either the CDDL, the GPL Version 2 or to extend the choice of license to
31 * its licensees as provided above. However, if you add GPL Version 2 code
32 * and therefore, elected the GPL Version 2 license, then the option applies
33 * only if the new code is made subject to such option by the copyright
34 * holder.
35 */
36
37 /*
38 * @(#)Transport.java 1.40 07/05/17
39 */
40
41 package javax.mail;
42
43 import java.io.IOException;
44 import java.net;
45 import java.util.Vector;
46 import java.util.Hashtable;
47 import java.util.Enumeration;
48 import javax.mail.event;
49
50 /**
51 * An abstract class that models a message transport.
52 * Subclasses provide actual implementations. <p>
53 *
54 * Note that <code>Transport</code> extends the <code>Service</code>
55 * class, which provides many common methods for naming transports,
56 * connecting to transports, and listening to connection events.
57 *
58 * @author John Mani
59 * @author Max Spivak
60 * @author Bill Shannon
61 * @version 1.40, 07/05/17
62 *
63 * @see javax.mail.Service
64 * @see javax.mail.event.ConnectionEvent
65 * @see javax.mail.event.TransportEvent
66 */
67
68 public abstract class Transport extends Service {
69
70 /**
71 * Constructor.
72 *
73 * @param session Session object for this Transport.
74 * @param urlname URLName object to be used for this Transport
75 */
76 public Transport(Session session, URLName urlname) {
77 super(session, urlname);
78 }
79
80 /**
81 * Send a message. The message will be sent to all recipient
82 * addresses specified in the message (as returned from the
83 * <code>Message</code> method <code>getAllRecipients</code>),
84 * using message transports appropriate to each address. The
85 * <code>send</code> method calls the <code>saveChanges</code>
86 * method on the message before sending it. <p>
87 *
88 * If any of the recipient addresses is detected to be invalid by
89 * the Transport during message submission, a SendFailedException
90 * is thrown. Clients can get more detail about the failure by examining
91 * the exception. Whether or not the message is still sent succesfully to
92 * any valid addresses depends on the Transport implementation. See
93 * SendFailedException for more details. Note also that success does
94 * not imply that the message was delivered to the ultimate recipient,
95 * as failures may occur in later stages of delivery. Once a Transport
96 * accepts a message for delivery to a recipient, failures that occur later
97 * should be reported to the user via another mechanism, such as
98 * returning the undeliverable message. <p>
99 *
100 * Note that <code>send</code> is a static method that creates and
101 * manages its own connection. Any connection associated with any
102 * Transport instance used to invoke this method is ignored and not
103 * used. This method should only be invoked using the form
104 * <code>Transport.send(msg);</code>, and should never be invoked
105 * using an instance variable.
106 *
107 * @param msg the message to send
108 * @exception SendFailedException if the message could not
109 * be sent to some or any of the recipients.
110 * @exception MessagingException
111 * @see Message#saveChanges
112 * @see Message#getAllRecipients
113 * @see #send(Message, Address[])
114 * @see javax.mail.SendFailedException
115 */
116 public static void send(Message msg) throws MessagingException {
117 msg.saveChanges(); // do this first
118 send0(msg, msg.getAllRecipients());
119 }
120
121 /**
122 * Send the message to the specified addresses, ignoring any
123 * recipients specified in the message itself. The
124 * <code>send</code> method calls the <code>saveChanges</code>
125 * method on the message before sending it. <p>
126 *
127 * @param msg the message to send
128 * @param addresses the addresses to which to send the message
129 * @exception SendFailedException if the message could not
130 * be sent to some or any of the recipients.
131 * @exception MessagingException
132 * @see Message#saveChanges
133 * @see #send(Message)
134 * @see javax.mail.SendFailedException
135 */
136 public static void send(Message msg, Address[] addresses)
137 throws MessagingException {
138
139 msg.saveChanges();
140 send0(msg, addresses);
141 }
142
143 // send, but without the saveChanges
144 private static void send0(Message msg, Address[] addresses)
145 throws MessagingException {
146
147 if (addresses == null || addresses.length == 0)
148 throw new SendFailedException("No recipient addresses");
149
150 /*
151 * protocols is a hashtable containing the addresses
152 * indexed by address type
153 */
154 Hashtable protocols = new Hashtable();
155
156 // Vectors of addresses
157 Vector invalid = new Vector();
158 Vector validSent = new Vector();
159 Vector validUnsent = new Vector();
160
161 for (int i = 0; i < addresses.length; i++) {
162 // is this address type already in the hashtable?
163 if (protocols.containsKey(addresses[i].getType())) {
164 Vector v = (Vector)protocols.get(addresses[i].getType());
165 v.addElement(addresses[i]);
166 } else {
167 // need to add a new protocol
168 Vector w = new Vector();
169 w.addElement(addresses[i]);
170 protocols.put(addresses[i].getType(), w);
171 }
172 }
173
174 int dsize = protocols.size();
175 if (dsize == 0)
176 throw new SendFailedException("No recipient addresses");
177
178 Session s = (msg.session != null) ? msg.session :
179 Session.getDefaultInstance(System.getProperties(), null);
180 Transport transport;
181
182 /*
183 * Optimize the case of a single protocol.
184 */
185 if (dsize == 1) {
186 transport = s.getTransport(addresses[0]);
187 try {
188 transport.connect();
189 transport.sendMessage(msg, addresses);
190 } finally {
191 transport.close();
192 }
193 return;
194 }
195
196 /*
197 * More than one protocol. Have to do them one at a time
198 * and collect addresses and chain exceptions.
199 */
200 MessagingException chainedEx = null;
201 boolean sendFailed = false;
202
203 Enumeration e = protocols.elements();
204 while (e.hasMoreElements()) {
205 Vector v = (Vector)e.nextElement();
206 Address[] protaddresses = new Address[v.size()];
207 v.copyInto(protaddresses);
208
209 // Get a Transport that can handle this address type.
210 if ((transport = s.getTransport(protaddresses[0])) == null) {
211 // Could not find an appropriate Transport ..
212 // Mark these addresses invalid.
213 for (int j = 0; j < protaddresses.length; j++)
214 invalid.addElement(protaddresses[j]);
215 continue;
216 }
217 try {
218 transport.connect();
219 transport.sendMessage(msg, protaddresses);
220 } catch (SendFailedException sex) {
221 sendFailed = true;
222 // chain the exception we're catching to any previous ones
223 if (chainedEx == null)
224 chainedEx = sex;
225 else
226 chainedEx.setNextException(sex);
227
228 // retrieve invalid addresses
229 Address[] a = sex.getInvalidAddresses();
230 if (a != null)
231 for (int j = 0; j < a.length; j++)
232 invalid.addElement(a[j]);
233
234 // retrieve validSent addresses
235 a = sex.getValidSentAddresses();
236 if (a != null)
237 for (int k = 0; k < a.length; k++)
238 validSent.addElement(a[k]);
239
240 // retrieve validUnsent addresses
241 Address[] c = sex.getValidUnsentAddresses();
242 if (c != null)
243 for (int l = 0; l < c.length; l++)
244 validUnsent.addElement(c[l]);
245 } catch (MessagingException mex) {
246 sendFailed = true;
247 // chain the exception we're catching to any previous ones
248 if (chainedEx == null)
249 chainedEx = mex;
250 else
251 chainedEx.setNextException(mex);
252 } finally {
253 transport.close();
254 }
255 }
256
257 // done with all protocols. throw exception if something failed
258 if (sendFailed || invalid.size() != 0 || validUnsent.size() != 0) {
259 Address[] a = null, b = null, c = null;
260
261 // copy address vectors into arrays
262 if (validSent.size() > 0) {
263 a = new Address[validSent.size()];
264 validSent.copyInto(a);
265 }
266 if (validUnsent.size() > 0) {
267 b = new Address[validUnsent.size()];
268 validUnsent.copyInto(b);
269 }
270 if (invalid.size() > 0) {
271 c = new Address[invalid.size()];
272 invalid.copyInto(c);
273 }
274 throw new SendFailedException("Sending failed", chainedEx,
275 a, b, c);
276 }
277 }
278
279 /**
280 * Send the Message to the specified list of addresses. An appropriate
281 * TransportEvent indicating the delivery status is delivered to any
282 * TransportListener registered on this Transport. Also, if any of
283 * the addresses is invalid, a SendFailedException is thrown.
284 * Whether or not the message is still sent succesfully to
285 * any valid addresses depends on the Transport implementation. <p>
286 *
287 * Unlike the static <code>send</code> method, the <code>sendMessage</code>
288 * method does <em>not</em> call the <code>saveChanges</code> method on
289 * the message; the caller should do so.
290 *
291 * @param msg The Message to be sent
292 * @param addresses array of addresses to send this message to
293 * @see javax.mail.event.TransportEvent
294 * @exception SendFailedException if the send failed because of
295 * invalid addresses.
296 * @exception MessagingException if the connection is dead or not in the
297 * connected state
298 */
299 public abstract void sendMessage(Message msg, Address[] addresses)
300 throws MessagingException;
301
302 // Vector of Transport listeners
303 private Vector transportListeners = null;
304
305 /**
306 * Add a listener for Transport events. <p>
307 *
308 * The default implementation provided here adds this listener
309 * to an internal list of TransportListeners.
310 *
311 * @param l the Listener for Transport events
312 * @see javax.mail.event.TransportEvent
313 */
314 public synchronized void addTransportListener(TransportListener l) {
315 if (transportListeners == null)
316 transportListeners = new Vector();
317 transportListeners.addElement(l);
318 }
319
320 /**
321 * Remove a listener for Transport events. <p>
322 *
323 * The default implementation provided here removes this listener
324 * from the internal list of TransportListeners.
325 *
326 * @param l the listener
327 * @see #addTransportListener
328 */
329 public synchronized void removeTransportListener(TransportListener l) {
330 if (transportListeners != null)
331 transportListeners.removeElement(l);
332 }
333
334 /**
335 * Notify all TransportListeners. Transport implementations are
336 * expected to use this method to broadcast TransportEvents.<p>
337 *
338 * The provided default implementation queues the event into
339 * an internal event queue. An event dispatcher thread dequeues
340 * events from the queue and dispatches them to the registered
341 * TransportListeners. Note that the event dispatching occurs
342 * in a separate thread, thus avoiding potential deadlock problems.
343 */
344 protected void notifyTransportListeners(int type, Address[] validSent,
345 Address[] validUnsent,
346 Address[] invalid, Message msg) {
347 if (transportListeners == null)
348 return;
349
350 TransportEvent e = new TransportEvent(this, type, validSent,
351 validUnsent, invalid, msg);
352 queueEvent(e, transportListeners);
353 }
354 }