31
31
import java .net .URL ;
32
32
import java .net .URLClassLoader ;
33
33
import java .net .URLConnection ;
34
+ import java .nio .file .FileSystem ;
35
+ import java .nio .file .FileSystemNotFoundException ;
36
+ import java .nio .file .FileSystems ;
34
37
import java .nio .file .Files ;
35
38
import java .nio .file .Path ;
36
39
import java .util .Collections ;
37
40
import java .util .Enumeration ;
38
41
import java .util .LinkedHashSet ;
42
+ import java .util .Map ;
39
43
import java .util .Objects ;
40
44
import java .util .Set ;
41
45
import java .util .function .Predicate ;
@@ -736,6 +740,19 @@ protected Set<Resource> doFindPathMatchingFileResources(Resource rootDirResource
736
740
URI rootDirUri ;
737
741
try {
738
742
rootDirUri = rootDirResource .getURI ();
743
+ // If the URI is for a "resource" in the GraalVM native image file system, we have to
744
+ // ensure that the root directory does not end in a slash while simultaneously ensuring
745
+ // that the root directory is not an empty string (since Path#resolve throws an
746
+ // ArrayIndexOutOfBoundsException in a native image if the initial Path is created
747
+ // from an empty string).
748
+ String scheme = rootDirUri .getScheme ();
749
+ String path = rootDirUri .getPath ();
750
+ if ("resource" .equals (scheme ) && (path .length () > 1 ) && path .endsWith ("/" )) {
751
+ path = path .substring (0 , path .length () - 1 );
752
+ // Retain the fragment as well, since root folders in the native image
753
+ // file system are indexed via the fragment (e.g., resource:/#1).
754
+ rootDirUri = new URI (scheme , path , rootDirUri .getFragment ());
755
+ }
739
756
}
740
757
catch (Exception ex ) {
741
758
if (logger .isInfoEnabled ()) {
@@ -744,60 +761,75 @@ protected Set<Resource> doFindPathMatchingFileResources(Resource rootDirResource
744
761
return Collections .emptySet ();
745
762
}
746
763
747
- Path rootPath = null ;
748
- if (rootDirUri .isAbsolute () && !rootDirUri .isOpaque ()) {
749
- // Prefer Path resolution from URI if possible
750
- try {
751
- rootPath = Path .of (rootDirUri );
752
- }
753
- catch (Exception ex ) {
754
- if (logger .isDebugEnabled ()) {
755
- logger .debug ("Failed to resolve %s in file system: %s" .formatted (rootDirUri , ex ));
764
+ FileSystem fileSystem = null ;
765
+ try {
766
+ Path rootPath = null ;
767
+ if (rootDirUri .isAbsolute () && !rootDirUri .isOpaque ()) {
768
+ // Prefer Path resolution from URI if possible
769
+ try {
770
+ try {
771
+ rootPath = Path .of (rootDirUri );
772
+ }
773
+ catch (FileSystemNotFoundException ex ) {
774
+ // If the file system was not found, assume it's a custom file system that needs to be installed.
775
+ fileSystem = FileSystems .newFileSystem (rootDirUri , Map .of (), ClassUtils .getDefaultClassLoader ());
776
+ rootPath = Path .of (rootDirUri );
777
+ }
778
+ }
779
+ catch (Exception ex ) {
780
+ if (logger .isDebugEnabled ()) {
781
+ logger .debug ("Failed to resolve %s in file system: %s" .formatted (rootDirUri , ex ));
782
+ }
783
+ // Fallback via Resource.getFile() below
756
784
}
757
- // Fallback via Resource.getFile() below
758
785
}
759
- }
760
- if (rootPath == null ) {
761
- // Resource.getFile() resolution as a fallback -
762
- // for custom URI formats and custom Resource implementations
763
- rootPath = Path .of (rootDirResource .getFile ().getAbsolutePath ());
764
- }
786
+ if (rootPath == null ) {
787
+ // Resource.getFile() resolution as a fallback -
788
+ // for custom URI formats and custom Resource implementations
789
+ rootPath = Path .of (rootDirResource .getFile ().getAbsolutePath ());
790
+ }
765
791
766
- String rootDir = StringUtils .cleanPath (rootPath .toString ());
767
- if (!rootDir .endsWith ("/" )) {
768
- rootDir += "/" ;
769
- }
792
+ String rootDir = StringUtils .cleanPath (rootPath .toString ());
793
+ if (!rootDir .endsWith ("/" )) {
794
+ rootDir += "/" ;
795
+ }
770
796
771
- String resourcePattern = rootDir + StringUtils .cleanPath (subPattern );
772
- Predicate <Path > isMatchingFile = ( path -> Files .isRegularFile (path ) &&
773
- getPathMatcher ().match (resourcePattern , StringUtils .cleanPath (path .toString ())));
797
+ String resourcePattern = rootDir + StringUtils .cleanPath (subPattern );
798
+ Predicate <Path > isMatchingFile = path -> ( Files .isRegularFile (path ) &&
799
+ getPathMatcher ().match (resourcePattern , StringUtils .cleanPath (path .toString ())));
774
800
775
- if (logger .isTraceEnabled ()) {
776
- logger .trace ("Searching directory [%s] for files matching pattern [%s]"
777
- .formatted (rootPath .toAbsolutePath (), subPattern ));
778
- }
801
+ if (logger .isTraceEnabled ()) {
802
+ logger .trace ("Searching directory [%s] for files matching pattern [%s]"
803
+ .formatted (rootPath .toAbsolutePath (), subPattern ));
804
+ }
779
805
780
- Set <Resource > result = new LinkedHashSet <>();
781
- try (Stream <Path > files = Files .walk (rootPath )) {
782
- files .filter (isMatchingFile ).sorted ().forEach (file -> {
783
- try {
784
- result .add (new FileSystemResource (file ));
785
- }
786
- catch (Exception ex ) {
787
- if (logger .isDebugEnabled ()) {
788
- logger .debug ("Failed to convert file %s to an org.springframework.core.io.Resource: %s"
789
- .formatted (file , ex ));
806
+ Set <Resource > result = new LinkedHashSet <>();
807
+ try (Stream <Path > files = Files .walk (rootPath )) {
808
+ files .filter (isMatchingFile ).sorted ().forEach (file -> {
809
+ try {
810
+ result .add (new FileSystemResource (file ));
811
+ }
812
+ catch (Exception ex ) {
813
+ if (logger .isDebugEnabled ()) {
814
+ logger .debug ("Failed to convert file %s to an org.springframework.core.io.Resource: %s"
815
+ .formatted (file , ex ));
816
+ }
790
817
}
818
+ });
819
+ }
820
+ catch (Exception ex ) {
821
+ if (logger .isDebugEnabled ()) {
822
+ logger .debug ("Failed to complete search in directory [%s] for files matching pattern [%s]: %s"
823
+ .formatted (rootPath .toAbsolutePath (), subPattern , ex ));
791
824
}
792
- });
825
+ }
826
+ return result ;
793
827
}
794
- catch (Exception ex ) {
795
- if (logger .isDebugEnabled ()) {
796
- logger .debug ("Failed to complete search in directory [%s] for files matching pattern [%s]: %s"
797
- .formatted (rootPath .toAbsolutePath (), subPattern , ex ));
828
+ finally {
829
+ if (fileSystem != null ) {
830
+ fileSystem .close ();
798
831
}
799
832
}
800
- return result ;
801
833
}
802
834
803
835
/**
0 commit comments