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