From 9f4f2a42a38b91f10ecfefb651bda2383d3e6137 Mon Sep 17 00:00:00 2001 From: yongjunhong Date: Fri, 15 Nov 2024 01:09:09 +0900 Subject: [PATCH] Add Java 9+ variant for optimized ServiceLoader filtering Issue: #3717 Signed-off-by: yongjunhong --- .../extension/MutableExtensionRegistry.java | 21 +++---- .../junit-platform-commons.gradle.kts | 1 + .../commons/util/ServiceLoaderUtils.java | 57 +++++++++++++++++++ .../commons/util/ServiceLoaderUtils.java | 50 ++++++++++++++++ 4 files changed, 119 insertions(+), 10 deletions(-) create mode 100644 junit-platform-commons/src/main/java/org/junit/platform/commons/util/ServiceLoaderUtils.java create mode 100644 junit-platform-commons/src/main/java9/org/junit/platform/commons/util/ServiceLoaderUtils.java diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/MutableExtensionRegistry.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/MutableExtensionRegistry.java index 23ef07ca68cf..df6370b4c232 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/MutableExtensionRegistry.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/MutableExtensionRegistry.java @@ -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}. @@ -97,21 +98,21 @@ public static MutableExtensionRegistry createRegistryWithDefaultExtensions(Jupit private static void registerAutoDetectedExtensions(MutableExtensionRegistry extensionRegistry, JupiterConfiguration configuration) { - Predicate filter = createExtensionFilterByPatterns( + Predicate> 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 createExtensionFilterByPatterns(String include, String exclude) { - return ClassNamePatternFilterUtils.includeMatchingClassNames(include) // - .and(ClassNamePatternFilterUtils.excludeMatchingClassNames(exclude)); + private static Predicate> createExtensionFilterByPatterns(String include, + String exclude) { + return clazz -> { + String className = clazz.getName(); + return ClassNamePatternFilterUtils.includeMatchingClassNames(include).test(className) // + && ClassNamePatternFilterUtils.excludeMatchingClassNames(exclude).test(className); + }; } /** diff --git a/junit-platform-commons/junit-platform-commons.gradle.kts b/junit-platform-commons/junit-platform-commons.gradle.kts index ef52ce77ab56..051e2937c844 100644 --- a/junit-platform-commons/junit-platform-commons.gradle.kts +++ b/junit-platform-commons/junit-platform-commons.gradle.kts @@ -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 { diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ServiceLoaderUtils.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ServiceLoaderUtils.java new file mode 100644 index 000000000000..fb6f7f3e5c56 --- /dev/null +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ServiceLoaderUtils.java @@ -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}. + * + *

DISCLAIMER

+ * + *

These utilities are intended solely for usage within the JUnit framework + * itself. Any usage by external parties is not supported. + * 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 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 Stream load(Class service, Predicate> providerPredicate, + ClassLoader loader) { + + return StreamSupport.stream(ServiceLoader.load(service, loader).spliterator(), false) + .filter(it -> { + @SuppressWarnings("unchecked") Class type = (Class) it.getClass(); + return providerPredicate.test(type); + }); + } + +} diff --git a/junit-platform-commons/src/main/java9/org/junit/platform/commons/util/ServiceLoaderUtils.java b/junit-platform-commons/src/main/java9/org/junit/platform/commons/util/ServiceLoaderUtils.java new file mode 100644 index 000000000000..4186b782e213 --- /dev/null +++ b/junit-platform-commons/src/main/java9/org/junit/platform/commons/util/ServiceLoaderUtils.java @@ -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}. + * + *

DISCLAIMER

+ * + *

These utilities are intended solely for usage within the JUnit framework + * itself. Any usage by external parties is not supported. + * 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 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 Stream load(Class service, Predicate> providerPredicate, + ClassLoader loader) { + return ServiceLoader.load(service, loader).stream() + .filter(provider -> providerPredicate.test(provider.type())) + .map(ServiceLoader.Provider::get); + } + +}