From 307dcf05592156d7e1e008c9b554d0a51695eba3 Mon Sep 17 00:00:00 2001 From: jmehrens Date: Sat, 11 Nov 2023 23:12:04 -0600 Subject: [PATCH 01/13] Jakarta Mail erroneously assumes that classes can be loaded from Thread#getContextClassLoader Signed-off-by: jmehrens --- .../java/jakarta/mail/util/FactoryFinder.java | 76 ++++++++++++------- 1 file changed, 50 insertions(+), 26 deletions(-) diff --git a/api/src/main/java/jakarta/mail/util/FactoryFinder.java b/api/src/main/java/jakarta/mail/util/FactoryFinder.java index e1f91b3e..fbfbb1da 100644 --- a/api/src/main/java/jakarta/mail/util/FactoryFinder.java +++ b/api/src/main/java/jakarta/mail/util/FactoryFinder.java @@ -33,46 +33,71 @@ class FactoryFinder { * @throws RuntimeException if there is an error */ static T find(Class factoryClass) throws RuntimeException { + T result; + result = find(factoryClass, Thread.currentThread().getContextClassLoader()); + if (result != null) { + return result; + } + + result = find(factoryClass, factoryClass.getClassLoader()); + if (result != null) { + return result; + } + + result = find(factoryClass, FactoryFinder.class.getClassLoader()); + if (result != null) { + return result; + } + result = find(factoryClass, ClassLoader.getSystemClassLoader()); + if (result != null) { + return result; + } + + throw new IllegalStateException("No provider of " + factoryClass.getName() + " was found"); + } + + private static T find(Class factoryClass, ClassLoader loader) throws RuntimeException { String factoryId = factoryClass.getName(); // Use the system property first String className = fromSystemProperty(factoryId); if (className != null) { - T result = newInstance(className); + T result = newInstance(className, factoryClass, loader); if (result != null) { return result; } } // standard services: java.util.ServiceLoader - T factory = factoryFromServiceLoader(factoryClass); + T factory = factoryFromServiceLoader(factoryClass, loader); if (factory != null) { return factory; } // handling Glassfish/OSGi (platform specific default) - if (isHk2Available()) { - T result = lookupUsingHk2ServiceLoader(factoryId); - if (result != null) { - return result; - } + + T result = lookupUsingHk2ServiceLoader(factoryId, factoryClass, loader); + if (result != null) { + return result; } - throw new IllegalStateException("Not provider of " + factoryClass.getName() + " was found"); + + return null; } @SuppressWarnings({"unchecked"}) - private static T newInstance(String className) throws RuntimeException { - ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + private static T newInstance(String className, Class factoryClass, ClassLoader classLoader) throws RuntimeException { checkPackageAccess(className); - Class clazz = null; try { + Class clazz; if (classLoader == null) { clazz = (Class) Class.forName(className); } else { - clazz = (Class) classLoader.loadClass(className); + clazz = (Class) Class.forName(className, false, classLoader); } - return clazz.getConstructor().newInstance(); + return clazz.asSubclass(factoryClass).getConstructor().newInstance(); + } catch (ClassCastException wrongLoader) { + return null; } catch (ReflectiveOperationException e) { throw new IllegalArgumentException("Cannot instance " + className, e); } @@ -85,44 +110,43 @@ private static String fromSystemProperty(String factoryId) { private static final String OSGI_SERVICE_LOADER_CLASS_NAME = "org.glassfish.hk2.osgiresourcelocator.ServiceLoader"; - private static boolean isHk2Available() { + @SuppressWarnings({"unchecked"}) + private static T lookupUsingHk2ServiceLoader(String factoryId, Class factoryClass, ClassLoader loader) { + Class target; try { - Class.forName(OSGI_SERVICE_LOADER_CLASS_NAME); - return true; + target = Class.forName(OSGI_SERVICE_LOADER_CLASS_NAME, false, loader); } catch (ClassNotFoundException ignored) { + return null; } - return false; - } - @SuppressWarnings({"unchecked"}) - private static T lookupUsingHk2ServiceLoader(String factoryId) { try { // Use reflection to avoid having any dependency on HK2 ServiceLoader class - Class serviceClass = Class.forName(factoryId); + Class serviceClass = Class.forName(factoryId, false, loader); Class[] args = new Class[]{serviceClass}; - Class target = Class.forName(OSGI_SERVICE_LOADER_CLASS_NAME); Method m = target.getMethod("lookupProviderInstances", Class.class); Iterable iterable = ((Iterable) m.invoke(null, (Object[]) args)); if (iterable == null) { return null; } Iterator iter = iterable.iterator(); - return iter.hasNext() ? (T) iter.next() : null; + return iter.hasNext() ? factoryClass.cast(iter.next()) : null; } catch (Exception ignored) { // log and continue return null; } } - private static T factoryFromServiceLoader(Class factory) { + private static T factoryFromServiceLoader(Class factory, ClassLoader loader) { try { - ServiceLoader sl = ServiceLoader.load(factory); + ServiceLoader sl = ServiceLoader.load(factory, loader); Iterator iter = sl.iterator(); if (iter.hasNext()) { - return iter.next(); + return factory.cast(iter.next()); } else { return null; } + } catch (ClassCastException wrongLoader) { + return null; } catch (Throwable t) { // For example, ServiceConfigurationError can be thrown if the factory class is not declared with 'uses' in module-info throw new IllegalStateException("Cannot load " + factory + " as ServiceLoader", t); From c7c706539e6e4be5b2f1e67bc83282d7d69ee647 Mon Sep 17 00:00:00 2001 From: jmehrens Date: Sat, 11 Nov 2023 23:55:35 -0600 Subject: [PATCH 02/13] Jakarta Mail erroneously assumes that classes can be loaded from Thread#getContextClassLoader Signed-off-by: jmehrens --- api/src/main/java/jakarta/mail/Session.java | 51 +++++++++---------- .../java/jakarta/mail/util/FactoryFinder.java | 5 -- 2 files changed, 23 insertions(+), 33 deletions(-) diff --git a/api/src/main/java/jakarta/mail/Session.java b/api/src/main/java/jakarta/mail/Session.java index a0c58330..c1476a66 100644 --- a/api/src/main/java/jakarta/mail/Session.java +++ b/api/src/main/java/jakarta/mail/Session.java @@ -255,7 +255,7 @@ private Session(Properties props, Authenticator authenticator) { this.authenticator = authenticator; this.streamProvider = StreamProvider.provider(); - if (Boolean.valueOf(props.getProperty("mail.debug")).booleanValue()) + if (Boolean.parseBoolean(props.getProperty("mail.debug"))) debug = true; initLogger(); @@ -983,21 +983,20 @@ public void load(InputStream is) throws IOException { } // next, add all the non-default services - ServiceLoader sl = ServiceLoader.load(Provider.class); + ServiceLoader sl = ServiceLoader.load(Provider.class, cl.getClassLoader()); for (Provider p : sl) { if (!containsDefaultProvider(p)) addProvider(p); } // + handle Glassfish/OSGi (platform specific default) - if (isHk2Available()) { - Iterator iter = lookupUsingHk2ServiceLoader(Provider.class.getName()); - while (iter.hasNext()) { - Provider p = iter.next(); - if (!containsDefaultProvider(p)) - addProvider(p); - } + Iterator iter = lookupUsingHk2ServiceLoader(Provider.class, cl.getClassLoader()); + 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); @@ -1006,27 +1005,26 @@ public void load(InputStream is) throws IOException { loadResource("/META-INF/javamail.default.providers", cl, loader, false); // finally, add all the default services - sl = ServiceLoader.load(Provider.class); + sl = ServiceLoader.load(Provider.class, cl.getClassLoader()); for (Provider p : sl) { if (containsDefaultProvider(p)) addProvider(p); } // + handle Glassfish/OSGi (platform specific default) - if (isHk2Available()) { - Iterator iter = lookupUsingHk2ServiceLoader(Provider.class.getName()); - while (iter.hasNext()) { - Provider p = iter.next(); - if (containsDefaultProvider(p)) { - addProvider(p); - } + iter = lookupUsingHk2ServiceLoader(Provider.class, cl.getClassLoader()); + while (iter.hasNext()) { + Provider p = iter.next(); + if (containsDefaultProvider(p)) { + addProvider(p); } } + /* * If we haven't loaded any providers, fake it. */ - if (providers.size() == 0) { + 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, @@ -1388,22 +1386,19 @@ EventQueue getEventQueue() { private static final String OSGI_SERVICE_LOADER_CLASS_NAME = "org.glassfish.hk2.osgiresourcelocator.ServiceLoader"; - private static boolean isHk2Available() { + @SuppressWarnings({"unchecked"}) + private Iterator lookupUsingHk2ServiceLoader(Class factoryId, ClassLoader loader) { + Class target; try { - Class.forName(OSGI_SERVICE_LOADER_CLASS_NAME); - return true; + target = Class.forName(OSGI_SERVICE_LOADER_CLASS_NAME, false, loader); } catch (ClassNotFoundException ignored) { + return Collections.emptyIterator(); } - return false; - } - - @SuppressWarnings({"unchecked"}) - private Iterator lookupUsingHk2ServiceLoader(String factoryId) { + try { // Use reflection to avoid having any dependency on HK2 ServiceLoader class - Class serviceClass = Class.forName(factoryId); + Class serviceClass = Class.forName(factoryId.getName(), false, loader); Class[] args = new Class[]{serviceClass}; - Class target = Class.forName(OSGI_SERVICE_LOADER_CLASS_NAME); Method m = target.getMethod("lookupProviderInstances", Class.class); Iterable result = ((Iterable) m.invoke(null, (Object[]) args)); return result != null ? result.iterator() : Collections.emptyIterator(); diff --git a/api/src/main/java/jakarta/mail/util/FactoryFinder.java b/api/src/main/java/jakarta/mail/util/FactoryFinder.java index fbfbb1da..838f503d 100644 --- a/api/src/main/java/jakarta/mail/util/FactoryFinder.java +++ b/api/src/main/java/jakarta/mail/util/FactoryFinder.java @@ -44,11 +44,6 @@ static T find(Class factoryClass) throws RuntimeException { return result; } - result = find(factoryClass, FactoryFinder.class.getClassLoader()); - if (result != null) { - return result; - } - result = find(factoryClass, ClassLoader.getSystemClassLoader()); if (result != null) { return result; From d50a76f0fe9ce56a38e93ca5962e9a8298ca5dc7 Mon Sep 17 00:00:00 2001 From: jmehrens Date: Wed, 15 Nov 2023 23:09:16 -0600 Subject: [PATCH 03/13] Handle null classloader as system classloader. Probe for HK2 targets in different loaders. Signed-off-by: jmehrens --- api/src/main/java/jakarta/mail/Session.java | 56 ++++++---- .../java/jakarta/mail/util/FactoryFinder.java | 103 +++++++++++------- 2 files changed, 100 insertions(+), 59 deletions(-) diff --git a/api/src/main/java/jakarta/mail/Session.java b/api/src/main/java/jakarta/mail/Session.java index c1476a66..bdcddcc0 100644 --- a/api/src/main/java/jakarta/mail/Session.java +++ b/api/src/main/java/jakarta/mail/Session.java @@ -36,6 +36,7 @@ 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; @@ -1384,30 +1385,47 @@ EventQueue getEventQueue() { return q; } - private static final String OSGI_SERVICE_LOADER_CLASS_NAME = "org.glassfish.hk2.osgiresourcelocator.ServiceLoader"; - - @SuppressWarnings({"unchecked"}) - private Iterator lookupUsingHk2ServiceLoader(Class factoryId, ClassLoader loader) { - Class target; - try { - target = Class.forName(OSGI_SERVICE_LOADER_CLASS_NAME, false, loader); - } catch (ClassNotFoundException ignored) { - return Collections.emptyIterator(); + private static Class[] getHk2ServiceLoaderTargets(Class factoryClass) { + ClassLoader[] loaders = new ClassLoader[]{ + Thread.currentThread().getContextClassLoader(), + factoryClass.getClassLoader(), + ClassLoader.getSystemClassLoader()}; + + 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 (ClassNotFoundException | LinkageError ignored) { + } + } } - 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)); - return result != null ? result.iterator() : Collections.emptyIterator(); - } catch (Exception ignored) { - // log and continue - return Collections.emptyIterator(); + 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(); + } } /** diff --git a/api/src/main/java/jakarta/mail/util/FactoryFinder.java b/api/src/main/java/jakarta/mail/util/FactoryFinder.java index 838f503d..05658463 100644 --- a/api/src/main/java/jakarta/mail/util/FactoryFinder.java +++ b/api/src/main/java/jakarta/mail/util/FactoryFinder.java @@ -17,6 +17,7 @@ package jakarta.mail.util; import java.lang.reflect.Method; +import java.util.Arrays; import java.util.Iterator; import java.util.ServiceLoader; @@ -34,16 +35,24 @@ class FactoryFinder { */ static T find(Class factoryClass) throws RuntimeException { T result; - result = find(factoryClass, Thread.currentThread().getContextClassLoader()); - if (result != null) { - return result; + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + if (loader != null) { + result = find(factoryClass, loader); + if (result != null) { + return result; + } } - result = find(factoryClass, factoryClass.getClassLoader()); - if (result != null) { - return result; + //JakartaMail API ClassLoader / caller classloader + loader = factoryClass.getClassLoader(); + if (loader != null) { + result = find(factoryClass, loader); + if (result != null) { + return result; + } } + //Fallback to system class loader result = find(factoryClass, ClassLoader.getSystemClassLoader()); if (result != null) { return result; @@ -53,10 +62,8 @@ static T find(Class factoryClass) throws RuntimeException { } private static T find(Class factoryClass, ClassLoader loader) throws RuntimeException { - String factoryId = factoryClass.getName(); - // Use the system property first - String className = fromSystemProperty(factoryId); + String className = fromSystemProperty(factoryClass.getName()); if (className != null) { T result = newInstance(className, factoryClass, loader); if (result != null) { @@ -72,7 +79,7 @@ private static T find(Class factoryClass, ClassLoader loader) throws Runt // handling Glassfish/OSGi (platform specific default) - T result = lookupUsingHk2ServiceLoader(factoryId, factoryClass, loader); + T result = lookupUsingHk2ServiceLoader(factoryClass, loader); if (result != null) { return result; } @@ -80,16 +87,14 @@ private static T find(Class factoryClass, ClassLoader loader) throws Runt return null; } - @SuppressWarnings({"unchecked"}) private static T newInstance(String className, Class factoryClass, ClassLoader classLoader) throws RuntimeException { checkPackageAccess(className); try { - Class clazz; - if (classLoader == null) { - clazz = (Class) Class.forName(className); - } else { - clazz = (Class) Class.forName(className, false, classLoader); + Class clazz; + if (classLoader == null) { //Match behavior of ServiceLoader + classLoader = ClassLoader.getSystemClassLoader(); } + clazz = Class.forName(className, false, classLoader); return clazz.asSubclass(factoryClass).getConstructor().newInstance(); } catch (ClassCastException wrongLoader) { return null; @@ -102,41 +107,59 @@ private static String fromSystemProperty(String factoryId) { String systemProp = System.getProperty(factoryId); return systemProp; } - - private static final String OSGI_SERVICE_LOADER_CLASS_NAME = "org.glassfish.hk2.osgiresourcelocator.ServiceLoader"; - - @SuppressWarnings({"unchecked"}) - private static T lookupUsingHk2ServiceLoader(String factoryId, Class factoryClass, ClassLoader loader) { - Class target; - try { - target = Class.forName(OSGI_SERVICE_LOADER_CLASS_NAME, false, loader); - } catch (ClassNotFoundException ignored) { - return null; + + private static Class[] getHk2ServiceLoaderTargets(Class factoryClass) { + ClassLoader[] loaders = new ClassLoader[]{ + Thread.currentThread().getContextClassLoader(), + factoryClass.getClassLoader(), + ClassLoader.getSystemClassLoader()}; + + 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 (ClassNotFoundException | LinkageError ignored) { + } + } + } + + if (classes.length != w) { + classes = Arrays.copyOf(classes, w); } + return classes; + } - try { - // Use reflection to avoid having any dependency on HK2 ServiceLoader class - Class serviceClass = Class.forName(factoryId, false, loader); - Class[] args = new Class[]{serviceClass}; - Method m = target.getMethod("lookupProviderInstances", Class.class); - Iterable iterable = ((Iterable) m.invoke(null, (Object[]) args)); - if (iterable == null) { - return null; + @SuppressWarnings({"unchecked"}) + private static T lookupUsingHk2ServiceLoader(Class factoryClass, ClassLoader loader) { + for (Class target : getHk2ServiceLoaderTargets(factoryClass)) { + try { + // Use reflection to avoid having any dependency on HK2 ServiceLoader class + Class serviceClass = Class.forName(factoryClass.getName(), false, loader); + Class[] args = new Class[]{serviceClass}; + Method m = target.getMethod("lookupProviderInstances", Class.class); + Iterable iterable = ((Iterable) m.invoke(null, (Object[]) args)); + if (iterable != null) { + Iterator iter = iterable.iterator(); + if (iter.hasNext()) { + return factoryClass.cast(iter.next()); //Verify classloader. + } + } + } catch (Exception ignored) { + // log and continue } - Iterator iter = iterable.iterator(); - return iter.hasNext() ? factoryClass.cast(iter.next()) : null; - } catch (Exception ignored) { - // log and continue - return null; } + return null; } private static T factoryFromServiceLoader(Class factory, ClassLoader loader) { + //ClassLoader of null is treated as system classloader try { ServiceLoader sl = ServiceLoader.load(factory, loader); Iterator iter = sl.iterator(); if (iter.hasNext()) { - return factory.cast(iter.next()); + return factory.cast(iter.next()); //Verify loader } else { return null; } From dbc7a3ffad6bb96b00bb4aad22fe8de5a285d65d Mon Sep 17 00:00:00 2001 From: jmehrens Date: Wed, 15 Nov 2023 23:28:16 -0600 Subject: [PATCH 04/13] Handle null classloader as system classloader. Signed-off-by: jmehrens --- api/src/main/java/jakarta/mail/Session.java | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/api/src/main/java/jakarta/mail/Session.java b/api/src/main/java/jakarta/mail/Session.java index bdcddcc0..794f6c4f 100644 --- a/api/src/main/java/jakarta/mail/Session.java +++ b/api/src/main/java/jakarta/mail/Session.java @@ -982,16 +982,25 @@ public void load(InputStream is) throws IOException { loadFile(confDir + "javamail.providers", loader); } catch (SecurityException ex) { } + + ClassLoader gcl = cl.getClassLoader(); + if (gcl == null) { + gcl = Provider.class.getClassLoader(); + } + + if (gcl == null) { + gcl = ClassLoader.getSystemClassLoader(); + } // next, add all the non-default services - ServiceLoader sl = ServiceLoader.load(Provider.class, cl.getClassLoader()); + 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, cl.getClassLoader()); + Iterator iter = lookupUsingHk2ServiceLoader(Provider.class, gcl); while (iter.hasNext()) { Provider p = iter.next(); if (!containsDefaultProvider(p)) @@ -1006,14 +1015,14 @@ public void load(InputStream is) throws IOException { loadResource("/META-INF/javamail.default.providers", cl, loader, false); // finally, add all the default services - sl = ServiceLoader.load(Provider.class, cl.getClassLoader()); + 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, cl.getClassLoader()); + iter = lookupUsingHk2ServiceLoader(Provider.class, gcl); while (iter.hasNext()) { Provider p = iter.next(); if (containsDefaultProvider(p)) { From 993a874d38cd879cef4b4255f5c143326ddea831 Mon Sep 17 00:00:00 2001 From: jmehrens Date: Sat, 18 Nov 2023 23:29:42 -0600 Subject: [PATCH 05/13] Get classloaders in bulk. Signed-off-by: jmehrens --- api/src/main/java/jakarta/mail/Session.java | 59 +++++++++++++++++---- 1 file changed, 48 insertions(+), 11 deletions(-) diff --git a/api/src/main/java/jakarta/mail/Session.java b/api/src/main/java/jakarta/mail/Session.java index 794f6c4f..a26a5463 100644 --- a/api/src/main/java/jakarta/mail/Session.java +++ b/api/src/main/java/jakarta/mail/Session.java @@ -982,12 +982,12 @@ public void load(InputStream is) throws IOException { loadFile(confDir + "javamail.providers", loader); } catch (SecurityException ex) { } - + ClassLoader gcl = cl.getClassLoader(); if (gcl == null) { gcl = Provider.class.getClassLoader(); } - + if (gcl == null) { gcl = ClassLoader.getSystemClassLoader(); } @@ -1006,7 +1006,7 @@ public void load(InputStream is) throws IOException { if (!containsDefaultProvider(p)) addProvider(p); } - + // load the META-INF/javamail.providers file supplied by an application loadAllResources("META-INF/javamail.providers", cl, loader); @@ -1029,7 +1029,7 @@ public void load(InputStream is) throws IOException { addProvider(p); } } - + /* * If we haven't loaded any providers, fake it. @@ -1315,6 +1315,46 @@ public ClassLoader run() { ); } + 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( @@ -1395,22 +1435,19 @@ EventQueue getEventQueue() { } private static Class[] getHk2ServiceLoaderTargets(Class factoryClass) { - ClassLoader[] loaders = new ClassLoader[]{ - Thread.currentThread().getContextClassLoader(), - factoryClass.getClassLoader(), - ClassLoader.getSystemClassLoader()}; - + 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 (ClassNotFoundException | LinkageError ignored) { + } catch (ClassNotFoundException | LinkageError ignored) { } } } - + if (classes.length != w) { classes = Arrays.copyOf(classes, w); } From ef2697ad23ff3d6bbacbe1e99348bd59dea60a4b Mon Sep 17 00:00:00 2001 From: jmehrens Date: Sat, 18 Nov 2023 23:46:46 -0600 Subject: [PATCH 06/13] Fix default providers. Signed-off-by: jmehrens --- api/src/main/java/jakarta/mail/Session.java | 30 ++++++++++----------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/api/src/main/java/jakarta/mail/Session.java b/api/src/main/java/jakarta/mail/Session.java index a26a5463..ee015a2f 100644 --- a/api/src/main/java/jakarta/mail/Session.java +++ b/api/src/main/java/jakarta/mail/Session.java @@ -983,15 +983,15 @@ public void load(InputStream is) throws IOException { } catch (SecurityException ex) { } - ClassLoader gcl = cl.getClassLoader(); - if (gcl == null) { - gcl = Provider.class.getClassLoader(); - } - - if (gcl == null) { - gcl = ClassLoader.getSystemClassLoader(); + + ClassLoader gcl; + ClassLoader[] loaders = getClassLoaders(cl, Thread.class, System.class); + if (loaders.length != 0) { + gcl = loaders[0]; + } else { + gcl = getContextClassLoader(); } - + // next, add all the non-default services ServiceLoader sl = ServiceLoader.load(Provider.class, gcl); for (Provider p : sl) { @@ -1038,22 +1038,22 @@ public void load(InputStream is) throws IOException { 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", "com.sun.mail.imap.IMAPStore", + "imap", "org.eclipse.angus.mail.imap.IMAPStore", "Oracle", Version.version)); addProvider(new Provider(Provider.Type.STORE, - "imaps", "com.sun.mail.imap.IMAPSSLStore", + "imaps", "org.eclipse.angus.mail.imap.IMAPSSLStore", "Oracle", Version.version)); addProvider(new Provider(Provider.Type.STORE, - "pop3", "com.sun.mail.pop3.POP3Store", + "pop3", "org.eclipse.angus.mail.pop3.POP3Store", "Oracle", Version.version)); addProvider(new Provider(Provider.Type.STORE, - "pop3s", "com.sun.mail.pop3.POP3SSLStore", + "pop3s", "org.eclipse.angus.mail.pop3.POP3SSLStore", "Oracle", Version.version)); addProvider(new Provider(Provider.Type.TRANSPORT, - "smtp", "com.sun.mail.smtp.SMTPTransport", + "smtp", "org.eclipse.angus.mail.smtp.SMTPTransport", "Oracle", Version.version)); addProvider(new Provider(Provider.Type.TRANSPORT, - "smtps", "com.sun.mail.smtp.SMTPSSLTransport", + "smtps", "org.eclipse.angus.mail.smtp.SMTPSSLTransport", "Oracle", Version.version)); } @@ -1315,7 +1315,7 @@ public ClassLoader run() { ); } - static ClassLoader[] getClassLoaders(final Class... classes) { + private static ClassLoader[] getClassLoaders(final Class... classes) { return AccessController.doPrivileged( new PrivilegedAction() { @Override From 3762e155493f9fbadc3ef7febff3cedcf462a976 Mon Sep 17 00:00:00 2001 From: jmehrens Date: Sat, 18 Nov 2023 23:48:40 -0600 Subject: [PATCH 07/13] Remove newline. Signed-off-by: jmehrens --- api/src/main/java/jakarta/mail/Session.java | 1 - 1 file changed, 1 deletion(-) diff --git a/api/src/main/java/jakarta/mail/Session.java b/api/src/main/java/jakarta/mail/Session.java index ee015a2f..12781470 100644 --- a/api/src/main/java/jakarta/mail/Session.java +++ b/api/src/main/java/jakarta/mail/Session.java @@ -1007,7 +1007,6 @@ public void load(InputStream is) throws IOException { addProvider(p); } - // load the META-INF/javamail.providers file supplied by an application loadAllResources("META-INF/javamail.providers", cl, loader); From 56377e49f86433b40cef981adc78e9027b252f86 Mon Sep 17 00:00:00 2001 From: jmehrens Date: Sat, 18 Nov 2023 23:49:20 -0600 Subject: [PATCH 08/13] Remove newline. Signed-off-by: jmehrens --- api/src/main/java/jakarta/mail/Session.java | 1 - 1 file changed, 1 deletion(-) diff --git a/api/src/main/java/jakarta/mail/Session.java b/api/src/main/java/jakarta/mail/Session.java index 12781470..d87ce8d1 100644 --- a/api/src/main/java/jakarta/mail/Session.java +++ b/api/src/main/java/jakarta/mail/Session.java @@ -1029,7 +1029,6 @@ public void load(InputStream is) throws IOException { } } - /* * If we haven't loaded any providers, fake it. */ From cca3b2608f7441daa5da4aef5ca8dd0c12b34539 Mon Sep 17 00:00:00 2001 From: jmehrens Date: Sun, 19 Nov 2023 00:07:41 -0600 Subject: [PATCH 09/13] Add test Signed-off-by: jmehrens --- .../jakarta/mail/util/DummyStreamProvider.java | 3 +++ .../jakarta/mail/util/FactoryFinderTest.java | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/api/src/test/java/jakarta/mail/util/DummyStreamProvider.java b/api/src/test/java/jakarta/mail/util/DummyStreamProvider.java index b3e20a9e..e13550e7 100644 --- a/api/src/test/java/jakarta/mail/util/DummyStreamProvider.java +++ b/api/src/test/java/jakarta/mail/util/DummyStreamProvider.java @@ -21,6 +21,9 @@ import java.io.OutputStream; public class DummyStreamProvider implements StreamProvider { + + public DummyStreamProvider() { + } @Override public InputStream inputBase64(InputStream in) { diff --git a/api/src/test/java/jakarta/mail/util/FactoryFinderTest.java b/api/src/test/java/jakarta/mail/util/FactoryFinderTest.java index 53ffd1f7..763bdfd4 100644 --- a/api/src/test/java/jakarta/mail/util/FactoryFinderTest.java +++ b/api/src/test/java/jakarta/mail/util/FactoryFinderTest.java @@ -24,6 +24,9 @@ public class FactoryFinderTest { + public FactoryFinderTest() { + } + @Test public void specifiedInSystemProperty() { System.setProperty(Class1.class.getName(), Class2.class.getName()); @@ -55,6 +58,21 @@ public void doesNotExist() { // } } + + @Test + public void contextClassLoaderIsBootLoader() { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader((ClassLoader) null); + //Thread.currentThread().setContextClassLoader(ClassLoader.getPlatformClassLoader()); + try { + doesNotExist(); + specifiedInSystemProperty(); + specifiedInServiceLoader(); + } finally { + Thread.currentThread().setContextClassLoader(cl); + } + } + public static class Class1 { } From ea6b91f0f7312c9680f777c5d39806b1e52464a2 Mon Sep 17 00:00:00 2001 From: jmehrens Date: Sun, 19 Nov 2023 00:16:53 -0600 Subject: [PATCH 10/13] Add test Signed-off-by: jmehrens --- api/src/test/java/jakarta/mail/util/FactoryFinderTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/api/src/test/java/jakarta/mail/util/FactoryFinderTest.java b/api/src/test/java/jakarta/mail/util/FactoryFinderTest.java index 763bdfd4..6a3ec317 100644 --- a/api/src/test/java/jakarta/mail/util/FactoryFinderTest.java +++ b/api/src/test/java/jakarta/mail/util/FactoryFinderTest.java @@ -74,11 +74,14 @@ public void contextClassLoaderIsBootLoader() { } public static class Class1 { + public Class1() {} } public static class Class2 extends Class1 { + public Class2() {} } public static class Class3 { + public Class3() {} } } From 683898d4583eb1b78526e97c35bb545cbeb93847 Mon Sep 17 00:00:00 2001 From: jmehrens Date: Sun, 19 Nov 2023 00:19:30 -0600 Subject: [PATCH 11/13] Update changes Signed-off-by: jmehrens --- doc/release/CHANGES.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/release/CHANGES.txt b/doc/release/CHANGES.txt index 961ddbd2..dad53ff3 100644 --- a/doc/release/CHANGES.txt +++ b/doc/release/CHANGES.txt @@ -19,6 +19,11 @@ Bug IDs that start with "G" can be found in the GlassFish Issue Tracker Seven digit bug numbers are from the old Sun bug database, which is no longer available. + CHANGES IN THE 2.1.3 RELEASE + ---------------------------- +E 665 Jakarta Mail erroneously assumes that classes can be loaded from Thread#getContextClassLoader + + CHANGES IN THE 2.1.2 RELEASE ---------------------------- E 629 jakarta.mail-api-2.1.0.jar does not work in OSGi environment (hk2servicelocator) From 88a791ea0043ffba453cc1388dd3426af36a4464 Mon Sep 17 00:00:00 2001 From: jmehrens Date: Tue, 21 Nov 2023 22:16:19 -0600 Subject: [PATCH 12/13] Glassfish can throw IllegalStateException Signed-off-by: jmehrens --- api/src/main/java/jakarta/mail/Session.java | 4 ++-- api/src/main/java/jakarta/mail/util/FactoryFinder.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/api/src/main/java/jakarta/mail/Session.java b/api/src/main/java/jakarta/mail/Session.java index d87ce8d1..06aed73d 100644 --- a/api/src/main/java/jakarta/mail/Session.java +++ b/api/src/main/java/jakarta/mail/Session.java @@ -1441,8 +1441,8 @@ private static Class[] getHk2ServiceLoaderTargets(Class factoryClass) { if (loader != null) { try { classes[w++] = Class.forName("org.glassfish.hk2.osgiresourcelocator.ServiceLoader", false, loader); - } catch (ClassNotFoundException | LinkageError ignored) { - } + } catch (Exception | LinkageError ignored) { + } //GlassFish class loaders can throw undocumented exceptions } } diff --git a/api/src/main/java/jakarta/mail/util/FactoryFinder.java b/api/src/main/java/jakarta/mail/util/FactoryFinder.java index 05658463..9c144941 100644 --- a/api/src/main/java/jakarta/mail/util/FactoryFinder.java +++ b/api/src/main/java/jakarta/mail/util/FactoryFinder.java @@ -120,8 +120,8 @@ private static Class[] getHk2ServiceLoaderTargets(Class factoryClass) { if (loader != null) { try { classes[w++] = Class.forName("org.glassfish.hk2.osgiresourcelocator.ServiceLoader", false, loader); - } catch (ClassNotFoundException | LinkageError ignored) { - } + } catch (Exception | LinkageError ignored) { + } //GlassFish class loaders can throw undocumented exceptions } } From 5acf47b253b7a788dc7463b6e9c1690ebca3d30a Mon Sep 17 00:00:00 2001 From: jmehrens Date: Tue, 21 Nov 2023 22:21:08 -0600 Subject: [PATCH 13/13] Add comment Signed-off-by: jmehrens --- api/src/main/java/jakarta/mail/Session.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/jakarta/mail/Session.java b/api/src/main/java/jakarta/mail/Session.java index 06aed73d..32224691 100644 --- a/api/src/main/java/jakarta/mail/Session.java +++ b/api/src/main/java/jakarta/mail/Session.java @@ -983,13 +983,13 @@ public void load(InputStream is) throws IOException { } 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(); + gcl = getContextClassLoader(); //Fail safe } // next, add all the non-default services