|
|||||||||||||||||||
| Source file | Conditionals | Statements | Methods | TOTAL | |||||||||||||||
| Service.java | 0% | 9% | 17.6% | 7.1% |
|
||||||||||||||
| 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 | } |
|
||||||||||