@ -27,60 +27,52 @@ import org.springframework.util.ResourceUtils;
/ * *
/ * *
* Register the necessary resource hints for loading files from the classpath ,
* Register the necessary resource hints for loading files from the classpath ,
* based on a file name prefix and an extension with convenience to support
* based on file name prefixes and file extensions with convenience to support
* multiple classpath locations .
* multiple classpath locations .
*
*
* < p > Only registers hints for matching classpath locations , which allows for
* < p > Only registers hints for matching classpath locations , which allows for
* several locations to be provided without contributing unnecessary hints .
* several locations to be provided without contributing unnecessary hints .
*
*
* @author Stephane Nicoll
* @author Stephane Nicoll
* @author Sam Brannen
* @since 6 . 0
* @since 6 . 0
* /
* /
public class FilePatternResourceHintsRegistrar {
public class FilePatternResourceHintsRegistrar {
private final List < String > name s ;
private final List < String > classpathLocatio ns;
private final List < String > locations ;
private final List < String > filePrefixes ;
private final List < String > fileExtensions ;
private final List < String > extensions ;
/ * *
/ * *
* Create a new instance for the specified file names , locations , and file
* Create a new instance for the specified file prefixes , classpath locations ,
* extensions .
* and file extensions .
* @param names the file nam es
* @param filePrefixes the file prefix es
* @param locations the classpath locations
* @param c lasspathL ocations the classpath locations
* @param extensions the file extensions ( starting with a dot )
* @param fil eE xtensions the file extensions ( starting with a dot )
* @deprecated as of 6 . 0 . 12 in favor of { @linkplain # forClassPathLocations ( String . . . ) the builder }
* @deprecated as of 6 . 0 . 12 in favor of { @linkplain # forClassPathLocations ( String . . . ) the builder }
* /
* /
@Deprecated ( since = "6.0.12" , forRemoval = true )
@Deprecated ( since = "6.0.12" , forRemoval = true )
public FilePatternResourceHintsRegistrar ( List < String > names , List < String > locations ,
public FilePatternResourceHintsRegistrar ( List < String > filePrefixes , List < String > classpathLocations ,
List < String > extensions ) {
List < String > fileExtensions ) {
this . names = Builder . validateFilePrefixes ( names . toArray ( String [ ] : : new ) ) ;
this . locations = Builder . validateClasspathLocations ( locations . toArray ( String [ ] : : new ) ) ;
this . extensions = Builder . validateFileExtensions ( extensions . toArray ( String [ ] : : new ) ) ;
}
/ * *
this . classpathLocations = validateClasspathLocations ( classpathLocations ) ;
* Configure the registrar with the specified
this . filePrefixes = validateFilePrefixes ( filePrefixes ) ;
* { @linkplain Builder # withClasspathLocations ( String . . . ) classpath locations } .
this . fileExtensions = validateFileExtensions ( fileExtensions ) ;
* @param locations the classpath locations
* @return a { @link Builder } to further configure the registrar
* @since 6 . 0 . 12
* /
public static Builder forClassPathLocations ( String . . . locations ) {
Assert . notEmpty ( locations , "At least one classpath location should be specified" ) ;
return new Builder ( ) . withClasspathLocations ( locations ) ;
}
}
@Deprecated ( since = "6.0.12" , forRemoval = true )
@Deprecated ( since = "6.0.12" , forRemoval = true )
public void registerHints ( ResourceHints hints , @Nullable ClassLoader classLoader ) {
public void registerHints ( ResourceHints hints , @Nullable ClassLoader classLoader ) {
ClassLoader classLoaderToUse = ( classLoader ! = null ? classLoader : getClass ( ) . getClassLoader ( ) ) ;
ClassLoader classLoaderToUse = ( classLoader ! = null ? classLoader : getClass ( ) . getClassLoader ( ) ) ;
List < String > includes = new ArrayList < > ( ) ;
List < String > includes = new ArrayList < > ( ) ;
for ( String location : this . locations ) {
for ( String location : this . c lasspathL ocations) {
if ( classLoaderToUse . getResource ( location ) ! = null ) {
if ( classLoaderToUse . getResource ( location ) ! = null ) {
for ( String extension : this . extension s) {
for ( String filePrefix : this . filePrefixe s) {
for ( String name : this . name s) {
for ( String fileExtension : this . fileExtension s) {
includes . add ( location + name + "*" + extension ) ;
includes . add ( location + filePrefix + "*" + fil eE xtension) ;
}
}
}
}
}
}
@ -90,6 +82,68 @@ public class FilePatternResourceHintsRegistrar {
}
}
}
}
/ * *
* Configure the registrar with the specified
* { @linkplain Builder # withClasspathLocations ( String . . . ) classpath locations } .
* @param classpathLocations the classpath locations
* @return a { @link Builder } to further configure the registrar
* @since 6 . 0 . 12
* @see # forClassPathLocations ( List )
* /
public static Builder forClassPathLocations ( String . . . classpathLocations ) {
return forClassPathLocations ( Arrays . asList ( classpathLocations ) ) ;
}
/ * *
* Configure the registrar with the specified
* { @linkplain Builder # withClasspathLocations ( List ) classpath locations } .
* @param classpathLocations the classpath locations
* @return a { @link Builder } to further configure the registrar
* @since 6 . 0 . 12
* @see # forClassPathLocations ( String . . . )
* /
public static Builder forClassPathLocations ( List < String > classpathLocations ) {
return new Builder ( ) . withClasspathLocations ( classpathLocations ) ;
}
private static List < String > validateClasspathLocations ( List < String > classpathLocations ) {
Assert . notEmpty ( classpathLocations , "At least one classpath location must be specified" ) ;
List < String > parsedLocations = new ArrayList < > ( ) ;
for ( String location : classpathLocations ) {
if ( location . startsWith ( ResourceUtils . CLASSPATH_URL_PREFIX ) ) {
location = location . substring ( ResourceUtils . CLASSPATH_URL_PREFIX . length ( ) ) ;
}
if ( location . startsWith ( "/" ) ) {
location = location . substring ( 1 ) ;
}
if ( ! location . isEmpty ( ) & & ! location . endsWith ( "/" ) ) {
location = location + "/" ;
}
parsedLocations . add ( location ) ;
}
return parsedLocations ;
}
private static List < String > validateFilePrefixes ( List < String > filePrefixes ) {
for ( String filePrefix : filePrefixes ) {
if ( filePrefix . contains ( "*" ) ) {
throw new IllegalArgumentException ( "File prefix '" + filePrefix + "' cannot contain '*'" ) ;
}
}
return filePrefixes ;
}
private static List < String > validateFileExtensions ( List < String > fileExtensions ) {
for ( String fileExtension : fileExtensions ) {
if ( ! fileExtension . startsWith ( "." ) ) {
throw new IllegalArgumentException ( "Extension '" + fileExtension + "' must start with '.'" ) ;
}
}
return fileExtensions ;
}
/ * *
/ * *
* Builder for { @link FilePatternResourceHintsRegistrar } .
* Builder for { @link FilePatternResourceHintsRegistrar } .
* @since 6 . 0 . 12
* @since 6 . 0 . 12
@ -103,15 +157,34 @@ public class FilePatternResourceHintsRegistrar {
private final List < String > fileExtensions = new ArrayList < > ( ) ;
private final List < String > fileExtensions = new ArrayList < > ( ) ;
private Builder ( ) {
// no-op
}
/ * *
/ * *
* Consider the specified classpath locations . A location can either be
* Consider the specified classpath locations .
* a special "classpath" pseudo location or a standard location , such as
* < p > A location can either be a special { @value ResourceUtils # CLASSPATH_URL_PREFIX }
* { @code com / example / resources } . An empty String represents the root of
* pseudo location or a standard location , such as { @code com / example / resources } .
* the classpath .
* An empty String represents the root of the classpath .
* @param classpathLocations the classpath locations to consider
* @param classpathLocations the classpath locations to consider
* @return this builder
* @return this builder
* @see # withClasspathLocations ( List )
* /
* /
public Builder withClasspathLocations ( String . . . classpathLocations ) {
public Builder withClasspathLocations ( String . . . classpathLocations ) {
return withClasspathLocations ( Arrays . asList ( classpathLocations ) ) ;
}
/ * *
* Consider the specified classpath locations .
* < p > A location can either be a special { @value ResourceUtils # CLASSPATH_URL_PREFIX }
* pseudo location or a standard location , such as { @code com / example / resources } .
* An empty String represents the root of the classpath .
* @param classpathLocations the classpath locations to consider
* @return this builder
* @see # withClasspathLocations ( String . . . )
* /
public Builder withClasspathLocations ( List < String > classpathLocations ) {
this . classpathLocations . addAll ( validateClasspathLocations ( classpathLocations ) ) ;
this . classpathLocations . addAll ( validateClasspathLocations ( classpathLocations ) ) ;
return this ;
return this ;
}
}
@ -122,8 +195,21 @@ public class FilePatternResourceHintsRegistrar {
* character .
* character .
* @param filePrefixes the file prefixes to consider
* @param filePrefixes the file prefixes to consider
* @return this builder
* @return this builder
* @see # withFilePrefixes ( List )
* /
* /
public Builder withFilePrefixes ( String . . . filePrefixes ) {
public Builder withFilePrefixes ( String . . . filePrefixes ) {
return withFilePrefixes ( Arrays . asList ( filePrefixes ) ) ;
}
/ * *
* Consider the specified file prefixes . Any file whose name starts with one
* of the specified prefixes is considered . A prefix cannot contain the { @code * }
* character .
* @param filePrefixes the file prefixes to consider
* @return this builder
* @see # withFilePrefixes ( String . . . )
* /
public Builder withFilePrefixes ( List < String > filePrefixes ) {
this . filePrefixes . addAll ( validateFilePrefixes ( filePrefixes ) ) ;
this . filePrefixes . addAll ( validateFilePrefixes ( filePrefixes ) ) ;
return this ;
return this ;
}
}
@ -133,14 +219,26 @@ public class FilePatternResourceHintsRegistrar {
* { @code . } character .
* { @code . } character .
* @param fileExtensions the file extensions to consider
* @param fileExtensions the file extensions to consider
* @return this builder
* @return this builder
* @see # withFileExtensions ( List )
* /
* /
public Builder withFileExtensions ( String . . . fileExtensions ) {
public Builder withFileExtensions ( String . . . fileExtensions ) {
return withFileExtensions ( Arrays . asList ( fileExtensions ) ) ;
}
/ * *
* Consider the specified file extensions . A file extension must start with a
* { @code . } character .
* @param fileExtensions the file extensions to consider
* @return this builder
* @see # withFileExtensions ( String . . . )
* /
public Builder withFileExtensions ( List < String > fileExtensions ) {
this . fileExtensions . addAll ( validateFileExtensions ( fileExtensions ) ) ;
this . fileExtensions . addAll ( validateFileExtensions ( fileExtensions ) ) ;
return this ;
return this ;
}
}
FilePatternResourceHintsRegistrar build ( ) {
Assert . notEmpty ( this . classpathLocations , "At least one location should be specified" ) ;
private FilePatternResourceHintsRegistrar build ( ) {
return new FilePatternResourceHintsRegistrar ( this . filePrefixes ,
return new FilePatternResourceHintsRegistrar ( this . filePrefixes ,
this . classpathLocations , this . fileExtensions ) ;
this . classpathLocations , this . fileExtensions ) ;
}
}
@ -150,47 +248,13 @@ public class FilePatternResourceHintsRegistrar {
* classpath location that resolves against the { @code ClassLoader } , files
* classpath location that resolves against the { @code ClassLoader } , files
* with the configured file prefixes and extensions are registered .
* with the configured file prefixes and extensions are registered .
* @param hints the hints contributed so far for the deployment unit
* @param hints the hints contributed so far for the deployment unit
* @param classLoader the classloader , or { @code null } if even the system ClassLoader isn ' t accessible
* @param classLoader the classloader , or { @code null } if even the system
* ClassLoader isn ' t accessible
* /
* /
public void registerHints ( ResourceHints hints , @Nullable ClassLoader classLoader ) {
public void registerHints ( ResourceHints hints , @Nullable ClassLoader classLoader ) {
build ( ) . registerHints ( hints , classLoader ) ;
build ( ) . registerHints ( hints , classLoader ) ;
}
}
private static List < String > validateClasspathLocations ( String . . . locations ) {
List < String > parsedLocations = new ArrayList < > ( ) ;
for ( String location : locations ) {
if ( location . startsWith ( ResourceUtils . CLASSPATH_URL_PREFIX ) ) {
location = location . substring ( ResourceUtils . CLASSPATH_URL_PREFIX . length ( ) ) ;
}
if ( location . startsWith ( "/" ) ) {
location = location . substring ( 1 ) ;
}
if ( ! location . isEmpty ( ) & & ! location . endsWith ( "/" ) ) {
location = location + "/" ;
}
parsedLocations . add ( location ) ;
}
return parsedLocations ;
}
private static List < String > validateFilePrefixes ( String . . . filePrefixes ) {
for ( String filePrefix : filePrefixes ) {
if ( filePrefix . contains ( "*" ) ) {
throw new IllegalArgumentException ( "File prefix '" + filePrefix + "' cannot contain '*'" ) ;
}
}
return Arrays . asList ( filePrefixes ) ;
}
private static List < String > validateFileExtensions ( String . . . fileExtensions ) {
for ( String fileExtension : fileExtensions ) {
if ( ! fileExtension . startsWith ( "." ) ) {
throw new IllegalArgumentException ( "Extension '" + fileExtension + "' should start with '.'" ) ;
}
}
return Arrays . asList ( fileExtensions ) ;
}
}
}
}
}