Skip to content

Commit

Permalink
Add Java 9+ variant for optimized ServiceLoader filtering
Browse files Browse the repository at this point in the history
Issue: junit-team#3717
Signed-off-by: yongjunhong <[email protected]>
  • Loading branch information
YongGoose committed Nov 14, 2024
1 parent 07a6ae3 commit 9f4f2a4
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import org.junit.platform.commons.util.ClassLoaderUtils;
import org.junit.platform.commons.util.ClassNamePatternFilterUtils;
import org.junit.platform.commons.util.Preconditions;
import org.junit.platform.commons.util.ServiceLoaderUtils;

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

private static void registerAutoDetectedExtensions(MutableExtensionRegistry extensionRegistry,
JupiterConfiguration configuration) {
Predicate<String> filter = createExtensionFilterByPatterns(
Predicate<? super Class<? extends Extension>> filter = createExtensionFilterByPatterns(
configuration.getExtensionAutodetectionIncludePattern().orElse("*"),
configuration.getExtensionAutodetectionExcludePattern().orElse(""));

ServiceLoader.load(Extension.class, ClassLoaderUtils.getDefaultClassLoader())//
.forEach(extension -> {
if (filter.test(extension.getClass().getName())) {
extensionRegistry.registerAutoDetectedExtension(extension);
}
});
ServiceLoaderUtils.load(Extension.class, filter, ClassLoaderUtils.getDefaultClassLoader()) //
.forEach(extensionRegistry::registerAutoDetectedExtension);
}

private static Predicate<String> createExtensionFilterByPatterns(String include, String exclude) {
return ClassNamePatternFilterUtils.includeMatchingClassNames(include) //
.and(ClassNamePatternFilterUtils.excludeMatchingClassNames(exclude));
private static Predicate<? super Class<? extends Extension>> createExtensionFilterByPatterns(String include,
String exclude) {
return clazz -> {
String className = clazz.getName();
return ClassNamePatternFilterUtils.includeMatchingClassNames(include).test(className) //
&& ClassNamePatternFilterUtils.excludeMatchingClassNames(exclude).test(className);
};
}

/**
Expand Down
1 change: 1 addition & 0 deletions junit-platform-commons/junit-platform-commons.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ tasks.jar {
tasks.codeCoverageClassesJar {
exclude("org/junit/platform/commons/util/ModuleUtils.class")
exclude("org/junit/platform/commons/util/PackageNameUtils.class")
exclude("org/junit/platform/commons/util/ServiceloaderUtils.class")
}

eclipse {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright 2015-2024 the original author or authors.
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v2.0 which
* accompanies this distribution and is available at
*
* https://www.eclipse.org/legal/epl-v20.html
*/

package org.junit.platform.commons.util;

import java.util.ServiceLoader;
import java.util.function.Predicate;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import org.apiguardian.api.API;

/**
* Collection of utilities for working with {@link ServiceLoader}.
*
* <h2>DISCLAIMER</h2>
*
* <p>These utilities are intended solely for usage within the JUnit framework
* itself. <strong>Any usage by external parties is not supported.</strong>
* Use at your own risk!
*
* @since 5.11
*/
@API(status = API.Status.INTERNAL, since = "5.11")
public final class ServiceLoaderUtils {

private ServiceLoaderUtils() {
/* no-op */
}

/**
* Loads services of the given type using the specified class loader and filters them using the provided predicate.
*
* @param <T> the type of the service
* @param service the class of the service to be loaded
* @param providerPredicate the predicate to filter the loaded services
* @param loader the class loader to be used to load the services
* @return a stream of loaded services that match the predicate
*/
public static <T> Stream<T> load(Class<T> service, Predicate<? super Class<? extends T>> providerPredicate,
ClassLoader loader) {

return StreamSupport.stream(ServiceLoader.load(service, loader).spliterator(), false)
.filter(it -> {
@SuppressWarnings("unchecked") Class<? extends T> type = (Class<? extends T>) it.getClass();
return providerPredicate.test(type);
});
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright 2015-2024 the original author or authors.
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v2.0 which
* accompanies this distribution and is available at
*
* https://www.eclipse.org/legal/epl-v20.html
*/

package org.junit.platform.commons.util;

import java.util.ServiceLoader;
import java.util.function.Predicate;
import java.util.stream.Stream;

import org.apiguardian.api.API;
import org.apiguardian.api.API.Status;

/**
* Collection of utilities for working with {@link ServiceLoader}.
*
* <h2>DISCLAIMER</h2>
*
* <p>These utilities are intended solely for usage within the JUnit framework
* itself. <strong>Any usage by external parties is not supported.</strong>
* Use at your own risk!
*
* @since 5.11
*/
@API(status = Status.INTERNAL, since = "5.11")
public class ServiceLoaderUtils {

/**
* Loads services of the given type using the specified class loader and filters them using the provided predicate.
*
* @param <T> the type of the service
* @param service the class of the service to be loaded
* @param providerPredicate the predicate to filter the loaded services
* @param loader the class loader to be used to load the services
* @return a stream of loaded services that match the predicate
*/
public static <T> Stream<T> load(Class<T> service, Predicate<? super Class<? extends T>> providerPredicate,
ClassLoader loader) {
return ServiceLoader.load(service, loader).stream()
.filter(provider -> providerPredicate.test(provider.type()))
.map(ServiceLoader.Provider::get);
}

}

0 comments on commit 9f4f2a4

Please sign in to comment.