001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019
020 package javax.mail;
021
022 import java.io.BufferedReader;
023 import java.io.File;
024 import java.io.FileInputStream;
025 import java.io.IOException;
026 import java.io.InputStream;
027 import java.io.InputStreamReader;
028 import java.io.PrintStream;
029 import java.lang.reflect.Constructor;
030 import java.lang.reflect.InvocationTargetException;
031 import java.net.InetAddress;
032 import java.net.URL;
033 import java.util.ArrayList;
034 import java.util.Collection;
035 import java.util.Enumeration;
036 import java.util.HashMap;
037 import java.util.List;
038 import java.util.Map;
039 import java.util.Properties;
040 import java.util.StringTokenizer;
041 import java.util.WeakHashMap;
042
043 import org.apache.geronimo.osgi.locator.ProviderLocator;
044 import org.apache.geronimo.mail.MailProviderRegistry;
045
046 /**
047 * OK, so we have a final class in the API with a heck of a lot of implementation required...
048 * let's try and figure out what it is meant to do.
049 * <p/>
050 * It is supposed to collect together properties and defaults so that they can be
051 * shared by multiple applications on a desktop; with process isolation and no
052 * real concept of shared memory, this seems challenging. These properties and
053 * defaults rely on system properties, making management in a app server harder,
054 * and on resources loaded from "mail.jar" which may lead to skew between
055 * differnet independent implementations of this API.
056 *
057 * @version $Rev: 924365 $ $Date: 2010-03-17 12:52:03 -0400 (Wed, 17 Mar 2010) $
058 */
059 public final class Session {
060 private static final Class[] PARAM_TYPES = {Session.class, URLName.class};
061 private static final WeakHashMap addressMapsByClassLoader = new WeakHashMap();
062 private static Session DEFAULT_SESSION;
063
064 private Map passwordAuthentications = new HashMap();
065
066 private final Properties properties;
067 private final Authenticator authenticator;
068 private boolean debug;
069 private PrintStream debugOut = System.out;
070
071 private static final WeakHashMap providersByClassLoader = new WeakHashMap();
072
073 /**
074 * No public constrcutor allowed.
075 */
076 private Session(Properties properties, Authenticator authenticator) {
077 this.properties = properties;
078 this.authenticator = authenticator;
079 debug = Boolean.valueOf(properties.getProperty("mail.debug")).booleanValue();
080 }
081
082 /**
083 * Create a new session initialized with the supplied properties which uses the supplied authenticator.
084 * Clients should ensure the properties listed in Appendix A of the JavaMail specification are
085 * set as the defaults are unlikey to work in most scenarios; particular attention should be given
086 * to:
087 * <ul>
088 * <li>mail.store.protocol</li>
089 * <li>mail.transport.protocol</li>
090 * <li>mail.host</li>
091 * <li>mail.user</li>
092 * <li>mail.from</li>
093 * </ul>
094 *
095 * @param properties the session properties
096 * @param authenticator an authenticator for callbacks to the user
097 * @return a new session
098 */
099 public static Session getInstance(Properties properties, Authenticator authenticator) {
100 // if we have a properties bundle, we need a copy of the provided one
101 if (properties != null) {
102 properties = (Properties)properties.clone();
103 }
104
105 return new Session(properties, authenticator);
106 }
107
108 /**
109 * Create a new session initialized with the supplied properties with no authenticator.
110 *
111 * @param properties the session properties
112 * @return a new session
113 * @see #getInstance(java.util.Properties, Authenticator)
114 */
115 public static Session getInstance(Properties properties) {
116 return getInstance(properties, null);
117 }
118
119 /**
120 * Get the "default" instance assuming no authenticator is required.
121 *
122 * @param properties the session properties
123 * @return if "default" session
124 * @throws SecurityException if the does not have permission to access the default session
125 */
126 public synchronized static Session getDefaultInstance(Properties properties) {
127 return getDefaultInstance(properties, null);
128 }
129
130 /**
131 * Get the "default" session.
132 * If there is not current "default", a new Session is created and installed as the default.
133 *
134 * @param properties
135 * @param authenticator
136 * @return if "default" session
137 * @throws SecurityException if the does not have permission to access the default session
138 */
139 public synchronized static Session getDefaultInstance(Properties properties, Authenticator authenticator) {
140 if (DEFAULT_SESSION == null) {
141 DEFAULT_SESSION = getInstance(properties, authenticator);
142 } else {
143 if (authenticator != DEFAULT_SESSION.authenticator) {
144 if (authenticator == null || DEFAULT_SESSION.authenticator == null || authenticator.getClass().getClassLoader() != DEFAULT_SESSION.authenticator.getClass().getClassLoader()) {
145 throw new SecurityException();
146 }
147 }
148 // todo we should check with the SecurityManager here as well
149 }
150 return DEFAULT_SESSION;
151 }
152
153 /**
154 * Enable debugging for this session.
155 * Debugging can also be enabled by setting the "mail.debug" property to true when
156 * the session is being created.
157 *
158 * @param debug the debug setting
159 */
160 public void setDebug(boolean debug) {
161 this.debug = debug;
162 }
163
164 /**
165 * Get the debug setting for this session.
166 *
167 * @return the debug setting
168 */
169 public boolean getDebug() {
170 return debug;
171 }
172
173 /**
174 * Set the output stream where debug information should be sent.
175 * If set to null, System.out will be used.
176 *
177 * @param out the stream to write debug information to
178 */
179 public void setDebugOut(PrintStream out) {
180 debugOut = out == null ? System.out : out;
181 }
182
183 /**
184 * Return the debug output stream.
185 *
186 * @return the debug output stream
187 */
188 public PrintStream getDebugOut() {
189 return debugOut;
190 }
191
192 /**
193 * Return the list of providers available to this application.
194 * This method searches for providers that are defined in the javamail.providers
195 * and javamail.default.providers resources available through the current context
196 * classloader, or if that is not available, the classloader that loaded this class.
197 * <p/>
198 * As searching for providers is potentially expensive, this implementation maintains
199 * a WeakHashMap of providers indexed by ClassLoader.
200 *
201 * @return an array of providers
202 */
203 public Provider[] getProviders() {
204 ProviderInfo info = getProviderInfo();
205 return (Provider[]) info.all.toArray(new Provider[info.all.size()]);
206 }
207
208 /**
209 * Return the provider for a specific protocol.
210 * This implementation initially looks in the Session properties for an property with the name
211 * "mail.<protocol>.class"; if found it attempts to create an instance of the class named in that
212 * property throwing a NoSuchProviderException if the class cannot be loaded.
213 * If this property is not found, it searches the providers returned by {@link #getProviders()}
214 * for a entry for the specified protocol.
215 *
216 * @param protocol the protocol to get a provider for
217 * @return a provider for that protocol
218 * @throws NoSuchProviderException
219 */
220 public Provider getProvider(String protocol) throws NoSuchProviderException {
221 ProviderInfo info = getProviderInfo();
222 Provider provider = null;
223 String providerName = properties.getProperty("mail." + protocol + ".class");
224 if (providerName != null) {
225 provider = (Provider) info.byClassName.get(providerName);
226 if (debug) {
227 writeDebug("DEBUG: new provider loaded: " + provider.toString());
228 }
229 }
230
231 // if not able to locate this by class name, just grab a registered protocol.
232 if (provider == null) {
233 provider = (Provider) info.byProtocol.get(protocol);
234 }
235
236 if (provider == null) {
237 throw new NoSuchProviderException("Unable to locate provider for protocol: " + protocol);
238 }
239 if (debug) {
240 writeDebug("DEBUG: getProvider() returning provider " + provider.toString());
241 }
242 return provider;
243 }
244
245 /**
246 * Make the supplied Provider the default for its protocol.
247 *
248 * @param provider the new default Provider
249 * @throws NoSuchProviderException
250 */
251 public void setProvider(Provider provider) throws NoSuchProviderException {
252 ProviderInfo info = getProviderInfo();
253 info.byProtocol.put(provider.getProtocol(), provider);
254 }
255
256 /**
257 * Return a Store for the default protocol defined by the mail.store.protocol property.
258 *
259 * @return the store for the default protocol
260 * @throws NoSuchProviderException
261 */
262 public Store getStore() throws NoSuchProviderException {
263 String protocol = properties.getProperty("mail.store.protocol");
264 if (protocol == null) {
265 throw new NoSuchProviderException("mail.store.protocol property is not set");
266 }
267 return getStore(protocol);
268 }
269
270 /**
271 * Return a Store for the specified protocol.
272 *
273 * @param protocol the protocol to get a Store for
274 * @return a Store
275 * @throws NoSuchProviderException if no provider is defined for the specified protocol
276 */
277 public Store getStore(String protocol) throws NoSuchProviderException {
278 Provider provider = getProvider(protocol);
279 return getStore(provider);
280 }
281
282 /**
283 * Return a Store for the protocol specified in the given URL
284 *
285 * @param url the URL of the Store
286 * @return a Store
287 * @throws NoSuchProviderException if no provider is defined for the specified protocol
288 */
289 public Store getStore(URLName url) throws NoSuchProviderException {
290 return (Store) getService(getProvider(url.getProtocol()), url);
291 }
292
293 /**
294 * Return the Store specified by the given provider.
295 *
296 * @param provider the provider to create from
297 * @return a Store
298 * @throws NoSuchProviderException if there was a problem creating the Store
299 */
300 public Store getStore(Provider provider) throws NoSuchProviderException {
301 if (Provider.Type.STORE != provider.getType()) {
302 throw new NoSuchProviderException("Not a Store Provider: " + provider);
303 }
304 return (Store) getService(provider, null);
305 }
306
307 /**
308 * Return a closed folder for the supplied URLName, or null if it cannot be obtained.
309 * <p/>
310 * The scheme portion of the URL is used to locate the Provider and create the Store;
311 * the returned Store is then used to obtain the folder.
312 *
313 * @param name the location of the folder
314 * @return the requested folder, or null if it is unavailable
315 * @throws NoSuchProviderException if there is no provider
316 * @throws MessagingException if there was a problem accessing the Store
317 */
318 public Folder getFolder(URLName name) throws MessagingException {
319 Store store = getStore(name);
320 return store.getFolder(name);
321 }
322
323 /**
324 * Return a Transport for the default protocol specified by the
325 * <code>mail.transport.protocol</code> property.
326 *
327 * @return a Transport
328 * @throws NoSuchProviderException
329 */
330 public Transport getTransport() throws NoSuchProviderException {
331 String protocol = properties.getProperty("mail.transport.protocol");
332 if (protocol == null) {
333 throw new NoSuchProviderException("mail.transport.protocol property is not set");
334 }
335 return getTransport(protocol);
336 }
337
338 /**
339 * Return a Transport for the specified protocol.
340 *
341 * @param protocol the protocol to use
342 * @return a Transport
343 * @throws NoSuchProviderException
344 */
345 public Transport getTransport(String protocol) throws NoSuchProviderException {
346 Provider provider = getProvider(protocol);
347 return getTransport(provider);
348 }
349
350 /**
351 * Return a transport for the protocol specified in the URL.
352 *
353 * @param name the URL whose scheme specifies the protocol
354 * @return a Transport
355 * @throws NoSuchProviderException
356 */
357 public Transport getTransport(URLName name) throws NoSuchProviderException {
358 return (Transport) getService(getProvider(name.getProtocol()), name);
359 }
360
361 /**
362 * Return a transport for the protocol associated with the type of this address.
363 *
364 * @param address the address we are trying to deliver to
365 * @return a Transport
366 * @throws NoSuchProviderException
367 */
368 public Transport getTransport(Address address) throws NoSuchProviderException {
369 String type = address.getType();
370 // load the address map from the resource files.
371 Map addressMap = getAddressMap();
372 String protocolName = (String)addressMap.get(type);
373 if (protocolName == null) {
374 throw new NoSuchProviderException("No provider for address type " + type);
375 }
376 return getTransport(protocolName);
377 }
378
379 /**
380 * Return the Transport specified by a Provider
381 *
382 * @param provider the defining Provider
383 * @return a Transport
384 * @throws NoSuchProviderException
385 */
386 public Transport getTransport(Provider provider) throws NoSuchProviderException {
387 return (Transport) getService(provider, null);
388 }
389
390 /**
391 * Set the password authentication associated with a URL.
392 *
393 * @param name the url
394 * @param authenticator the authenticator
395 */
396 public void setPasswordAuthentication(URLName name, PasswordAuthentication authenticator) {
397 if (authenticator == null) {
398 passwordAuthentications.remove(name);
399 } else {
400 passwordAuthentications.put(name, authenticator);
401 }
402 }
403
404 /**
405 * Get the password authentication associated with a URL
406 *
407 * @param name the URL
408 * @return any authenticator for that url, or null if none
409 */
410 public PasswordAuthentication getPasswordAuthentication(URLName name) {
411 return (PasswordAuthentication) passwordAuthentications.get(name);
412 }
413
414 /**
415 * Call back to the application supplied authenticator to get the needed username add password.
416 *
417 * @param host the host we are trying to connect to, may be null
418 * @param port the port on that host
419 * @param protocol the protocol trying to be used
420 * @param prompt a String to show as part of the prompt, may be null
421 * @param defaultUserName the default username, may be null
422 * @return the authentication information collected by the authenticator; may be null
423 */
424 public PasswordAuthentication requestPasswordAuthentication(InetAddress host, int port, String protocol, String prompt, String defaultUserName) {
425 if (authenticator == null) {
426 return null;
427 }
428 return authenticator.authenticate(host, port, protocol, prompt, defaultUserName);
429 }
430
431 /**
432 * Return the properties object for this Session; this is a live collection.
433 *
434 * @return the properties for the Session
435 */
436 public Properties getProperties() {
437 return properties;
438 }
439
440 /**
441 * Return the specified property.
442 *
443 * @param property the property to get
444 * @return its value, or null if not present
445 */
446 public String getProperty(String property) {
447 return getProperties().getProperty(property);
448 }
449
450
451 /**
452 * Add a provider to the Session managed provider list.
453 *
454 * @param provider The new provider to add.
455 */
456 public synchronized void addProvider(Provider provider) {
457 ProviderInfo info = getProviderInfo();
458 info.addProvider(provider);
459 }
460
461
462
463 /**
464 * Add a mapping between an address type and a protocol used
465 * to process that address type.
466 *
467 * @param addressType
468 * The address type identifier.
469 * @param protocol The protocol name mapping.
470 */
471 public void setProtocolForAddress(String addressType, String protocol) {
472 Map addressMap = getAddressMap();
473
474 // no protocol specified is a removal
475 if (protocol == null) {
476 addressMap.remove(addressType);
477 }
478 else {
479 addressMap.put(addressType, protocol);
480 }
481 }
482
483
484 private Service getService(Provider provider, URLName name) throws NoSuchProviderException {
485 try {
486 if (name == null) {
487 name = new URLName(provider.getProtocol(), null, -1, null, null, null);
488 }
489 ClassLoader cl = getClassLoader();
490 Class clazz = null;
491 try {
492 clazz = ProviderLocator.loadClass(provider.getClassName(), this.getClass(), cl);
493 } catch (ClassNotFoundException e) {
494 throw (NoSuchProviderException) new NoSuchProviderException("Unable to load class for provider: " + provider).initCause(e);
495 }
496 Constructor ctr = clazz.getConstructor(PARAM_TYPES);
497 return(Service) ctr.newInstance(new Object[]{this, name});
498 } catch (NoSuchMethodException e) {
499 throw (NoSuchProviderException) new NoSuchProviderException("Provider class does not have a constructor(Session, URLName): " + provider).initCause(e);
500 } catch (InstantiationException e) {
501 throw (NoSuchProviderException) new NoSuchProviderException("Unable to instantiate provider class: " + provider).initCause(e);
502 } catch (IllegalAccessException e) {
503 throw (NoSuchProviderException) new NoSuchProviderException("Unable to instantiate provider class: " + provider).initCause(e);
504 } catch (InvocationTargetException e) {
505 throw (NoSuchProviderException) new NoSuchProviderException("Exception from constructor of provider class: " + provider).initCause(e.getCause());
506 }
507 }
508
509 private ProviderInfo getProviderInfo() {
510 ClassLoader cl = getClassLoader();
511 synchronized (providersByClassLoader) {
512 ProviderInfo info = (ProviderInfo) providersByClassLoader.get(cl);
513 if (info == null) {
514 info = loadProviders(cl);
515 }
516 return info;
517 }
518 }
519
520 private Map getAddressMap() {
521 ClassLoader cl = getClassLoader();
522 Map addressMap = (Map)addressMapsByClassLoader.get(cl);
523 if (addressMap == null) {
524 addressMap = loadAddressMap(cl);
525 }
526 return addressMap;
527 }
528
529
530 /**
531 * Resolve a class loader used to resolve context resources. The
532 * class loader used is either a current thread context class
533 * loader (if set), the class loader used to load an authenticator
534 * we've been initialized with, or the class loader used to load
535 * this class instance (which may be a subclass of Session).
536 *
537 * @return The class loader used to load resources.
538 */
539 private ClassLoader getClassLoader() {
540 ClassLoader cl = Thread.currentThread().getContextClassLoader();
541 if (cl == null) {
542 if (authenticator != null) {
543 cl = authenticator.getClass().getClassLoader();
544 }
545 else {
546 cl = this.getClass().getClassLoader();
547 }
548 }
549 return cl;
550 }
551
552 private ProviderInfo loadProviders(ClassLoader cl) {
553 // we create a merged map from reading all of the potential address map entries. The locations
554 // searched are:
555 // 1. java.home/lib/javamail.address.map
556 // 2. META-INF/javamail.address.map
557 // 3. META-INF/javamail.default.address.map
558 //
559 ProviderInfo info = new ProviderInfo();
560
561 // NOTE: Unlike the addressMap, we process these in the defined order. The loading routine
562 // will not overwrite entries if they already exist in the map.
563
564 try {
565 File file = new File(System.getProperty("java.home"), "lib/javamail.providers");
566 InputStream is = new FileInputStream(file);
567 try {
568 loadProviders(info, is);
569 if (debug) {
570 writeDebug("Loaded lib/javamail.providers from " + file.toString());
571 }
572 } finally{
573 is.close();
574 }
575 } catch (SecurityException e) {
576 // ignore
577 } catch (IOException e) {
578 // ignore
579 }
580
581 try {
582 Enumeration e = cl.getResources("META-INF/javamail.providers");
583 while (e.hasMoreElements()) {
584 URL url = (URL) e.nextElement();
585 if (debug) {
586 writeDebug("Loading META-INF/javamail.providers from " + url.toString());
587 }
588 InputStream is = url.openStream();
589 try {
590 loadProviders(info, is);
591 } finally{
592 is.close();
593 }
594 }
595 } catch (SecurityException e) {
596 // ignore
597 } catch (IOException e) {
598 // ignore
599 }
600
601 // we could be running in an OSGi environment, so there might be some globally defined
602 // providers
603 try {
604 Collection<URL> l = MailProviderRegistry.getProviders();
605 for (URL url : l) {
606 if (debug) {
607 writeDebug("Loading META-INF/javamail.providers from " + url.toString());
608 }
609 InputStream is = url.openStream();
610 try {
611 loadProviders(info, is);
612 } finally{
613 is.close();
614 }
615 }
616 } catch (SecurityException e) {
617 // ignore
618 } catch (IOException e) {
619 // ignore
620 }
621
622 try {
623 Enumeration e = cl.getResources("META-INF/javamail.default.providers");
624 while (e.hasMoreElements()) {
625 URL url = (URL) e.nextElement();
626 if (debug) {
627 writeDebug("Loading javamail.default.providers from " + url.toString());
628 }
629
630 InputStream is = url.openStream();
631 try {
632 loadProviders(info, is);
633 } finally{
634 is.close();
635 }
636 }
637 } catch (SecurityException e) {
638 // ignore
639 } catch (IOException e) {
640 // ignore
641 }
642
643 // we could be running in an OSGi environment, so there might be some globally defined
644 // providers
645 try {
646 Collection<URL> l = MailProviderRegistry.getDefaultProviders();
647 for (URL url : l) {
648 if (debug) {
649 writeDebug("Loading META-INF/javamail.providers from " + url.toString());
650 }
651 InputStream is = url.openStream();
652 try {
653 loadProviders(info, is);
654 } finally{
655 is.close();
656 }
657 }
658 } catch (SecurityException e) {
659 // ignore
660 } catch (IOException e) {
661 // ignore
662 }
663
664 // make sure this is added to the global map.
665 providersByClassLoader.put(cl, info);
666
667 return info;
668 }
669
670 private void loadProviders(ProviderInfo info, InputStream is) throws IOException {
671 BufferedReader reader = new BufferedReader(new InputStreamReader(is));
672 String line;
673 while ((line = reader.readLine()) != null) {
674 // Lines beginning with "#" are just comments.
675 if (line.startsWith("#")) {
676 continue;
677 }
678
679 StringTokenizer tok = new StringTokenizer(line, ";");
680 String protocol = null;
681 Provider.Type type = null;
682 String className = null;
683 String vendor = null;
684 String version = null;
685 while (tok.hasMoreTokens()) {
686 String property = tok.nextToken();
687 int index = property.indexOf('=');
688 if (index == -1) {
689 continue;
690 }
691 String key = property.substring(0, index).trim().toLowerCase();
692 String value = property.substring(index+1).trim();
693 if (protocol == null && "protocol".equals(key)) {
694 protocol = value;
695 } else if (type == null && "type".equals(key)) {
696 if ("store".equals(value)) {
697 type = Provider.Type.STORE;
698 } else if ("transport".equals(value)) {
699 type = Provider.Type.TRANSPORT;
700 }
701 } else if (className == null && "class".equals(key)) {
702 className = value;
703 } else if ("vendor".equals(key)) {
704 vendor = value;
705 } else if ("version".equals(key)) {
706 version = value;
707 }
708 }
709 if (protocol == null || type == null || className == null) {
710 //todo should we log a warning?
711 continue;
712 }
713
714 if (debug) {
715 writeDebug("DEBUG: loading new provider protocol=" + protocol + ", className=" + className + ", vendor=" + vendor + ", version=" + version);
716 }
717 Provider provider = new Provider(type, protocol, className, vendor, version);
718 // add to the info list.
719 info.addProvider(provider);
720 }
721 }
722
723 /**
724 * Load up an address map associated with a using class loader
725 * instance.
726 *
727 * @param cl The class loader used to resolve the address map.
728 *
729 * @return A map containing the entries associated with this classloader
730 * instance.
731 */
732 private static Map loadAddressMap(ClassLoader cl) {
733 // we create a merged map from reading all of the potential address map entries. The locations
734 // searched are:
735 // 1. java.home/lib/javamail.address.map
736 // 2. META-INF/javamail.address.map
737 // 3. META-INF/javamail.default.address.map
738 //
739 // if all of the above searches fail, we just set up some "default" defaults.
740
741 // the format of the address.map file is defined as a property file. We can cheat and
742 // just use Properties.load() to read in the files.
743 Properties addressMap = new Properties();
744
745 // add this to the tracking map.
746 addressMapsByClassLoader.put(cl, addressMap);
747
748 // NOTE: We are reading these resources in reverse order of what's cited above. This allows
749 // user defined entries to overwrite default entries if there are similarly named items.
750
751 try {
752 Enumeration e = cl.getResources("META-INF/javamail.default.address.map");
753 while (e.hasMoreElements()) {
754 URL url = (URL) e.nextElement();
755 InputStream is = url.openStream();
756 try {
757 // load as a property file
758 addressMap.load(is);
759 } finally{
760 is.close();
761 }
762 }
763 } catch (SecurityException e) {
764 // ignore
765 } catch (IOException e) {
766 // ignore
767 }
768
769
770 try {
771 Enumeration e = cl.getResources("META-INF/javamail.address.map");
772 while (e.hasMoreElements()) {
773 URL url = (URL) e.nextElement();
774 InputStream is = url.openStream();
775 try {
776 // load as a property file
777 addressMap.load(is);
778 } finally{
779 is.close();
780 }
781 }
782 } catch (SecurityException e) {
783 // ignore
784 } catch (IOException e) {
785 // ignore
786 }
787
788
789 try {
790 File file = new File(System.getProperty("java.home"), "lib/javamail.address.map");
791 InputStream is = new FileInputStream(file);
792 try {
793 // load as a property file
794 addressMap.load(is);
795 } finally{
796 is.close();
797 }
798 } catch (SecurityException e) {
799 // ignore
800 } catch (IOException e) {
801 // ignore
802 }
803
804 try {
805 Enumeration e = cl.getResources("META-INF/javamail.address.map");
806 while (e.hasMoreElements()) {
807 URL url = (URL) e.nextElement();
808 InputStream is = url.openStream();
809 try {
810 // load as a property file
811 addressMap.load(is);
812 } finally{
813 is.close();
814 }
815 }
816 } catch (SecurityException e) {
817 // ignore
818 } catch (IOException e) {
819 // ignore
820 }
821
822
823 // if unable to load anything, at least create the MimeMessage-smtp protocol mapping.
824 if (addressMap.isEmpty()) {
825 addressMap.put("rfc822", "smtp");
826 }
827
828 return addressMap;
829 }
830
831 /**
832 * Private convenience routine for debug output.
833 *
834 * @param msg The message to write out to the debug stream.
835 */
836 private void writeDebug(String msg) {
837 debugOut.println(msg);
838 }
839
840
841 private static class ProviderInfo {
842 private final Map byClassName = new HashMap();
843 private final Map byProtocol = new HashMap();
844 private final List all = new ArrayList();
845
846 public void addProvider(Provider provider) {
847 String className = provider.getClassName();
848
849 if (!byClassName.containsKey(className)) {
850 byClassName.put(className, provider);
851 }
852
853 String protocol = provider.getProtocol();
854 if (!byProtocol.containsKey(protocol)) {
855 byProtocol.put(protocol, provider);
856 }
857 all.add(provider);
858 }
859 }
860 }