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