Save This Page
Home » glassfish-v2ur2-b04-src » javax » mail » [javadoc | source]
    1   /*
    2    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    3    *
    4    * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
    5    *
    6    * The contents of this file are subject to the terms of either the GNU
    7    * General Public License Version 2 only ("GPL") or the Common Development
    8    * and Distribution License("CDDL") (collectively, the "License").  You
    9    * may not use this file except in compliance with the License. You can obtain
   10    * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
   11    * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
   12    * language governing permissions and limitations under the License.
   13    *
   14    * When distributing the software, include this License Header Notice in each
   15    * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
   16    * Sun designates this particular file as subject to the "Classpath" exception
   17    * as provided by Sun in the GPL Version 2 section of the License file that
   18    * accompanied this code.  If applicable, add the following below the License
   19    * Header, with the fields enclosed by brackets [] replaced by your own
   20    * identifying information: "Portions Copyrighted [year]
   21    * [name of copyright owner]"
   22    *
   23    * Contributor(s):
   24    *
   25    * If you wish your version of this file to be governed by only the CDDL or
   26    * only the GPL Version 2, indicate your decision by adding "[Contributor]
   27    * elects to include this software in this distribution under the [CDDL or GPL
   28    * Version 2] license."  If you don't indicate a single choice of license, a
   29    * recipient has the option to distribute your version of this file under
   30    * either the CDDL, the GPL Version 2 or to extend the choice of license to
   31    * its licensees as provided above.  However, if you add GPL Version 2 code
   32    * and therefore, elected the GPL Version 2 license, then the option applies
   33    * only if the new code is made subject to such option by the copyright
   34    * holder.
   35    */
   36   
   37   /*
   38    * @(#)Service.java	1.33 07/05/14
   39    */
   40   
   41   package javax.mail;
   42   
   43   import java.io;
   44   import java.net;
   45   import java.util;
   46   import javax.mail.event;
   47   
   48   /**
   49    * An abstract class that contains the functionality
   50    * common to messaging services, such as stores and transports. <p>
   51    * A messaging service is created from a <code>Session</code> and is
   52    * named using a <code>URLName</code>.  A service must be connected
   53    * before it can be used.  Connection events are sent to reflect
   54    * its connection status.
   55    *
   56    * @author Christopher Cotton
   57    * @author Bill Shannon
   58    * @author Kanwar Oberoi
   59    * @version 1.33, 07/05/14
   60    */
   61   
   62   public abstract class Service {
   63   
   64       /**
   65        * The session from which this service was created.
   66        */
   67       protected Session	session;
   68   
   69       /**
   70        * The <code>URLName</code> of this service.
   71        */
   72       protected URLName	url = null;
   73   
   74       /**
   75        * Debug flag for this service.  Set from the session's debug
   76        * flag when this service is created.
   77        */
   78       protected boolean	debug = false;
   79   
   80       private boolean	connected = false;
   81       private Vector	connectionListeners = null;
   82   
   83       /**
   84        * Constructor.
   85        *
   86        * @param	session Session object for this service
   87        * @param	urlname	URLName object to be used for this service
   88        */
   89       protected Service(Session session, URLName urlname) {
   90   	this.session = session;
   91   	url = urlname;
   92   	debug = session.getDebug();
   93       }
   94   
   95       /**
   96        * A generic connect method that takes no parameters. Subclasses
   97        * can implement the appropriate authentication schemes. Subclasses
   98        * that need additional information might want to use some properties
   99        * or might get it interactively using a popup window. <p>
  100        *
  101        * If the connection is successful, an "open" <code>ConnectionEvent</code>
  102        * is delivered to any <code>ConnectionListeners</code> on this service. <p>
  103        *
  104        * Most clients should just call this method to connect to the service.<p>
  105        *
  106        * It is an error to connect to an already connected service. <p>
  107        *
  108        * The implementation provided here simply calls the following
  109        * <code>connect(String, String, String)</code> method with nulls.
  110        *
  111        * @exception AuthenticationFailedException	for authentication failures
  112        * @exception MessagingException	for other failures
  113        * @exception IllegalStateException	if the service is already connected
  114        *
  115        * @see javax.mail.event.ConnectionEvent
  116        */
  117       public void connect() throws MessagingException {
  118   	connect(null, null, null);
  119       }
  120   
  121       /**
  122        * Connect to the specified address. This method provides a simple
  123        * authentication scheme that requires a username and password. <p>
  124        *
  125        * If the connection is successful, an "open" <code>ConnectionEvent</code>
  126        * is delivered to any <code>ConnectionListeners</code> on this service. <p>
  127        *
  128        * It is an error to connect to an already connected service. <p>
  129        *
  130        * The implementation in the Service class will collect defaults
  131        * for the host, user, and password from the session, from the
  132        * <code>URLName</code> for this service, and from the supplied
  133        * parameters and then call the <code>protocolConnect</code> method.
  134        * If the <code>protocolConnect</code> method returns <code>false</code>,
  135        * the user will be prompted for any missing information and the
  136        * <code>protocolConnect</code> method will be called again.  The
  137        * subclass should override the <code>protocolConnect</code> method.
  138        * The subclass should also implement the <code>getURLName</code>
  139        * method, or use the implementation in this class. <p>
  140        *
  141        * On a successful connection, the <code>setURLName</code> method is
  142        * called with a URLName that includes the information used to make
  143        * the connection, including the password. <p>
  144        *
  145        * If the username passed in is null, a default value will be chosen
  146        * as described above.
  147        *
  148        * If the password passed in is null and this is the first successful
  149        * connection to this service, the user name and the password
  150        * collected from the user will be saved as defaults for subsequent
  151        * connection attempts to this same service when using other Service object
  152        * instances (the connection information is typically always saved within
  153        * a particular Service object instance).  The password is saved using the
  154        * Session method <code>setPasswordAuthentication</code>.  If the
  155        * password passed in is not null, it is not saved, on the assumption
  156        * that the application is managing passwords explicitly.
  157        *
  158        * @param host 	the host to connect to
  159        * @param user	the user name
  160        * @param password	this user's password
  161        * @exception AuthenticationFailedException	for authentication failures
  162        * @exception MessagingException		for other failures
  163        * @exception IllegalStateException	if the service is already connected
  164        * @see javax.mail.event.ConnectionEvent
  165        * @see javax.mail.Session#setPasswordAuthentication
  166        */
  167       public void connect(String host, String user, String password)
  168   			throws MessagingException {
  169   	connect(host, -1, user, password);
  170       }
  171   
  172       /**
  173        * Connect to the current host using the specified username
  174        * and password.  This method is equivalent to calling the
  175        * <code>connect(host, user, password)</code> method with null
  176        * for the host name.
  177        *
  178        * @param user      the user name
  179        * @param password  this user's password
  180        * @exception AuthenticationFailedException for authentication failures
  181        * @exception MessagingException            for other failures
  182        * @exception IllegalStateException if the service is already connected
  183        * @see javax.mail.event.ConnectionEvent
  184        * @see javax.mail.Session#setPasswordAuthentication
  185        * @see #connect(java.lang.String, java.lang.String, java.lang.String)
  186        * @since           JavaMail 1.4
  187        */
  188       public void connect(String user, String password) throws MessagingException {
  189           connect(null, user, password);
  190       }
  191   
  192       /**
  193        * Similar to connect(host, user, password) except a specific port
  194        * can be specified.
  195        *
  196        * @param host 	the host to connect to
  197        * @param port	the port to connect to (-1 means the default port)
  198        * @param user	the user name
  199        * @param password	this user's password
  200        * @exception AuthenticationFailedException	for authentication failures
  201        * @exception MessagingException		for other failures
  202        * @exception IllegalStateException	if the service is already connected
  203        * @see #connect(java.lang.String, java.lang.String, java.lang.String)
  204        * @see javax.mail.event.ConnectionEvent
  205        */
  206       public synchronized void connect(String host, int port,
  207   		String user, String password) throws MessagingException {
  208   
  209   	// see if the service is already connected
  210   	if (isConnected())
  211   	    throw new IllegalStateException("already connected");
  212   
  213   	PasswordAuthentication pw;
  214   	boolean connected = false;
  215   	boolean save = false;
  216   	String protocol = null;
  217   	String file = null;
  218   
  219   	// get whatever information we can from the URL
  220   	// XXX - url should always be non-null here, Session
  221   	//       passes it into the constructor
  222   	if (url != null) {
  223   	    protocol = url.getProtocol();
  224   	    if (host == null)
  225   		host = url.getHost();
  226   	    if (port == -1)
  227   		port = url.getPort();
  228   
  229   	    if (user == null) {
  230   		user = url.getUsername();
  231   		if (password == null)	// get password too if we need it
  232   		    password = url.getPassword();
  233   	    } else {
  234   		if (password == null && user.equals(url.getUsername()))
  235   		    // only get the password if it matches the username
  236   		    password = url.getPassword();
  237   	    }
  238   
  239   	    file = url.getFile();
  240   	}
  241   
  242   	// try to get protocol-specific default properties
  243   	if (protocol != null) {
  244   	    if (host == null)
  245   		host = session.getProperty("mail." + protocol + ".host");
  246   	    if (user == null)
  247   		user = session.getProperty("mail." + protocol + ".user");
  248   	}
  249   
  250   	// try to get mail-wide default properties
  251   	if (host == null)
  252   	    host = session.getProperty("mail.host");
  253   
  254   	if (user == null)
  255   	    user = session.getProperty("mail.user");
  256   
  257   	// try using the system username
  258   	if (user == null) {
  259   	    try {
  260   		user = System.getProperty("user.name");
  261   	    } catch (SecurityException sex) {
  262   		if (debug)
  263   		    sex.printStackTrace(session.getDebugOut());
  264   	    }
  265   	}
  266   
  267   	// if we don't have a password, look for saved authentication info
  268   	if (password == null && url != null) {
  269   	    // canonicalize the URLName
  270   	    setURLName(new URLName(protocol, host, port, file, user, null));
  271   	    pw = session.getPasswordAuthentication(getURLName());
  272   	    if (pw != null) {
  273   		if (user == null) {
  274   		    user = pw.getUserName();
  275   		    password = pw.getPassword();
  276   		} else if (user.equals(pw.getUserName())) {
  277   		    password = pw.getPassword();
  278   		}
  279   	    } else
  280   		save = true;
  281   	}
  282   
  283   	// try connecting, if the protocol needs some missing
  284   	// information (user, password) it will not connect.
  285   	// if it tries to connect and fails, remember why for later.
  286   	AuthenticationFailedException authEx = null;
  287   	try {
  288   	    connected = protocolConnect(host, port, user, password);
  289   	} catch (AuthenticationFailedException ex) {
  290   	    authEx = ex;
  291   	}
  292   
  293   	// if not connected, ask the user and try again
  294   	if (!connected) {
  295   	    InetAddress addr;
  296   	    try {
  297   		addr = InetAddress.getByName(host);
  298   	    } catch (UnknownHostException e) {
  299   		addr = null;
  300   	    }
  301   	    pw = session.requestPasswordAuthentication(
  302   			    addr, port,
  303   			    protocol,
  304   			    null, user);
  305   	    if (pw != null) {
  306   		user = pw.getUserName();
  307   		password = pw.getPassword();
  308   
  309   		// have the service connect again
  310   		connected = protocolConnect(host, port, user, password);
  311   	    }
  312   	}
  313   
  314   	// if we're not connected by now, we give up
  315   	if (!connected) {
  316   	    if (authEx != null)
  317   		throw authEx;
  318   	    else
  319   		throw new AuthenticationFailedException();
  320   	}
  321   
  322   	setURLName(new URLName(protocol, host, port, file, user, password));
  323   
  324   	if (save)
  325   	    session.setPasswordAuthentication(getURLName(),
  326   			    new PasswordAuthentication(user, password));
  327   
  328   	// set our connected state
  329   	setConnected(true);
  330   
  331   	// finally, deliver the connection event
  332   	notifyConnectionListeners(ConnectionEvent.OPENED);
  333       }
  334   
  335   
  336       /**
  337        * The service implementation should override this method to
  338        * perform the actual protocol-specific connection attempt.
  339        * The default implementation of the <code>connect</code> method
  340        * calls this method as needed. <p>
  341        *
  342        * The <code>protocolConnect</code> method should return
  343        * <code>false</code> if a user name or password is required
  344        * for authentication but the corresponding parameter is null;
  345        * the <code>connect</code> method will prompt the user when
  346        * needed to supply missing information.  This method may
  347        * also return <code>false</code> if authentication fails for
  348        * the supplied user name or password.  Alternatively, this method
  349        * may throw an AuthenticationFailedException when authentication
  350        * fails.  This exception may include a String message with more
  351        * detail about the failure. <p>
  352        *
  353        * The <code>protocolConnect</code> method should throw an
  354        * exception to report failures not related to authentication,
  355        * such as an invalid host name or port number, loss of a
  356        * connection during the authentication process, unavailability
  357        * of the server, etc.
  358        *
  359        * @param	host		the name of the host to connect to
  360        * @param	port		the port to use (-1 means use default port)
  361        * @param	user		the name of the user to login as
  362        * @param	password	the user's password
  363        * @return	true if connection successful, false if authentication failed
  364        * @exception AuthenticationFailedException	for authentication failures
  365        * @exception MessagingException	for non-authentication failures
  366        */
  367       protected boolean protocolConnect(String host, int port, String user,
  368   				String password) throws MessagingException {
  369   	return false;
  370       }
  371   
  372       /**
  373        * Is this service currently connected? <p>
  374        *
  375        * This implementation uses a private boolean field to 
  376        * store the connection state. This method returns the value
  377        * of that field. <p>
  378        *
  379        * Subclasses may want to override this method to verify that any
  380        * connection to the message store is still alive.
  381        *
  382        * @return	true if the service is connected, false if it is not connected
  383        */
  384       public synchronized boolean isConnected() {
  385   	return connected;
  386       }
  387   
  388       /**
  389        * Set the connection state of this service.  The connection state
  390        * will automatically be set by the service implementation during the
  391        * <code>connect</code> and <code>close</code> methods.
  392        * Subclasses will need to call this method to set the state
  393        * if the service was automatically disconnected. <p>
  394        *
  395        * The implementation in this class merely sets the private field
  396        * returned by the <code>isConnected</code> method.
  397        *
  398        * @param connected true if the service is connected,
  399        *                  false if it is not connected
  400        */
  401       protected synchronized void setConnected(boolean connected) {
  402   	this.connected = connected;
  403       }
  404   
  405       /**
  406        * Close this service and terminate its connection. A close
  407        * ConnectionEvent is delivered to any ConnectionListeners. Any
  408        * Messaging components (Folders, Messages, etc.) belonging to this
  409        * service are invalid after this service is closed. Note that the service
  410        * is closed even if this method terminates abnormally by throwing
  411        * a MessagingException. <p>
  412        *
  413        * This implementation uses <code>setConnected(false)</code> to set
  414        * this service's connected state to <code>false</code>. It will then
  415        * send a close ConnectionEvent to any registered ConnectionListeners.
  416        * Subclasses overriding this method to do implementation specific
  417        * cleanup should call this method as a last step to insure event
  418        * notification, probably by including a call to <code>super.close()</code>
  419        * in a <code>finally</code> clause.
  420        *
  421        * @see javax.mail.event.ConnectionEvent
  422        * @throws	MessagingException	for errors while closing
  423        */
  424       public synchronized void close() throws MessagingException {
  425   	setConnected(false);
  426   	notifyConnectionListeners(ConnectionEvent.CLOSED);
  427       }
  428   
  429       /**
  430        * Return a URLName representing this service.  The returned URLName
  431        * does <em>not</em> include the password field.  <p>
  432        *
  433        * Subclasses should only override this method if their
  434        * URLName does not follow the standard format. <p>
  435        *
  436        * The implementation in the Service class returns (usually a copy of)
  437        * the <code>url</code> field with the password and file information
  438        * stripped out.
  439        *
  440        * @return	the URLName representing this service
  441        * @see	URLName
  442        */
  443       public synchronized URLName getURLName() {
  444   	if (url != null && (url.getPassword() != null || url.getFile() != null))
  445   	    return new URLName(url.getProtocol(), url.getHost(),
  446   			url.getPort(), null /* no file */,
  447   			url.getUsername(), null /* no password */);
  448   	else
  449   	    return url;
  450       }
  451   
  452       /**
  453        * Set the URLName representing this service.
  454        * Normally used to update the <code>url</code> field
  455        * after a service has successfully connected. <p>
  456        *
  457        * Subclasses should only override this method if their
  458        * URL does not follow the standard format.  In particular,
  459        * subclasses should override this method if their URL
  460        * does not require all the possible fields supported by
  461        * <code>URLName</code>; a new <code>URLName</code> should
  462        * be constructed with any unneeded fields removed. <p>
  463        *
  464        * The implementation in the Service class simply sets the
  465        * <code>url</code> field.
  466        *
  467        * @see URLName
  468        */
  469       protected synchronized void setURLName(URLName url) {
  470   	this.url = url;
  471       }
  472   
  473       /**
  474        * Add a listener for Connection events on this service. <p>
  475        *
  476        * The default implementation provided here adds this listener
  477        * to an internal list of ConnectionListeners.
  478        *
  479        * @param l         the Listener for Connection events
  480        * @see             javax.mail.event.ConnectionEvent
  481        */
  482       public synchronized void addConnectionListener(ConnectionListener l) {
  483      	if (connectionListeners == null)
  484   	    connectionListeners = new Vector();
  485   	connectionListeners.addElement(l);
  486       }
  487   
  488       /**
  489        * Remove a Connection event listener. <p>
  490        *
  491        * The default implementation provided here removes this listener
  492        * from the internal list of ConnectionListeners.
  493        *
  494        * @param l         the listener
  495        * @see             #addConnectionListener
  496        */
  497       public synchronized void removeConnectionListener(ConnectionListener l) {
  498           if (connectionListeners != null)
  499   	    connectionListeners.removeElement(l);
  500       }
  501   
  502       /**
  503        * Notify all ConnectionListeners. Service implementations are
  504        * expected to use this method to broadcast connection events. <p>
  505        *
  506        * The provided default implementation queues the event into
  507        * an internal event queue. An event dispatcher thread dequeues
  508        * events from the queue and dispatches them to the registered
  509        * ConnectionListeners. Note that the event dispatching occurs
  510        * in a separate thread, thus avoiding potential deadlock problems.
  511        */
  512       protected synchronized void notifyConnectionListeners(int type) {
  513      	if (connectionListeners != null) {
  514   	    ConnectionEvent e = new ConnectionEvent(this, type);
  515   	    queueEvent(e, connectionListeners);
  516   	}
  517   
  518           /* Fix for broken JDK1.1.x Garbage collector :
  519            *  The 'conservative' GC in JDK1.1.x occasionally fails to
  520            *  garbage-collect Threads which are in the wait state.
  521            *  This would result in thread (and consequently memory) leaks.
  522            *
  523            * We attempt to fix this by sending a 'terminator' event
  524            * to the queue, after we've sent the CLOSED event. The
  525            * terminator event causes the event-dispatching thread to
  526            * self destruct.
  527            */
  528           if (type == ConnectionEvent.CLOSED)
  529               terminateQueue();
  530       }
  531   
  532       /**
  533        * Return <code>getURLName.toString()</code> if this service has a URLName,
  534        * otherwise it will return the default <code>toString</code>.
  535        */
  536       public String toString() {
  537   	URLName url = getURLName();
  538   	if (url != null)
  539   	    return url.toString();
  540   	else
  541   	    return super.toString();
  542       }
  543   
  544       /*
  545        * The queue of events to be delivered.
  546        */
  547       private EventQueue q;
  548   
  549       /*
  550        * A lock for creating the EventQueue object.  Only one thread should
  551        * create an EventQueue for this service.  We can't synchronize on the
  552        * service's lock because that might violate the locking hierarchy in
  553        * some cases.
  554        */
  555       private Object qLock = new Object();
  556   
  557       /**
  558        * Add the event and vector of listeners to the queue to be delivered.
  559        */
  560       protected void queueEvent(MailEvent event, Vector vector) {
  561   	// synchronize creation of the event queue
  562   	synchronized (qLock) {
  563   	    if (q == null)
  564   		q = new EventQueue();
  565   	}
  566   
  567   	/*
  568            * Copy the vector in order to freeze the state of the set
  569            * of EventListeners the event should be delivered to prior
  570            * to delivery.  This ensures that any changes made to the
  571            * Vector from a target listener's method during the delivery
  572            * of this event will not take effect until after the event is
  573            * delivered.
  574            */
  575   	Vector v = (Vector)vector.clone();
  576   	q.enqueue(event, v);
  577       }
  578   
  579       static class TerminatorEvent extends MailEvent {
  580   	private static final long serialVersionUID = 5542172141759168416L;
  581   
  582   	TerminatorEvent() {
  583   	    super(new Object());
  584   	}
  585   
  586   	public void dispatch(Object listener) {
  587   	    // Kill the event dispatching thread.
  588   	    Thread.currentThread().interrupt();
  589   	}
  590       }
  591   
  592       // Dispatch the terminator
  593       private void terminateQueue() {
  594   	synchronized (qLock) {
  595   	    if (q != null) {
  596   		Vector dummyListeners = new Vector();
  597   		dummyListeners.setSize(1); // need atleast one listener
  598   		q.enqueue(new TerminatorEvent(), dummyListeners);
  599   		q = null;
  600   	    }
  601   	}
  602       }
  603   
  604       /**
  605        * Stop the event dispatcher thread so the queue can be garbage collected.
  606        */
  607       protected void finalize() throws Throwable {
  608   	super.finalize();
  609   	terminateQueue();
  610       }
  611   }

Save This Page
Home » glassfish-v2ur2-b04-src » javax » mail » [javadoc | source]