@ -1,5 +1,5 @@
@@ -1,5 +1,5 @@
/ *
* Copyright 2002 - 2019 the original author or authors .
* Copyright 2002 - 2022 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 .
@ -16,14 +16,16 @@
@@ -16,14 +16,16 @@
package org.springframework.core.io.support ;
import java.io.File ;
import java.io.FileNotFoundException ;
import java.io.IOException ;
import java.util.ArrayList ;
import java.io.UncheckedIOException ;
import java.nio.file.Path ;
import java.nio.file.Paths ;
import java.util.Arrays ;
import java.util.List ;
import java.util.stream.Collectors ;
import org.junit.jupiter.api.Disabl ed ;
import org.junit.jupiter.api.Nest ed ;
import org.junit.jupiter.api.Test ;
import org.springframework.core.io.Resource ;
@ -33,8 +35,9 @@ import static org.assertj.core.api.Assertions.assertThat;
@@ -33,8 +35,9 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType ;
/ * *
* If this test case fails , uncomment diagnostics in the
* { @link # assertProtocolAndFilenames } method .
* Tests for { @link PathMatchingResourcePatternResolver } .
*
* < p > If tests fail , uncomment the diagnostics in { @link # assertFilenames ( String , boolean , String . . . ) } .
*
* @author Oliver Hutchison
* @author Juergen Hoeller
@ -44,124 +47,138 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
@@ -44,124 +47,138 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
* /
class PathMatchingResourcePatternResolverTests {
private static final String [ ] CLASSES_IN_CORE_IO_SUPPORT =
new String [ ] { "EncodedResource.class" , "LocalizedResourceHelper.class" ,
"PathMatchingResourcePatternResolver.class" , "PropertiesLoaderSupport.class" ,
"PropertiesLoaderUtils.class" , "ResourceArrayPropertyEditor.class" ,
"ResourcePatternResolver.class" , "ResourcePatternUtils.class" } ;
private static final String [ ] CLASSES_IN_CORE_IO_SUPPORT = { "EncodedResource.class" ,
"LocalizedResourceHelper.class" , "PathMatchingResourcePatternResolver.class" , "PropertiesLoaderSupport.class" ,
"PropertiesLoaderUtils.class" , "ResourceArrayPropertyEditor.class" , "ResourcePatternResolver.class" ,
"ResourcePatternUtils.class" , "SpringFactoriesLoader.class" } ;
private static final String [ ] TEST_CLASSES_IN_CORE_IO_SUPPORT =
new String [ ] { "PathMatchingResourcePatternResolverTests.class" } ;
private static final String [ ] TEST_CLASSES_IN_CORE_IO_SUPPORT = { "PathMatchingResourcePatternResolverTests.class" } ;
private static final String [ ] CLASSES_IN_REACTOR_UTIL_ANNOTATIONS =
new String [ ] { "NonNull.class" , "NonNullApi.class" , "Nullable.class" } ;
private static final String [ ] CLASSES_IN_REACTOR_UTIL_ANNOTATION = { "NonNull.class" , "NonNullApi.class" , "Nullable.class" } ;
private PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver ( ) ;
private final PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver ( ) ;
@Test
void invalidPrefixWithPatternElementInIt ( ) throws IOException {
assertThatExceptionOfType ( FileNotFoundException . class ) . isThrownBy ( ( ) - >
resolver . getResources ( "xx**:**/*.xy" ) ) ;
}
@Test
void singleResourceOnFileSystem ( ) throws IOException {
Resource [ ] resources =
resolver . getResources ( "org/springframework/core/io/support/PathMatchingResourcePatternResolverTests.class" ) ;
assertThat ( resources . length ) . isEqualTo ( 1 ) ;
assertProtocolAndFilenames ( resources , "file" , "PathMatchingResourcePatternResolverTests.class" ) ;
}
@Nested
class InvalidPatterns {
@Test
void invalidPrefixWithPatternElementInItThrowsException ( ) {
assertThatExceptionOfType ( FileNotFoundException . class ) . isThrownBy ( ( ) - > resolver . getResources ( "xx**:**/*.xy" ) ) ;
}
@Test
void singleResourceInJar ( ) throws IOException {
Resource [ ] resources = resolver . getResources ( "org/reactivestreams/Publisher.class" ) ;
assertThat ( resources . length ) . isEqualTo ( 1 ) ;
assertProtocolAndFilenames ( resources , "jar" , "Publisher.class" ) ;
}
@Disabled
@Test
void classpathStarWithPatternOnFileSystem ( ) throws IOException {
Resource [ ] resources = resolver . getResources ( "classpath*:org/springframework/core/io/sup*/*.class" ) ;
// Have to exclude Clover-generated class files here,
// as we might be running as part of a Clover test run.
List < Resource > noCloverResources = new ArrayList < > ( ) ;
for ( Resource resource : resources ) {
if ( ! resource . getFilename ( ) . contains ( "$__CLOVER_" ) ) {
noCloverResources . add ( resource ) ;
}
@Nested
class FileSystemResources {
@Test
void singleResourceOnFileSystem ( ) {
String pattern = "org/springframework/core/io/support/PathMatchingResourcePatternResolverTests.class" ;
assertExactFilenames ( pattern , "PathMatchingResourcePatternResolverTests.class" ) ;
}
resources = noCloverResources . toArray ( new Resource [ 0 ] ) ;
assertProtocolAndFilenames ( resources , "file" ,
StringUtils . concatenateStringArrays ( CLASSES_IN_CORE_IO_SUPPORT , TEST_CLASSES_IN_CORE_IO_SUPPORT ) ) ;
}
@Test
void getResourcesOnFileSystemContainingHashtagsInTheirFileNames ( ) throws IOException {
Resource [ ] resources = resolver . getResources ( "classpath*:org/springframework/core/io/**/resource#test*.txt" ) ;
assertThat ( resources ) . extracting ( Resource : : getFile ) . extracting ( File : : getName )
. containsExactlyInAnyOrder ( "resource#test1.txt" , "resource#test2.txt" ) ;
}
@Test
void classpathStarWithPatternOnFileSystem ( ) {
String pattern = "classpath*:org/springframework/core/io/sup*/*.class" ;
String [ ] expectedFilenames = StringUtils . concatenateStringArrays ( CLASSES_IN_CORE_IO_SUPPORT , TEST_CLASSES_IN_CORE_IO_SUPPORT ) ;
assertFilenames ( pattern , expectedFilenames ) ;
}
@Test
void classpathWithPatternInJar ( ) throws IOException {
Resource [ ] resources = resolver . getResources ( "classpath:reactor/util/annotation/*.class" ) ;
assertProtocolAndFilenames ( resources , "jar" , CLASSES_IN_REACTOR_UTIL_ANNOTATIONS ) ;
}
@Nested
class WithHashtagsInTheirFileNames {
@Test
void classpathStarWithPatternInJar ( ) throws IOException {
Resource [ ] resources = resolver . getResources ( "classpath*:reactor/util/annotation/*.class" ) ;
assertProtocolAndFilenames ( resources , "ja r" , CLASSES_IN_REACTOR_UTIL_ANNOTATIONS ) ;
}
@Test
void usingClasspathStarProtocol ( ) {
String pattern = "classpath*:org/springframework/core/io/**/resource#test*.txt" ;
assertExactFilenames ( pattern , "resource#test1.txt " , "resource#test2.txt" ) ;
}
@Test
void rootPatternRetrievalInJarFiles ( ) throws IOException {
Resource [ ] resources = resolver . getResources ( "classpath*:*.dtd" ) ;
boolean found = false ;
for ( Resource resource : resources ) {
if ( resource . getFilename ( ) . equals ( "aspectj_1_5_0.dtd" ) ) {
found = true ;
break ;
@Test
void usingFilePrototol ( ) {
Path testResourcesDir = Paths . get ( "src/test/resources" ) . toAbsolutePath ( ) ;
String pattern = String . format ( "file:%s/scanned-resources/**" , testResourcesDir ) ;
assertExactFilenames ( pattern , "resource#test1.txt" , "resource#test2.txt" ) ;
}
}
assertThat ( found ) . as ( "Could not find aspectj_1_5_0.dtd in the root of the aspectjweaver jar" ) . isTrue ( ) ;
}
private void assertProtocolAndFilenames ( Resource [ ] resources , String protocol , String . . . filenames )
throws IOException {
// Uncomment the following if you encounter problems with matching against the file system
// It shows file locations.
// String[] actualNames = new String[resources.length];
// for (int i = 0; i < resources.length; i++) {
// actualNames[i] = resources[i].getFilename();
// }
// List sortedActualNames = new LinkedList(Arrays.asList(actualNames));
// List expectedNames = new LinkedList(Arrays.asList(fileNames));
// Collections.sort(sortedActualNames);
// Collections.sort(expectedNames);
//
// System.out.println("-----------");
// System.out.println("Expected: " + StringUtils.collectionToCommaDelimitedString(expectedNames));
// System.out.println("Actual: " + StringUtils.collectionToCommaDelimitedString(sortedActualNames));
// for (int i = 0; i < resources.length; i++) {
// System.out.println(resources[i]);
// }
assertThat ( resources . length ) . as ( "Correct number of files found" ) . isEqualTo ( filenames . length ) ;
for ( Resource resource : resources ) {
String actualProtocol = resource . getURL ( ) . getProtocol ( ) ;
assertThat ( actualProtocol ) . isEqualTo ( protocol ) ;
assertFilenameIn ( resource , filenames ) ;
@Nested
class JarResources {
@Test
void singleResourceInJar ( ) {
String pattern = "org/reactivestreams/Publisher.class" ;
assertExactFilenames ( pattern , "Publisher.class" ) ;
}
@Test
void singleResourceInRootOfJar ( ) {
String pattern = "aspectj_1_5_0.dtd" ;
assertExactFilenames ( pattern , "aspectj_1_5_0.dtd" ) ;
}
@Test
void classpathWithPatternInJar ( ) {
String pattern = "classpath:reactor/util/annotation/*.class" ;
assertExactFilenames ( pattern , CLASSES_IN_REACTOR_UTIL_ANNOTATION ) ;
}
@Test
void classpathStarWithPatternInJar ( ) {
String pattern = "classpath*:reactor/util/annotation/*.class" ;
assertExactFilenames ( pattern , CLASSES_IN_REACTOR_UTIL_ANNOTATION ) ;
}
// Fails in a native image -- https://github.com/oracle/graal/issues/5020
@Test
void rootPatternRetrievalInJarFiles ( ) throws IOException {
assertThat ( resolver . getResources ( "classpath*:aspectj*.dtd" ) ) . extracting ( Resource : : getFilename )
. as ( "Could not find aspectj_1_5_0.dtd in the root of the aspectjweaver jar" )
. containsExactly ( "aspectj_1_5_0.dtd" ) ;
}
}
private void assertFilenameIn ( Resource resource , String . . . filenames ) {
String filename = resource . getFilename ( ) ;
assertThat ( Arrays . stream ( filenames ) . anyMatch ( filename : : endsWith ) ) . as ( resource + " does not have a filename that matches any of the specified names" ) . isTrue ( ) ;
private void assertFilenames ( String pattern , String . . . filenames ) {
assertFilenames ( pattern , false , filenames ) ;
}
private void assertExactFilenames ( String pattern , String . . . filenames ) {
assertFilenames ( pattern , true , filenames ) ;
}
private void assertFilenames ( String pattern , boolean exactly , String . . . filenames ) {
try {
Resource [ ] resources = resolver . getResources ( pattern ) ;
List < String > actualNames = Arrays . stream ( resources )
. map ( Resource : : getFilename )
. sorted ( )
. collect ( Collectors . toList ( ) ) ;
// Uncomment the following if you encounter problems with matching against the file system.
// List<String> expectedNames = Arrays.stream(filenames).sorted().toList();
// System.out.println("----------------------------------------------------------------------");
// System.out.println("Expected: " + expectedNames);
// System.out.println("Actual: " + actualNames);
// Arrays.stream(resources).forEach(System.out::println);
if ( exactly ) {
assertThat ( actualNames ) . as ( "subset of files found" ) . containsExactlyInAnyOrder ( filenames ) ;
}
else {
assertThat ( actualNames ) . as ( "subset of files found" ) . contains ( filenames ) ;
}
}
catch ( IOException ex ) {
throw new UncheckedIOException ( ex ) ;
}
}
}