KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > suberic > pooka > OutgoingMailServer


1 package net.suberic.pooka;
2
3 import java.util.*;
4
5 import javax.mail.*;
6 import javax.mail.internet.*;
7
8 import net.suberic.util.*;
9 import net.suberic.util.thread.*;
10
11 /**
12  * <p>Represents a mail server than can be used to send outgoing messages.
13  *
14  * @author Allen Petersen
15  * @version $Revision: 1501 $
16  */

17 public class OutgoingMailServer implements net.suberic.util.Item, net.suberic.util.ValueChangeListener, NetworkConnectionListener {
18
19   String JavaDoc id = null;
20
21   String JavaDoc propertyName = null;
22
23   protected URLName sendMailURL = null;
24
25   String JavaDoc connectionID = null;
26
27   String JavaDoc outboxID = null;
28
29   String JavaDoc mProtocol = "smtp://";
30
31   NetworkConnection.ConnectionLock connectionLock = null;
32
33   boolean sending = false;
34
35   boolean mStopped = false;
36
37   ActionThread mailServerThread = null;
38
39   /**
40    * <p>Creates a new OutgoingMailServer from the given property.</p>
41    */

42   public OutgoingMailServer (String JavaDoc newId) {
43     id = newId;
44     propertyName = "OutgoingServer." + newId;
45     
46     configure();
47   }
48
49   /**
50    * <p>Configures this mail server.</p>
51    */

52   protected void configure() {
53     VariableBundle bundle = Pooka.getResources();
54
55     connectionID = bundle.getProperty(getItemProperty() + ".connection", "");
56     NetworkConnection currentConnection = getConnection();
57     if (currentConnection != null)
58       currentConnection.addConnectionListener(this);
59
60
61     if (Pooka.getProperty(getItemProperty() + ".authenticated", "false").equalsIgnoreCase("true")) {
62       String JavaDoc mProtocol = "smtps://";
63     } else {
64       String JavaDoc mProtocol = "smtp://";
65     }
66
67     sendMailURL = new URLName(mProtocol + bundle.getProperty(getItemProperty() + ".server", "") + ":" + bundle.getProperty(getItemProperty() + ".port", "") + "/");
68
69     outboxID = bundle.getProperty(getItemProperty() + ".outbox", "");
70
71     bundle.addValueChangeListener(this, getItemProperty() + ".connection");
72     bundle.addValueChangeListener(this, getItemProperty() + ".server");
73     bundle.addValueChangeListener(this, getItemProperty() + ".outbox");
74
75     mailServerThread = new net.suberic.util.thread.ActionThread(getItemID() + " - smtp thread");
76     mailServerThread.start();
77   }
78
79   /**
80    * <p>Called when one of the values that defines this OutgoingMailServer
81    * is changed.</p>
82    */

83   public void valueChanged(String JavaDoc changedValue) {
84     VariableBundle bundle = Pooka.getResources();
85
86     if (changedValue != null) {
87       if (changedValue.equals(getItemProperty() + ".connection")) {
88     NetworkConnection currentConnection = getConnection();
89     if (currentConnection != null)
90       currentConnection.removeConnectionListener(this);
91
92     connectionID = bundle.getProperty(getItemProperty() + ".connection", "");
93     currentConnection = getConnection();
94     if (currentConnection != null)
95       currentConnection.addConnectionListener(this);
96     
97       } else if (changedValue.equals(getItemProperty() + ".server")) {
98     sendMailURL = new URLName(mProtocol + bundle.getProperty(getItemProperty() + ".server", "") + ":" + bundle.getProperty(getItemProperty() + ".port", "") + "/");
99       } else if (changedValue.equals(getItemProperty() + ".outbox")) {
100     String JavaDoc newOutboxID = bundle.getProperty(getItemProperty() + ".outbox", "");
101     if (newOutboxID != outboxID) {
102       FolderInfo outbox = getOutbox();
103       if (outbox != null) {
104         outbox.setOutboxFolder(null);
105       }
106
107       outboxID = newOutboxID;
108       loadOutboxFolder();
109     }
110       }
111     }
112   }
113   
114   /**
115    * Loads the outbox folders.
116    */

117   public void loadOutboxFolder() {
118     FolderInfo outbox = getOutbox();
119     if (outbox != null) {
120       outbox.setOutboxFolder(this);
121     }
122   }
123
124   /**
125    * Stops the server so that it will continue to send its current
126    * message(s), but will not accept any new ones.
127    */

128   public void stopServer() {
129     mStopped = true;
130   }
131
132
133   /**
134    * Stops the thread.
135    */

136   public void stopThread() {
137     if (mailServerThread != null) {
138       mailServerThread.setStop(true);
139       mailServerThread = null;
140     }
141   }
142
143   /**
144    * Sends all available messages in the outbox.
145    */

146   public void sendAll() {
147     mailServerThread.addToQueue(new javax.swing.AbstractAction JavaDoc() {
148     public void actionPerformed(java.awt.event.ActionEvent JavaDoc ae) {
149       try {
150         internal_sendAll();
151       } catch (javax.mail.MessagingException JavaDoc me) {
152         Pooka.getUIFactory().showError(Pooka.getProperty("Error.sendingMessage", "Error sending message: "), me);
153       }
154     }
155       }, new java.awt.event.ActionEvent JavaDoc(this, 0, "message-send-all"));
156   }
157
158   /**
159    * Sends all available messages in the outbox.
160    */

161   protected synchronized void internal_sendAll() throws javax.mail.MessagingException JavaDoc {
162     
163     try {
164       sending = true;
165       
166       Transport sendTransport = prepareTransport(true);
167
168       try {
169     sendTransport.connect();
170     
171     sendAll(sendTransport);
172       } finally {
173     sendTransport.close();
174       }
175     } finally {
176       sending = false;
177       if (connectionLock != null)
178     connectionLock.release();
179     }
180   }
181   
182   /**
183    * Sends all available messages in the outbox using the given, already
184    * open Transport object. Leaves the Transport object open.
185    */

186   private void sendAll(Transport sendTransport) throws javax.mail.MessagingException JavaDoc {
187     
188     LinkedList exceptionList = new LinkedList();
189
190     FolderInfo outbox = getOutbox();
191     
192     if (outbox != null) {
193       // we need the thread lock for this folder.
194
Object JavaDoc runLock = outbox.getFolderThread().getRunLock();
195       synchronized(runLock) {
196     if ( ! outbox.isConnected()) {
197       outbox.openFolder(Folder.READ_WRITE);
198     }
199
200     Folder outboxFolder = outbox.getFolder();
201     
202     if (outboxFolder != null) {
203       Message[] msgs = outboxFolder.getMessages();
204       
205       try {
206         for (int i = 0; i < msgs.length; i++) {
207           Message m = msgs[i];
208           if (! m.isSet(Flags.Flag.DRAFT)) {
209         try {
210           sendTransport.sendMessage(m, m.getAllRecipients());
211           m.setFlag(Flags.Flag.DELETED, true);
212         } catch (MessagingException me) {
213           exceptionList.add(me);
214         }
215           }
216         }
217       } finally {
218         if (exceptionList.size() > 0) {
219           final int exceptionCount = exceptionList.size();
220           javax.swing.SwingUtilities.invokeLater( new Runnable JavaDoc() {
221           public void run() {
222             Pooka.getUIFactory().showError(Pooka.getProperty("error.OutgoingServer.queuedSendFailed", "Failed to send message(s) in the Outbox. Number of errors: ") + exceptionCount );
223         }
224         } );
225         }
226         outboxFolder.expunge();
227       }
228     }
229       }
230     }
231   }
232
233   /**
234    * Virtually sends a message. If the current status is connected, then
235    * the message will actually be sent now. If not, and the
236    * Message.sendImmediately setting is true, then we'll attempt to send
237    * the message anyway.
238    */

239   public synchronized void sendMessage(NewMessageInfo nmi, boolean connect) {
240     if (mStopped) {
241       throw new IllegalStateException JavaDoc("MailServer " + getItemID() + " has been stopped and is not accepting new messages.");
242     }
243
244     final NewMessageInfo nmi_final = nmi;
245     final boolean connect_final = connect;
246     
247     mailServerThread.addToQueue(new javax.swing.AbstractAction JavaDoc() {
248     public void actionPerformed(java.awt.event.ActionEvent JavaDoc ae) {
249       internal_sendMessage(nmi_final, connect_final);
250     }
251       }, new java.awt.event.ActionEvent JavaDoc(this, 0, "message-send-all"));
252   }
253
254   private synchronized void internal_sendMessage(NewMessageInfo nmi, boolean connect) {
255     sending = true;
256
257     try {
258       boolean connected = false;
259       Transport sendTransport = null;
260       try {
261     Pooka.getUIFactory().showStatusMessage(Pooka.getProperty("info.smtpServer.connecting", "Connecting to SMTP server..."));
262
263     sendTransport = prepareTransport(connect);
264     sendTransport.connect();
265     connected = true;
266       } catch (MessagingException me) {
267     if (Pooka.getProperty("Pooka.outbox.autoSave", "false").equalsIgnoreCase("true")) {
268       try {
269         saveToOutbox(nmi);
270       } catch (MessagingException nme) {
271         ((net.suberic.pooka.gui.NewMessageProxy)nmi.getMessageProxy()).sendFailed(this, nme);
272       }
273     } else {
274       ((net.suberic.pooka.gui.NewMessageProxy)nmi.getMessageProxy()).sendFailed(this, me);
275     }
276       }
277       
278       // if the connection worked.
279
if (connected) {
280     try {
281       try {
282           /*
283         Message m = nmi.getMessage();
284         sendTransport.sendMessage(m, m.getAllRecipients());
285           */

286         Pooka.getUIFactory().showStatusMessage(Pooka.getProperty("info.smtpServer.sending", "Sending message over SMTP."));
287
288         Map sendMessageMap = nmi.getSendMessageMap();
289         if (sendMessageMap != null && sendMessageMap.keySet().size() > 0) {
290           Set keySet = sendMessageMap.keySet();
291           Iterator iter = keySet.iterator();
292           while (iter.hasNext()) {
293         Message m = (Message) iter.next();
294         Address[] recipients = (Address[]) sendMessageMap.get(m);
295         if (recipients == null)
296           recipients = m.getAllRecipients();
297         
298         sendTransport.sendMessage(m, recipients);
299           }
300         } else {
301           sendTransport.sendMessage(nmi.getMessage(), nmi.getMessage().getAllRecipients());
302         }
303         
304         ((net.suberic.pooka.gui.NewMessageProxy)nmi.getMessageProxy()).sendSucceeded(true);
305       } catch (MessagingException me) {
306         ((net.suberic.pooka.gui.NewMessageProxy)nmi.getMessageProxy()).sendFailed(this, me);
307       }
308       // whether or not the send failed. try sending all the other
309
// messages in the queue, too.
310
try {
311         sendAll(sendTransport);
312       } catch (MessagingException exp) {
313         final MessagingException me = exp;
314         javax.swing.SwingUtilities.invokeLater(new Runnable JavaDoc() {
315         public void run() {
316           Pooka.getUIFactory().showError(Pooka.getProperty("error.OutgoingServer.outboxSendFailed", "Failed to send all messages in Outbox: "), me);
317         }
318           });
319       }
320     } finally {
321       try {
322         sendTransport.close();
323       } catch (MessagingException me) {
324         // we don't care.
325
}
326     }
327       }
328     } finally {
329       sending = false;
330       if (connectionLock != null)
331     connectionLock.release();
332     }
333   }
334   
335   /**
336    * Virtually sends a message. If the current status is connected, then
337    * the message will actually be sent now.
338    */

339   public void sendMessage(NewMessageInfo nmi) {
340     sendMessage(nmi, false);
341   }
342
343   /**
344    * Gets a Transport object for this OutgoingMailServer.
345    */

346   protected Transport prepareTransport(boolean connect) throws javax.mail.MessagingException JavaDoc {
347     
348     NetworkConnection connection = getConnection();
349     
350     if (connect) {
351       if (connection.getStatus() == NetworkConnection.DISCONNECTED) {
352     connection.connect();
353       }
354     }
355     
356     if (connection.getStatus() != NetworkConnection.CONNECTED) {
357       throw new MessagingException(Pooka.getProperty("error.connectionDown", "Connection down for Mail Server: ") + getItemID());
358     } else {
359       connectionLock = connection.getConnectionLock();
360     }
361     
362     Session session = Pooka.getDefaultSession();
363
364
365     boolean useAuth = Pooka.getProperty(getItemProperty() + ".authenticated", "false").equalsIgnoreCase("true");
366
367     if (useAuth) {
368       java.util.Properties JavaDoc sysProps = new java.util.Properties JavaDoc(System.getProperties());
369       sysProps.setProperty("mail.mbox.mailspool", Pooka.getProperty("Pooka.spoolDir", "/var/spool/mail"));
370       sysProps.setProperty("mail.smtp.auth", "true");
371       String JavaDoc userName = Pooka.getProperty(getItemProperty() + ".user", "");
372       if (! userName.equals(""))
373     sysProps.setProperty("mail.smtp.user", userName);
374       String JavaDoc password = Pooka.getProperty(getItemProperty() + ".password", "");
375
376       if (! password.equals("")) {
377     password = net.suberic.util.gui.propedit.PasswordEditorPane.descrambleString(password);
378     sysProps.setProperty("mail.smtp.password", password);
379       }
380
381       sysProps.setProperty("mail.smtp.starttls.enable", "true");
382
383       session = javax.mail.Session.getInstance(sysProps, new FailoverAuthenticator (userName, password));
384       if (Pooka.getProperty("Pooka.sessionDebug", "false").equalsIgnoreCase("true"))
385     session.setDebug(true);
386     }
387     Transport sendTransport = session.getTransport(getSendMailURL());
388     return sendTransport;
389   }
390
391   /**
392    * Saves the given message to the Outbox for sending later.
393    */

394   public void saveToOutbox(NewMessageInfo nmi) throws MessagingException {
395     Pooka.getUIFactory().showStatusMessage(Pooka.getProperty("info.smtpServer.outbox.connecting", "SMTP server not available; getting outbox."));
396
397     FolderInfo outbox = getOutbox();
398     
399     if (outbox != null) {
400       Pooka.getUIFactory().showStatusMessage(Pooka.getProperty("info.smtpServer.outbox.waitForLock", "SMTP server not available; waiting for outbox lock."));
401       
402       // we need the lock
403
Object JavaDoc runLock = outbox.getFolderThread().getRunLock();
404       synchronized(runLock) {
405     try {
406       if ( ! outbox.isConnected()) {
407         Pooka.getUIFactory().showStatusMessage(Pooka.getProperty("info.smtpServer.outbox.opening", "SMTP server not available; opening outbox."));
408         outbox.openFolder(Folder.READ_WRITE);
409       }
410       
411       Pooka.getUIFactory().showStatusMessage(Pooka.getProperty("info.smtpServer.outbox.appending", "SMTP server not available; appending to outbox."));
412       outbox.appendMessages(new MessageInfo[] { nmi });
413       
414       if (Pooka.getProperty("Pooka.outbox.autoSave", "false").equalsIgnoreCase("true")) {
415         // assume that if we aren't automatically saving to the outbox
416
// that we did this explicitly.
417
javax.swing.SwingUtilities.invokeLater(new Runnable JavaDoc() {
418         public void run() {
419           Pooka.getUIFactory().showError(Pooka.getProperty("error.MessageWindow.sendDelayed", "Connection unavailable. Message saved to Outbox."));
420         }
421           });
422       }
423
424       ((net.suberic.pooka.gui.NewMessageProxy)nmi.getMessageProxy()).sendSucceeded(true);
425     } catch(MessagingException nme) {
426       ((net.suberic.pooka.gui.NewMessageProxy)nmi.getMessageProxy()).sendFailed(this, nme);
427     }
428       }
429     } else {
430       throw new MessagingException("Error saving to Outbox -- no Outbox specified.");
431     }
432   }
433
434   /**
435    * <p>The NetworkConnection that this OutgoingMailServer depends on.
436    */

437   public NetworkConnection getConnection() {
438     NetworkConnectionManager connectionManager = Pooka.getConnectionManager();
439     NetworkConnection returnValue = connectionManager.getConnection(connectionID);
440     if (returnValue != null)
441       return returnValue;
442     else
443       return connectionManager.getDefaultConnection();
444   }
445
446   /**
447    * Notifies this component that the state of a network connection has
448    * changed.
449    */

450   public void connectionStatusChanged(NetworkConnection connection, int newStatus) {
451     if (newStatus == NetworkConnection.CONNECTED && ! sending && Pooka.getProperty(getItemProperty() + ".sendOnConnect", "false").equalsIgnoreCase("true")) {
452       sendAll();
453     }
454   }
455
456   /**
457    * <p>The FolderInfo where messages for this MailServer are
458    * stored until they're ready to be sent.
459    */

460   public FolderInfo getOutbox() {
461     
462     return Pooka.getStoreManager().getFolder(outboxID);
463   }
464
465   /**
466    * <p>The Item ID for this OutgoingMailServer.</p>
467    */

468   public String JavaDoc getItemID() {
469     return id;
470   }
471
472   /**
473    * <p>The Item property for this OutgoingMailServer. This is usually
474    * OutgoingMailServer.<i>itemID</i>.</p>
475    */

476   public String JavaDoc getItemProperty() {
477     return propertyName;
478   }
479
480   /**
481    * Returns the sendMailURL.
482    */

483   public URLName getSendMailURL() {
484     return sendMailURL;
485   }
486
487   /**
488    * Returns true if this MailServer is currently in the process of sending
489    * a message.
490    */

491   public boolean isSending() {
492     return sending;
493   }
494
495   /**
496    * Returns true if this MailServer has been set not to send any more messages
497    * this session.
498    */

499   public boolean isStopped() {
500     return mStopped;
501   }
502
503   /**
504    * An Authenticator that first tries the configured User and Password,
505    * then uses the underlying Authenticator.
506    */

507   class FailoverAuthenticator extends net.suberic.pooka.gui.SimpleAuthenticator {
508
509     String JavaDoc mUser;
510     String JavaDoc mPassword;
511     boolean firstTry = true;
512     
513     Authenticator mAuth;
514     
515     /**
516      * Creates an Authenticator that will try using the given username and
517      * password, and if that fails, then will try a ui dialog.
518      */

519     public FailoverAuthenticator(String JavaDoc pUser, String JavaDoc pPassword) {
520       super();
521       mUser = pUser;
522       mPassword = pPassword;
523     }
524
525     protected PasswordAuthentication getPasswordAuthentication() {
526       if (firstTry) {
527     firstTry = false;
528
529     return new PasswordAuthentication(mUser, mPassword);
530       } else {
531     return super.getPasswordAuthentication();
532       }
533     }
534
535   }
536 }
537
Popular Tags