Skip to content

Method: Transport(Session, URLName)

1: /*
2: * Copyright (c) 1997, 2023 Oracle and/or its affiliates. All rights reserved.
3: *
4: * This program and the accompanying materials are made available under the
5: * terms of the Eclipse Public License v. 2.0, which is available at
6: * http://www.eclipse.org/legal/epl-2.0.
7: *
8: * This Source Code may also be made available under the following Secondary
9: * Licenses when the conditions for such availability set forth in the
10: * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
11: * version 2 with the GNU Classpath Exception, which is available at
12: * https://www.gnu.org/software/classpath/license.html.
13: *
14: * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15: */
16:
17: package jakarta.mail;
18:
19: import jakarta.mail.event.TransportEvent;
20: import jakarta.mail.event.TransportListener;
21:
22: import java.util.ArrayList;
23: import java.util.Collections;
24: import java.util.HashMap;
25: import java.util.List;
26: import java.util.Map;
27: import java.util.Vector;
28:
29: /**
30: * An abstract class that models a message transport.
31: * Subclasses provide actual implementations. <p>
32: *
33: * Note that <code>Transport</code> extends the <code>Service</code>
34: * class, which provides many common methods for naming transports,
35: * connecting to transports, and listening to connection events.
36: *
37: * @author John Mani
38: * @author Max Spivak
39: * @author Bill Shannon
40: * @see jakarta.mail.Service
41: * @see jakarta.mail.event.ConnectionEvent
42: * @see jakarta.mail.event.TransportEvent
43: */
44:
45: public abstract class Transport extends Service {
46:
47: /**
48: * Constructor.
49: *
50: * @param session Session object for this Transport.
51: * @param urlname URLName object to be used for this Transport
52: */
53: public Transport(Session session, URLName urlname) {
54: super(session, urlname);
55: }
56:
57: /**
58: * Send a message. The message will be sent to all recipient
59: * addresses specified in the message (as returned from the
60: * <code>Message</code> method <code>getAllRecipients</code>),
61: * using message transports appropriate to each address. The
62: * <code>send</code> method calls the <code>saveChanges</code>
63: * method on the message before sending it. <p>
64: *
65: * If any of the recipient addresses is detected to be invalid by
66: * the Transport during message submission, a SendFailedException
67: * is thrown. Clients can get more detail about the failure by examining
68: * the exception. Whether or not the message is still sent successfully
69: * to any valid addresses depends on the Transport implementation. See
70: * SendFailedException for more details. Note also that success does
71: * not imply that the message was delivered to the ultimate recipient,
72: * as failures may occur in later stages of delivery. Once a Transport
73: * accepts a message for delivery to a recipient, failures that occur later
74: * should be reported to the user via another mechanism, such as
75: * returning the undeliverable message. <p>
76: *
77: * In typical usage, a SendFailedException reflects an error detected
78: * by the server. The details of the SendFailedException will usually
79: * contain the error message from the server (such as an SMTP error
80: * message). An address may be detected as invalid for a variety of
81: * reasons - the address may not exist, the address may have invalid
82: * syntax, the address may have exceeded its quota, etc. <p>
83: *
84: * Note that <code>send</code> is a static method that creates and
85: * manages its own connection. Any connection associated with any
86: * Transport instance used to invoke this method is ignored and not
87: * used. This method should only be invoked using the form
88: * <code>Transport.send(msg);</code>, and should never be invoked
89: * using an instance variable.
90: *
91: * @param msg the message to send
92: * @throws SendFailedException if the message could not
93: * be sent to some or any of the recipients.
94: * @throws MessagingException for other failures
95: * @see Message#saveChanges
96: * @see Message#getAllRecipients
97: * @see #send(Message, Address[])
98: * @see jakarta.mail.SendFailedException
99: */
100: public static void send(Message msg) throws MessagingException {
101: msg.saveChanges(); // do this first
102: send0(msg, msg.getAllRecipients(), null, null);
103: }
104:
105: /**
106: * Send the message to the specified addresses, ignoring any
107: * recipients specified in the message itself. The
108: * <code>send</code> method calls the <code>saveChanges</code>
109: * method on the message before sending it.
110: *
111: * @param msg the message to send
112: * @param addresses the addresses to which to send the message
113: * @throws SendFailedException if the message could not
114: * be sent to some or any of the recipients.
115: * @throws MessagingException for other failures
116: * @see Message#saveChanges
117: * @see jakarta.mail.SendFailedException
118: * @see #send(Message)
119: */
120: public static void send(Message msg, Address[] addresses)
121: throws MessagingException {
122:
123: msg.saveChanges();
124: send0(msg, addresses, null, null);
125: }
126:
127: /**
128: * Send a message. The message will be sent to all recipient
129: * addresses specified in the message (as returned from the
130: * <code>Message</code> method <code>getAllRecipients</code>).
131: * The <code>send</code> method calls the <code>saveChanges</code>
132: * method on the message before sending it. <p>
133: *
134: * Use the specified user name and password to authenticate to
135: * the mail server.
136: *
137: * @param msg the message to send
138: * @param user the user name
139: * @param password this user's password
140: * @throws SendFailedException if the message could not
141: * be sent to some or any of the recipients.
142: * @throws MessagingException for other failures
143: * @see Message#saveChanges
144: * @see jakarta.mail.SendFailedException
145: * @see #send(Message)
146: * @since JavaMail 1.5
147: */
148: public static void send(Message msg,
149: String user, String password) throws MessagingException {
150:
151: msg.saveChanges();
152: send0(msg, msg.getAllRecipients(), user, password);
153: }
154:
155: /**
156: * Send the message to the specified addresses, ignoring any
157: * recipients specified in the message itself. The
158: * <code>send</code> method calls the <code>saveChanges</code>
159: * method on the message before sending it. <p>
160: *
161: * Use the specified user name and password to authenticate to
162: * the mail server.
163: *
164: * @param msg the message to send
165: * @param addresses the addresses to which to send the message
166: * @param user the user name
167: * @param password this user's password
168: * @throws SendFailedException if the message could not
169: * be sent to some or any of the recipients.
170: * @throws MessagingException for other failures
171: * @see Message#saveChanges
172: * @see jakarta.mail.SendFailedException
173: * @see #send(Message)
174: * @since JavaMail 1.5
175: */
176: public static void send(Message msg, Address[] addresses,
177: String user, String password) throws MessagingException {
178:
179: msg.saveChanges();
180: send0(msg, addresses, user, password);
181: }
182:
183: // send, but without the saveChanges
184: private static void send0(Message msg, Address[] addresses,
185: String user, String password) throws MessagingException {
186:
187: if (addresses == null || addresses.length == 0)
188: throw new SendFailedException("No recipient addresses");
189:
190: /*
191: * protocols is a map containing the addresses
192: * indexed by address type
193: */
194: Map<String, List<Address>> protocols
195: = new HashMap<>();
196:
197: // Lists of addresses
198: List<Address> invalid = new ArrayList<>();
199: List<Address> validSent = new ArrayList<>();
200: List<Address> validUnsent = new ArrayList<>();
201:
202: for (int i = 0; i < addresses.length; i++) {
203: // is this address type already in the map?
204: if (protocols.containsKey(addresses[i].getType())) {
205: List<Address> v = protocols.get(addresses[i].getType());
206: v.add(addresses[i]);
207: } else {
208: // need to add a new protocol
209: List<Address> w = new ArrayList<>();
210: w.add(addresses[i]);
211: protocols.put(addresses[i].getType(), w);
212: }
213: }
214:
215: int dsize = protocols.size();
216: if (dsize == 0)
217: throw new SendFailedException("No recipient addresses");
218:
219: Session s = (msg.session != null) ? msg.session :
220: Session.getDefaultInstance(System.getProperties(), null);
221: Transport transport;
222:
223: /*
224: * Optimize the case of a single protocol.
225: */
226: if (dsize == 1) {
227: transport = s.getTransport(addresses[0]);
228: try {
229: if (user != null)
230: transport.connect(user, password);
231: else
232: transport.connect();
233: transport.sendMessage(msg, addresses);
234: } finally {
235: transport.close();
236: }
237: return;
238: }
239:
240: /*
241: * More than one protocol. Have to do them one at a time
242: * and collect addresses and chain exceptions.
243: */
244: MessagingException chainedEx = null;
245: boolean sendFailed = false;
246:
247: for (List<Address> v : protocols.values()) {
248: Address[] protaddresses = new Address[v.size()];
249: v.toArray(protaddresses);
250:
251: // Get a Transport that can handle this address type.
252: if ((transport = s.getTransport(protaddresses[0])) == null) {
253: // Could not find an appropriate Transport ..
254: // Mark these addresses invalid.
255: Collections.addAll(invalid, protaddresses);
256: continue;
257: }
258: try {
259: transport.connect();
260: transport.sendMessage(msg, protaddresses);
261: } catch (SendFailedException sex) {
262: sendFailed = true;
263: // chain the exception we're catching to any previous ones
264: if (chainedEx == null)
265: chainedEx = sex;
266: else
267: chainedEx.setNextException(sex);
268:
269: // retrieve invalid addresses
270: Address[] a = sex.getInvalidAddresses();
271: if (a != null)
272: Collections.addAll(invalid, a);
273:
274: // retrieve validSent addresses
275: a = sex.getValidSentAddresses();
276: if (a != null)
277: Collections.addAll(validSent, a);
278:
279: // retrieve validUnsent addresses
280: Address[] c = sex.getValidUnsentAddresses();
281: if (c != null)
282: Collections.addAll(validUnsent, c);
283: } catch (MessagingException mex) {
284: sendFailed = true;
285: // chain the exception we're catching to any previous ones
286: if (chainedEx == null)
287: chainedEx = mex;
288: else
289: chainedEx.setNextException(mex);
290: } finally {
291: transport.close();
292: }
293: }
294:
295: // done with all protocols. throw exception if something failed
296: if (sendFailed || invalid.size() != 0 || validUnsent.size() != 0) {
297: Address[] a = null, b = null, c = null;
298:
299: // copy address lists into arrays
300: if (validSent.size() > 0) {
301: a = new Address[validSent.size()];
302: validSent.toArray(a);
303: }
304: if (validUnsent.size() > 0) {
305: b = new Address[validUnsent.size()];
306: validUnsent.toArray(b);
307: }
308: if (invalid.size() > 0) {
309: c = new Address[invalid.size()];
310: invalid.toArray(c);
311: }
312: throw new SendFailedException("Sending failed", chainedEx,
313: a, b, c);
314: }
315: }
316:
317: /**
318: * Send the Message to the specified list of addresses. An appropriate
319: * TransportEvent indicating the delivery status is delivered to any
320: * TransportListener registered on this Transport. Also, if any of
321: * the addresses is invalid, a SendFailedException is thrown.
322: * Whether or not the message is still sent succesfully to
323: * any valid addresses depends on the Transport implementation. <p>
324: *
325: * Unlike the static <code>send</code> method, the <code>sendMessage</code>
326: * method does <em>not</em> call the <code>saveChanges</code> method on
327: * the message; the caller should do so.
328: *
329: * @param msg The Message to be sent
330: * @param addresses array of addresses to send this message to
331: * @throws SendFailedException if the send failed because of
332: * invalid addresses.
333: * @throws MessagingException if the connection is dead or not in the
334: * connected state
335: * @see jakarta.mail.event.TransportEvent
336: */
337: public abstract void sendMessage(Message msg, Address[] addresses)
338: throws MessagingException;
339:
340: // Vector of Transport listeners
341: private volatile Vector<TransportListener> transportListeners = null;
342:
343: /**
344: * Add a listener for Transport events. <p>
345: *
346: * The default implementation provided here adds this listener
347: * to an internal list of TransportListeners.
348: *
349: * @param l the Listener for Transport events
350: * @see jakarta.mail.event.TransportEvent
351: */
352: public synchronized void addTransportListener(TransportListener l) {
353: if (transportListeners == null)
354: transportListeners = new Vector<>();
355: transportListeners.addElement(l);
356: }
357:
358: /**
359: * Remove a listener for Transport events. <p>
360: *
361: * The default implementation provided here removes this listener
362: * from the internal list of TransportListeners.
363: *
364: * @param l the listener
365: * @see #addTransportListener
366: */
367: public synchronized void removeTransportListener(TransportListener l) {
368: if (transportListeners != null)
369: transportListeners.removeElement(l);
370: }
371:
372: /**
373: * Notify all TransportListeners. Transport implementations are
374: * expected to use this method to broadcast TransportEvents.<p>
375: *
376: * The provided default implementation queues the event into
377: * an internal event queue. An event dispatcher thread dequeues
378: * events from the queue and dispatches them to the registered
379: * TransportListeners. Note that the event dispatching occurs
380: * in a separate thread, thus avoiding potential deadlock problems.
381: *
382: * @param type the TransportEvent type
383: * @param validSent valid addresses to which message was sent
384: * @param validUnsent valid addresses to which message was not sent
385: * @param invalid the invalid addresses
386: * @param msg the message
387: */
388: protected void notifyTransportListeners(int type, Address[] validSent,
389: Address[] validUnsent,
390: Address[] invalid, Message msg) {
391: if (transportListeners == null)
392: return;
393:
394: TransportEvent e = new TransportEvent(this, type, validSent,
395: validUnsent, invalid, msg);
396: queueEvent(e, transportListeners);
397: }
398: }