Clover coverage report - Maven Clover report
Coverage timestamp: Sun Aug 20 2006 04:01:44 PDT
file stats: LOC: 387   Methods: 18
NCLOC: 163   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
Service.java 0% 8.9% 16.7% 7%
coverage coverage
 1    /**
 2    *
 3    * Copyright 2003-2006 The Apache Software Foundation
 4    *
 5    * Licensed under the Apache License, Version 2.0 (the "License");
 6    * you may not use this file except in compliance with the License.
 7    * You may obtain a copy of the License at
 8    *
 9    * http://www.apache.org/licenses/LICENSE-2.0
 10    *
 11    * Unless required by applicable law or agreed to in writing, software
 12    * distributed under the License is distributed on an "AS IS" BASIS,
 13    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14    * See the License for the specific language governing permissions and
 15    * limitations under the License.
 16    */
 17   
 18    package javax.mail;
 19   
 20    import java.net.InetAddress;
 21    import java.net.UnknownHostException;
 22    import java.util.Vector;
 23   
 24    import javax.mail.event.ConnectionEvent;
 25    import javax.mail.event.ConnectionListener;
 26    import javax.mail.event.MailEvent;
 27   
 28    /**
 29    * @version $Rev: 421852 $ $Date: 2006-07-14 03:02:19 -0700 (Fri, 14 Jul 2006) $
 30    */
 31    public abstract class Service {
 32    /**
 33    * The session from which this service was created.
 34    */
 35    protected Session session;
 36    /**
 37    * The URLName of this service
 38    */
 39    protected URLName url;
 40    /**
 41    * Debug flag for this service, set from the Session's debug flag.
 42    */
 43    protected boolean debug;
 44   
 45    private boolean connected;
 46    private final Vector connectionListeners = new Vector(2);
 47    private final EventQueue queue = new EventQueue();
 48   
 49    /**
 50    * Construct a new Service.
 51    * @param session the session from which this service was created
 52    * @param url the URLName of this service
 53    */
 54  16 protected Service(Session session, URLName url) {
 55  16 this.session = session;
 56  16 this.url = url;
 57  16 this.debug = session.getDebug();
 58    }
 59   
 60    /**
 61    * A generic connect method that takes no parameters allowing subclasses
 62    * to implement an appropriate authentication scheme.
 63    * The default implementation calls <code>connect(null, null, null)</code>
 64    * @throws AuthenticationFailedException if authentication fails
 65    * @throws MessagingException for other failures
 66    */
 67  0 public void connect() throws MessagingException {
 68  0 connect(null, null, null);
 69    }
 70   
 71    /**
 72    * Connect to the specified host using a simple username/password authenticaion scheme
 73    * and the default port.
 74    * The default implementation calls <code>connect(host, -1, user, password)</code>
 75    *
 76    * @param host the host to connect to
 77    * @param user the user name
 78    * @param password the user's password
 79    * @throws AuthenticationFailedException if authentication fails
 80    * @throws MessagingException for other failures
 81    */
 82  0 public void connect(String host, String user, String password) throws MessagingException {
 83  0 connect(host, -1, user, password);
 84    }
 85   
 86    /**
 87    * Connect to the specified host using a simple username/password authenticaion scheme
 88    * and the default host and port.
 89    * The default implementation calls <code>connect(host, -1, user, password)</code>
 90    *
 91    * @param user the user name
 92    * @param password the user's password
 93    * @throws AuthenticationFailedException if authentication fails
 94    * @throws MessagingException for other failures
 95    */
 96  0 public void connect(String user, String password) throws MessagingException {
 97  0 connect(null, -1, user, password);
 98    }
 99   
 100    /**
 101    * Connect to the specified host at the specified port using a simple username/password authenticaion scheme.
 102    *
 103    * If this Service is already connected, an IllegalStateException is thrown.
 104    *
 105    * @param host the host to connect to
 106    * @param port the port to connect to; pass -1 to use the default for the protocol
 107    * @param user the user name
 108    * @param password the user's password
 109    * @throws AuthenticationFailedException if authentication fails
 110    * @throws MessagingException for other failures
 111    * @throws IllegalStateException if this service is already connected
 112    */
 113  0 public void connect(String host, int port, String user, String password) throws MessagingException {
 114   
 115  0 if (isConnected()) {
 116  0 throw new IllegalStateException("Already connected");
 117    }
 118   
 119    // before we try to connect, we need to derive values for some parameters that may not have
 120    // been explicitly specified. For example, the normal connect() method leaves us to derive all
 121    // of these from other sources. Some of the values are derived from our URLName value, others
 122    // from session parameters. We need to go through all of these to develop a set of values we
 123    // can connect with.
 124   
 125    // this is the protocol we're connecting with. We use this largely to derive configured values from
 126    // session properties.
 127  0 String protocol = null;
 128   
 129    // if we're working with the URL form, then we can retrieve the protocol from the URL.
 130  0 if (url != null) {
 131  0 protocol = url.getProtocol();
 132    }
 133   
 134    // now try to derive values for any of the arguments we've been given as defaults
 135  0 if (host == null) {
 136    // first choice is from the url, if we have
 137  0 if (url != null) {
 138  0 host = url.getHost();
 139    // it is possible that this could return null (rare). If it does, try to get a
 140    // value from a protocol specific session variable.
 141  0 if (host == null) {
 142  0 host = session.getProperty("mail." + protocol + ".host");
 143    }
 144    }
 145    // this may still be null...get the global mail property
 146  0 if (host == null) {
 147  0 host = session.getProperty("mail.host");
 148    }
 149    }
 150   
 151    // ok, go after userid information next.
 152  0 if (user == null) {
 153    // first choice is from the url, if we have
 154  0 if (url != null) {
 155  0 user = url.getUsername();
 156    // make sure we get the password from the url, if we can.
 157  0 if (password == null) {
 158  0 password = url.getPassword();
 159    }
 160    // user still null? We have several levels of properties to try yet
 161  0 if (user == null) {
 162  0 user = session.getProperty("mail." + protocol + ".user");
 163    }
 164    }
 165   
 166    // this may still be null...get the global mail property
 167  0 if (user == null) {
 168  0 user = session.getProperty("mail.user");
 169    }
 170   
 171    // finally, we try getting the system defined user name
 172  0 try {
 173  0 user = System.getProperty("user.name");
 174    } catch (SecurityException e) {
 175    // we ignore this, and just us a null username.
 176    }
 177    }
 178    // if we have an explicitly given user name, we need to see if this matches the url one and
 179    // grab the password from there.
 180    else {
 181  0 if (url != null && user.equals(url.getUsername())) {
 182  0 password = url.getPassword();
 183    }
 184    }
 185   
 186    // we need to update the URLName associated with this connection once we have all of the information,
 187    // which means we also need to propogate the file portion of the URLName if we have this form when
 188    // we start.
 189  0 String file = null;
 190  0 if (url != null) {
 191  0 file = url.getFile();
 192    }
 193   
 194    // see if we have cached security information to use. If this is not cached, we'll save it
 195    // after we successfully connect.
 196  0 boolean cachePassword = false;
 197   
 198   
 199    // still have a null password to this point, and using a url form?
 200  0 if (password == null && url != null) {
 201    // construct a new URL, filling in any pieces that may have been explicitly specified.
 202  0 setURLName(new URLName(protocol, host, port, file, user, password));
 203    // now see if we have a saved password from a previous request.
 204  0 PasswordAuthentication cachedPassword = session.getPasswordAuthentication(getURLName());
 205   
 206    // if we found a saved one, see if we need to get any the pieces from here.
 207  0 if (cachedPassword != null) {
 208    // not even a resolved userid? Then use both bits.
 209  0 if (user == null) {
 210  0 user = cachedPassword.getUserName();
 211  0 password = cachedPassword.getPassword();
 212    }
 213    // our user name must match the cached name to be valid.
 214  0 else if (user.equals(cachedPassword.getUserName())) {
 215  0 password = cachedPassword.getPassword();
 216    }
 217    }
 218    else
 219    {
 220    // nothing found in the cache, so we need to save this if we can connect successfully.
 221  0 cachePassword = true;
 222    }
 223    }
 224   
 225    // we've done our best up to this point to obtain all of the information needed to make the
 226    // connection. Now we pass this off to the protocol handler to see if it works. If we get a
 227    // connection failure, we may need to prompt for a password before continuing.
 228  0 try {
 229  0 connected = protocolConnect(host, port, user, password);
 230    }
 231    catch (AuthenticationFailedException e) {
 232    }
 233   
 234  0 if (!connected) {
 235  0 InetAddress ipAddress = null;
 236   
 237  0 try {
 238  0 ipAddress = InetAddress.getByName(host);
 239    } catch (UnknownHostException e) {
 240    }
 241   
 242    // now ask the session to try prompting for a password.
 243  0 PasswordAuthentication promptPassword = session.requestPasswordAuthentication(ipAddress, port, protocol, null, user);
 244   
 245    // if we were able to obtain new information from the session, then try again using the
 246    // provided information .
 247  0 if (promptPassword != null) {
 248  0 user = promptPassword.getUserName();
 249  0 password = promptPassword.getPassword();
 250    }
 251   
 252  0 connected = protocolConnect(host, port, user, password);
 253    }
 254   
 255   
 256    // if we're still not connected, then this is an exception.
 257  0 if (!connected) {
 258  0 throw new AuthenticationFailedException();
 259    }
 260   
 261    // the URL name needs to reflect the most recent information.
 262  0 setURLName(new URLName(protocol, host, port, file, user, password));
 263   
 264    // we need to update the global password cache with this information.
 265  0 if (cachePassword) {
 266  0 session.setPasswordAuthentication(getURLName(), new PasswordAuthentication(user, password));
 267    }
 268   
 269    // we're now connected....broadcast this to any interested parties.
 270  0 setConnected(connected);
 271  0 notifyConnectionListeners(ConnectionEvent.OPENED);
 272    }
 273   
 274    /**
 275    * Attempt the protocol-specific connection; subclasses should override this to establish
 276    * a connection in the appropriate manner.
 277    *
 278    * This method should return true if the connection was established.
 279    * It may return false to cause the {@link #connect(String, int, String, String)} method to
 280    * reattempt the connection after trying to obtain user and password information from the user.
 281    * Alternatively it may throw a AuthenticatedFailedException to abandon the conection attempt.
 282    *
 283    * @param host
 284    * @param port
 285    * @param user
 286    * @param password
 287    * @return
 288    * @throws AuthenticationFailedException if authentication fails
 289    * @throws MessagingException for other failures
 290    */
 291  0 protected boolean protocolConnect(String host, int port, String user, String password) throws MessagingException {
 292  0 return false;
 293    }
 294   
 295    /**
 296    * Check if this service is currently connected.
 297    * The default implementation simply returns the value of a private boolean field;
 298    * subclasses may wish to override this method to verify the physical connection.
 299    *
 300    * @return true if this service is connected
 301    */
 302  0 public boolean isConnected() {
 303  0 return connected;
 304    }
 305   
 306    /**
 307    * Notification to subclasses that the connection state has changed.
 308    * This method is called by the connect() and close() methods to indicate state change;
 309    * subclasses should also call this method if the connection is automatically closed
 310    * for some reason.
 311    *
 312    * @param connected the connection state
 313    */
 314  0 protected void setConnected(boolean connected) {
 315  0 this.connected = connected;
 316    }
 317   
 318    /**
 319    * Close this service and terminate its physical connection.
 320    * The default implementation simply calls setConnected(false) and then
 321    * sends a CLOSED event to all registered ConnectionListeners.
 322    * Subclasses overriding this method should still ensure it is closed; they should
 323    * also ensure that it is called if the connection is closed automatically, for
 324    * for example in a finalizer.
 325    *
 326    *@throws MessagingException if there were errors closing; the connection is still closed
 327    */
 328  0 public void close() throws MessagingException {
 329  0 setConnected(false);
 330  0 notifyConnectionListeners(ConnectionEvent.CLOSED);
 331    }
 332   
 333    /**
 334    * Return a copy of the URLName representing this service with the password and file information removed.
 335    *
 336    * @return the URLName for this service
 337    */
 338  0 public URLName getURLName() {
 339   
 340  0 return url == null ? null : new URLName(url.getProtocol(), url.getHost(), url.getPort(), null, url.getUsername(), null);
 341    }
 342   
 343    /**
 344    * Set the url field.
 345    * @param url the new value
 346    */
 347  0 protected void setURLName(URLName url) {
 348  0 this.url = url;
 349    }
 350   
 351  0 public void addConnectionListener(ConnectionListener listener) {
 352  0 connectionListeners.add(listener);
 353    }
 354   
 355  0 public void removeConnectionListener(ConnectionListener listener) {
 356  0 connectionListeners.remove(listener);
 357    }
 358   
 359  0 protected void notifyConnectionListeners(int type) {
 360  0 queue.queueEvent(new ConnectionEvent(this, type), connectionListeners);
 361    }
 362   
 363  0 public String toString() {
 364  0 return url == null ? super.toString() : url.toString();
 365    }
 366   
 367  0 protected void queueEvent(MailEvent event, Vector listeners) {
 368  0 queue.queueEvent(event, listeners);
 369    }
 370   
 371  15 protected void finalize() throws Throwable {
 372  15 queue.stop();
 373  15 connectionListeners.clear();
 374  15 super.finalize();
 375    }
 376   
 377   
 378    /**
 379    * Package scope utility method to allow Message instances
 380    * access to the Service's session.
 381    *
 382    * @return The Session the service is associated with.
 383    */
 384  3 Session getSession() {
 385  3 return session;
 386    }
 387    }