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