Commit d3ca1a7b authored by Roy Jacobs's avatar Roy Jacobs Committed by Stephane Nicoll

Allow ClassPathResources to be filtered by FilteredClassLoader

See gh-14774
parent 29c0aa44
...@@ -20,12 +20,15 @@ import java.net.URL; ...@@ -20,12 +20,15 @@ import java.net.URL;
import java.net.URLClassLoader; import java.net.URLClassLoader;
import java.util.function.Predicate; import java.util.function.Predicate;
import org.springframework.core.io.ClassPathResource;
/** /**
* Test {@link URLClassLoader} that can filter the classes it can load. * Test {@link URLClassLoader} that can filter the classes and resources it can load.
* *
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Phillip Webb * @author Phillip Webb
* @author Roy Jacobs
* @since 2.0.0 * @since 2.0.0
*/ */
public class FilteredClassLoader extends URLClassLoader { public class FilteredClassLoader extends URLClassLoader {
...@@ -48,10 +51,19 @@ public class FilteredClassLoader extends URLClassLoader { ...@@ -48,10 +51,19 @@ public class FilteredClassLoader extends URLClassLoader {
this(PackageFilter.of(hiddenPackages)); this(PackageFilter.of(hiddenPackages));
} }
/**
* Create a {@link FilteredClassLoader} that hides resources from the given packages.
* @param hiddenResources the resources to hide
*/
public FilteredClassLoader(ClassPathResource... hiddenResources) {
this(ClassPathResourceFilter.of(hiddenResources));
}
/** /**
* Create a {@link FilteredClassLoader} that filters based on the given predicate. * Create a {@link FilteredClassLoader} that filters based on the given predicate.
* @param filters a set of filters to determine when a class name should be hidden. A * @param filters a set of filters to determine when a class name or resource should
* {@link Predicate#test(Object) result} of {@code true} indicates a filtered class. * be hidden. A {@link Predicate#test(Object) result} of {@code true} indicates a
* filtered class or resource.
*/ */
@SafeVarargs @SafeVarargs
public FilteredClassLoader(Predicate<String>... filters) { public FilteredClassLoader(Predicate<String>... filters) {
...@@ -70,6 +82,16 @@ public class FilteredClassLoader extends URLClassLoader { ...@@ -70,6 +82,16 @@ public class FilteredClassLoader extends URLClassLoader {
return super.loadClass(name, resolve); return super.loadClass(name, resolve);
} }
@Override
public URL getResource(String name) {
for (Predicate<String> filter : this.filters) {
if (filter.test(name)) {
return null;
}
}
return super.getResource(name);
}
/** /**
* Filter to restrict the classes that can be loaded. * Filter to restrict the classes that can be loaded.
*/ */
...@@ -124,4 +146,32 @@ public class FilteredClassLoader extends URLClassLoader { ...@@ -124,4 +146,32 @@ public class FilteredClassLoader extends URLClassLoader {
} }
/**
* Filter to restrict the resources that can be loaded.
*/
public static final class ClassPathResourceFilter implements Predicate<String> {
private final ClassPathResource[] hiddenResources;
private ClassPathResourceFilter(ClassPathResource[] hiddenResources) {
this.hiddenResources = hiddenResources;
}
@Override
public boolean test(String resourceName) {
for (ClassPathResource hiddenResource : this.hiddenResources) {
if (hiddenResource.getFilename() != null
&& resourceName.equals(hiddenResource.getPath())) {
return true;
}
}
return false;
}
public static ClassPathResourceFilter of(ClassPathResource... hiddenResources) {
return new ClassPathResourceFilter(hiddenResources);
}
}
} }
...@@ -16,8 +16,12 @@ ...@@ -16,8 +16,12 @@
package org.springframework.boot.test.context; package org.springframework.boot.test.context;
import java.net.URL;
import org.junit.Test; import org.junit.Test;
import org.springframework.core.io.ClassPathResource;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
...@@ -25,9 +29,13 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; ...@@ -25,9 +29,13 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
* Tests for {@link FilteredClassLoader}. * Tests for {@link FilteredClassLoader}.
* *
* @author Phillip Webb * @author Phillip Webb
* @author Roy Jacobs
*/ */
public class FilteredClassLoaderTests { public class FilteredClassLoaderTests {
private static ClassPathResource TEST_RESOURCE = new ClassPathResource(
"org/springframework/boot/test/context/FilteredClassLoaderTestResource.txt");
@Test @Test
public void loadClassWhenFilteredOnPackageShouldThrowClassNotFound() public void loadClassWhenFilteredOnPackageShouldThrowClassNotFound()
throws Exception { throws Exception {
...@@ -55,4 +63,22 @@ public class FilteredClassLoaderTests { ...@@ -55,4 +63,22 @@ public class FilteredClassLoaderTests {
classLoader.close(); classLoader.close();
} }
@Test
public void loadResourceWhenFilteredOnResourceShouldReturnNotFound()
throws Exception {
try (FilteredClassLoader classLoader = new FilteredClassLoader(TEST_RESOURCE)) {
final URL loaded = classLoader.getResource(TEST_RESOURCE.getPath());
assertThat(loaded).isNull();
}
}
@Test
public void loadResourceWhenNotFilteredShouldLoadResource() throws Exception {
try (FilteredClassLoader classLoader = new FilteredClassLoader(
(resourceName) -> false)) {
final URL loaded = classLoader.getResource(TEST_RESOURCE.getPath());
assertThat(loaded).isNotNull();
}
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment