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