Skip to content

Commit

Permalink
Merge pull request #5743 from bjhargrave/issues/5568
Browse files Browse the repository at this point in the history
tester: Use CompositeClassLoader for TCCL when loading tests
  • Loading branch information
bjhargrave authored Aug 7, 2023
2 parents 58b5998 + bef0458 commit 535466e
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 2 deletions.
121 changes: 121 additions & 0 deletions aQute.libg/src/aQute/libg/classloaders/CompositeClassLoader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package aQute.libg.classloaders;

import java.io.IOException;
import java.net.URL;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

public class CompositeClassLoader extends ClassLoader {
@SafeVarargs
public static ClassLoader of(ClassLoader... elements) {
List<ClassLoader> list = Arrays.stream(elements)
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
int size = list.size();
if (size == 0) {
throw new IllegalArgumentException("Must have at least one element");
}
if (size == 1) {
return list.get(0);
}
return new CompositeClassLoader(list);
}

private final List<ClassLoader> elements;

private CompositeClassLoader(List<ClassLoader> elements) {
super(null); // no parent
this.elements = List.copyOf(elements); // immutable list
}

@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
Stream<Class<?>> classes = elements.stream()
.<Class<?>>map(loader -> {
try {
return loader.loadClass(name);
} catch (ClassNotFoundException e) {
return null;
}
})
.filter(Objects::nonNull);
Class<?> result = classes.findFirst()
.orElseThrow(() -> new ClassNotFoundException(name));
return result;
}

@Override
public URL getResource(String name) {
Stream<URL> resources = elements.stream()
.<URL>map(loader -> loader.getResource(name))
.filter(Objects::nonNull);
URL result = resources.findFirst()
.orElse(null);
return result;
}

@Override
public Enumeration<URL> getResources(String name) throws IOException {
Stream<URL> resources = elements.stream()
.<Enumeration<URL>>map(loader -> {
try {
return loader.getResources(name);
} catch (IOException e) {
return Collections.emptyEnumeration();
}
})
.map(enumeration -> Spliterators.spliteratorUnknownSize(enumeration.asIterator(), Spliterator.ORDERED))
.flatMap(spliterator -> StreamSupport.stream(spliterator, false))
.distinct();
Enumeration<URL> result = new SpliteratorEnumeration<>(resources.spliterator());
return result;
}

final static class SpliteratorEnumeration<T> implements Enumeration<T>, Consumer<T> {
private final Spliterator<T> spliterator;
private boolean hasNext = false;
private T next;

SpliteratorEnumeration(Spliterator<T> spliterator) {
this.spliterator = Objects.requireNonNull(spliterator);
}

@Override
public boolean hasMoreElements() {
if (hasNext) {
return true;
}
if (spliterator.tryAdvance(this)) {
return hasNext = true;
}
return false;
}

@Override
public T nextElement() {
if (hasMoreElements()) {
T t = next;
hasNext = false;
next = null;
return t;
}
throw new NoSuchElementException();
}

@Override
public void accept(T t) {
next = t;
}
}
}
4 changes: 4 additions & 0 deletions aQute.libg/src/aQute/libg/classloaders/package-info.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@Version("1.1.0")
package aQute.libg.classloaders;

import org.osgi.annotation.versioning.Version;
1 change: 0 additions & 1 deletion aQute.libg/src/aQute/libg/classloaders/packageinfo

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
import org.osgi.util.tracker.ServiceTracker;

import aQute.junit.system.BndSystem;
import aQute.libg.classloaders.CompositeClassLoader;
import aQute.tester.bundle.engine.BundleEngine;
import aQute.tester.bundle.engine.discovery.BundleSelector;
import aQute.tester.junit.platform.reporting.legacy.xml.LegacyXmlReportGeneratingListener;
Expand Down Expand Up @@ -129,7 +130,7 @@ public void run() {
// We can be started on our own thread or from the main code
thread = Thread.currentThread();
final ClassLoader contextClassLoader = thread.getContextClassLoader();
thread.setContextClassLoader(getClass().getClassLoader());
thread.setContextClassLoader(CompositeClassLoader.of(getClass().getClassLoader(), contextClassLoader));
try {
launcher = LauncherFactory.create(LauncherConfig.builder()
.enableTestEngineAutoRegistration(false)
Expand Down

0 comments on commit 535466e

Please sign in to comment.