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    }