KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > mail > Service


1 /*
2  * The contents of this file are subject to the terms
3  * of the Common Development and Distribution License
4  * (the "License"). You may not use this file except
5  * in compliance with the License.
6  *
7  * You can obtain a copy of the license at
8  * glassfish/bootstrap/legal/CDDLv1.0.txt or
9  * https://glassfish.dev.java.net/public/CDDLv1.0.html.
10  * See the License for the specific language governing
11  * permissions and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL
14  * HEADER in each file and include the License file at
15  * glassfish/bootstrap/legal/CDDLv1.0.txt. If applicable,
16  * add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your
18  * own identifying information: Portions Copyright [yyyy]
19  * [name of copyright owner]
20  */

21
22 /*
23  * @(#)Service.java 1.30 05/08/29
24  *
25  * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved.
26  */

27
28 package javax.mail;
29
30 import java.io.*;
31 import java.net.*;
32 import java.util.*;
33 import javax.mail.event.*;
34
35 /**
36  * An abstract class that contains the functionality
37  * common to messaging services, such as stores and transports. <p>
38  * A messaging service is created from a <code>Session</code> and is
39  * named using a <code>URLName</code>. A service must be connected
40  * before it can be used. Connection events are sent to reflect
41  * its connection status.
42  *
43  * @author Christopher Cotton
44  * @author Bill Shannon
45  * @author Kanwar Oberoi
46  * @version 1.30, 05/08/29
47  */

48
49 public abstract class Service {
50
51     /**
52      * The session from which this service was created.
53      */

54     protected Session JavaDoc session;
55
56     /**
57      * The <code>URLName</code> of this service.
58      */

59     protected URLName JavaDoc url = null;
60
61     /**
62      * Debug flag for this service. Set from the session's debug
63      * flag when this service is created.
64      */

65     protected boolean debug = false;
66
67     private boolean connected = false;
68     private Vector connectionListeners = null;
69
70     /**
71      * Constructor.
72      *
73      * @param session Session object for this service
74      * @param urlname URLName object to be used for this service
75      */

76     protected Service(Session JavaDoc session, URLName JavaDoc urlname) {
77     this.session = session;
78     url = urlname;
79     debug = session.getDebug();
80     }
81
82     /**
83      * A generic connect method that takes no parameters. Subclasses
84      * can implement the appropriate authentication schemes. Subclasses
85      * that need additional information might want to use some properties
86      * or might get it interactively using a popup window. <p>
87      *
88      * If the connection is successful, an "open" <code>ConnectionEvent</code>
89      * is delivered to any <code>ConnectionListeners</code> on this service. <p>
90      *
91      * Most clients should just call this method to connect to the service.<p>
92      *
93      * It is an error to connect to an already connected service. <p>
94      *
95      * The implementation provided here simply calls the following
96      * <code>connect(String, String, String)</code> method with nulls.
97      *
98      * @exception AuthenticationFailedException for authentication failures
99      * @exception MessagingException for other failures
100      * @exception IllegalStateException if the service is already connected
101      *
102      * @see javax.mail.event.ConnectionEvent
103      */

104     public void connect() throws MessagingException JavaDoc {
105     connect(null, null, null);
106     }
107
108     /**
109      * Connect to the specified address. This method provides a simple
110      * authentication scheme that requires a username and password. <p>
111      *
112      * If the connection is successful, an "open" <code>ConnectionEvent</code>
113      * is delivered to any <code>ConnectionListeners</code> on this service. <p>
114      *
115      * It is an error to connect to an already connected service. <p>
116      *
117      * The implementation in the Service class will collect defaults
118      * for the host, user, and password from the session, from the
119      * <code>URLName</code> for this service, and from the supplied
120      * parameters and then call the <code>protocolConnect</code> method.
121      * If the <code>protocolConnect</code> method returns <code>false</code>,
122      * the user will be prompted for any missing information and the
123      * <code>protocolConnect</code> method will be called again. The
124      * subclass should override the <code>protocolConnect</code> method.
125      * The subclass should also implement the <code>getURLName</code>
126      * method, or use the implementation in this class. <p>
127      *
128      * On a successful connection, the <code>setURLName</code> method is
129      * called with a URLName that includes the information used to make
130      * the connection, including the password. <p>
131      *
132      * If the username passed in is null, a default value will be chosen
133      * as described above.
134      *
135      * If the password passed in is null and this is the first successful
136      * connection to this service, the user name and the password
137      * collected from the user will be saved as defaults for subsequent
138      * connection attempts to this same service when using other Service object
139      * instances (the connection information is typically always saved within
140      * a particular Service object instance). The password is saved using the
141      * Session method <code>setPasswordAuthentication</code>. If the
142      * password passed in is not null, it is not saved, on the assumption
143      * that the application is managing passwords explicitly.
144      *
145      * @param host the host to connect to
146      * @param user the user name
147      * @param password this user's password
148      * @exception AuthenticationFailedException for authentication failures
149      * @exception MessagingException for other failures
150      * @exception IllegalStateException if the service is already connected
151      * @see javax.mail.event.ConnectionEvent
152      * @see javax.mail.Session#setPasswordAuthentication
153      */

154     public void connect(String JavaDoc host, String JavaDoc user, String JavaDoc password)
155             throws MessagingException JavaDoc {
156     connect(host, -1, user, password);
157     }
158
159     /**
160      * Connect to the current host using the specified username
161      * and password. This method is equivalent to calling the
162      * <code>connect(host, user, password)</code> method with null
163      * for the host name.
164      *
165      * @param user the user name
166      * @param password this user's password
167      * @exception AuthenticationFailedException for authentication failures
168      * @exception MessagingException for other failures
169      * @exception IllegalStateException if the service is already connected
170      * @see javax.mail.event.ConnectionEvent
171      * @see javax.mail.Session#setPasswordAuthentication
172      * @see #connect(java.lang.String, java.lang.String, java.lang.String)
173      * @since JavaMail 1.4
174      */

175     public void connect(String JavaDoc user, String JavaDoc password) throws MessagingException JavaDoc {
176         connect(null, user, password);
177     }
178
179     /**
180      * Similar to connect(host, user, password) except a specific port
181      * can be specified.
182      *
183      * @param host the host to connect to
184      * @param port the port to connect to (-1 means the default port)
185      * @param user the user name
186      * @param password this user's password
187      * @exception AuthenticationFailedException for authentication failures
188      * @exception MessagingException for other failures
189      * @exception IllegalStateException if the service is already connected
190      * @see #connect(java.lang.String, java.lang.String, java.lang.String)
191      * @see javax.mail.event.ConnectionEvent
192      */

193     public void connect(String JavaDoc host, int port, String JavaDoc user, String JavaDoc password)
194             throws MessagingException JavaDoc {
195
196     // see if the service is already connected
197
if (isConnected())
198         throw new IllegalStateException JavaDoc("already connected");
199
200     PasswordAuthentication JavaDoc pw;
201     boolean connected = false;
202     boolean save = false;
203     String JavaDoc protocol = null;
204     String JavaDoc file = null;
205
206     // get whatever information we can from the URL
207
// XXX - url should always be non-null here, Session
208
// passes it into the constructor
209
if (url != null) {
210         protocol = url.getProtocol();
211         if (host == null)
212         host = url.getHost();
213         if (port == -1)
214         port = url.getPort();
215
216         if (user == null) {
217         user = url.getUsername();
218         if (password == null) // get password too if we need it
219
password = url.getPassword();
220         } else {
221         if (password == null && user.equals(url.getUsername()))
222             // only get the password if it matches the username
223
password = url.getPassword();
224         }
225
226         file = url.getFile();
227     }
228
229     // try to get protocol-specific default properties
230
if (protocol != null) {
231         if (host == null)
232         host = session.getProperty("mail." + protocol + ".host");
233         if (user == null)
234         user = session.getProperty("mail." + protocol + ".user");
235     }
236
237     // try to get mail-wide default properties
238
if (host == null)
239         host = session.getProperty("mail.host");
240
241     if (user == null)
242         user = session.getProperty("mail.user");
243
244     // try using the system username
245
if (user == null) {
246         try {
247         user = System.getProperty("user.name");
248         } catch (SecurityException JavaDoc sex) {
249         if (debug)
250             sex.printStackTrace(session.getDebugOut());
251         }
252     }
253
254     // if we don't have a password, look for saved authentication info
255
if (password == null && url != null) {
256         // canonicalize the URLName
257
setURLName(new URLName JavaDoc(protocol, host, port, file, user, password));
258         pw = session.getPasswordAuthentication(getURLName());
259         if (pw != null) {
260         if (user == null) {
261             user = pw.getUserName();
262             password = pw.getPassword();
263         } else if (user.equals(pw.getUserName())) {
264             password = pw.getPassword();
265         }
266         } else
267         save = true;
268     }
269
270     // try connecting, if the protocol needs some missing
271
// information (user, password) it will not connect.
272
// if it tries to connect and fails, remember why for later.
273
AuthenticationFailedException JavaDoc authEx = null;
274     try {
275         connected = protocolConnect(host, port, user, password);
276     } catch (AuthenticationFailedException JavaDoc ex) {
277         authEx = ex;
278     }
279
280     // if not connected, ask the user and try again
281
if (!connected) {
282         InetAddress addr;
283         try {
284         addr = InetAddress.getByName(host);
285         } catch (UnknownHostException e) {
286         addr = null;
287         }
288         pw = session.requestPasswordAuthentication(
289                 addr, port,
290                 protocol,
291                 null, user);
292         if (pw != null) {
293         user = pw.getUserName();
294         password = pw.getPassword();
295
296         // have the service connect again
297
connected = protocolConnect(host, port, user, password);
298         }
299     }
300
301     // if we're not connected by now, we give up
302
if (!connected) {
303         if (authEx != null)
304         throw authEx;
305         else
306         throw new AuthenticationFailedException JavaDoc();
307     }
308
309     setURLName(new URLName JavaDoc(protocol, host, port, file, user, password));
310
311     if (save)
312         session.setPasswordAuthentication(getURLName(),
313                 new PasswordAuthentication JavaDoc(user, password));
314
315     // set our connected state
316
setConnected(true);
317
318     // finally, deliver the connection event
319
notifyConnectionListeners(ConnectionEvent.OPENED);
320     }
321
322
323     /**
324      * The service implementation should override this method to
325      * perform the actual protocol-specific connection attempt.
326      * The default implementation of the <code>connect</code> method
327      * calls this method as needed. <p>
328      *
329      * The <code>protocolConnect</code> method should return
330      * <code>false</code> if a user name or password is required
331      * for authentication but the corresponding parameter is null;
332      * the <code>connect</code> method will prompt the user when
333      * needed to supply missing information. This method may
334      * also return <code>false</code> if authentication fails for
335      * the supplied user name or password. Alternatively, this method
336      * may throw an AuthenticationFailedException when authentication
337      * fails. This exception may include a String message with more
338      * detail about the failure. <p>
339      *
340      * The <code>protocolConnect</code> method should throw an
341      * exception to report failures not related to authentication,
342      * such as an invalid host name or port number, loss of a
343      * connection during the authentication process, unavailability
344      * of the server, etc.
345      *
346      * @param host the name of the host to connect to
347      * @param port the port to use (-1 means use default port)
348      * @param user the name of the user to login as
349      * @param password the user's password
350      * @return true if connection successful, false if authentication failed
351      * @exception AuthenticationFailedException for authentication failures
352      * @exception MessagingException for non-authentication failures
353      */

354     protected boolean protocolConnect(String JavaDoc host, int port, String JavaDoc user,
355                 String JavaDoc password) throws MessagingException JavaDoc {
356     return false;
357     }
358
359     /**
360      * Is this service currently connected? <p>
361      *
362      * This implementation uses a private boolean field to
363      * store the connection state. This method returns the value
364      * of that field. <p>
365      *
366      * Subclasses may want to override this method to verify that any
367      * connection to the message store is still alive.
368      *
369      * @return true if the service is connected, false if it is not connected
370      */

371     public boolean isConnected() {
372     return connected;
373     }
374
375     /**
376      * Set the connection state of this service. The connection state
377      * will automatically be set by the service implementation during the
378      * <code>connect</code> and <code>close</code> methods.
379      * Subclasses will need to call this method to set the state
380      * if the service was automatically disconnected. <p>
381      *
382      * The implementation in this class merely sets the private field
383      * returned by the <code>isConnected</code> method.
384      *
385      * @param connected true if the service is connected,
386      * false if it is not connected
387      */

388     protected void setConnected(boolean connected) {
389     this.connected = connected;
390     }
391
392     /**
393      * Close this service and terminate its connection. A close
394      * ConnectionEvent is delivered to any ConnectionListeners. Any
395      * Messaging components (Folders, Messages, etc.) belonging to this
396      * service are invalid after this service is closed. Note that the service
397      * is closed even if this method terminates abnormally by throwing
398      * a MessagingException. <p>
399      *
400      * This implementation uses <code>setConnected(false)</code> to set
401      * this service's connected state to <code>false</code>. It will then
402      * send a close ConnectionEvent to any registered ConnectionListeners.
403      * Subclasses overriding this method to do implementation specific
404      * cleanup should call this method as a last step to insure event
405      * notification, probably by including a call to <code>super.close()</code>
406      * in a <code>finally</code> clause.
407      *
408      * @see javax.mail.event.ConnectionEvent
409      * @throws MessagingException for errors while closing
410      */

411     public synchronized void close() throws MessagingException JavaDoc {
412     setConnected(false);
413     notifyConnectionListeners(ConnectionEvent.CLOSED);
414     }
415
416     /**
417      * Return a URLName representing this service. The returned URLName
418      * does <em>not</em> include the password field. <p>
419      *
420      * Subclasses should only override this method if their
421      * URLName does not follow the standard format. <p>
422      *
423      * The implementation in the Service class returns (usually a copy of)
424      * the <code>url</code> field with the password and file information
425      * stripped out.
426      *
427      * @return the URLName representing this service
428      * @see URLName
429      */

430     public URLName JavaDoc getURLName() {
431     if (url != null && (url.getPassword() != null || url.getFile() != null))
432         return new URLName JavaDoc(url.getProtocol(), url.getHost(),
433             url.getPort(), null /* no file */,
434             url.getUsername(), null /* no password */);
435     else
436         return url;
437     }
438
439     /**
440      * Set the URLName representing this service.
441      * Normally used to update the <code>url</code> field
442      * after a service has successfully connected. <p>
443      *
444      * Subclasses should only override this method if their
445      * URL does not follow the standard format. In particular,
446      * subclasses should override this method if their URL
447      * does not require all the possible fields supported by
448      * <code>URLName</code>; a new <code>URLName</code> should
449      * be constructed with any unneeded fields removed. <p>
450      *
451      * The implementation in the Service class simply sets the
452      * <code>url</code> field.
453      *
454      * @see URLName
455      */

456     protected void setURLName(URLName JavaDoc url) {
457     this.url = url;
458     }
459
460     /**
461      * Add a listener for Connection events on this service. <p>
462      *
463      * The default implementation provided here adds this listener
464      * to an internal list of ConnectionListeners.
465      *
466      * @param l the Listener for Connection events
467      * @see javax.mail.event.ConnectionEvent
468      */

469     public synchronized void addConnectionListener(ConnectionListener l) {
470     if (connectionListeners == null)
471         connectionListeners = new Vector();
472     connectionListeners.addElement(l);
473     }
474
475     /**
476      * Remove a Connection event listener. <p>
477      *
478      * The default implementation provided here removes this listener
479      * from the internal list of ConnectionListeners.
480      *
481      * @param l the listener
482      * @see #addConnectionListener
483      */

484     public synchronized void removeConnectionListener(ConnectionListener l) {
485         if (connectionListeners != null)
486         connectionListeners.removeElement(l);
487     }
488
489     /**
490      * Notify all ConnectionListeners. Service implementations are
491      * expected to use this method to broadcast connection events. <p>
492      *
493      * The provided default implementation queues the event into
494      * an internal event queue. An event dispatcher thread dequeues
495      * events from the queue and dispatches them to the registered
496      * ConnectionListeners. Note that the event dispatching occurs
497      * in a separate thread, thus avoiding potential deadlock problems.
498      */

499     protected synchronized void notifyConnectionListeners(int type) {
500     if (connectionListeners != null) {
501         ConnectionEvent e = new ConnectionEvent(this, type);
502         queueEvent(e, connectionListeners);
503     }
504
505         /* Fix for broken JDK1.1.x Garbage collector :
506          * The 'conservative' GC in JDK1.1.x occasionally fails to
507          * garbage-collect Threads which are in the wait state.
508          * This would result in thread (and consequently memory) leaks.
509          *
510          * We attempt to fix this by sending a 'terminator' event
511          * to the queue, after we've sent the CLOSED event. The
512          * terminator event causes the event-dispatching thread to
513          * self destruct.
514          */

515         if (type == ConnectionEvent.CLOSED)
516             terminateQueue();
517     }
518
519     /**
520      * Return <code>getURLName.toString()</code> if this service has a URLName,
521      * otherwise it will return the default <code>toString</code>.
522      */

523     public String JavaDoc toString() {
524     URLName JavaDoc url = getURLName();
525     if (url != null)
526         return url.toString();
527     else
528         return super.toString();
529     }
530
531     /*
532      * The queue of events to be delivered.
533      */

534     private EventQueue JavaDoc q;
535
536     /*
537      * A lock for creating the EventQueue object. Only one thread should
538      * create an EventQueue for this service. We can't synchronize on the
539      * service's lock because that might violate the locking hierarchy in
540      * some cases.
541      */

542     private Object JavaDoc qLock = new Object JavaDoc();
543
544     /**
545      * Add the event and vector of listeners to the queue to be delivered.
546      */

547     protected void queueEvent(MailEvent event, Vector vector) {
548     // synchronize creation of the event queue
549
synchronized (qLock) {
550         if (q == null)
551         q = new EventQueue JavaDoc();
552     }
553
554     /*
555          * Copy the vector in order to freeze the state of the set
556          * of EventListeners the event should be delivered to prior
557          * to delivery. This ensures that any changes made to the
558          * Vector from a target listener's method during the delivery
559          * of this event will not take effect until after the event is
560          * delivered.
561          */

562     Vector v = (Vector)vector.clone();
563     q.enqueue(event, v);
564     }
565
566     static class TerminatorEvent extends MailEvent {
567     private static final long serialVersionUID = 5542172141759168416L;
568
569     TerminatorEvent() {
570         super(new Object JavaDoc());
571     }
572
573     public void dispatch(Object JavaDoc listener) {
574         // Kill the event dispatching thread.
575
Thread.currentThread().interrupt();
576     }
577     }
578
579     // Dispatch the terminator
580
private void terminateQueue() {
581     synchronized (qLock) {
582         if (q != null) {
583         Vector dummyListeners = new Vector();
584         dummyListeners.setSize(1); // need atleast one listener
585
q.enqueue(new TerminatorEvent(), dummyListeners);
586         q = null;
587         }
588     }
589     }
590
591     /**
592      * Stop the event dispatcher thread so the queue can be garbage collected.
593      */

594     protected void finalize() throws Throwable JavaDoc {
595     super.finalize();
596     terminateQueue();
597     }
598 }
599
Popular Tags