|
|||||||||||||||||||
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 | } |
|