Skip to content

Commit ce3e704

Browse files
jmehrenslukasj
authored andcommitted
Jakarta Activation erroneously assumes that classes can be loaded from Thread#getContextClassLoader (#145)
Signed-off-by: jmehrens <[email protected]> (cherry picked from commit 4b8285b)
1 parent 80cb0f8 commit ce3e704

File tree

4 files changed

+140
-139
lines changed

4 files changed

+140
-139
lines changed
Lines changed: 106 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2021, 2024 Oracle and/or its affiliates. All rights reserved.
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Distribution License v. 1.0, which is available at
@@ -13,6 +13,7 @@
1313
import java.lang.reflect.Method;
1414
import java.security.AccessController;
1515
import java.security.PrivilegedAction;
16+
import java.util.Arrays;
1617
import java.util.Iterator;
1718
import java.util.logging.Level;
1819
import java.util.logging.Logger;
@@ -26,48 +27,41 @@ class FactoryFinder {
2627
new ServiceLoaderUtil.ExceptionHandler<RuntimeException>() {
2728
@Override
2829
public RuntimeException createException(Throwable throwable, String message) {
29-
return new RuntimeException(message, throwable);
30+
return new IllegalStateException(message, throwable);
3031
}
3132
};
3233

3334
/**
3435
* Finds the implementation {@code Class} object for the given
35-
* factory type. If it fails and {@code tryFallback} is {@code true}
36-
* finds the {@code Class} object for the given default class name.
37-
* The arguments supplied must be used in order
38-
* Note the default class name may be needed even if fallback
39-
* is not to be attempted in order to check if requested type is fallback.
36+
* factory type.
4037
* <P>
4138
* This method is package private so that this code can be shared.
4239
*
40+
* @param factoryClass factory abstract class or interface to be found
4341
* @return the {@code Class} object of the specified message factory;
44-
* may not be {@code null}
45-
*
46-
* @param factoryClass factory abstract class or interface to be found
47-
* @param defaultClassName the implementation class name, which is
48-
* to be used only if nothing else
49-
* is found; {@code null} to indicate
50-
* that there is no default class name
51-
* @param tryFallback whether to try the default class as a
52-
* fallback
53-
* @exception RuntimeException if there is no factory found
42+
* may not be {@code null}
43+
* @throws IllegalStateException if there is no factory found
5444
*/
55-
static <T> T find(Class<T> factoryClass,
56-
String defaultClassName,
57-
boolean tryFallback) throws RuntimeException {
45+
static <T> T find(Class<T> factoryClass) throws RuntimeException {
46+
for (ClassLoader l : getClassLoaders(
47+
Thread.class,
48+
FactoryFinder.class,
49+
System.class)) {
50+
T f = find(factoryClass, l);
51+
if (f != null) {
52+
return f;
53+
}
54+
}
5855

59-
ClassLoader tccl = ServiceLoaderUtil.contextClassLoader(EXCEPTION_HANDLER);
60-
String factoryId = factoryClass.getName();
56+
throw EXCEPTION_HANDLER.createException((Throwable) null,
57+
"Provider for " + factoryClass.getName() + " cannot be found");
58+
}
6159

60+
static <T> T find(Class<T> factoryClass, ClassLoader loader) throws RuntimeException {
6261
// Use the system property first
63-
String className = fromSystemProperty(factoryId);
62+
String className = fromSystemProperty(factoryClass.getName());
6463
if (className != null) {
65-
T result = newInstance(className, defaultClassName, tccl);
66-
if (result != null) {
67-
return result;
68-
}
69-
// try api loader
70-
result = newInstance(className, defaultClassName, FactoryFinder.class.getClassLoader());
64+
T result = newInstance(className, factoryClass, loader);
7165
if (result != null) {
7266
return result;
7367
}
@@ -76,40 +70,29 @@ static <T> T find(Class<T> factoryClass,
7670
// standard services: java.util.ServiceLoader
7771
T factory = ServiceLoaderUtil.firstByServiceLoader(
7872
factoryClass,
73+
loader,
7974
logger,
8075
EXCEPTION_HANDLER);
8176
if (factory != null) {
8277
return factory;
8378
}
8479

8580
// handling Glassfish/OSGi (platform specific default)
86-
if (isOsgi()) {
87-
T result = lookupUsingOSGiServiceLoader(factoryId);
88-
if (result != null) {
89-
return result;
90-
}
81+
T result = lookupUsingHk2ServiceLoader(factoryClass, loader);
82+
if (result != null) {
83+
return result;
9184
}
9285

93-
// If not found and fallback should not be tried, throw RuntimeException.
94-
if (!tryFallback) {
95-
throw new RuntimeException(
96-
"Provider for " + factoryId + " cannot be found", null);
97-
}
98-
99-
// We didn't find the class through the usual means so try the default
100-
// (built in) factory if specified.
101-
if (defaultClassName == null) {
102-
throw new RuntimeException(
103-
"Provider for " + factoryId + " cannot be found", null);
104-
}
105-
return newInstance(defaultClassName, defaultClassName, tccl);
86+
return null;
10687
}
10788

108-
private static <T> T newInstance(String className, String defaultClassName, ClassLoader tccl) throws RuntimeException {
89+
private static <T> T newInstance(String className,
90+
Class<? extends T> service, ClassLoader loader)
91+
throws RuntimeException {
10992
return ServiceLoaderUtil.newInstance(
11093
className,
111-
defaultClassName,
112-
tccl,
94+
service,
95+
loader,
11396
EXCEPTION_HANDLER);
11497
}
11598

@@ -138,31 +121,85 @@ private static void logFound(String value) {
138121
}
139122
}
140123

141-
private static final String OSGI_SERVICE_LOADER_CLASS_NAME = "org.glassfish.hk2.osgiresourcelocator.ServiceLoader";
124+
private static Class<?>[] getHk2ServiceLoaderTargets(Class<?> factoryClass) {
125+
ClassLoader[] loaders = getClassLoaders(Thread.class, factoryClass, System.class);
126+
127+
Class<?>[] classes = new Class<?>[loaders.length];
128+
int w = 0;
129+
for (ClassLoader loader : loaders) {
130+
if (loader != null) {
131+
try {
132+
classes[w++] = Class.forName("org.glassfish.hk2.osgiresourcelocator.ServiceLoader", false, loader);
133+
} catch (Exception | LinkageError ignored) {
134+
} //GlassFish class loaders can throw undocumented exceptions
135+
}
136+
}
142137

143-
private static boolean isOsgi() {
144-
try {
145-
Class.forName(OSGI_SERVICE_LOADER_CLASS_NAME);
146-
return true;
147-
} catch (ClassNotFoundException ignored) {
138+
if (classes.length != w) {
139+
classes = Arrays.copyOf(classes, w);
148140
}
149-
return false;
141+
return classes;
150142
}
151143

152144
@SuppressWarnings({"unchecked"})
153-
private static <T> T lookupUsingOSGiServiceLoader(String factoryId) {
154-
try {
155-
// Use reflection to avoid having any dependency on HK2 ServiceLoader class
156-
Class<?> serviceClass = Class.forName(factoryId);
157-
Class<?>[] args = new Class<?>[]{serviceClass};
158-
Class<?> target = Class.forName(OSGI_SERVICE_LOADER_CLASS_NAME);
159-
Method m = target.getMethod("lookupProviderInstances", Class.class);
160-
Iterator<?> iter = ((Iterable<?>) m.invoke(null, (Object[]) args)).iterator();
161-
return iter.hasNext() ? (T) iter.next() : null;
162-
} catch (Exception ignored) {
163-
// log and continue
164-
return null;
145+
private static <T> T lookupUsingHk2ServiceLoader(Class<T> factoryClass, ClassLoader loader) {
146+
for (Class<?> target : getHk2ServiceLoaderTargets(factoryClass)) {
147+
try {
148+
// Use reflection to avoid having any dependency on HK2 ServiceLoader class
149+
Class<?> serviceClass = Class.forName(factoryClass.getName(), false, loader);
150+
Class<?>[] args = new Class<?>[]{serviceClass};
151+
Method m = target.getMethod("lookupProviderInstances", Class.class);
152+
Iterable<?> iterable = ((Iterable<?>) m.invoke(null, (Object[]) args));
153+
if (iterable != null) {
154+
Iterator<?> iter = iterable.iterator();
155+
if (iter.hasNext()) {
156+
return factoryClass.cast(iter.next()); //Verify classloader.
157+
}
158+
}
159+
} catch (Exception ignored) {
160+
// log and continue
161+
}
165162
}
163+
return null;
166164
}
167165

166+
private static ClassLoader[] getClassLoaders(final Class<?>... classes) {
167+
return AccessController.doPrivileged(
168+
new PrivilegedAction<ClassLoader[]>() {
169+
@Override
170+
public ClassLoader[] run() {
171+
ClassLoader[] loaders = new ClassLoader[classes.length];
172+
int w = 0;
173+
for (Class<?> k : classes) {
174+
ClassLoader cl = null;
175+
if (k == Thread.class) {
176+
try {
177+
cl = Thread.currentThread().getContextClassLoader();
178+
} catch (SecurityException ex) {
179+
}
180+
} else if (k == System.class) {
181+
try {
182+
cl = ClassLoader.getSystemClassLoader();
183+
} catch (SecurityException ex) {
184+
}
185+
} else {
186+
try {
187+
cl = k.getClassLoader();
188+
} catch (SecurityException ex) {
189+
}
190+
}
191+
192+
if (cl != null) {
193+
loaders[w++] = cl;
194+
}
195+
}
196+
197+
if (loaders.length != w) {
198+
loaders = Arrays.copyOf(loaders, w);
199+
}
200+
return loaders;
201+
}
202+
}
203+
);
204+
}
168205
}

api/src/main/java/jakarta/activation/MailcapCommandMap.java

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1997, 2022 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1997, 2024 Oracle and/or its affiliates. All rights reserved.
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Distribution License v. 1.0, which is available at
@@ -200,7 +200,7 @@ private MailcapRegistry loadResource(String name) {
200200
} catch (IOException | SecurityException e) {
201201
if (LogSupport.isLoggable())
202202
LogSupport.log("MailcapCommandMap: can't load " + name, e);
203-
} catch (NoSuchElementException | ServiceConfigurationError e) {
203+
} catch (NoSuchElementException | IllegalStateException | ServiceConfigurationError e) {
204204
if (LogSupport.isLoggable()) {
205205
LogSupport.log("Cannot find or load an implementation for MailcapRegistryProvider. " +
206206
"MailcapRegistry: can't load " + name, e);
@@ -251,7 +251,7 @@ private void loadAllResources(List<MailcapRegistry> v, String name) {
251251
if (LogSupport.isLoggable())
252252
LogSupport.log("MailcapCommandMap: can't load " +
253253
url, ioex);
254-
} catch (NoSuchElementException | ServiceConfigurationError e) {
254+
} catch (NoSuchElementException | IllegalStateException | ServiceConfigurationError e) {
255255
if (LogSupport.isLoggable()) {
256256
LogSupport.log("Cannot find or load an implementation for MailcapRegistryProvider. " +
257257
"MailcapRegistry: can't load " + name, e);
@@ -286,7 +286,7 @@ private MailcapRegistry loadFile(String name) {
286286
if (LogSupport.isLoggable()) {
287287
LogSupport.log("MailcapRegistry: can't load from file - " + name, e);
288288
}
289-
} catch (NoSuchElementException | ServiceConfigurationError e) {
289+
} catch (NoSuchElementException | IllegalStateException | ServiceConfigurationError e) {
290290
if (LogSupport.isLoggable()) {
291291
LogSupport.log("Cannot find or load an implementation for MailcapRegistryProvider. " +
292292
"MailcapRegistry: can't load " + name, e);
@@ -307,7 +307,7 @@ public MailcapCommandMap(String fileName) throws IOException {
307307
if (DB[PROG] == null) {
308308
try {
309309
DB[PROG] = getImplementation().getByFileName(fileName);
310-
} catch (NoSuchElementException | ServiceConfigurationError e) {
310+
} catch (NoSuchElementException | IllegalStateException | ServiceConfigurationError e) {
311311
String message = "Cannot find or load an implementation for MailcapRegistryProvider. " +
312312
"MailcapRegistry: can't load " + fileName;
313313
if (LogSupport.isLoggable()) {
@@ -336,7 +336,7 @@ public MailcapCommandMap(InputStream is) {
336336
DB[PROG] = getImplementation().getByInputStream(is);
337337
} catch (IOException ex) {
338338
// XXX - should throw it
339-
} catch (NoSuchElementException | ServiceConfigurationError e) {
339+
} catch (NoSuchElementException | IllegalStateException | ServiceConfigurationError e) {
340340
if (LogSupport.isLoggable()) {
341341
LogSupport.log("Cannot find or load an implementation for MailcapRegistryProvider." +
342342
"MailcapRegistry: can't load InputStream", e);
@@ -537,7 +537,7 @@ public synchronized void addMailcap(String mail_cap) {
537537
DB[PROG] = getImplementation().getInMemory();
538538
}
539539
DB[PROG].appendToMailcap(mail_cap);
540-
} catch (NoSuchElementException | ServiceConfigurationError e) {
540+
} catch (NoSuchElementException | IllegalStateException | ServiceConfigurationError e) {
541541
if (LogSupport.isLoggable()) {
542542
LogSupport.log("Cannot find or load an implementation for MailcapRegistryProvider. " +
543543
"MailcapRegistry: can't load", e);
@@ -696,15 +696,11 @@ private MailcapRegistryProvider getImplementation() {
696696
if (System.getSecurityManager() != null) {
697697
return AccessController.doPrivileged(new PrivilegedAction<MailcapRegistryProvider>() {
698698
public MailcapRegistryProvider run() {
699-
return FactoryFinder.find(MailcapRegistryProvider.class,
700-
null,
701-
false);
699+
return FactoryFinder.find(MailcapRegistryProvider.class);
702700
}
703701
});
704702
} else {
705-
return FactoryFinder.find(MailcapRegistryProvider.class,
706-
null,
707-
false);
703+
return FactoryFinder.find(MailcapRegistryProvider.class);
708704
}
709705
}
710706

0 commit comments

Comments
 (0)