All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jakarta.mail.Session Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 1997, 2023 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

package jakarta.mail;

import jakarta.mail.util.LineInputStream;
import jakarta.mail.util.StreamProvider;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.ServiceLoader;
import java.util.StringTokenizer;
import java.util.concurrent.Executor;
import java.util.logging.Level;

/**
 * The Session class represents a mail session and is not subclassed.
 * It collects together properties and defaults used by the mail API's.
 * A single default session can be shared by multiple applications on the
 * desktop.  Unshared sessions can also be created. 

* * The Session class provides access to the protocol providers that * implement the Store, Transport, and related * classes. The protocol providers are configured using the following files: *

    *
  • javamail.providers and * javamail.default.providers
  • *
  • javamail.address.map and * javamail.default.address.map
  • *
*

* Each javamail.X resource file is searched for using * three methods in the following order: *

    *
  1. java.home/conf/javamail.X
  2. *
  3. META-INF/javamail.X
  4. *
  5. META-INF/javamail.default.X
  6. *
*

* (Where java.home is the value of the "java.home" System property * and conf is the directory named "conf" if it exists, * otherwise the directory named "lib"; the "conf" directory was * introduced in JDK 1.9.) *

* The first method allows the user to include their own version of the * resource file by placing it in the conf directory where the * java.home property points. The second method allows an * application that uses the Jakarta Mail APIs to include their own resource * files in their application's or jar file's META-INF * directory. The javamail.default.X default files * are part of the Jakarta Mail mail.jar file and should not be * supplied by users.

* * File location depends upon how the ClassLoader method * getResource is implemented. Usually, the * getResource method searches through CLASSPATH until it * finds the requested file and then stops.

* * The ordering of entries in the resource files matters. If multiple * entries exist, the first entries take precedence over the later * entries. For example, the first IMAP provider found will be set as the * default IMAP implementation until explicitly changed by the * application. The user- or system-supplied resource files augment, they * do not override, the default files included with the Jakarta Mail APIs. * This means that all entries in all files loaded will be available.

* * javamail.providers and * javamail.default.providers

* * These resource files specify the stores and transports that are * available on the system, allowing an application to "discover" what * store and transport implementations are available. The protocol * implementations are listed one per line. The file format defines four * attributes that describe a protocol implementation. Each attribute is * an "="-separated name-value pair with the name in lowercase. Each * name-value pair is semi-colon (";") separated. The following names * are defined. * *

* * * * * * * * * * * * * * * * * * * * * * * * *
* Attribute Names in Providers Files *
NameDescription
protocolName assigned to protocol. * For example, smtp for Transport.
typeValid entries are store and transport.
classClass name that implements this protocol.
vendorOptional string identifying the vendor.
versionOptional string identifying the version.

* * Here's an example of META-INF/javamail.default.providers * file contents: *

 * protocol=imap; type=store; class=org.eclipse.angus.mail.imap.IMAPStore; vendor=Oracle;
 * protocol=smtp; type=transport; class=org.eclipse.angus.mail.smtp.SMTPTransport; vendor=Oracle;
 * 

* * The current implementation also supports configuring providers using * the Java SE {@link java.util.ServiceLoader ServiceLoader} mechanism. * When creating your own provider, create a {@link Provider} subclass, * for example: *

 * package com.example;
 *
 * import jakarta.mail.Provider;
 *
 * public class MyProvider extends Provider {
 *     public MyProvider() {
 *         super(Provider.Type.STORE, "myprot", MyStore.class.getName(),
 *             "Example", null);
 *     }
 * }
 * 
* Then include a file named META-INF/services/jakarta.mail.Provider * in your jar file that lists the name of your Provider class: *
 * com.example.MyProvider
 * 
*

* * javamail.address.map and * javamail.default.address.map

* * These resource files map transport address types to the transport * protocol. The getType method of * jakarta.mail.Address returns the address type. The * javamail.address.map file maps the transport type to the * protocol. The file format is a series of name-value pairs. Each key * name should correspond to an address type that is currently installed * on the system; there should also be an entry for each * jakarta.mail.Address implementation that is present if it is * to be used. For example, the * jakarta.mail.internet.InternetAddress method * getType returns "rfc822". Each referenced protocol should * be installed on the system. For the case of news, below, * the client should install a Transport provider supporting the nntp * protocol.

* * Here are the typical contents of a javamail.address.map file: *

 * rfc822=smtp
 * news=nntp
 * 
* * @author John Mani * @author Bill Shannon * @author Max Spivak */ public final class Session { // Support legacy @DefaultProvider private static final String DEFAULT_PROVIDER = "org.eclipse.angus.mail.util.DefaultProvider"; private final StreamProvider streamProvider; private final Properties props; private final Authenticator authenticator; private final Hashtable authTable = new Hashtable<>(); private boolean debug = false; private PrintStream out; // debug output stream private MailLogger logger; private final List providers = new ArrayList<>(); private final Map providersByProtocol = new HashMap<>(); private final Map providersByClassName = new HashMap<>(); private final Properties addressMap = new Properties(); // maps type to protocol // the queue of events to be delivered, if mail.event.scope===session private final EventQueue q; // The default session. private static Session defaultSession = null; private static final String confDir; static { String dir = null; try { dir = AccessController.doPrivileged( new PrivilegedAction() { @Override public String run() { String home = System.getProperty("java.home"); String newdir = home + File.separator + "conf"; File conf = new File(newdir); if (conf.exists()) return newdir + File.separator; else return home + File.separator + "lib" + File.separator; } }); } catch (Exception ex) { // ignore any exceptions } confDir = dir; } // Constructor is not public private Session(Properties props, Authenticator authenticator) { this.props = props; this.authenticator = authenticator; this.streamProvider = StreamProvider.provider(); if (Boolean.parseBoolean(props.getProperty("mail.debug"))) debug = true; initLogger(); logger.log(Level.CONFIG, "Jakarta Mail version {0}", Version.version); // get the Class associated with the Authenticator Class cl; if (authenticator != null) { cl = authenticator.getClass(); } else { // Use implementation class, because that class loader has access to jakarta.mail module and implementation resources cl = streamProvider.getClass(); } // load the resources loadProviders(cl); loadAddressMap(cl); q = new EventQueue((Executor) props.get("mail.event.executor")); } /** * Get the stream provider instance of the session. * * @return the stream provider * @since JavaMail 2.1 */ public StreamProvider getStreamProvider() { return streamProvider; } private final synchronized void initLogger() { logger = new MailLogger(this.getClass(), "DEBUG", debug, getDebugOut()); } /** * Get a new Session object. * * @param props Properties object that hold relevant properties.
* It is expected that the client supplies values * for the properties listed in Appendix A of the * Jakarta Mail spec (particularly mail.store.protocol, * mail.transport.protocol, mail.host, mail.user, * and mail.from) as the defaults are unlikely to * work in all cases. * @param authenticator Authenticator object used to call back to * the application when a user name and password is * needed. * @return a new Session object * @see jakarta.mail.Authenticator */ public static Session getInstance(Properties props, Authenticator authenticator) { return new Session(props, authenticator); } /** * Get a new Session object. * * @param props Properties object that hold relevant properties.
* It is expected that the client supplies values * for the properties listed in Appendix A of the * Jakarta Mail spec (particularly mail.store.protocol, * mail.transport.protocol, mail.host, mail.user, * and mail.from) as the defaults are unlikely to * work in all cases. * @return a new Session object * @since JavaMail 1.2 */ public static Session getInstance(Properties props) { return new Session(props, null); } /** * Get the default Session object. If a default has not yet been * setup, a new Session object is created and installed as the * default.

* * Since the default session is potentially available to all * code executing in the same Java virtual machine, and the session * can contain security sensitive information such as user names * and passwords, access to the default session is restricted. * The Authenticator object, which must be created by the caller, * is used indirectly to check access permission. The Authenticator * object passed in when the session is created is compared with * the Authenticator object passed in to subsequent requests to * get the default session. If both objects are the same, or are * from the same ClassLoader, the request is allowed. Otherwise, * it is denied.

* * Note that if the Authenticator object used to create the session * is null, anyone can get the default session by passing in null.

* * Note also that the Properties object is used only the first time * this method is called, when a new Session object is created. * Subsequent calls return the Session object that was created by the * first call, and ignore the passed Properties object. Use the * getInstance method to get a new Session object every * time the method is called.

* * Additional security Permission objects may be used to * control access to the default session.

* * In the current implementation, if a SecurityManager is set, the * caller must have the RuntimePermission("setFactory") * permission. * * @param props Properties object. Used only if a new Session * object is created.
* It is expected that the client supplies values * for the properties listed in Appendix A of the * Jakarta Mail spec (particularly mail.store.protocol, * mail.transport.protocol, mail.host, mail.user, * and mail.from) as the defaults are unlikely to * work in all cases. * @param authenticator Authenticator object. Used only if a * new Session object is created. Otherwise, * it must match the Authenticator used to create * the Session. * @return the default Session object */ public static synchronized Session getDefaultInstance(Properties props, Authenticator authenticator) { if (defaultSession == null) { SecurityManager security = System.getSecurityManager(); if (security != null) security.checkSetFactory(); defaultSession = new Session(props, authenticator); } else { // have to check whether caller is allowed to see default session if (defaultSession.authenticator == authenticator) ; // either same object or both null, either way OK else if (defaultSession.authenticator != null && authenticator != null && defaultSession.authenticator.getClass().getClassLoader() == authenticator.getClass().getClassLoader()) ; // both objects came from the same class loader, OK else // anything else is not allowed throw new SecurityException("Access to default session denied"); } return defaultSession; } /** * Get the default Session object. If a default has not yet been * setup, a new Session object is created and installed as the * default.

* * Note that a default session created with no Authenticator is * available to all code executing in the same Java virtual * machine, and the session can contain security sensitive * information such as user names and passwords. * * @param props Properties object. Used only if a new Session * object is created.
* It is expected that the client supplies values * for the properties listed in Appendix A of the * Jakarta Mail spec (particularly mail.store.protocol, * mail.transport.protocol, mail.host, mail.user, * and mail.from) as the defaults are unlikely to * work in all cases. * @return the default Session object * @since JavaMail 1.2 */ public static Session getDefaultInstance(Properties props) { return getDefaultInstance(props, null); } /** * Set the debug setting for this Session. *

* Since the debug setting can be turned on only after the Session * has been created, to turn on debugging in the Session * constructor, set the property mail.debug in the * Properties object passed in to the constructor to true. The * value of the mail.debug property is used to * initialize the per-Session debugging flag. Subsequent calls to * the setDebug method manipulate the per-Session * debugging flag and have no effect on the mail.debug * property. * * @param debug Debug setting */ public synchronized void setDebug(boolean debug) { this.debug = debug; initLogger(); logger.log(Level.CONFIG, "setDebug: Jakarta Mail version {0}", Version.version); } /** * Get the debug setting for this Session. * * @return current debug setting */ public synchronized boolean getDebug() { return debug; } /** * Set the stream to be used for debugging output for this session. * If out is null, System.out will be used. * Note that debugging output that occurs before any session is created, * as a result of setting the mail.debug system property, * will always be sent to System.out. * * @param out the PrintStream to use for debugging output * @since JavaMail 1.3 */ public synchronized void setDebugOut(PrintStream out) { this.out = out; initLogger(); } /** * Returns the stream to be used for debugging output. If no stream * has been set, System.out is returned. * * @return the PrintStream to use for debugging output * @since JavaMail 1.3 */ public synchronized PrintStream getDebugOut() { if (out == null) return System.out; else return out; } /** * This method returns an array of all the implementations installed * via the javamail.[default.]providers files that can * be loaded using the ClassLoader available to this application. * * @return Array of configured providers */ public synchronized Provider[] getProviders() { Provider[] _providers = new Provider[providers.size()]; providers.toArray(_providers); return _providers; } /** * Returns the default Provider for the protocol * specified. Checks mail.<protocol>.class property * first and if it exists, returns the Provider * associated with this implementation. If it doesn't exist, * returns the Provider that appeared first in the * configuration files. If an implementation for the protocol * isn't found, throws NoSuchProviderException * * @param protocol Configured protocol (i.e. smtp, imap, etc) * @return Currently configured Provider for the specified protocol * @throws NoSuchProviderException If a provider for the given * protocol is not found. */ public synchronized Provider getProvider(String protocol) throws NoSuchProviderException { if (protocol == null || protocol.length() == 0) { throw new NoSuchProviderException("Invalid protocol: null"); } Provider _provider = null; // check if the mail..class property exists String _className = props.getProperty("mail." + protocol + ".class"); if (_className != null) { if (logger.isLoggable(Level.FINE)) { logger.fine("mail." + protocol + ".class property exists and points to " + _className); } _provider = providersByClassName.get(_className); } if (_provider != null) { return _provider; } else { // returning currently default protocol in providersByProtocol _provider = providersByProtocol.get(protocol); } if (_provider == null) { throw new NoSuchProviderException("No provider for " + protocol); } else { if (logger.isLoggable(Level.FINE)) { logger.fine("getProvider() returning " + _provider); } return _provider; } } /** * Set the passed Provider to be the default implementation * for the protocol in Provider.protocol overriding any previous values. * * @param provider Currently configured Provider which will be * set as the default for the protocol * @throws NoSuchProviderException If the provider passed in * is invalid. */ public synchronized void setProvider(Provider provider) throws NoSuchProviderException { if (provider == null) { throw new NoSuchProviderException("Can't set null provider"); } providersByProtocol.put(provider.getProtocol(), provider); providersByClassName.put(provider.getClassName(), provider); props.put("mail." + provider.getProtocol() + ".class", provider.getClassName()); } /** * Get a Store object that implements this user's desired Store * protocol. The mail.store.protocol property specifies the * desired protocol. If an appropriate Store object is not obtained, * NoSuchProviderException is thrown * * @return a Store object * @throws NoSuchProviderException If a provider for the given * protocol is not found. */ public Store getStore() throws NoSuchProviderException { return getStore(getProperty("mail.store.protocol")); } /** * Get a Store object that implements the specified protocol. If an * appropriate Store object cannot be obtained, * NoSuchProviderException is thrown. * * @param protocol the Store protocol * @return a Store object * @throws NoSuchProviderException If a provider for the given * protocol is not found. */ public Store getStore(String protocol) throws NoSuchProviderException { return getStore(new URLName(protocol, null, -1, null, null, null)); } /** * Get a Store object for the given URLName. If the requested Store * object cannot be obtained, NoSuchProviderException is thrown. * * The "scheme" part of the URL string (Refer RFC 1738) is used * to locate the Store protocol. * * @param url URLName that represents the desired Store * @return a closed Store object * @throws NoSuchProviderException If a provider for the given * URLName is not found. * @see #getFolder(URLName) * @see jakarta.mail.URLName */ public Store getStore(URLName url) throws NoSuchProviderException { String protocol = url.getProtocol(); Provider p = getProvider(protocol); return getStore(p, url); } /** * Get an instance of the store specified by Provider. Instantiates * the store and returns it. * * @param provider Store Provider that will be instantiated * @return Instantiated Store * @throws NoSuchProviderException If a provider for the given * Provider is not found. */ public Store getStore(Provider provider) throws NoSuchProviderException { return getStore(provider, null); } /** * Get an instance of the store specified by Provider. If the URLName * is not null, uses it, otherwise creates a new one. Instantiates * the store and returns it. This is a private method used by * getStore(Provider) and getStore(URLName) * * @param provider Store Provider that will be instantiated * @param url URLName used to instantiate the Store * @return Instantiated Store * @throws NoSuchProviderException If a provider for the given * Provider/URLName is not found. */ private Store getStore(Provider provider, URLName url) throws NoSuchProviderException { // make sure we have the correct type of provider if (provider == null || provider.getType() != Provider.Type.STORE) { throw new NoSuchProviderException("invalid provider"); } return getService(provider, url, Store.class); } /** * Get a closed Folder object for the given URLName. If the requested * Folder object cannot be obtained, null is returned.

* * The "scheme" part of the URL string (Refer RFC 1738) is used * to locate the Store protocol. The rest of the URL string (that is, * the "schemepart", as per RFC 1738) is used by that Store * in a protocol dependent manner to locate and instantiate the * appropriate Folder object.

* * Note that RFC 1738 also specifies the syntax for the * "schemepart" for IP-based protocols (IMAP4, POP3, etc.). * Providers of IP-based mail Stores should implement that * syntax for referring to Folders. * * @param url URLName that represents the desired folder * @return Folder * @throws NoSuchProviderException If a provider for the given * URLName is not found. * @throws MessagingException if the Folder could not be * located or created. * @see #getStore(URLName) * @see jakarta.mail.URLName */ public Folder getFolder(URLName url) throws MessagingException { // First get the Store Store store = getStore(url); store.connect(); return store.getFolder(url); } /** * Get a Transport object that implements this user's desired * Transport protcol. The mail.transport.protocol property * specifies the desired protocol. If an appropriate Transport * object cannot be obtained, MessagingException is thrown. * * @return a Transport object * @throws NoSuchProviderException If the provider is not found. */ public Transport getTransport() throws NoSuchProviderException { String prot = getProperty("mail.transport.protocol"); if (prot != null) return getTransport(prot); // if the property isn't set, use the protocol for "rfc822" prot = (String) addressMap.get("rfc822"); if (prot != null) return getTransport(prot); return getTransport("smtp"); // if all else fails } /** * Get a Transport object that implements the specified protocol. * If an appropriate Transport object cannot be obtained, null is * returned. * * @param protocol the Transport protocol * @return a Transport object * @throws NoSuchProviderException If provider for the given * protocol is not found. */ public Transport getTransport(String protocol) throws NoSuchProviderException { return getTransport(new URLName(protocol, null, -1, null, null, null)); } /** * Get a Transport object for the given URLName. If the requested * Transport object cannot be obtained, NoSuchProviderException is thrown. * * The "scheme" part of the URL string (Refer RFC 1738) is used * to locate the Transport protocol. * * @param url URLName that represents the desired Transport * @return a closed Transport object * @throws NoSuchProviderException If a provider for the given * URLName is not found. * @see jakarta.mail.URLName */ public Transport getTransport(URLName url) throws NoSuchProviderException { String protocol = url.getProtocol(); Provider p = getProvider(protocol); return getTransport(p, url); } /** * Get an instance of the transport specified in the Provider. Instantiates * the transport and returns it. * * @param provider Transport Provider that will be instantiated * @return Instantiated Transport * @throws NoSuchProviderException If provider for the given * provider is not found. */ public Transport getTransport(Provider provider) throws NoSuchProviderException { return getTransport(provider, null); } /** * Get a Transport object that can transport a Message of the * specified address type. * * @param address an address for which a Transport is needed * @return A Transport object * @throws NoSuchProviderException If provider for the * Address type is not found * @see jakarta.mail.Address */ public Transport getTransport(Address address) throws NoSuchProviderException { String transportProtocol; transportProtocol = getProperty("mail.transport.protocol." + address.getType()); if (transportProtocol != null) return getTransport(transportProtocol); transportProtocol = (String) addressMap.get(address.getType()); if (transportProtocol != null) return getTransport(transportProtocol); throw new NoSuchProviderException("No provider for Address type: " + address.getType()); } /** * Get a Transport object using the given provider and urlname. * * @param provider the provider to use * @param url urlname to use (can be null) * @return A Transport object * @throws NoSuchProviderException If no provider or the provider * was the wrong class. */ private Transport getTransport(Provider provider, URLName url) throws NoSuchProviderException { // make sure we have the correct type of provider if (provider == null || provider.getType() != Provider.Type.TRANSPORT) { throw new NoSuchProviderException("invalid provider"); } return getService(provider, url, Transport.class); } /** * Get a Service object. Needs a provider object, but will * create a URLName if needed. It attempts to instantiate * the correct class. * * @param provider which provider to use * @param url which URLName to use (can be null) * @param type the service type (class) * @throws NoSuchProviderException thrown when the class cannot be * found or when it does not have the correct constructor * (Session, URLName), or if it is not derived from * Service. */ private T getService(Provider provider, URLName url, Class type) throws NoSuchProviderException { // need a provider and url if (provider == null) { throw new NoSuchProviderException("null"); } // create a url if needed if (url == null) { url = new URLName(provider.getProtocol(), null, -1, null, null, null); } // get the ClassLoader associated with the Authenticator Class acl; if (authenticator != null) acl = authenticator.getClass(); else acl = streamProvider.getClass(); Class serviceClass = null; for (ClassLoader l : getClassLoaders(Thread.class, provider.getClass(), acl, streamProvider.getClass(), getClass(), System.class)) { try { //load and verify provider is compatible in this classloader serviceClass = Class.forName(provider.getClassName(), false, l).asSubclass(type); break; } catch (ClassNotFoundException | ClassCastException ex) { // ignore it } } if (serviceClass == null) { // That didn't work, now try the "system" class loader. // (Need both of these because JDK 1.1 class loaders // may not delegate to their parent class loader.) try { serviceClass = Class.forName(provider.getClassName()) .asSubclass(type); } catch (Exception ex) { // Nothing worked, give up. logger.log(Level.FINE, "Exception loading provider", ex); throw new NoSuchProviderException(provider.getProtocol()); } } // construct an instance of the class try { Class[] c = {jakarta.mail.Session.class, jakarta.mail.URLName.class}; Constructor cons = serviceClass.getConstructor(c); Object[] o = {this, url}; return type.cast(cons.newInstance(o)); } catch (Exception ex) { logger.log(Level.FINE, "Exception loading provider", ex); throw new NoSuchProviderException(provider.getProtocol()); } } /** * Save a PasswordAuthentication for this (store or transport) URLName. * If pw is null the entry corresponding to the URLName is removed. *

* This is normally used only by the store or transport implementations * to allow authentication information to be shared among multiple * uses of a session. * * @param url the URLName * @param pw the PasswordAuthentication to save */ public void setPasswordAuthentication(URLName url, PasswordAuthentication pw) { if (pw == null) authTable.remove(url); else authTable.put(url, pw); } /** * Return any saved PasswordAuthentication for this (store or transport) * URLName. Normally used only by store or transport implementations. * * @param url the URLName * @return the PasswordAuthentication corresponding to the URLName */ public PasswordAuthentication getPasswordAuthentication(URLName url) { return authTable.get(url); } /** * Call back to the application to get the needed user name and password. * The application should put up a dialog something like: *

     * Connecting to <protocol> mail service on host <addr>, port <port>.
     * <prompt>
     *
     * User Name: <defaultUserName>
     * Password:
     * 
* * @param addr InetAddress of the host. may be null. * @param port the port on the host * @param protocol protocol scheme (e.g. imap, pop3, etc.) * @param prompt any additional String to show as part of * the prompt; may be null. * @param defaultUserName the default username. may be null. * @return the authentication which was collected by the authenticator; * may be null. */ public PasswordAuthentication requestPasswordAuthentication(InetAddress addr, int port, String protocol, String prompt, String defaultUserName) { if (authenticator != null) { return authenticator.requestPasswordAuthentication( addr, port, protocol, prompt, defaultUserName); } else { return null; } } /** * Returns the Properties object associated with this Session * * @return Properties object */ public Properties getProperties() { return props; } /** * Returns the value of the specified property. Returns null * if this property does not exist. * * @param name the property name * @return String that is the property value */ public String getProperty(String name) { return props.getProperty(name); } static boolean containsDefaultProvider(Provider provider) { Annotation[] annotations = provider.getClass().getDeclaredAnnotations(); for (Annotation annotation : annotations) { if (DEFAULT_PROVIDER.equals(annotation.annotationType().getName())) { return true; } } return false; } /** * Load the protocol providers config files. */ private void loadProviders(Class cl) { StreamLoader loader = new StreamLoader() { @Override public void load(InputStream is) throws IOException { loadProvidersFromStream(is); } }; // load system-wide javamail.providers from the // /{conf,lib} directory try { if (confDir != null) loadFile(confDir + "javamail.providers", loader); } catch (SecurityException ex) { } //Fetch classloader of given class, falling back to others if needed. ClassLoader gcl; ClassLoader[] loaders = getClassLoaders(cl, Thread.class, System.class); if (loaders.length != 0) { gcl = loaders[0]; } else { gcl = getContextClassLoader(); //Fail safe } // next, add all the non-default services ServiceLoader sl = ServiceLoader.load(Provider.class, gcl); for (Provider p : sl) { if (!containsDefaultProvider(p)) addProvider(p); } // + handle Glassfish/OSGi (platform specific default) Iterator iter = lookupUsingHk2ServiceLoader(Provider.class, gcl); while (iter.hasNext()) { Provider p = iter.next(); if (!containsDefaultProvider(p)) addProvider(p); } // load the META-INF/javamail.providers file supplied by an application loadAllResources("META-INF/javamail.providers", cl, loader); // load default META-INF/javamail.default.providers from mail.jar file loadResource("/META-INF/javamail.default.providers", cl, loader, false); // finally, add all the default services sl = ServiceLoader.load(Provider.class, gcl); for (Provider p : sl) { if (containsDefaultProvider(p)) addProvider(p); } // + handle Glassfish/OSGi (platform specific default) iter = lookupUsingHk2ServiceLoader(Provider.class, gcl); while (iter.hasNext()) { Provider p = iter.next(); if (containsDefaultProvider(p)) { addProvider(p); } } /* * If we haven't loaded any providers, fake it. */ if (providers.isEmpty()) { logger.config("failed to load any providers, using defaults"); // failed to load any providers, initialize with our defaults addProvider(new Provider(Provider.Type.STORE, "imap", "org.eclipse.angus.mail.imap.IMAPStore", "Oracle", Version.version)); addProvider(new Provider(Provider.Type.STORE, "imaps", "org.eclipse.angus.mail.imap.IMAPSSLStore", "Oracle", Version.version)); addProvider(new Provider(Provider.Type.STORE, "pop3", "org.eclipse.angus.mail.pop3.POP3Store", "Oracle", Version.version)); addProvider(new Provider(Provider.Type.STORE, "pop3s", "org.eclipse.angus.mail.pop3.POP3SSLStore", "Oracle", Version.version)); addProvider(new Provider(Provider.Type.TRANSPORT, "smtp", "org.eclipse.angus.mail.smtp.SMTPTransport", "Oracle", Version.version)); addProvider(new Provider(Provider.Type.TRANSPORT, "smtps", "org.eclipse.angus.mail.smtp.SMTPSSLTransport", "Oracle", Version.version)); } if (logger.isLoggable(Level.CONFIG)) { // dump the output of the tables for debugging logger.config("Tables of loaded providers"); logger.config("Providers Listed By Class Name: " + providersByClassName); logger.config("Providers Listed By Protocol: " + providersByProtocol); } } private void loadProvidersFromStream(InputStream is) throws IOException { if (is != null) { LineInputStream lis = streamProvider.inputLineStream(is, false); String currLine; // load and process one line at a time using LineInputStream while ((currLine = lis.readLine()) != null) { if (currLine.startsWith("#")) continue; if (currLine.trim().length() == 0) continue; // skip blank line Provider.Type type = null; String protocol = null, className = null; String vendor = null, version = null; // separate line into key-value tuples StringTokenizer tuples = new StringTokenizer(currLine, ";"); while (tuples.hasMoreTokens()) { String currTuple = tuples.nextToken().trim(); // set the value of each attribute based on its key int sep = currTuple.indexOf("="); if (currTuple.startsWith("protocol=")) { protocol = currTuple.substring(sep + 1); } else if (currTuple.startsWith("type=")) { String strType = currTuple.substring(sep + 1); if (strType.equalsIgnoreCase("store")) { type = Provider.Type.STORE; } else if (strType.equalsIgnoreCase("transport")) { type = Provider.Type.TRANSPORT; } } else if (currTuple.startsWith("class=")) { className = currTuple.substring(sep + 1); } else if (currTuple.startsWith("vendor=")) { vendor = currTuple.substring(sep + 1); } else if (currTuple.startsWith("version=")) { version = currTuple.substring(sep + 1); } } // check if a valid Provider; else, continue if (type == null || protocol == null || className == null || protocol.length() == 0 || className.length() == 0) { logger.log(Level.CONFIG, "Bad provider entry: {0}", currLine); continue; } Provider provider = new Provider(type, protocol, className, vendor, version); // add the newly-created Provider to the lookup tables addProvider(provider); } } } /** * Add a provider to the session. * * @param provider the provider to add * @since JavaMail 1.4 */ public synchronized void addProvider(Provider provider) { providers.add(provider); providersByClassName.put(provider.getClassName(), provider); if (!providersByProtocol.containsKey(provider.getProtocol())) providersByProtocol.put(provider.getProtocol(), provider); } // load maps in reverse order of preference so that the preferred // map is loaded last since its entries will override the previous ones private void loadAddressMap(Class cl) { StreamLoader loader = new StreamLoader() { @Override public void load(InputStream is) throws IOException { addressMap.load(is); } }; // load default META-INF/javamail.default.address.map from mail.jar loadResource("/META-INF/javamail.default.address.map", cl, loader, true); // load the META-INF/javamail.address.map file supplied by an app loadAllResources("META-INF/javamail.address.map", cl, loader); // load system-wide javamail.address.map from the // /{conf,lib} directory try { if (confDir != null) loadFile(confDir + "javamail.address.map", loader); } catch (SecurityException ex) { } if (addressMap.isEmpty()) { logger.config("failed to load address map, using defaults"); addressMap.put("rfc822", "smtp"); } } /** * Set the default transport protocol to use for addresses of * the specified type. Normally the default is set by the * javamail.default.address.map or * javamail.address.map files or resources. * * @param addresstype type of address * @param protocol name of protocol * @see #getTransport(Address) * @since JavaMail 1.4 */ public synchronized void setProtocolForAddress(String addresstype, String protocol) { if (protocol == null) addressMap.remove(addresstype); else addressMap.put(addresstype, protocol); } /** * Load from the named file. */ private void loadFile(String name, StreamLoader loader) { InputStream clis = null; try { clis = new BufferedInputStream(new FileInputStream(name)); loader.load(clis); logger.log(Level.CONFIG, "successfully loaded file: {0}", name); } catch (FileNotFoundException fex) { // ignore it } catch (IOException | SecurityException e) { if (logger.isLoggable(Level.CONFIG)) logger.log(Level.CONFIG, "not loading file: " + name, e); } finally { try { if (clis != null) clis.close(); } catch (IOException ex) { } // ignore it } } /** * Load from the named resource. */ private void loadResource(String name, Class cl, StreamLoader loader, boolean expected) { InputStream clis = null; try { clis = getResourceAsStream(cl, name); if (clis != null) { loader.load(clis); logger.log(Level.CONFIG, "successfully loaded resource: {0}", name); } else { if (expected) logger.log(Level.WARNING, "expected resource not found: {0}", name); } } catch (IOException | SecurityException e) { logger.log(Level.CONFIG, "Exception loading resource", e); } finally { try { if (clis != null) clis.close(); } catch (IOException ex) { } // ignore it } } /** * Load all of the named resource. */ private void loadAllResources(String name, Class cl, StreamLoader loader) { boolean anyLoaded = false; try { URL[] urls; ClassLoader cld = null; // First try the "application's" class loader. cld = getContextClassLoader(); if (cld == null) cld = cl.getClassLoader(); if (cld != null) urls = getResources(cld, name); else urls = getSystemResources(name); if (urls != null) { for (int i = 0; i < urls.length; i++) { URL url = urls[i]; InputStream clis = null; logger.log(Level.CONFIG, "URL {0}", url); try { clis = openStream(url); if (clis != null) { loader.load(clis); anyLoaded = true; logger.log(Level.CONFIG, "successfully loaded resource: {0}", url); } else { logger.log(Level.CONFIG, "not loading resource: {0}", url); } } catch (FileNotFoundException fex) { // ignore it } catch (IOException | SecurityException ioex) { logger.log(Level.CONFIG, "Exception loading resource", ioex); } finally { try { if (clis != null) clis.close(); } catch (IOException cex) { } } } } } catch (Exception ex) { logger.log(Level.CONFIG, "Exception loading resource", ex); } // if failed to load anything, fall back to old technique, just in case if (!anyLoaded) { /* logger.config("!anyLoaded"); */ loadResource("/" + name, cl, loader, false); } } /* * Following are security related methods that work on JDK 1.2 or newer. */ static ClassLoader getContextClassLoader() { return AccessController.doPrivileged( new PrivilegedAction() { @Override public ClassLoader run() { ClassLoader cl = null; try { cl = Thread.currentThread().getContextClassLoader(); } catch (SecurityException ex) { } return cl; } } ); } private static ClassLoader[] getClassLoaders(final Class... classes) { return AccessController.doPrivileged( new PrivilegedAction() { @Override public ClassLoader[] run() { ClassLoader[] loaders = new ClassLoader[classes.length]; int w = 0; for (Class k : classes) { ClassLoader cl = null; if (k == Thread.class) { try { cl = Thread.currentThread().getContextClassLoader(); } catch (SecurityException ex) { } } else if (k == System.class) { try { cl = ClassLoader.getSystemClassLoader(); } catch (SecurityException ex) { } } else { try { cl = k.getClassLoader(); } catch (SecurityException ex) { } } if (cl != null) { loaders[w++] = cl; } } if (loaders.length != w) { loaders = Arrays.copyOf(loaders, w); } return loaders; } } ); } private static InputStream getResourceAsStream(final Class c, final String name) throws IOException { try { return AccessController.doPrivileged( new PrivilegedExceptionAction() { @Override public InputStream run() throws IOException { try { return c.getResourceAsStream(name); } catch (RuntimeException e) { // gracefully handle ClassLoader bugs (Tomcat) IOException ioex = new IOException( "ClassLoader.getResourceAsStream failed"); ioex.initCause(e); throw ioex; } } } ); } catch (PrivilegedActionException e) { throw (IOException) e.getException(); } } private static URL[] getResources(final ClassLoader cl, final String name) { return AccessController.doPrivileged(new PrivilegedAction() { @Override public URL[] run() { URL[] ret = null; try { List v = Collections.list(cl.getResources(name)); if (!v.isEmpty()) { ret = new URL[v.size()]; v.toArray(ret); } } catch (IOException | SecurityException ioex) { } return ret; } }); } private static URL[] getSystemResources(final String name) { return AccessController.doPrivileged(new PrivilegedAction() { @Override public URL[] run() { URL[] ret = null; try { List v = Collections.list( ClassLoader.getSystemResources(name)); if (!v.isEmpty()) { ret = new URL[v.size()]; v.toArray(ret); } } catch (IOException | SecurityException ioex) { } return ret; } }); } private static InputStream openStream(final URL url) throws IOException { try { return AccessController.doPrivileged( new PrivilegedExceptionAction() { @Override public InputStream run() throws IOException { return url.openStream(); } } ); } catch (PrivilegedActionException e) { throw (IOException) e.getException(); } } EventQueue getEventQueue() { return q; } private static Class[] getHk2ServiceLoaderTargets(Class factoryClass) { ClassLoader[] loaders = getClassLoaders(Thread.class, factoryClass, System.class); Class[] classes = new Class[loaders.length]; int w = 0; for (ClassLoader loader : loaders) { if (loader != null) { try { classes[w++] = Class.forName("org.glassfish.hk2.osgiresourcelocator.ServiceLoader", false, loader); } catch (Exception | LinkageError ignored) { } //GlassFish class loaders can throw undocumented exceptions } } if (classes.length != w) { classes = Arrays.copyOf(classes, w); } return classes; } @SuppressWarnings({"unchecked"}) private Iterator lookupUsingHk2ServiceLoader(Class factoryId, ClassLoader loader) { for (Class target : getHk2ServiceLoaderTargets(factoryId)) { try { // Use reflection to avoid having any dependency on HK2 ServiceLoader class Class serviceClass = Class.forName(factoryId.getName(), false, loader); Class[] args = new Class[]{serviceClass}; Method m = target.getMethod("lookupProviderInstances", Class.class); Iterable result = ((Iterable) m.invoke(null, (Object[]) args)); if (result != null) { return result.iterator(); } } catch (Exception ignored) { // log and continue } } return Collections.emptyIterator(); } } /** * Support interface to generalize * code that loads resources from stream. */ interface StreamLoader { void load(InputStream is) throws IOException; }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy