Skip to content

Commit

Permalink
Fix ExtensionException in OSGi environment for global extension (#2079)
Browse files Browse the repository at this point in the history
Implement the same resource logic with ContextClassLoader as JUnit
platform.

Fixes #2076
  • Loading branch information
AndreasTu authored Jan 12, 2025
1 parent 9d1e819 commit 8017fc3
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 26 deletions.
7 changes: 6 additions & 1 deletion docs/release_notes.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@

include::include.adoc[]

== 2.4 (tbd)
== 2.4-M6 (tbd)

=== Misc

* Fix ExtensionException in OSGi environment for global extension spockIssue:2076[]
** This issue was introduced with spockPull:1995[]

== 2.4-M5 (2025-01-07)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public <T> List<Class<? extends T>> loadClasses(String descriptorPath, Class<T>

private Collection<URL> locateDescriptors(String descriptorPath) {
try {
return ReflectionUtil.getResourcesFromClassLoaders(descriptorPath);
return ReflectionUtil.getResourcesFromClassLoader(descriptorPath);
} catch (Exception e) {
throw new ExtensionException("Failed to locate extension descriptors", e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,30 +86,24 @@ public static Class<?> loadClass(String className) throws ClassNotFoundException
}

/**
* Returns the resources from the following classloaders:
* <ul>
* <li>Spock {@link ClassLoader}</li>
* <li>{@code Thread.currentThread().getContextClassLoader()}</li>
* </ul>
* Returns the resources from the {@code Thread.currentThread().getContextClassLoader()}.
*
* @param resourcePath the path of the resource
* @return the list of resources
* @throws IOException if the resources can't be loaded
*/
public static Collection<URL> getResourcesFromClassLoaders(String resourcePath) throws IOException {
ClassLoader spockClassLoader = ReflectionUtil.class.getClassLoader();
// We need to use a sorted Set here, to filter out duplicates, if the ContextClassLoader can also reach the Spock classloader
TreeSet<URL> set = new TreeSet<>(Comparator.comparing(URL::toString));
set.addAll(Collections.list(spockClassLoader.getResources(resourcePath)));
public static Collection<URL> getResourcesFromClassLoader(String resourcePath) throws IOException {
try {
//Also resolve resources via ContextClassLoader to better support for runtimes like OSGi
//Resolve resources via ContextClassLoader to better support for runtimes like OSGi
//Issue #2076: But du not use ContextClassLoader and the Spock ClassLoader and merge the results, because this fails for bundle resources
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
if (contextClassLoader != null && spockClassLoader != contextClassLoader) {
set.addAll(Collections.list(contextClassLoader.getResources(resourcePath)));
if (contextClassLoader != null) {
return Collections.list(contextClassLoader.getResources(resourcePath));
}
} catch (SecurityException ignored) {
}
return set;
ClassLoader spockClassLoader = ReflectionUtil.class.getClassLoader();
return Collections.list(spockClassLoader.getResources(resourcePath));
}

public static boolean isClassAvailable(String className) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,14 @@ class ReflectionUtilSpec extends Specification {
Thread.currentThread().setContextClassLoader(oldLoader)
}

def "getResourcesFromClassLoaders"() {
def getResourcesFromClassLoader() {
when:
def res = ReflectionUtil.getResourcesFromClassLoaders(JarFile.MANIFEST_NAME)
def res = ReflectionUtil.getResourcesFromClassLoader(JarFile.MANIFEST_NAME)
then:
res.size() >= 1
}

@Issue("https://github.com/spockframework/spock/issues/2076")
def "getResourcesFromClassLoaders - ContextClassLoader"() {
given:
def oldLoader = Thread.currentThread().getContextClassLoader()
Expand All @@ -74,23 +75,18 @@ class ReflectionUtilSpec extends Specification {
@Override
Enumeration<URL> getResources(String name) throws IOException {
if (name == resPath) {
def url = new URL("file:/" + resPath)
return new Vector([
//Check that we filter out duplicates
url,
url
]).elements()
return new Vector([new URL("file:/" + resPath)]).elements()
}
return super.getResources(name)
}
}

expect:
ReflectionUtil.getResourcesFromClassLoaders(resPath).isEmpty()
ReflectionUtil.getResourcesFromClassLoader(resPath).isEmpty()

when:
Thread.currentThread().setContextClassLoader(resCl)
def res = ReflectionUtil.getResourcesFromClassLoaders(resPath)
def res = ReflectionUtil.getResourcesFromClassLoader(resPath)
then:
res.size() == 1
res[0].toString().contains(resPath)
Expand All @@ -99,6 +95,21 @@ class ReflectionUtilSpec extends Specification {
Thread.currentThread().setContextClassLoader(oldLoader)
}

@Issue("https://github.com/spockframework/spock/issues/2076")
def "getResourcesFromClassLoaders - ContextClassLoader == null"() {
given:
def oldLoader = Thread.currentThread().getContextClassLoader()

when:
Thread.currentThread().setContextClassLoader(null)
def res = ReflectionUtil.getResourcesFromClassLoader(JarFile.MANIFEST_NAME)
then:
res.size() >= 1

cleanup:
Thread.currentThread().setContextClassLoader(oldLoader)
}

def "check if class exists"() {
expect:
ReflectionUtil.isClassAvailable("java.util.List")
Expand Down

0 comments on commit 8017fc3

Please sign in to comment.