Skip to content

Commit 4a896f1

Browse files
committed
Add Java 9+ variant for optimized ServiceLoader filtering
Issue: #3717 Signed-off-by: yongjunhong <[email protected]>
1 parent 07a6ae3 commit 4a896f1

File tree

4 files changed

+121
-10
lines changed

4 files changed

+121
-10
lines changed

junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/MutableExtensionRegistry.java

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import org.junit.platform.commons.util.ClassLoaderUtils;
4141
import org.junit.platform.commons.util.ClassNamePatternFilterUtils;
4242
import org.junit.platform.commons.util.Preconditions;
43+
import org.junit.platform.commons.util.ServiceLoaderUtils;
4344

4445
/**
4546
* Default, mutable implementation of {@link ExtensionRegistry}.
@@ -97,21 +98,21 @@ public static MutableExtensionRegistry createRegistryWithDefaultExtensions(Jupit
9798

9899
private static void registerAutoDetectedExtensions(MutableExtensionRegistry extensionRegistry,
99100
JupiterConfiguration configuration) {
100-
Predicate<String> filter = createExtensionFilterByPatterns(
101+
Predicate<? super Class<? extends Extension>> filter = createExtensionFilterByPatterns(
101102
configuration.getExtensionAutodetectionIncludePattern().orElse("*"),
102103
configuration.getExtensionAutodetectionExcludePattern().orElse(""));
103104

104-
ServiceLoader.load(Extension.class, ClassLoaderUtils.getDefaultClassLoader())//
105-
.forEach(extension -> {
106-
if (filter.test(extension.getClass().getName())) {
107-
extensionRegistry.registerAutoDetectedExtension(extension);
108-
}
109-
});
105+
ServiceLoaderUtils.load(Extension.class, filter, ClassLoaderUtils.getDefaultClassLoader()) //
106+
.forEach(extensionRegistry::registerAutoDetectedExtension);
110107
}
111108

112-
private static Predicate<String> createExtensionFilterByPatterns(String include, String exclude) {
113-
return ClassNamePatternFilterUtils.includeMatchingClassNames(include) //
114-
.and(ClassNamePatternFilterUtils.excludeMatchingClassNames(exclude));
109+
private static Predicate<? super Class<? extends Extension>> createExtensionFilterByPatterns(String include,
110+
String exclude) {
111+
return clazz -> {
112+
String className = clazz.getName();
113+
return ClassNamePatternFilterUtils.includeMatchingClassNames(include).test(className) //
114+
&& ClassNamePatternFilterUtils.excludeMatchingClassNames(exclude).test(className);
115+
};
115116
}
116117

117118
/**

junit-platform-commons/junit-platform-commons.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ tasks.jar {
3030
tasks.codeCoverageClassesJar {
3131
exclude("org/junit/platform/commons/util/ModuleUtils.class")
3232
exclude("org/junit/platform/commons/util/PackageNameUtils.class")
33+
exclude("org/junit/platform/commons/util/ServiceLoaderUtils.class")
3334
}
3435

3536
eclipse {
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright 2015-2024 the original author or authors.
3+
*
4+
* All rights reserved. This program and the accompanying materials are
5+
* made available under the terms of the Eclipse Public License v2.0 which
6+
* accompanies this distribution and is available at
7+
*
8+
* https://www.eclipse.org/legal/epl-v20.html
9+
*/
10+
11+
package org.junit.platform.commons.util;
12+
13+
import java.util.ServiceLoader;
14+
import java.util.function.Predicate;
15+
import java.util.stream.Stream;
16+
import java.util.stream.StreamSupport;
17+
18+
import org.apiguardian.api.API;
19+
20+
/**
21+
* Collection of utilities for working with {@link ServiceLoader}.
22+
*
23+
* <h2>DISCLAIMER</h2>
24+
*
25+
* <p>These utilities are intended solely for usage within the JUnit framework
26+
* itself. <strong>Any usage by external parties is not supported.</strong>
27+
* Use at your own risk!
28+
*
29+
* @since 5.11
30+
*/
31+
@API(status = API.Status.INTERNAL, since = "5.11")
32+
public final class ServiceLoaderUtils {
33+
34+
private ServiceLoaderUtils() {
35+
/* no-op */
36+
}
37+
38+
/**
39+
* Loads services of the given type using the specified class loader and filters them using the provided predicate.
40+
*
41+
* @param <T> the type of the service
42+
* @param service the class of the service to be loaded
43+
* @param providerPredicate the predicate to filter the loaded services
44+
* @param loader the class loader to be used to load the services
45+
* @return a stream of loaded services that match the predicate
46+
*/
47+
public static <T> Stream<T> load(Class<T> service, Predicate<? super Class<? extends T>> providerPredicate,
48+
ClassLoader loader) {
49+
return StreamSupport.stream(ServiceLoader.load(service, loader).spliterator(), false).filter(it -> {
50+
@SuppressWarnings("unchecked")
51+
Class<? extends T> type = (Class<? extends T>) it.getClass();
52+
return providerPredicate.test(type);
53+
});
54+
}
55+
56+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright 2015-2024 the original author or authors.
3+
*
4+
* All rights reserved. This program and the accompanying materials are
5+
* made available under the terms of the Eclipse Public License v2.0 which
6+
* accompanies this distribution and is available at
7+
*
8+
* https://www.eclipse.org/legal/epl-v20.html
9+
*/
10+
11+
package org.junit.platform.commons.util;
12+
13+
import java.util.ServiceLoader;
14+
import java.util.function.Predicate;
15+
import java.util.stream.Stream;
16+
17+
import org.apiguardian.api.API;
18+
import org.apiguardian.api.API.Status;
19+
20+
/**
21+
* Collection of utilities for working with {@link ServiceLoader}.
22+
*
23+
* <h2>DISCLAIMER</h2>
24+
*
25+
* <p>These utilities are intended solely for usage within the JUnit framework
26+
* itself. <strong>Any usage by external parties is not supported.</strong>
27+
* Use at your own risk!
28+
*
29+
* @since 5.11
30+
*/
31+
@API(status = Status.INTERNAL, since = "5.11")
32+
public class ServiceLoaderUtils {
33+
34+
/**
35+
* Loads services of the given type using the specified class loader and filters them using the provided predicate.
36+
*
37+
* @param <T> the type of the service
38+
* @param service the class of the service to be loaded
39+
* @param providerPredicate the predicate to filter the loaded services
40+
* @param loader the class loader to be used to load the services
41+
* @return a stream of loaded services that match the predicate
42+
*/
43+
public static <T> Stream<T> load(Class<T> service, Predicate<? super Class<? extends T>> providerPredicate,
44+
ClassLoader loader) {
45+
// @formatter:off
46+
return ServiceLoader.load(service, loader)
47+
.stream()
48+
.filter(provider -> providerPredicate.test(provider.type()))
49+
.map(ServiceLoader.Provider::get);
50+
// @formatter:on
51+
}
52+
53+
}

0 commit comments

Comments
 (0)