001 /**
002 *
003 * Copyright 2003-2004 The Apache Software Foundation
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018 package javax.mail;
019
020 import java.util.ArrayList;
021 import java.util.HashMap;
022 import java.util.Iterator;
023 import java.util.List;
024 import java.util.Map;
025 import java.util.Vector;
026 import javax.mail.event.TransportEvent;
027 import javax.mail.event.TransportListener;
028
029 /**
030 * Abstract class modeling a message transport.
031 *
032 * @version $Rev: 382388 $ $Date: 2006-03-02 06:20:38 -0800 (Thu, 02 Mar 2006) $
033 */
034 public abstract class Transport extends Service {
035 /**
036 * Send a message to all recipient addresses it contains (as returned by {@link Message#getAllRecipients()})
037 * using message transports appropriate for each address. Message addresses are checked during submission,
038 * but there is no guarantee that the ultimate address is valid or that the message will ever be delivered.
039 * <p/>
040 * {@link Message#saveChanges()} will be called before the message is actually sent.
041 *
042 * @param message the message to send
043 * @throws MessagingException if there was a problem sending the message
044 */
045 public static void send(Message message) throws MessagingException {
046 send(message, message.getAllRecipients());
047 }
048
049 /**
050 * Send a message to all addresses provided irrespective of any recipients contained in the message itself
051 * using message transports appropriate for each address. Message addresses are checked during submission,
052 * but there is no guarantee that the ultimate address is valid or that the message will ever be delivered.
053 * <p/>
054 * {@link Message#saveChanges()} will be called before the message is actually sent.
055 *
056 * @param message the message to send
057 * @param addresses the addesses to send to
058 * @throws MessagingException if there was a problem sending the message
059 */
060 public static void send(Message message, Address[] addresses) throws MessagingException {
061 Session session = message.session;
062 Map msgsByTransport = new HashMap();
063 for (int i = 0; i < addresses.length; i++) {
064 Address address = addresses[i];
065 Transport transport = session.getTransport(address);
066 List addrs = (List) msgsByTransport.get(transport);
067 if (addrs == null) {
068 addrs = new ArrayList();
069 msgsByTransport.put(transport, addrs);
070 }
071 addrs.add(address);
072 }
073
074 message.saveChanges();
075
076 // Since we might be sending to multiple protocols, we need to catch and process each exception
077 // when we send and then throw a new SendFailedException when everything is done. Unfortunately, this
078 // also means unwrapping the information in any SendFailedExceptions we receive and building
079 // composite failed list.
080 MessagingException chainedException = null;
081 ArrayList sentAddresses = new ArrayList();
082 ArrayList unsentAddresses = new ArrayList();
083 ArrayList invalidAddresses = new ArrayList();
084
085
086 for (Iterator i = msgsByTransport.entrySet().iterator(); i.hasNext();) {
087 Map.Entry entry = (Map.Entry) i.next();
088 Transport transport = (Transport) entry.getKey();
089 List addrs = (List) entry.getValue();
090 try {
091 // we MUST connect to the transport before attempting to send.
092 transport.connect();
093 transport.sendMessage(message, (Address[]) addrs.toArray(new Address[addrs.size()]));
094 // if we have to throw an exception because of another failure, these addresses need to
095 // be in the valid list. Since we succeeded here, we can add these now.
096 sentAddresses.addAll(addrs);
097 } catch (SendFailedException e) {
098 // a true send failure. The exception contains a wealth of information about
099 // 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 }