Clover coverage report - Maven Clover report
Coverage timestamp: Sun Aug 20 2006 04:01:04 PDT
file stats: LOC: 373   Methods: 17
NCLOC: 160   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
Service.java 0% 9% 17.6% 7.1%
coverage coverage
 1    /**
 2    *
 3    * Copyright 2003-2004 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: 390529 $ $Date: 2006-03-31 14:40:02 -0800 (Fri, 31 Mar 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  14 protected Service(Session session, URLName url) {
 55  14 this.session = session;
 56  14 this.url = url;
 57  14 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 at the specified port using a simple username/password authenticaion scheme.
 88    *
 89    * If this Service is already connected, an IllegalStateException is thrown.
 90    *
 91    * @param host the host to connect to
 92    * @param port the port to connect to; pass -1 to use the default for the protocol
 93    * @param user the user name
 94    * @param password the user's password
 95    * @throws AuthenticationFailedException if authentication fails
 96    * @throws MessagingException for other failures
 97    * @throws IllegalStateException if this service is already connected
 98    */
 99  0 public void connect(String host, int port, String user, String password) throws MessagingException {
 100   
 101  0 if (isConnected()) {
 102  0 throw new IllegalStateException("Already connected");
 103    }
 104   
 105    // before we try to connect, we need to derive values for some parameters that may not have
 106    // been explicitly specified. For example, the normal connect() method leaves us to derive all
 107    // of these from other sources. Some of the values are derived from our URLName value, others
 108    // from session parameters. We need to go through all of these to develop a set of values we
 109    // can connect with.
 110   
 111    // this is the protocol we're connecting with. We use this largely to derive configured values from
 112    // session properties.
 113  0 String protocol = null;
 114   
 115    // if we're working with the URL form, then we can retrieve the protocol from the URL.
 116  0 if (url != null) {
 117  0 protocol = url.getProtocol();
 118    }
 119   
 120    // now try to derive values for any of the arguments we've been given as defaults
 121  0 if (host == null) {
 122    // first choice is from the url, if we have
 123  0 if (url != null) {
 124  0 host = url.getHost();
 125    // it is possible that this could return null (rare). If it does, try to get a
 126    // value from a protocol specific session variable.
 127  0 if (host == null) {
 128  0 host = session.getProperty("mail." + protocol + ".host");
 129    }
 130    }
 131    // this may still be null...get the global mail property
 132  0 if (host == null) {
 133  0 host = session.getProperty("mail.host");
 134    }
 135    }
 136   
 137    // ok, go after userid information next.
 138  0 if (user == null) {
 139    // first choice is from the url, if we have
 140  0 if (url != null) {
 141  0 user = url.getUsername();
 142    // make sure we get the password from the url, if we can.
 143  0 if (password == null) {
 144  0 password = url.getPassword();
 145    }
 146    // user still null? We have several levels of properties to try yet
 147  0 if (user == null) {
 148  0 user = session.getProperty("mail." + protocol + ".user");
 149    }
 150    }
 151   
 152    // this may still be null...get the global mail property
 153  0 if (user == null) {
 154  0 user = session.getProperty("mail.user");
 155    }
 156   
 157    // finally, we try getting the system defined user name
 158  0 try {
 159  0 user = System.getProperty("user.name");
 160    } catch (SecurityException e) {
 161    // we ignore this, and just us a null username.
 162    }
 163    }
 164    // if we have an explicitly given user name, we need to see if this matches the url one and
 165    // grab the password from there.
 166    else {
 167  0 if (url != null && user.equals(url.getUsername())) {
 168  0 password = url.getPassword();
 169    }
 170    }
 171   
 172    // we need to update the URLName associated with this connection once we have all of the information,
 173    // which means we also need to propogate the file portion of the URLName if we have this form when
 174    // we start.
 175  0 String file = null;
 176  0 if (url != null) {
 177  0 file = url.getFile();
 178    }
 179   
 180    // see if we have cached security information to use. If this is not cached, we'll save it
 181    // after we successfully connect.
 182  0 boolean cachePassword = false;
 183   
 184   
 185    // still have a null password to this point, and using a url form?
 186  0 if (password == null && url != null) {
 187    // construct a new URL, filling in any pieces that may have been explicitly specified.
 188  0 setURLName(new URLName(protocol, host, port, file, user, password));
 189    // now see if we have a saved password from a previous request.
 190  0 PasswordAuthentication cachedPassword = session.getPasswordAuthentication(getURLName());
 191   
 192    // if we found a saved one, see if we need to get any the pieces from here.
 193  0 if (cachedPassword != null) {
 194    // not even a resolved userid? Then use both bits.
 195  0 if (user == null) {
 196  0 user = cachedPassword.getUserName();
 197  0 password = cachedPassword.getPassword();
 198    }
 199    // our user name must match the cached name to be valid.
 200  0 else if (user.equals(cachedPassword.getUserName())) {
 201  0 password = cachedPassword.getPassword();
 202    }
 203    }
 204    else
 205    {
 206    // nothing found in the cache, so we need to save this if we can connect successfully.
 207  0 cachePassword = true;
 208    }
 209    }
 210   
 211    // we've done our best up to this point to obtain all of the information needed to make the
 212    // connection. Now we pass this off to the protocol handler to see if it works. If we get a
 213    // connection failure, we may need to prompt for a password before continuing.
 214  0 try {
 215  0 connected = protocolConnect(host, port, user, password);
 216    }
 217    catch (AuthenticationFailedException e) {
 218    }
 219   
 220  0 if (!connected) {
 221  0 InetAddress ipAddress = null;
 222   
 223  0 try {
 224  0 ipAddress = InetAddress.getByName(host);
 225    } catch (UnknownHostException e) {
 226    }
 227   
 228    // now ask the session to try prompting for a password.
 229  0 PasswordAuthentication promptPassword = session.requestPasswordAuthentication(ipAddress, port, protocol, null, user);
 230   
 231    // if we were able to obtain new information from the session, then try again using the
 232    // provided information .
 233  0 if (promptPassword != null) {
 234  0 user = promptPassword.getUserName();
 235  0 password = promptPassword.getPassword();
 236    }
 237   
 238  0 connected = protocolConnect(host, port, user, password);
 239    }
 240   
 241   
 242    // if we're still not connected, then this is an exception.
 243  0 if (!connected) {
 244  0 throw new AuthenticationFailedException();
 245    }
 246   
 247    // the URL name needs to reflect the most recent information.
 248  0 setURLName(new URLName(protocol, host, port, file, user, password));
 249   
 250    // we need to update the global password cache with this information.
 251  0 if (cachePassword) {
 252  0 session.setPasswordAuthentication(getURLName(), new PasswordAuthentication(user, password));
 253    }
 254   
 255    // we're now connected....broadcast this to any interested parties.
 256  0 setConnected(connected);
 257  0 notifyConnectionListeners(ConnectionEvent.OPENED);
 258    }
 259   
 260    /**
 261    * Attempt the protocol-specific connection; subclasses should override this to establish
 262    * a connection in the appropriate manner.
 263    *
 264    * This method should return true if the connection was established.
 265    * It may return false to cause the {@link #connect(String, int, String, String)} method to
 266    * reattempt the connection after trying to obtain user and password information from the user.
 267    * Alternatively it may throw a AuthenticatedFailedException to abandon the conection attempt.
 268    *
 269    * @param host
 270    * @param port
 271    * @param user
 272    * @param password
 273    * @return
 274    * @throws AuthenticationFailedException if authentication fails
 275    * @throws MessagingException for other failures
 276    */
 277  0 protected boolean protocolConnect(String host, int port, String user, String password) throws MessagingException {
 278  0 return false;
 279    }
 280   
 281    /**
 282    * Check if this service is currently connected.
 283    * The default implementation simply returns the value of a private boolean field;
 284    * subclasses may wish to override this method to verify the physical connection.
 285    *
 286    * @return true if this service is connected
 287    */
 288  0 public boolean isConnected() {
 289  0 return connected;
 290    }
 291   
 292    /**
 293    * Notification to subclasses that the connection state has changed.
 294    * This method is called by the connect() and close() methods to indicate state change;
 295    * subclasses should also call this method if the connection is automatically closed
 296    * for some reason.
 297    *
 298    * @param connected the connection state
 299    */
 300  0 protected void setConnected(boolean connected) {
 301  0 this.connected = connected;
 302    }
 303   
 304    /**
 305    * Close this service and terminate its physical connection.
 306    * The default implementation simply calls setConnected(false) and then
 307    * sends a CLOSED event to all registered ConnectionListeners.
 308    * Subclasses overriding this method should still ensure it is closed; they should
 309    * also ensure that it is called if the connection is closed automatically, for
 310    * for example in a finalizer.
 311    *
 312    *@throws MessagingException if there were errors closing; the connection is still closed
 313    */
 314  0 public void close() throws MessagingException {
 315  0 setConnected(false);
 316  0 notifyConnectionListeners(ConnectionEvent.CLOSED);
 317    }
 318   
 319    /**
 320    * Return a copy of the URLName representing this service with the password and file information removed.
 321    *
 322    * @return the URLName for this service
 323    */
 324  0 public URLName getURLName() {
 325   
 326  0 return url == null ? null : new URLName(url.getProtocol(), url.getHost(), url.getPort(), null, url.getUsername(), null);
 327    }
 328   
 329    /**
 330    * Set the url field.
 331    * @param url the new value
 332    */
 333  0 protected void setURLName(URLName url) {
 334  0 this.url = url;
 335    }
 336   
 337  0 public void addConnectionListener(ConnectionListener listener) {
 338  0 connectionListeners.add(listener);
 339    }
 340   
 341  0 public void removeConnectionListener(ConnectionListener listener) {
 342  0 connectionListeners.remove(listener);
 343    }
 344   
 345  0 protected void notifyConnectionListeners(int type) {
 346  0 queue.queueEvent(new ConnectionEvent(this, type), connectionListeners);
 347    }
 348   
 349  0 public String toString() {
 350  0 return url == null ? super.toString() : url.toString();
 351    }
 352   
 353  0 protected void queueEvent(MailEvent event, Vector listeners) {
 354  0 queue.queueEvent(event, listeners);
 355    }
 356   
 357  9 protected void finalize() throws Throwable {
 358  9 queue.stop();
 359  9 connectionListeners.clear();
 360  9 super.finalize();
 361    }
 362   
 363   
 364    /**
 365    * Package scope utility method to allow Message instances
 366    * access to the Service's session.
 367    *
 368    * @return The Session the service is associated with.
 369    */
 370  3 Session getSession() {
 371  3 return session;
 372    }
 373    }