1. /*
  2. * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
  3. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  4. */
  5. package javax.mail;
  6. import java.io.IOException;
  7. import java.net.*;
  8. import java.util.Vector;
  9. import java.util.Hashtable;
  10. import java.util.Enumeration;
  11. import javax.mail.event.*;
  12. /**
  13. * An abstract class that models a message transport.
  14. * Subclasses provide actual implementations. <p>
  15. *
  16. * Note that <code>Transport</code> extends the <code>Service</code>
  17. * class, which provides many common methods for naming transports,
  18. * connecting to transports, and listening to connection events.
  19. *
  20. * @author John Mani
  21. * @author Max Spivak
  22. * @author Bill Shannon
  23. * @version 1.33, 00/04/20
  24. *
  25. * @see javax.mail.Service
  26. * @see javax.mail.event.ConnectionEvent
  27. * @see javax.mail.event.TransportEvent
  28. */
  29. public abstract class Transport extends Service {
  30. /**
  31. * Constructor.
  32. *
  33. * @param session Session object for this Transport.
  34. * @param urlName URLName object to be used for this Transport
  35. */
  36. public Transport(Session session, URLName urlname) {
  37. super(session, urlname);
  38. }
  39. /**
  40. * Send a message. The message will be sent to all recipient
  41. * addresses specified in the message (as returned from the
  42. * <code>Message</code> method <code>getAllRecipients</code>),
  43. * using message transports appropriate to each address. The
  44. * <code>send</code> method calls the <code>saveChanges</code>
  45. * method on the message before sending it. <p>
  46. *
  47. * If any of the recipient addresses is detected to be invalid by
  48. * the Transport during message submission, a SendFailedException
  49. * is thrown. Clients can get more detail about the failure by examining
  50. * the exception. Whether or not the message is still sent succesfully to
  51. * any valid addresses depends on the Transport implementation. See
  52. * SendFailedException for more details. Note also that success does
  53. * not imply that the message was delivered to the ultimate recipient,
  54. * as failures may occur in later stages of delivery. Once a Transport
  55. * accepts a message for delivery to a recipient, failures that occur later
  56. * should be reported to the user via another mechanism, such as
  57. * returning the undeliverable message. <p>
  58. *
  59. * @param msg the message to send
  60. * @exception SendFailedException if the message could not
  61. * be sent to some or any of the recipients.
  62. * @exception MessagingException
  63. * @see Message#saveChanges
  64. * @see Message#getAllRecipients
  65. * @see #send(Message, Address[])
  66. * @see javax.mail.SendFailedException
  67. */
  68. public static void send(Message msg) throws MessagingException {
  69. msg.saveChanges(); // do this first
  70. send0(msg, msg.getAllRecipients());
  71. }
  72. /**
  73. * Send the message to the specified addresses, ignoring any
  74. * recipients specified in the message itself. The
  75. * <code>send</code> method calls the <code>saveChanges</code>
  76. * method on the message before sending it. <p>
  77. *
  78. * @param msg the message to send
  79. * @param addresses the addresses to which to send the message
  80. * @exception SendFailedException if the message could not
  81. * be sent to some or any of the recipients.
  82. * @exception MessagingException
  83. * @see Message#saveChanges
  84. * @see #send(Message)
  85. * @see javax.mail.SendFailedException
  86. */
  87. public static void send(Message msg, Address[] addresses)
  88. throws MessagingException {
  89. msg.saveChanges();
  90. send0(msg, addresses);
  91. }
  92. // send, but without the saveChanges
  93. private static void send0(Message msg, Address[] addresses)
  94. throws MessagingException {
  95. if (addresses == null || addresses.length == 0)
  96. throw new SendFailedException("No recipient addresses");
  97. /*
  98. * protocols is a hashtable containing the addresses
  99. * indexed by address type
  100. */
  101. Hashtable protocols = new Hashtable();
  102. // Vectors of addresses
  103. Vector invalid = new Vector();
  104. Vector validSent = new Vector();
  105. Vector validUnsent = new Vector();
  106. for (int i = 0; i < addresses.length; i++) {
  107. // is this address type already in the hashtable?
  108. if (protocols.containsKey(addresses[i].getType())) {
  109. Vector v = (Vector)protocols.get(addresses[i].getType());
  110. v.addElement(addresses[i]);
  111. } else {
  112. // need to add a new protocol
  113. Vector w = new Vector();
  114. w.addElement(addresses[i]);
  115. protocols.put(addresses[i].getType(), w);
  116. }
  117. }
  118. int dsize = protocols.size();
  119. if (dsize == 0)
  120. throw new SendFailedException("No recipient addresses");
  121. Session s = (msg.session != null) ? msg.session :
  122. Session.getDefaultInstance(System.getProperties(), null);
  123. Transport transport;
  124. MessagingException chainedEx = null;
  125. boolean sendFailed = false;
  126. Enumeration e = protocols.elements();
  127. while (e.hasMoreElements()) {
  128. Vector v = (Vector)e.nextElement();
  129. Address[] protaddresses = new Address[v.size()];
  130. v.copyInto(protaddresses);
  131. // Get a Transport that can handle this address type.
  132. if ((transport = s.getTransport(protaddresses[0])) == null) {
  133. // Could not find an appropriate Transport ..
  134. // Mark these addresses invalid.
  135. for (int j = 0; j < protaddresses.length; j++)
  136. invalid.addElement(protaddresses[j]);
  137. continue;
  138. }
  139. try {
  140. transport.connect();
  141. transport.sendMessage(msg, protaddresses);
  142. } catch (SendFailedException sex) {
  143. sendFailed = true;
  144. // chain the exception we're catching to any previous ones
  145. if (chainedEx == null)
  146. chainedEx = sex;
  147. else
  148. chainedEx.setNextException(sex);
  149. // retrieve invalid addresses
  150. Address[] a = sex.getInvalidAddresses();
  151. if (a != null)
  152. for (int j = 0; j < a.length; j++)
  153. invalid.addElement(a[j]);
  154. // retrieve validSent addresses
  155. a = sex.getValidSentAddresses();
  156. if (a != null)
  157. for (int k = 0; k < a.length; k++)
  158. validSent.addElement(a[k]);
  159. // retrieve validUnsent addresses
  160. Address[] c = sex.getValidUnsentAddresses();
  161. if (c != null)
  162. for (int l = 0; l < c.length; l++)
  163. validUnsent.addElement(c[l]);
  164. } catch (MessagingException mex) {
  165. sendFailed = true;
  166. // chain the exception we're catching to any previous ones
  167. if (chainedEx == null)
  168. chainedEx = mex;
  169. else
  170. chainedEx.setNextException(mex);
  171. } finally {
  172. transport.close();
  173. }
  174. }
  175. // done with all protocols. throw exception if something failed
  176. if (sendFailed || invalid.size() != 0 || validUnsent.size() != 0) {
  177. Address[] a = null, b = null, c = null;
  178. // copy address vectors into arrays
  179. if (validSent.size() > 0) {
  180. a = new Address[validSent.size()];
  181. validSent.copyInto(a);
  182. }
  183. if (validUnsent.size() > 0) {
  184. b = new Address[validUnsent.size()];
  185. validUnsent.copyInto(b);
  186. }
  187. if (invalid.size() > 0) {
  188. c = new Address[invalid.size()];
  189. invalid.copyInto(c);
  190. }
  191. throw new SendFailedException("Sending failed", chainedEx,
  192. a, b, c);
  193. }
  194. }
  195. /**
  196. * Send the Message to the specified list of addresses. An appropriate
  197. * TransportEvent indicating the delivery status is delivered to any
  198. * TransportListener registered on this Transport. Also, if any of
  199. * the addresses is invalid, a SendFailedException is thrown. Note
  200. * however, that the message <em>is</em> sent to the valid addresses. <p>
  201. *
  202. * Unlike the static <code>send</code> method, the <code>sendMessage</code>
  203. * method does <em>not</em> call the <code>saveChanges</code> method on
  204. * the message; the caller should do so.
  205. *
  206. * @param Message The Message to be sent
  207. * @param address List of addresses to send this message to
  208. * @see javax.mail.event.TransportEvent
  209. * @exception SendFailedException if the send failed because of
  210. * invalid addresses.
  211. * @exception MessagingException if the connection is dead or not in the
  212. * connected state
  213. */
  214. public abstract void sendMessage(Message msg, Address[] addresses)
  215. throws MessagingException;
  216. // Vector of Transport listeners
  217. private Vector transportListeners = null;
  218. /**
  219. * Add a listener for Transport events. <p>
  220. *
  221. * The default implementation provided here adds this listener
  222. * to an internal list of TransportListeners.
  223. *
  224. * @param l the Listener for Transport events
  225. * @see javax.mail.event.TransportEvent
  226. */
  227. public synchronized void addTransportListener(TransportListener l) {
  228. if (transportListeners == null)
  229. transportListeners = new Vector();
  230. transportListeners.addElement(l);
  231. }
  232. /**
  233. * Remove a listener for Transport events. <p>
  234. *
  235. * The default implementation provided here removes this listener
  236. * from the internal list of TransportListeners.
  237. *
  238. * @param l the listener
  239. * @see #addTransportListener
  240. */
  241. public synchronized void removeTransportListener(TransportListener l) {
  242. if (transportListeners != null)
  243. transportListeners.removeElement(l);
  244. }
  245. /**
  246. * Notify all TransportListeners. Transport implementations are
  247. * expected to use this method to broadcast TransportEvents.<p>
  248. *
  249. * The provided default implementation queues the event into
  250. * an internal event queue. An event dispatcher thread dequeues
  251. * events from the queue and dispatches them to the registered
  252. * TransportListeners. Note that the event dispatching occurs
  253. * in a separate thread, thus avoiding potential deadlock problems.
  254. */
  255. protected void notifyTransportListeners(int type, Address[] validSent,
  256. Address[] validUnsent,
  257. Address[] invalid, Message msg) {
  258. if (transportListeners == null)
  259. return;
  260. TransportEvent e = new TransportEvent(this, type, validSent,
  261. validUnsent, invalid, msg);
  262. queueEvent(e, transportListeners);
  263. }
  264. }