001 /** 002 * 003 * Copyright 2003-2006 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: 421852 $ $Date: 2006-07-14 03:02:19 -0700 (Fri, 14 Jul 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 }