Add support to @ClassPathExclusions for excluding packages
Closes gh-36120
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2022 the original author or authors.
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -25,6 +25,8 @@ import java.lang.annotation.Target;
|
||||
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
|
||||
/**
|
||||
* Annotation used to exclude entries from the classpath.
|
||||
*
|
||||
@@ -38,12 +40,33 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
||||
public @interface ClassPathExclusions {
|
||||
|
||||
/**
|
||||
* Alias for {@code files}.
|
||||
* <p>
|
||||
* One or more Ant-style patterns that identify entries to be excluded from the class
|
||||
* path. Matching is performed against an entry's {@link File#getName() file name}.
|
||||
* For example, to exclude Hibernate Validator from the classpath,
|
||||
* {@code "hibernate-validator-*.jar"} can be used.
|
||||
* @return the exclusion patterns
|
||||
*/
|
||||
String[] value();
|
||||
@AliasFor("files")
|
||||
String[] value() default {};
|
||||
|
||||
/**
|
||||
* One or more Ant-style patterns that identify entries to be excluded from the class
|
||||
* path. Matching is performed against an entry's {@link File#getName() file name}.
|
||||
* For example, to exclude Hibernate Validator from the classpath,
|
||||
* {@code "hibernate-validator-*.jar"} can be used.
|
||||
* @return the exclusion patterns
|
||||
* @since 3.2.0
|
||||
*/
|
||||
@AliasFor("value")
|
||||
String[] files() default {};
|
||||
|
||||
/**
|
||||
* One or more packages that should be excluded from the classpath.
|
||||
* @return the excluded packages
|
||||
* @since 3.2.0
|
||||
*/
|
||||
String[] packages() default {};
|
||||
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import java.net.URLClassLoader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -56,6 +57,7 @@ import org.eclipse.aether.transport.http.HttpTransporterFactory;
|
||||
import org.springframework.core.annotation.MergedAnnotation;
|
||||
import org.springframework.core.annotation.MergedAnnotations;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ConcurrentReferenceHashMap;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -74,10 +76,14 @@ final class ModifiedClassPathClassLoader extends URLClassLoader {
|
||||
|
||||
private static final int MAX_RESOLUTION_ATTEMPTS = 5;
|
||||
|
||||
private final Set<String> excludedPackages;
|
||||
|
||||
private final ClassLoader junitLoader;
|
||||
|
||||
ModifiedClassPathClassLoader(URL[] urls, ClassLoader parent, ClassLoader junitLoader) {
|
||||
ModifiedClassPathClassLoader(URL[] urls, Set<String> excludedPackages, ClassLoader parent,
|
||||
ClassLoader junitLoader) {
|
||||
super(urls, parent);
|
||||
this.excludedPackages = excludedPackages;
|
||||
this.junitLoader = junitLoader;
|
||||
}
|
||||
|
||||
@@ -87,6 +93,10 @@ final class ModifiedClassPathClassLoader extends URLClassLoader {
|
||||
|| name.startsWith("io.netty.internal.tcnative")) {
|
||||
return Class.forName(name, false, this.junitLoader);
|
||||
}
|
||||
String packageName = ClassUtils.getPackageName(name);
|
||||
if (this.excludedPackages.contains(packageName)) {
|
||||
throw new ClassNotFoundException();
|
||||
}
|
||||
return super.loadClass(name);
|
||||
}
|
||||
|
||||
@@ -130,7 +140,7 @@ final class ModifiedClassPathClassLoader extends URLClassLoader {
|
||||
.map((source) -> MergedAnnotations.from(source, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY))
|
||||
.toList();
|
||||
return new ModifiedClassPathClassLoader(processUrls(extractUrls(classLoader), annotations),
|
||||
classLoader.getParent(), classLoader);
|
||||
excludedPackages(annotations), classLoader.getParent(), classLoader);
|
||||
}
|
||||
|
||||
private static URL[] extractUrls(ClassLoader classLoader) {
|
||||
@@ -269,6 +279,17 @@ final class ModifiedClassPathClassLoader extends URLClassLoader {
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
private static Set<String> excludedPackages(List<MergedAnnotations> annotations) {
|
||||
Set<String> excludedPackages = new HashSet<>();
|
||||
for (MergedAnnotations candidate : annotations) {
|
||||
MergedAnnotation<ClassPathExclusions> annotation = candidate.get(ClassPathExclusions.class);
|
||||
if (annotation.isPresent()) {
|
||||
excludedPackages.addAll(Arrays.asList(annotation.getStringArray("packages")));
|
||||
}
|
||||
}
|
||||
return excludedPackages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter for class path entries.
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -19,6 +19,8 @@ package org.springframework.boot.testsupport.classpath;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.hamcrest.Matchers.isA;
|
||||
|
||||
@@ -26,22 +28,34 @@ import static org.hamcrest.Matchers.isA;
|
||||
* Tests for {@link ModifiedClassPathExtension} excluding entries from the class path.
|
||||
*
|
||||
* @author Christoph Dreis
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@ClassPathExclusions("hibernate-validator-*.jar")
|
||||
@ClassPathExclusions(files = "hibernate-validator-*.jar", packages = "java.net.http")
|
||||
class ModifiedClassPathExtensionExclusionsTests {
|
||||
|
||||
private static final String EXCLUDED_RESOURCE = "META-INF/services/jakarta.validation.spi.ValidationProvider";
|
||||
|
||||
@Test
|
||||
void entriesAreFilteredFromTestClassClassLoader() {
|
||||
void fileExclusionsAreFilteredFromTestClassClassLoader() {
|
||||
assertThat(getClass().getClassLoader().getResource(EXCLUDED_RESOURCE)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void entriesAreFilteredFromThreadContextClassLoader() {
|
||||
void fileExclusionsAreFilteredFromThreadContextClassLoader() {
|
||||
assertThat(Thread.currentThread().getContextClassLoader().getResource(EXCLUDED_RESOURCE)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void packageExclusionsAreFilteredFromTestClassClassLoader() {
|
||||
assertThat(ClassUtils.isPresent("java.net.http.HttpClient", getClass().getClassLoader())).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void packageExclusionsAreFilteredFromThreadContextClassLoader() {
|
||||
assertThat(ClassUtils.isPresent("java.net.http.HttpClient", Thread.currentThread().getContextClassLoader()))
|
||||
.isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testsThatUseHamcrestWorkCorrectly() {
|
||||
Matcher<IllegalStateException> matcher = isA(IllegalStateException.class);
|
||||
|
||||
Reference in New Issue
Block a user