Replace "folder" with "directory"

Consistently use the term "directory" instead of "folder"

Closes gh-21218
pull/21259/head
Phillip Webb 5 years ago
parent ec871d6752
commit ad1248e4ec

@ -96,7 +96,7 @@ public class GroovyTemplateAutoConfiguration {
* MarkupTemplateEngine could be loaded from groovy-templates or groovy-all.
* Unfortunately it's quite common for people to use groovy-all and not actually
* need templating support. This method attempts to check the source jar so that
* we can skip the {@code /template} folder check for such cases.
* we can skip the {@code /template} directory check for such cases.
* @return true if the groovy-all jar is used
*/
private boolean isUsingGroovyAllJar() {

@ -245,13 +245,13 @@ class ArtemisAutoConfigurationTests {
@Test
void embeddedWithPersistentMode(@TempDir Path temp) throws IOException {
File dataFolder = Files.createTempDirectory(temp, null).toFile();
File dataDirectory = Files.createTempDirectory(temp, null).toFile();
final String messageId = UUID.randomUUID().toString();
// Start the server and post a message to some queue
this.contextRunner.withUserConfiguration(EmptyConfiguration.class)
.withPropertyValues("spring.artemis.embedded.queues=TestQueue",
"spring.artemis.embedded.persistent:true",
"spring.artemis.embedded.dataDirectory:" + dataFolder.getAbsolutePath())
"spring.artemis.embedded.dataDirectory:" + dataDirectory.getAbsolutePath())
.run((context) -> context.getBean(JmsTemplate.class).send("TestQueue",
(session) -> session.createTextMessage(messageId)))
.run((context) -> {

@ -147,13 +147,13 @@ class ServletWebServerFactoryCustomizerTests {
@Test
void sessionStoreDir() {
Map<String, String> map = new HashMap<>();
map.put("server.servlet.session.store-dir", "myfolder");
map.put("server.servlet.session.store-dir", "mydirectory");
bindProperties(map);
ConfigurableServletWebServerFactory factory = mock(ConfigurableServletWebServerFactory.class);
this.customizer.customize(factory);
ArgumentCaptor<Session> sessionCaptor = ArgumentCaptor.forClass(Session.class);
verify(factory).setSession(sessionCaptor.capture());
assertThat(sessionCaptor.getValue().getStoreDir()).isEqualTo(new File("myfolder"));
assertThat(sessionCaptor.getValue().getStoreDir()).isEqualTo(new File("mydirectory"));
}
@Test

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
@ -66,23 +66,23 @@ class ResourceMatcher {
matchedResources.add(new MatchedResource(root));
}
else {
matchedResources.addAll(findInFolder(root));
matchedResources.addAll(findInDirectory(root));
}
}
return matchedResources;
}
private List<MatchedResource> findInFolder(File folder) throws IOException {
private List<MatchedResource> findInDirectory(File directory) throws IOException {
List<MatchedResource> matchedResources = new ArrayList<>();
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(
new FolderResourceLoader(folder));
new DirectoryResourceLoader(directory));
for (String include : this.includes) {
for (Resource candidate : resolver.getResources(include)) {
File file = candidate.getFile();
if (file.isFile()) {
MatchedResource matchedResource = new MatchedResource(folder, file);
MatchedResource matchedResource = new MatchedResource(directory, file);
if (!isExcluded(matchedResource)) {
matchedResources.add(matchedResource);
}
@ -130,31 +130,31 @@ class ResourceMatcher {
}
/**
* {@link ResourceLoader} to get load resource from a folder.
* {@link ResourceLoader} to get load resource from a directory.
*/
private static class FolderResourceLoader extends DefaultResourceLoader {
private static class DirectoryResourceLoader extends DefaultResourceLoader {
private final File rootFolder;
private final File rootDirectory;
FolderResourceLoader(File root) throws MalformedURLException {
super(new FolderClassLoader(root));
this.rootFolder = root;
DirectoryResourceLoader(File root) throws MalformedURLException {
super(new DirectoryClassLoader(root));
this.rootDirectory = root;
}
@Override
protected Resource getResourceByPath(String path) {
return new FileSystemResource(new File(this.rootFolder, path));
return new FileSystemResource(new File(this.rootDirectory, path));
}
}
/**
* {@link ClassLoader} backed by a folder.
* {@link ClassLoader} backed by a directory.
*/
private static class FolderClassLoader extends URLClassLoader {
private static class DirectoryClassLoader extends URLClassLoader {
FolderClassLoader(File rootFolder) throws MalformedURLException {
super(new URL[] { rootFolder.toURI().toURL() });
DirectoryClassLoader(File rootDirectory) throws MalformedURLException {
super(new URL[] { rootDirectory.toURI().toURL() });
}
@Override
@ -186,9 +186,10 @@ class ResourceMatcher {
this.root = this.name.endsWith(".jar");
}
private MatchedResource(File rootFolder, File file) {
this.name = StringUtils
.cleanPath(file.getAbsolutePath().substring(rootFolder.getAbsolutePath().length() + 1));
private MatchedResource(File rootDirectory, File file) {
String filePath = file.getAbsolutePath();
String rootDirectoryPath = rootDirectory.getAbsolutePath();
this.name = StringUtils.cleanPath(filePath.substring(rootDirectoryPath.length() + 1));
this.file = file;
this.root = false;
}

@ -90,23 +90,24 @@ class ProjectGenerator {
}
private void extractProject(ProjectGenerationResponse entity, String output, boolean overwrite) throws IOException {
File outputFolder = (output != null) ? new File(output) : new File(System.getProperty("user.dir"));
if (!outputFolder.exists()) {
outputFolder.mkdirs();
File outputDirectory = (output != null) ? new File(output) : new File(System.getProperty("user.dir"));
if (!outputDirectory.exists()) {
outputDirectory.mkdirs();
}
try (ZipInputStream zipStream = new ZipInputStream(new ByteArrayInputStream(entity.getContent()))) {
extractFromStream(zipStream, overwrite, outputFolder);
fixExecutableFlag(outputFolder, "mvnw");
fixExecutableFlag(outputFolder, "gradlew");
Log.info("Project extracted to '" + outputFolder.getAbsolutePath() + "'");
extractFromStream(zipStream, overwrite, outputDirectory);
fixExecutableFlag(outputDirectory, "mvnw");
fixExecutableFlag(outputDirectory, "gradlew");
Log.info("Project extracted to '" + outputDirectory.getAbsolutePath() + "'");
}
}
private void extractFromStream(ZipInputStream zipStream, boolean overwrite, File outputFolder) throws IOException {
private void extractFromStream(ZipInputStream zipStream, boolean overwrite, File outputDirectory)
throws IOException {
ZipEntry entry = zipStream.getNextEntry();
String canonicalOutputPath = outputFolder.getCanonicalPath() + File.separator;
String canonicalOutputPath = outputDirectory.getCanonicalPath() + File.separator;
while (entry != null) {
File file = new File(outputFolder, entry.getName());
File file = new File(outputDirectory, entry.getName());
String canonicalEntryPath = file.getCanonicalPath();
if (!canonicalEntryPath.startsWith(canonicalOutputPath)) {
throw new ReportableException("Entry '" + entry.getName() + "' would be written to '"

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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,7 +19,7 @@ package org.springframework.boot.devtools.autoconfigure;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import org.springframework.boot.devtools.classpath.ClassPathFolders;
import org.springframework.boot.devtools.classpath.ClassPathDirectories;
import org.springframework.boot.devtools.filewatch.ChangedFiles;
import org.springframework.boot.devtools.filewatch.FileChangeListener;
import org.springframework.boot.devtools.filewatch.FileSystemWatcher;
@ -44,7 +44,7 @@ class FileWatchingFailureHandler implements FailureHandler {
public Outcome handle(Throwable failure) {
CountDownLatch latch = new CountDownLatch(1);
FileSystemWatcher watcher = this.fileSystemWatcherFactory.getFileSystemWatcher();
watcher.addSourceFolders(new ClassPathFolders(Restarter.getInstance().getInitialUrls()));
watcher.addSourceDirectories(new ClassPathDirectories(Restarter.getInstance().getInitialUrls()));
watcher.addListener(new Listener(latch));
watcher.start();
try {

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
@ -148,7 +148,7 @@ public class LocalDevToolsAutoConfiguration {
}
List<File> additionalPaths = restartProperties.getAdditionalPaths();
for (File path : additionalPaths) {
watcher.addSourceFolder(path.getAbsoluteFile());
watcher.addSourceDirectory(path.getAbsoluteFile());
}
return watcher;
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
@ -40,10 +40,10 @@ import org.springframework.boot.devtools.remote.server.HandlerMapper;
import org.springframework.boot.devtools.remote.server.HttpHeaderAccessManager;
import org.springframework.boot.devtools.remote.server.HttpStatusHandler;
import org.springframework.boot.devtools.remote.server.UrlHandlerMapper;
import org.springframework.boot.devtools.restart.server.DefaultSourceFolderUrlFilter;
import org.springframework.boot.devtools.restart.server.DefaultSourceDirectoryUrlFilter;
import org.springframework.boot.devtools.restart.server.HttpRestartServer;
import org.springframework.boot.devtools.restart.server.HttpRestartServerHandler;
import org.springframework.boot.devtools.restart.server.SourceFolderUrlFilter;
import org.springframework.boot.devtools.restart.server.SourceDirectoryUrlFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
@ -109,14 +109,14 @@ public class RemoteDevToolsAutoConfiguration {
@Bean
@ConditionalOnMissingBean
SourceFolderUrlFilter remoteRestartSourceFolderUrlFilter() {
return new DefaultSourceFolderUrlFilter();
SourceDirectoryUrlFilter remoteRestartSourceDirectoryUrlFilter() {
return new DefaultSourceDirectoryUrlFilter();
}
@Bean
@ConditionalOnMissingBean
HttpRestartServer remoteRestartHttpRestartServer(SourceFolderUrlFilter sourceFolderUrlFilter) {
return new HttpRestartServer(sourceFolderUrlFilter);
HttpRestartServer remoteRestartHttpRestartServer(SourceDirectoryUrlFilter sourceDirectoryUrlFilter) {
return new HttpRestartServer(sourceDirectoryUrlFilter);
}
@Bean

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
@ -30,18 +30,18 @@ import org.springframework.core.log.LogMessage;
import org.springframework.util.ResourceUtils;
/**
* Provides access to entries on the classpath that refer to folders.
* Provides access to entries on the classpath that refer to directories.
*
* @author Phillip Webb
* @since 1.3.0
* @since 2.3.0
*/
public class ClassPathFolders implements Iterable<File> {
public class ClassPathDirectories implements Iterable<File> {
private static final Log logger = LogFactory.getLog(ClassPathFolders.class);
private static final Log logger = LogFactory.getLog(ClassPathDirectories.class);
private final List<File> folders = new ArrayList<>();
private final List<File> directories = new ArrayList<>();
public ClassPathFolders(URL[] urls) {
public ClassPathDirectories(URL[] urls) {
if (urls != null) {
addUrls(urls);
}
@ -56,7 +56,7 @@ public class ClassPathFolders implements Iterable<File> {
private void addUrl(URL url) {
if (url.getProtocol().equals("file") && url.getPath().endsWith("/")) {
try {
this.folders.add(ResourceUtils.getFile(url));
this.directories.add(ResourceUtils.getFile(url));
}
catch (Exception ex) {
logger.warn(LogMessage.format("Unable to get classpath URL %s", url));
@ -67,7 +67,7 @@ public class ClassPathFolders implements Iterable<File> {
@Override
public Iterator<File> iterator() {
return Collections.unmodifiableList(this.folders).iterator();
return Collections.unmodifiableList(this.directories).iterator();
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
@ -28,7 +28,7 @@ import org.springframework.context.ApplicationContextAware;
import org.springframework.util.Assert;
/**
* Encapsulates a {@link FileSystemWatcher} to watch the local classpath folders for
* Encapsulates a {@link FileSystemWatcher} to watch the local classpath directories for
* changes.
*
* @author Phillip Webb
@ -58,7 +58,7 @@ public class ClassPathFileSystemWatcher implements InitializingBean, DisposableB
Assert.notNull(urls, "Urls must not be null");
this.fileSystemWatcher = fileSystemWatcherFactory.getFileSystemWatcher();
this.restartStrategy = restartStrategy;
this.fileSystemWatcher.addSourceFolders(new ClassPathFolders(urls));
this.fileSystemWatcher.addSourceDirectories(new ClassPathDirectories(urls));
}
/**

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
@ -40,7 +40,7 @@ import org.springframework.util.StringUtils;
/**
* {@link EnvironmentPostProcessor} to add devtools properties from the user's home
* folder.
* directory.
*
* @author Phillip Webb
* @author Andy Wilkinson
@ -93,7 +93,7 @@ public class DevToolsHomePropertiesPostProcessor implements EnvironmentPostProce
private void addPropertySource(List<PropertySource<?>> propertySources, String fileName,
Function<File, String> propertySourceNamer) {
File home = getHomeFolder();
File home = getHomeDirectory();
File file = (home != null) ? new File(home, fileName) : null;
FileSystemResource resource = (file != null) ? new FileSystemResource(file) : null;
if (resource != null && resource.exists() && resource.isFile()) {
@ -121,7 +121,7 @@ public class DevToolsHomePropertiesPostProcessor implements EnvironmentPostProce
.anyMatch((fileExtension) -> StringUtils.endsWithIgnoreCase(name, fileExtension));
}
protected File getHomeFolder() {
protected File getHomeDirectory() {
String home = System.getProperty("user.home");
if (StringUtils.hasLength(home)) {
return new File(home);

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
@ -30,7 +30,7 @@ import org.springframework.util.StringUtils;
*/
public final class ChangedFile {
private final File sourceFolder;
private final File sourceDirectory;
private final File file;
@ -38,15 +38,15 @@ public final class ChangedFile {
/**
* Create a new {@link ChangedFile} instance.
* @param sourceFolder the source folder
* @param sourceDirectory the source directory
* @param file the file
* @param type the type of change
*/
public ChangedFile(File sourceFolder, File file, Type type) {
Assert.notNull(sourceFolder, "SourceFolder must not be null");
public ChangedFile(File sourceDirectory, File file, Type type) {
Assert.notNull(sourceDirectory, "SourceDirectory must not be null");
Assert.notNull(file, "File must not be null");
Assert.notNull(type, "Type must not be null");
this.sourceFolder = sourceFolder;
this.sourceDirectory = sourceDirectory;
this.file = file;
this.type = type;
}
@ -68,17 +68,17 @@ public final class ChangedFile {
}
/**
* Return the name of the file relative to the source folder.
* Return the name of the file relative to the source directory.
* @return the relative name
*/
public String getRelativeName() {
File folder = this.sourceFolder.getAbsoluteFile();
File directory = this.sourceDirectory.getAbsoluteFile();
File file = this.file.getAbsoluteFile();
String folderName = StringUtils.cleanPath(folder.getPath());
String directoryName = StringUtils.cleanPath(directory.getPath());
String fileName = StringUtils.cleanPath(file.getPath());
Assert.state(fileName.startsWith(folderName),
() -> "The file " + fileName + " is not contained in the source folder " + folderName);
return fileName.substring(folderName.length() + 1);
Assert.state(fileName.startsWith(directoryName),
() -> "The file " + fileName + " is not contained in the source directory " + directoryName);
return fileName.substring(directoryName.length() + 1);
}
@Override

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
@ -22,7 +22,7 @@ import java.util.Iterator;
import java.util.Set;
/**
* A collections of files from a specific source folder that have changed.
* A collections of files from a specific source directory that have changed.
*
* @author Phillip Webb
* @since 1.3.0
@ -31,21 +31,21 @@ import java.util.Set;
*/
public final class ChangedFiles implements Iterable<ChangedFile> {
private final File sourceFolder;
private final File sourceDirectory;
private final Set<ChangedFile> files;
public ChangedFiles(File sourceFolder, Set<ChangedFile> files) {
this.sourceFolder = sourceFolder;
public ChangedFiles(File sourceDirectory, Set<ChangedFile> files) {
this.sourceDirectory = sourceDirectory;
this.files = Collections.unmodifiableSet(files);
}
/**
* The source folder being watched.
* @return the source folder
* The source directory being watched.
* @return the source directory
*/
public File getSourceFolder() {
return this.sourceFolder;
public File getSourceDirectory() {
return this.sourceDirectory;
}
@Override
@ -71,7 +71,7 @@ public final class ChangedFiles implements Iterable<ChangedFile> {
}
if (obj instanceof ChangedFiles) {
ChangedFiles other = (ChangedFiles) obj;
return this.sourceFolder.equals(other.sourceFolder) && this.files.equals(other.files);
return this.sourceDirectory.equals(other.sourceDirectory) && this.files.equals(other.files);
}
return super.equals(obj);
}
@ -83,7 +83,7 @@ public final class ChangedFiles implements Iterable<ChangedFile> {
@Override
public String toString() {
return this.sourceFolder + " " + this.files;
return this.sourceDirectory + " " + this.files;
}
}

@ -31,31 +31,31 @@ import org.springframework.boot.devtools.filewatch.ChangedFile.Type;
import org.springframework.util.Assert;
/**
* A snapshot of a folder at a given point in time.
* A snapshot of a directory at a given point in time.
*
* @author Phillip Webb
*/
class FolderSnapshot {
class DirectorySnapshot {
private static final Set<String> DOT_FOLDERS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(".", "..")));
private static final Set<String> DOTS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(".", "..")));
private final File folder;
private final File directory;
private final Date time;
private Set<FileSnapshot> files;
/**
* Create a new {@link FolderSnapshot} for the given folder.
* @param folder the source folder
* Create a new {@link DirectorySnapshot} for the given directory.
* @param directory the source directory
*/
FolderSnapshot(File folder) {
Assert.notNull(folder, "Folder must not be null");
Assert.isTrue(!folder.isFile(), () -> "Folder '" + folder + "' must not be a file");
this.folder = folder;
DirectorySnapshot(File directory) {
Assert.notNull(directory, "Directory must not be null");
Assert.isTrue(!directory.isFile(), () -> "Directory '" + directory + "' must not be a file");
this.directory = directory;
this.time = new Date();
Set<FileSnapshot> files = new LinkedHashSet<>();
collectFiles(folder, files);
collectFiles(directory, files);
this.files = Collections.unmodifiableSet(files);
}
@ -63,7 +63,7 @@ class FolderSnapshot {
File[] children = source.listFiles();
if (children != null) {
for (File child : children) {
if (child.isDirectory() && !DOT_FOLDERS.contains(child.getName())) {
if (child.isDirectory() && !DOTS.contains(child.getName())) {
collectFiles(child, result);
}
else if (child.isFile()) {
@ -73,29 +73,30 @@ class FolderSnapshot {
}
}
ChangedFiles getChangedFiles(FolderSnapshot snapshot, FileFilter triggerFilter) {
ChangedFiles getChangedFiles(DirectorySnapshot snapshot, FileFilter triggerFilter) {
Assert.notNull(snapshot, "Snapshot must not be null");
File folder = this.folder;
Assert.isTrue(snapshot.folder.equals(folder), () -> "Snapshot source folder must be '" + folder + "'");
File directory = this.directory;
Assert.isTrue(snapshot.directory.equals(directory),
() -> "Snapshot source directory must be '" + directory + "'");
Set<ChangedFile> changes = new LinkedHashSet<>();
Map<File, FileSnapshot> previousFiles = getFilesMap();
for (FileSnapshot currentFile : snapshot.files) {
if (acceptChangedFile(triggerFilter, currentFile)) {
FileSnapshot previousFile = previousFiles.remove(currentFile.getFile());
if (previousFile == null) {
changes.add(new ChangedFile(folder, currentFile.getFile(), Type.ADD));
changes.add(new ChangedFile(directory, currentFile.getFile(), Type.ADD));
}
else if (!previousFile.equals(currentFile)) {
changes.add(new ChangedFile(folder, currentFile.getFile(), Type.MODIFY));
changes.add(new ChangedFile(directory, currentFile.getFile(), Type.MODIFY));
}
}
}
for (FileSnapshot previousFile : previousFiles.values()) {
if (acceptChangedFile(triggerFilter, previousFile)) {
changes.add(new ChangedFile(folder, previousFile.getFile(), Type.DELETE));
changes.add(new ChangedFile(directory, previousFile.getFile(), Type.DELETE));
}
}
return new ChangedFiles(folder, changes);
return new ChangedFiles(directory, changes);
}
private boolean acceptChangedFile(FileFilter triggerFilter, FileSnapshot file) {
@ -118,14 +119,14 @@ class FolderSnapshot {
if (obj == null) {
return false;
}
if (obj instanceof FolderSnapshot) {
return equals((FolderSnapshot) obj, null);
if (obj instanceof DirectorySnapshot) {
return equals((DirectorySnapshot) obj, null);
}
return super.equals(obj);
}
boolean equals(FolderSnapshot other, FileFilter filter) {
if (this.folder.equals(other.folder)) {
boolean equals(DirectorySnapshot other, FileFilter filter) {
if (this.directory.equals(other.directory)) {
Set<FileSnapshot> ourFiles = filter(this.files, filter);
Set<FileSnapshot> otherFiles = filter(other.files, filter);
return ourFiles.equals(otherFiles);
@ -148,22 +149,22 @@ class FolderSnapshot {
@Override
public int hashCode() {
int hashCode = this.folder.hashCode();
int hashCode = this.directory.hashCode();
hashCode = 31 * hashCode + this.files.hashCode();
return hashCode;
}
/**
* Return the source folder of this snapshot.
* @return the source folder
* Return the source directory of this snapshot.
* @return the source directory
*/
File getFolder() {
return this.folder;
File getDirectory() {
return this.directory;
}
@Override
public String toString() {
return this.folder + " snapshot at " + this.time;
return this.directory + " snapshot at " + this.time;
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
@ -37,7 +37,7 @@ class FileSnapshot {
FileSnapshot(File file) {
Assert.notNull(file, "File must not be null");
Assert.isTrue(file.isFile() || !file.exists(), "File must not be a folder");
Assert.isTrue(file.isFile() || !file.exists(), "File must not be a directory");
this.file = file;
this.exists = file.exists();
this.length = file.length();

@ -33,7 +33,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import org.springframework.util.Assert;
/**
* Watches specific folders for file changes.
* Watches specific directories for file changes.
*
* @author Andy Clement
* @author Phillip Webb
@ -56,7 +56,7 @@ public class FileSystemWatcher {
private final AtomicInteger remainingScans = new AtomicInteger(-1);
private final Map<File, FolderSnapshot> folders = new HashMap<>();
private final Map<File, DirectorySnapshot> directories = new HashMap<>();
private Thread watchThread;
@ -104,30 +104,28 @@ public class FileSystemWatcher {
}
/**
* Add source folders to monitor. Cannot be called after the watcher has been
* Add source directories to monitor. Cannot be called after the watcher has been
* {@link #start() started}.
* @param folders the folders to monitor
* @param directories the directories to monitor
*/
public void addSourceFolders(Iterable<File> folders) {
Assert.notNull(folders, "Folders must not be null");
public void addSourceDirectories(Iterable<File> directories) {
Assert.notNull(directories, "Directories must not be null");
synchronized (this.monitor) {
for (File folder : folders) {
addSourceFolder(folder);
}
directories.forEach(this::addSourceDirectory);
}
}
/**
* Add a source folder to monitor. Cannot be called after the watcher has been
* Add a source directory to monitor. Cannot be called after the watcher has been
* {@link #start() started}.
* @param folder the folder to monitor
* @param directory the directory to monitor
*/
public void addSourceFolder(File folder) {
Assert.notNull(folder, "Folder must not be null");
Assert.isTrue(!folder.isFile(), () -> "Folder '" + folder + "' must not be a file");
public void addSourceDirectory(File directory) {
Assert.notNull(directory, "Directory must not be null");
Assert.isTrue(!directory.isFile(), () -> "Directory '" + directory + "' must not be a file");
synchronized (this.monitor) {
checkNotStarted();
this.folders.put(folder, null);
this.directories.put(directory, null);
}
}
@ -148,15 +146,15 @@ public class FileSystemWatcher {
}
/**
* Start monitoring the source folder for changes.
* Start monitoring the source directory for changes.
*/
public void start() {
synchronized (this.monitor) {
saveInitialSnapshots();
if (this.watchThread == null) {
Map<File, FolderSnapshot> localFolders = new HashMap<>(this.folders);
Map<File, DirectorySnapshot> localDirectories = new HashMap<>(this.directories);
this.watchThread = new Thread(new Watcher(this.remainingScans, new ArrayList<>(this.listeners),
this.triggerFilter, this.pollInterval, this.quietPeriod, localFolders));
this.triggerFilter, this.pollInterval, this.quietPeriod, localDirectories));
this.watchThread.setName("File Watcher");
this.watchThread.setDaemon(this.daemon);
this.watchThread.start();
@ -165,18 +163,18 @@ public class FileSystemWatcher {
}
private void saveInitialSnapshots() {
this.folders.replaceAll((f, v) -> new FolderSnapshot(f));
this.directories.replaceAll((f, v) -> new DirectorySnapshot(f));
}
/**
* Stop monitoring the source folders.
* Stop monitoring the source directories.
*/
public void stop() {
stopAfter(0);
}
/**
* Stop monitoring the source folders.
* Stop monitoring the source directories.
* @param remainingScans the number of remaining scans
*/
void stopAfter(int remainingScans) {
@ -213,16 +211,16 @@ public class FileSystemWatcher {
private final long quietPeriod;
private Map<File, FolderSnapshot> folders;
private Map<File, DirectorySnapshot> directories;
private Watcher(AtomicInteger remainingScans, List<FileChangeListener> listeners, FileFilter triggerFilter,
long pollInterval, long quietPeriod, Map<File, FolderSnapshot> folders) {
long pollInterval, long quietPeriod, Map<File, DirectorySnapshot> directories) {
this.remainingScans = remainingScans;
this.listeners = listeners;
this.triggerFilter = triggerFilter;
this.pollInterval = pollInterval;
this.quietPeriod = quietPeriod;
this.folders = folders;
this.directories = directories;
}
@Override
@ -244,47 +242,47 @@ public class FileSystemWatcher {
private void scan() throws InterruptedException {
Thread.sleep(this.pollInterval - this.quietPeriod);
Map<File, FolderSnapshot> previous;
Map<File, FolderSnapshot> current = this.folders;
Map<File, DirectorySnapshot> previous;
Map<File, DirectorySnapshot> current = this.directories;
do {
previous = current;
current = getCurrentSnapshots();
Thread.sleep(this.quietPeriod);
}
while (isDifferent(previous, current));
if (isDifferent(this.folders, current)) {
if (isDifferent(this.directories, current)) {
updateSnapshots(current.values());
}
}
private boolean isDifferent(Map<File, FolderSnapshot> previous, Map<File, FolderSnapshot> current) {
private boolean isDifferent(Map<File, DirectorySnapshot> previous, Map<File, DirectorySnapshot> current) {
if (!previous.keySet().equals(current.keySet())) {
return true;
}
for (Map.Entry<File, FolderSnapshot> entry : previous.entrySet()) {
FolderSnapshot previousFolder = entry.getValue();
FolderSnapshot currentFolder = current.get(entry.getKey());
if (!previousFolder.equals(currentFolder, this.triggerFilter)) {
for (Map.Entry<File, DirectorySnapshot> entry : previous.entrySet()) {
DirectorySnapshot previousDirectory = entry.getValue();
DirectorySnapshot currentDirectory = current.get(entry.getKey());
if (!previousDirectory.equals(currentDirectory, this.triggerFilter)) {
return true;
}
}
return false;
}
private Map<File, FolderSnapshot> getCurrentSnapshots() {
Map<File, FolderSnapshot> snapshots = new LinkedHashMap<>();
for (File folder : this.folders.keySet()) {
snapshots.put(folder, new FolderSnapshot(folder));
private Map<File, DirectorySnapshot> getCurrentSnapshots() {
Map<File, DirectorySnapshot> snapshots = new LinkedHashMap<>();
for (File directory : this.directories.keySet()) {
snapshots.put(directory, new DirectorySnapshot(directory));
}
return snapshots;
}
private void updateSnapshots(Collection<FolderSnapshot> snapshots) {
Map<File, FolderSnapshot> updated = new LinkedHashMap<>();
private void updateSnapshots(Collection<DirectorySnapshot> snapshots) {
Map<File, DirectorySnapshot> updated = new LinkedHashMap<>();
Set<ChangedFiles> changeSet = new LinkedHashSet<>();
for (FolderSnapshot snapshot : snapshots) {
FolderSnapshot previous = this.folders.get(snapshot.getFolder());
updated.put(snapshot.getFolder(), snapshot);
for (DirectorySnapshot snapshot : snapshots) {
DirectorySnapshot previous = this.directories.get(snapshot.getDirectory());
updated.put(snapshot.getDirectory(), snapshot);
ChangedFiles changedFiles = previous.getChangedFiles(snapshot, this.triggerFilter);
if (!changedFiles.getFiles().isEmpty()) {
changeSet.add(changedFiles);
@ -293,7 +291,7 @@ public class FileSystemWatcher {
if (!changeSet.isEmpty()) {
fireListeners(Collections.unmodifiableSet(changeSet));
}
this.folders = updated;
this.directories = updated;
}
private void fireListeners(Set<ChangedFiles> changeSet) {

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
@ -144,9 +144,9 @@ public class ClassPathChangeUploader implements ApplicationListener<ClassPathCha
private ClassLoaderFiles getClassLoaderFiles(ClassPathChangedEvent event) throws IOException {
ClassLoaderFiles files = new ClassLoaderFiles();
for (ChangedFiles changedFiles : event.getChangeSet()) {
String sourceFolder = changedFiles.getSourceFolder().getAbsolutePath();
String sourceDirectory = changedFiles.getSourceDirectory().getAbsolutePath();
for (ChangedFile changedFile : changedFiles) {
files.addFile(sourceFolder, changedFile.getRelativeName(), asClassLoaderFile(changedFile));
files.addFile(sourceDirectory, changedFile.getRelativeName(), asClassLoaderFile(changedFile));
}
}
return files;

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
@ -56,7 +56,7 @@ final class ChangeableUrls implements Iterable<URL> {
DevToolsSettings settings = DevToolsSettings.get();
List<URL> reloadableUrls = new ArrayList<>(urls.length);
for (URL url : urls) {
if ((settings.isRestartInclude(url) || isFolderUrl(url.toString())) && !settings.isRestartExclude(url)) {
if ((settings.isRestartInclude(url) || isDirectoryUrl(url.toString())) && !settings.isRestartExclude(url)) {
reloadableUrls.add(url);
}
}
@ -66,7 +66,7 @@ final class ChangeableUrls implements Iterable<URL> {
this.urls = Collections.unmodifiableList(reloadableUrls);
}
private boolean isFolderUrl(String urlString) {
private boolean isDirectoryUrl(String urlString) {
return urlString.startsWith("file:") && urlString.endsWith("/");
}

@ -31,7 +31,7 @@ import org.springframework.boot.devtools.restart.classloader.ClassLoaderFile;
import org.springframework.boot.devtools.restart.classloader.ClassLoaderFile.Kind;
import org.springframework.boot.devtools.restart.classloader.ClassLoaderFileURLStreamHandler;
import org.springframework.boot.devtools.restart.classloader.ClassLoaderFiles;
import org.springframework.boot.devtools.restart.classloader.ClassLoaderFiles.SourceFolder;
import org.springframework.boot.devtools.restart.classloader.ClassLoaderFiles.SourceDirectory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.core.io.AbstractResource;
@ -123,8 +123,8 @@ final class ClassLoaderFilesResourcePatternResolver implements ResourcePatternRe
private List<Resource> getAdditionalResources(String locationPattern) throws MalformedURLException {
List<Resource> additionalResources = new ArrayList<>();
String trimmedLocationPattern = trimLocationPattern(locationPattern);
for (SourceFolder sourceFolder : this.classLoaderFiles.getSourceFolders()) {
for (Entry<String, ClassLoaderFile> entry : sourceFolder.getFilesEntrySet()) {
for (SourceDirectory sourceDirectory : this.classLoaderFiles.getSourceDirectories()) {
for (Entry<String, ClassLoaderFile> entry : sourceDirectory.getFilesEntrySet()) {
String name = entry.getKey();
ClassLoaderFile file = entry.getValue();
if (file.getKind() != Kind.DELETED && this.antPathMatcher.match(trimmedLocationPattern, name)) {
@ -147,8 +147,8 @@ final class ClassLoaderFilesResourcePatternResolver implements ResourcePatternRe
}
private boolean isDeleted(Resource resource) {
for (SourceFolder sourceFolder : this.classLoaderFiles.getSourceFolders()) {
for (Entry<String, ClassLoaderFile> entry : sourceFolder.getFilesEntrySet()) {
for (SourceDirectory sourceDirectory : this.classLoaderFiles.getSourceDirectories()) {
for (Entry<String, ClassLoaderFile> entry : sourceDirectory.getFilesEntrySet()) {
try {
String name = entry.getKey();
ClassLoaderFile file = entry.getValue();

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
@ -30,7 +30,7 @@ import org.springframework.util.Assert;
/**
* {@link ClassLoaderFileRepository} that maintains a collection of
* {@link ClassLoaderFile} items grouped by source folders.
* {@link ClassLoaderFile} items grouped by source directories.
*
* @author Phillip Webb
* @since 1.3.0
@ -41,13 +41,13 @@ public class ClassLoaderFiles implements ClassLoaderFileRepository, Serializable
private static final long serialVersionUID = 1;
private final Map<String, SourceFolder> sourceFolders;
private final Map<String, SourceDirectory> sourceDirectories;
/**
* Create a new {@link ClassLoaderFiles} instance.
*/
public ClassLoaderFiles() {
this.sourceFolders = new LinkedHashMap<>();
this.sourceDirectories = new LinkedHashMap<>();
}
/**
@ -56,7 +56,7 @@ public class ClassLoaderFiles implements ClassLoaderFileRepository, Serializable
*/
public ClassLoaderFiles(ClassLoaderFiles classLoaderFiles) {
Assert.notNull(classLoaderFiles, "ClassLoaderFiles must not be null");
this.sourceFolders = new LinkedHashMap<>(classLoaderFiles.sourceFolders);
this.sourceDirectories = new LinkedHashMap<>(classLoaderFiles.sourceDirectories);
}
/**
@ -66,9 +66,9 @@ public class ClassLoaderFiles implements ClassLoaderFileRepository, Serializable
*/
public void addAll(ClassLoaderFiles files) {
Assert.notNull(files, "Files must not be null");
for (SourceFolder folder : files.getSourceFolders()) {
for (Map.Entry<String, ClassLoaderFile> entry : folder.getFilesEntrySet()) {
addFile(folder.getName(), entry.getKey(), entry.getValue());
for (SourceDirectory directory : files.getSourceDirectories()) {
for (Map.Entry<String, ClassLoaderFile> entry : directory.getFilesEntrySet()) {
addFile(directory.getName(), entry.getKey(), entry.getValue());
}
}
}
@ -84,45 +84,45 @@ public class ClassLoaderFiles implements ClassLoaderFileRepository, Serializable
/**
* Add a single {@link ClassLoaderFile} to the collection.
* @param sourceFolder the source folder of the file
* @param sourceDirectory the source directory of the file
* @param name the name of the file
* @param file the file to add
*/
public void addFile(String sourceFolder, String name, ClassLoaderFile file) {
Assert.notNull(sourceFolder, "SourceFolder must not be null");
public void addFile(String sourceDirectory, String name, ClassLoaderFile file) {
Assert.notNull(sourceDirectory, "SourceDirectory must not be null");
Assert.notNull(name, "Name must not be null");
Assert.notNull(file, "File must not be null");
removeAll(name);
getOrCreateSourceFolder(sourceFolder).add(name, file);
getOrCreateSourceDirectory(sourceDirectory).add(name, file);
}
private void removeAll(String name) {
for (SourceFolder sourceFolder : this.sourceFolders.values()) {
sourceFolder.remove(name);
for (SourceDirectory sourceDirectory : this.sourceDirectories.values()) {
sourceDirectory.remove(name);
}
}
/**
* Get or create a {@link SourceFolder} with the given name.
* @param name the name of the folder
* @return an existing or newly added {@link SourceFolder}
* Get or create a {@link SourceDirectory} with the given name.
* @param name the name of the directory
* @return an existing or newly added {@link SourceDirectory}
*/
protected final SourceFolder getOrCreateSourceFolder(String name) {
SourceFolder sourceFolder = this.sourceFolders.get(name);
if (sourceFolder == null) {
sourceFolder = new SourceFolder(name);
this.sourceFolders.put(name, sourceFolder);
protected final SourceDirectory getOrCreateSourceDirectory(String name) {
SourceDirectory sourceDirectory = this.sourceDirectories.get(name);
if (sourceDirectory == null) {
sourceDirectory = new SourceDirectory(name);
this.sourceDirectories.put(name, sourceDirectory);
}
return sourceFolder;
return sourceDirectory;
}
/**
* Return all {@link SourceFolder SourceFolders} that have been added to the
* Return all {@link SourceDirectory SourceDirectories} that have been added to the
* collection.
* @return a collection of {@link SourceFolder} items
* @return a collection of {@link SourceDirectory} items
*/
public Collection<SourceFolder> getSourceFolders() {
return Collections.unmodifiableCollection(this.sourceFolders.values());
public Collection<SourceDirectory> getSourceDirectories() {
return Collections.unmodifiableCollection(this.sourceDirectories.values());
}
/**
@ -131,16 +131,16 @@ public class ClassLoaderFiles implements ClassLoaderFileRepository, Serializable
*/
public int size() {
int size = 0;
for (SourceFolder sourceFolder : this.sourceFolders.values()) {
size += sourceFolder.getFiles().size();
for (SourceDirectory sourceDirectory : this.sourceDirectories.values()) {
size += sourceDirectory.getFiles().size();
}
return size;
}
@Override
public ClassLoaderFile getFile(String name) {
for (SourceFolder sourceFolder : this.sourceFolders.values()) {
ClassLoaderFile file = sourceFolder.get(name);
for (SourceDirectory sourceDirectory : this.sourceDirectories.values()) {
ClassLoaderFile file = sourceDirectory.get(name);
if (file != null) {
return file;
}
@ -149,9 +149,9 @@ public class ClassLoaderFiles implements ClassLoaderFileRepository, Serializable
}
/**
* An individual source folder that is being managed by the collection.
* An individual source directory that is being managed by the collection.
*/
public static class SourceFolder implements Serializable {
public static class SourceDirectory implements Serializable {
private static final long serialVersionUID = 1;
@ -159,7 +159,7 @@ public class ClassLoaderFiles implements ClassLoaderFileRepository, Serializable
private final Map<String, ClassLoaderFile> files = new LinkedHashMap<>();
SourceFolder(String name) {
SourceDirectory(String name) {
this.name = name;
}
@ -180,8 +180,8 @@ public class ClassLoaderFiles implements ClassLoaderFileRepository, Serializable
}
/**
* Return the name of the source folder.
* @return the name of the source folder
* Return the name of the source directory.
* @return the name of the source directory
*/
public String getName() {
return this.name;
@ -189,8 +189,8 @@ public class ClassLoaderFiles implements ClassLoaderFileRepository, Serializable
/**
* Return all {@link ClassLoaderFile ClassLoaderFiles} in the collection that are
* contained in this source folder.
* @return the files contained in the source folder
* contained in this source directory.
* @return the files contained in the source directory
*/
public Collection<ClassLoaderFile> getFiles() {
return Collections.unmodifiableCollection(this.files.values());

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
@ -26,13 +26,13 @@ import java.util.regex.Pattern;
import org.springframework.util.StringUtils;
/**
* Default implementation of {@link SourceFolderUrlFilter} that attempts to match URLs
* Default implementation of {@link SourceDirectoryUrlFilter} that attempts to match URLs
* using common naming conventions.
*
* @author Phillip Webb
* @since 1.3.0
* @since 2.3.0
*/
public class DefaultSourceFolderUrlFilter implements SourceFolderUrlFilter {
public class DefaultSourceDirectoryUrlFilter implements SourceDirectoryUrlFilter {
private static final String[] COMMON_ENDINGS = { "/target/classes", "/bin" };
@ -44,12 +44,12 @@ public class DefaultSourceFolderUrlFilter implements SourceFolderUrlFilter {
"spring-boot-devtools", "spring-boot-autoconfigure", "spring-boot-actuator", "spring-boot-starter"));
@Override
public boolean isMatch(String sourceFolder, URL url) {
public boolean isMatch(String sourceDirectory, URL url) {
String jarName = getJarName(url);
if (!StringUtils.hasLength(jarName)) {
return false;
}
return isMatch(sourceFolder, jarName);
return isMatch(sourceDirectory, jarName);
}
private String getJarName(URL url) {
@ -60,23 +60,23 @@ public class DefaultSourceFolderUrlFilter implements SourceFolderUrlFilter {
return null;
}
private boolean isMatch(String sourceFolder, String jarName) {
sourceFolder = stripTrailingSlash(sourceFolder);
sourceFolder = stripCommonEnds(sourceFolder);
String[] folders = StringUtils.delimitedListToStringArray(sourceFolder, "/");
for (int i = folders.length - 1; i >= 0; i--) {
if (isFolderMatch(folders[i], jarName)) {
private boolean isMatch(String sourceDirectory, String jarName) {
sourceDirectory = stripTrailingSlash(sourceDirectory);
sourceDirectory = stripCommonEnds(sourceDirectory);
String[] directories = StringUtils.delimitedListToStringArray(sourceDirectory, "/");
for (int i = directories.length - 1; i >= 0; i--) {
if (isDirectoryMatch(directories[i], jarName)) {
return true;
}
}
return false;
}
private boolean isFolderMatch(String folder, String jarName) {
if (!jarName.startsWith(folder) || SKIPPED_PROJECTS.contains(folder)) {
private boolean isDirectoryMatch(String directory, String jarName) {
if (!jarName.startsWith(directory) || SKIPPED_PROJECTS.contains(directory)) {
return false;
}
String version = jarName.substring(folder.length());
String version = jarName.substring(directory.length());
return version.isEmpty() || VERSION_PATTERN.matcher(version).matches();
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
@ -44,12 +44,12 @@ public class HttpRestartServer {
/**
* Create a new {@link HttpRestartServer} instance.
* @param sourceFolderUrlFilter the source filter used to link remote folder to the
* local classpath
* @param sourceDirectoryUrlFilter the source filter used to link remote directory to
* the local classpath
*/
public HttpRestartServer(SourceFolderUrlFilter sourceFolderUrlFilter) {
Assert.notNull(sourceFolderUrlFilter, "SourceFolderUrlFilter must not be null");
this.server = new RestartServer(sourceFolderUrlFilter);
public HttpRestartServer(SourceDirectoryUrlFilter sourceDirectoryUrlFilter) {
Assert.notNull(sourceDirectoryUrlFilter, "SourceDirectoryUrlFilter must not be null");
this.server = new RestartServer(sourceDirectoryUrlFilter);
}
/**

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
@ -32,7 +32,7 @@ import org.springframework.boot.devtools.restart.Restarter;
import org.springframework.boot.devtools.restart.classloader.ClassLoaderFile;
import org.springframework.boot.devtools.restart.classloader.ClassLoaderFile.Kind;
import org.springframework.boot.devtools.restart.classloader.ClassLoaderFiles;
import org.springframework.boot.devtools.restart.classloader.ClassLoaderFiles.SourceFolder;
import org.springframework.boot.devtools.restart.classloader.ClassLoaderFiles.SourceDirectory;
import org.springframework.util.Assert;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.ResourceUtils;
@ -48,29 +48,29 @@ public class RestartServer {
private static final Log logger = LogFactory.getLog(RestartServer.class);
private final SourceFolderUrlFilter sourceFolderUrlFilter;
private final SourceDirectoryUrlFilter sourceDirectoryUrlFilter;
private final ClassLoader classLoader;
/**
* Create a new {@link RestartServer} instance.
* @param sourceFolderUrlFilter the source filter used to link remote folder to the
* local classpath
* @param sourceDirectoryUrlFilter the source filter used to link remote directory to
* the local classpath
*/
public RestartServer(SourceFolderUrlFilter sourceFolderUrlFilter) {
this(sourceFolderUrlFilter, Thread.currentThread().getContextClassLoader());
public RestartServer(SourceDirectoryUrlFilter sourceDirectoryUrlFilter) {
this(sourceDirectoryUrlFilter, Thread.currentThread().getContextClassLoader());
}
/**
* Create a new {@link RestartServer} instance.
* @param sourceFolderUrlFilter the source filter used to link remote folder to the
* local classpath
* @param sourceDirectoryUrlFilter the source filter used to link remote directory to
* the local classpath
* @param classLoader the application classloader
*/
public RestartServer(SourceFolderUrlFilter sourceFolderUrlFilter, ClassLoader classLoader) {
Assert.notNull(sourceFolderUrlFilter, "SourceFolderUrlFilter must not be null");
public RestartServer(SourceDirectoryUrlFilter sourceDirectoryUrlFilter, ClassLoader classLoader) {
Assert.notNull(sourceDirectoryUrlFilter, "SourceDirectoryUrlFilter must not be null");
Assert.notNull(classLoader, "ClassLoader must not be null");
this.sourceFolderUrlFilter = sourceFolderUrlFilter;
this.sourceDirectoryUrlFilter = sourceDirectoryUrlFilter;
this.classLoader = classLoader;
}
@ -82,27 +82,27 @@ public class RestartServer {
public void updateAndRestart(ClassLoaderFiles files) {
Set<URL> urls = new LinkedHashSet<>();
Set<URL> classLoaderUrls = getClassLoaderUrls();
for (SourceFolder folder : files.getSourceFolders()) {
for (Entry<String, ClassLoaderFile> entry : folder.getFilesEntrySet()) {
for (SourceDirectory directory : files.getSourceDirectories()) {
for (Entry<String, ClassLoaderFile> entry : directory.getFilesEntrySet()) {
for (URL url : classLoaderUrls) {
if (updateFileSystem(url, entry.getKey(), entry.getValue())) {
urls.add(url);
}
}
}
urls.addAll(getMatchingUrls(classLoaderUrls, folder.getName()));
urls.addAll(getMatchingUrls(classLoaderUrls, directory.getName()));
}
updateTimeStamp(urls);
restart(urls, files);
}
private boolean updateFileSystem(URL url, String name, ClassLoaderFile classLoaderFile) {
if (!isFolderUrl(url.toString())) {
if (!isDirectoryUrl(url.toString())) {
return false;
}
try {
File folder = ResourceUtils.getFile(url);
File file = new File(folder, name);
File directory = ResourceUtils.getFile(url);
File file = new File(directory, name);
if (file.exists() && file.canWrite()) {
if (classLoaderFile.getKind() == Kind.DELETED) {
return file.delete();
@ -117,16 +117,16 @@ public class RestartServer {
return false;
}
private boolean isFolderUrl(String urlString) {
private boolean isDirectoryUrl(String urlString) {
return urlString.startsWith("file:") && urlString.endsWith("/");
}
private Set<URL> getMatchingUrls(Set<URL> urls, String sourceFolder) {
private Set<URL> getMatchingUrls(Set<URL> urls, String sourceDirectory) {
Set<URL> matchingUrls = new LinkedHashSet<>();
for (URL url : urls) {
if (this.sourceFolderUrlFilter.isMatch(sourceFolder, url)) {
if (this.sourceDirectoryUrlFilter.isMatch(sourceDirectory, url)) {
if (logger.isDebugEnabled()) {
logger.debug("URL " + url + " matched against source folder " + sourceFolder);
logger.debug("URL " + url + " matched against source directory " + sourceDirectory);
}
matchingUrls.add(url);
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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,22 +19,22 @@ package org.springframework.boot.devtools.restart.server;
import java.net.URL;
/**
* Filter URLs based on a source folder name. Used to match URLs from the running
* classpath against source folders on a remote system.
* Filter URLs based on a source directory name. Used to match URLs from the running
* classpath against source directory on a remote system.
*
* @author Phillip Webb
* @since 1.3.0
* @see DefaultSourceFolderUrlFilter
* @since 2.3.0
* @see DefaultSourceDirectoryUrlFilter
*/
@FunctionalInterface
public interface SourceFolderUrlFilter {
public interface SourceDirectoryUrlFilter {
/**
* Determine if the specified URL matches a source folder.
* @param sourceFolder the source folder
* Determine if the specified URL matches a source directory.
* @param sourceDirectory the source directory
* @param url the URL to check
* @return {@code true} if the URL matches
*/
boolean isMatch(String sourceFolder, URL url);
boolean isMatch(String sourceDirectory, URL url);
}

@ -213,8 +213,8 @@ class LocalDevToolsAutoConfigurationTests {
ClassPathFileSystemWatcher classPathWatcher = this.context.getBean(ClassPathFileSystemWatcher.class);
Object watcher = ReflectionTestUtils.getField(classPathWatcher, "fileSystemWatcher");
@SuppressWarnings("unchecked")
Map<File, Object> folders = (Map<File, Object>) ReflectionTestUtils.getField(watcher, "folders");
assertThat(folders).hasSize(2).containsKey(new File("src/main/java").getAbsoluteFile())
Map<File, Object> directories = (Map<File, Object>) ReflectionTestUtils.getField(watcher, "directories");
assertThat(directories).hasSize(2).containsKey(new File("src/main/java").getAbsoluteFile())
.containsKey(new File("src/test/java").getAbsoluteFile());
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
@ -32,7 +32,7 @@ import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfi
import org.springframework.boot.devtools.remote.server.DispatcherFilter;
import org.springframework.boot.devtools.restart.MockRestarter;
import org.springframework.boot.devtools.restart.server.HttpRestartServer;
import org.springframework.boot.devtools.restart.server.SourceFolderUrlFilter;
import org.springframework.boot.devtools.restart.server.SourceDirectoryUrlFilter;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebApplicationContext;
import org.springframework.context.annotation.Bean;
@ -244,8 +244,8 @@ class RemoteDevToolsAutoConfigurationTests {
@Bean
HttpRestartServer remoteRestartHttpRestartServer() {
SourceFolderUrlFilter sourceFolderUrlFilter = mock(SourceFolderUrlFilter.class);
return new MockHttpRestartServer(sourceFolderUrlFilter);
SourceDirectoryUrlFilter sourceDirectoryUrlFilter = mock(SourceDirectoryUrlFilter.class);
return new MockHttpRestartServer(sourceDirectoryUrlFilter);
}
}
@ -257,8 +257,8 @@ class RemoteDevToolsAutoConfigurationTests {
private boolean invoked;
MockHttpRestartServer(SourceFolderUrlFilter sourceFolderUrlFilter) {
super(sourceFolderUrlFilter);
MockHttpRestartServer(SourceDirectoryUrlFilter sourceDirectoryUrlFilter) {
super(sourceDirectoryUrlFilter);
}
@Override

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
@ -93,10 +93,10 @@ class ClassPathFileChangeListenerTests {
private void testSendsEvent(boolean restart) {
ClassPathFileChangeListener listener = new ClassPathFileChangeListener(this.eventPublisher,
this.restartStrategy, this.fileSystemWatcher);
File folder = new File("s1");
File directory = new File("s1");
File file = new File("f1");
ChangedFile file1 = new ChangedFile(folder, file, ChangedFile.Type.ADD);
ChangedFile file2 = new ChangedFile(folder, file, ChangedFile.Type.ADD);
ChangedFile file1 = new ChangedFile(directory, file, ChangedFile.Type.ADD);
ChangedFile file2 = new ChangedFile(directory, file, ChangedFile.Type.ADD);
Set<ChangedFile> files = new LinkedHashSet<>();
files.add(file1);
files.add(file2);

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
@ -57,19 +57,19 @@ class ClassPathFileSystemWatcherTests {
}
@Test
void configuredWithRestartStrategy(@TempDir File folder) throws Exception {
void configuredWithRestartStrategy(@TempDir File directory) throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
Map<String, Object> properties = new HashMap<>();
List<URL> urls = new ArrayList<>();
urls.add(new URL("https://spring.io"));
urls.add(folder.toURI().toURL());
urls.add(directory.toURI().toURL());
properties.put("urls", urls);
MapPropertySource propertySource = new MapPropertySource("test", properties);
context.getEnvironment().getPropertySources().addLast(propertySource);
context.register(Config.class);
context.refresh();
Thread.sleep(200);
File classFile = new File(folder, "Example.class");
File classFile = new File(directory, "Example.class");
FileCopyUtils.copy("file".getBytes(), classFile);
Thread.sleep(1000);
List<ClassPathChangedEvent> events = context.getBean(Listener.class).getEvents();

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
@ -49,29 +49,29 @@ class PatternClassPathRestartStrategyTests {
void singlePattern() {
ClassPathRestartStrategy strategy = createStrategy("static/**");
assertRestartRequired(strategy, "static/file.txt", false);
assertRestartRequired(strategy, "static/folder/file.txt", false);
assertRestartRequired(strategy, "static/directory/file.txt", false);
assertRestartRequired(strategy, "public/file.txt", true);
assertRestartRequired(strategy, "public/folder/file.txt", true);
assertRestartRequired(strategy, "public/directory/file.txt", true);
}
@Test
void multiplePatterns() {
ClassPathRestartStrategy strategy = createStrategy("static/**,public/**");
assertRestartRequired(strategy, "static/file.txt", false);
assertRestartRequired(strategy, "static/folder/file.txt", false);
assertRestartRequired(strategy, "static/directory/file.txt", false);
assertRestartRequired(strategy, "public/file.txt", false);
assertRestartRequired(strategy, "public/folder/file.txt", false);
assertRestartRequired(strategy, "public/directory/file.txt", false);
assertRestartRequired(strategy, "src/file.txt", true);
assertRestartRequired(strategy, "src/folder/file.txt", true);
assertRestartRequired(strategy, "src/directory/file.txt", true);
}
@Test
void pomChange() {
ClassPathRestartStrategy strategy = createStrategy("META-INF/maven/**");
assertRestartRequired(strategy, "pom.xml", true);
String mavenFolder = "META-INF/maven/org.springframework.boot/spring-boot-devtools";
assertRestartRequired(strategy, mavenFolder + "/pom.xml", false);
assertRestartRequired(strategy, mavenFolder + "/pom.properties", false);
String mavenDirectory = "META-INF/maven/org.springframework.boot/spring-boot-devtools";
assertRestartRequired(strategy, mavenDirectory + "/pom.xml", false);
assertRestartRequired(strategy, mavenDirectory + "/pom.properties", false);
}
@Test

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
@ -55,7 +55,7 @@ class DevToolsHomePropertiesPostProcessorTests {
}
@Test
void loadsPropertiesFromHomeFolderUsingProperties() throws Exception {
void loadsPropertiesFromHomeDirectoryUsingProperties() throws Exception {
Properties properties = new Properties();
properties.put("abc", "def");
writeFile(properties, ".spring-boot-devtools.properties");
@ -64,7 +64,7 @@ class DevToolsHomePropertiesPostProcessorTests {
}
@Test
void loadsPropertiesFromConfigFolderUsingProperties() throws Exception {
void loadsPropertiesFromConfigDirectoryUsingProperties() throws Exception {
Properties properties = new Properties();
properties.put("abc", "def");
OutputStream out = new FileOutputStream(new File(this.configDir, "spring-boot-devtools.properties"));
@ -75,7 +75,7 @@ class DevToolsHomePropertiesPostProcessorTests {
}
@Test
void loadsPropertiesFromConfigFolderUsingYml() throws Exception {
void loadsPropertiesFromConfigDirectoryUsingYml() throws Exception {
OutputStream out = new FileOutputStream(new File(this.configDir, "spring-boot-devtools.yml"));
File file = new ClassPathResource("spring-devtools.yaml", getClass()).getFile();
byte[] content = Files.readAllBytes(file.toPath());
@ -86,7 +86,7 @@ class DevToolsHomePropertiesPostProcessorTests {
}
@Test
void loadsPropertiesFromConfigFolderUsingYaml() throws Exception {
void loadsPropertiesFromConfigDirectoryUsingYaml() throws Exception {
OutputStream out = new FileOutputStream(new File(this.configDir, "spring-boot-devtools.yaml"));
File file = new ClassPathResource("spring-devtools.yaml", getClass()).getFile();
byte[] content = Files.readAllBytes(file.toPath());
@ -97,7 +97,7 @@ class DevToolsHomePropertiesPostProcessorTests {
}
@Test
void loadFromConfigFolderWithPropertiesTakingPrecedence() throws Exception {
void loadFromConfigDirectoryWithPropertiesTakingPrecedence() throws Exception {
OutputStream out = new FileOutputStream(new File(this.configDir, "spring-boot-devtools.yaml"));
File file = new ClassPathResource("spring-devtools.yaml", getClass()).getFile();
byte[] content = Files.readAllBytes(file.toPath());
@ -114,7 +114,7 @@ class DevToolsHomePropertiesPostProcessorTests {
}
@Test
void loadFromConfigFolderTakesPrecedenceOverHomeFolder() throws Exception {
void loadFromConfigDirectoryTakesPrecedenceOverHomeDirectory() throws Exception {
Properties properties = new Properties();
properties.put("abc", "def");
properties.put("bar", "baz");
@ -130,7 +130,7 @@ class DevToolsHomePropertiesPostProcessorTests {
}
@Test
void loadFromConfigFolderWithYamlTakesPrecedenceOverHomeFolder() throws Exception {
void loadFromConfigDirectoryWithYamlTakesPrecedenceOverHomeDirectory() throws Exception {
Properties properties = new Properties();
properties.put("abc.xyz", "jkl");
properties.put("bar", "baz");
@ -173,7 +173,7 @@ class DevToolsHomePropertiesPostProcessorTests {
private class MockDevToolHomePropertiesPostProcessor extends DevToolsHomePropertiesPostProcessor {
@Override
protected File getHomeFolder() {
protected File getHomeDirectory() {
return DevToolsHomePropertiesPostProcessorTests.this.home;
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
@ -37,45 +37,44 @@ class ChangedFileTests {
File tempDir;
@Test
void sourceFolderMustNotBeNull() throws Exception {
void sourceDirectoryMustNotBeNull() throws Exception {
assertThatIllegalArgumentException()
.isThrownBy(() -> new ChangedFile(null, new File(this.tempDir, "file"), Type.ADD))
.withMessageContaining("SourceFolder must not be null");
.withMessageContaining("SourceDirectory must not be null");
}
@Test
void fileMustNotBeNull() throws Exception {
assertThatIllegalArgumentException()
.isThrownBy(() -> new ChangedFile(new File(this.tempDir, "folder"), null, Type.ADD))
.isThrownBy(() -> new ChangedFile(new File(this.tempDir, "directory"), null, Type.ADD))
.withMessageContaining("File must not be null");
}
@Test
void typeMustNotBeNull() throws Exception {
assertThatIllegalArgumentException()
.isThrownBy(
() -> new ChangedFile(new File(this.tempDir, "file"), new File(this.tempDir, "folder"), null))
assertThatIllegalArgumentException().isThrownBy(
() -> new ChangedFile(new File(this.tempDir, "file"), new File(this.tempDir, "directory"), null))
.withMessageContaining("Type must not be null");
}
@Test
void getFile() throws Exception {
File file = new File(this.tempDir, "file");
ChangedFile changedFile = new ChangedFile(new File(this.tempDir, "folder"), file, Type.ADD);
ChangedFile changedFile = new ChangedFile(new File(this.tempDir, "directory"), file, Type.ADD);
assertThat(changedFile.getFile()).isEqualTo(file);
}
@Test
void getType() throws Exception {
ChangedFile changedFile = new ChangedFile(new File(this.tempDir, "folder"), new File(this.tempDir, "file"),
ChangedFile changedFile = new ChangedFile(new File(this.tempDir, "directory"), new File(this.tempDir, "file"),
Type.DELETE);
assertThat(changedFile.getType()).isEqualTo(Type.DELETE);
}
@Test
void getRelativeName() throws Exception {
File subFolder = new File(this.tempDir, "A");
File file = new File(subFolder, "B.txt");
File subDirectory = new File(this.tempDir, "A");
File file = new File(subDirectory, "B.txt");
ChangedFile changedFile = new ChangedFile(this.tempDir, file, Type.ADD);
assertThat(changedFile.getRelativeName()).isEqualTo("A/B.txt");
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
@ -31,72 +31,72 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
/**
* Tests for {@link FolderSnapshot}.
* Tests for {@link DirectorySnapshot}.
*
* @author Phillip Webb
*/
class FolderSnapshotTests {
class DirectorySnapshotTests {
@TempDir
File tempDir;
private File folder;
private File directory;
private FolderSnapshot initialSnapshot;
private DirectorySnapshot initialSnapshot;
@BeforeEach
void setup() throws Exception {
this.folder = createTestFolderStructure();
this.initialSnapshot = new FolderSnapshot(this.folder);
this.directory = createTestDirectoryStructure();
this.initialSnapshot = new DirectorySnapshot(this.directory);
}
@Test
void folderMustNotBeNull() {
assertThatIllegalArgumentException().isThrownBy(() -> new FolderSnapshot(null))
.withMessageContaining("Folder must not be null");
void directoryMustNotBeNull() {
assertThatIllegalArgumentException().isThrownBy(() -> new DirectorySnapshot(null))
.withMessageContaining("Directory must not be null");
}
@Test
void folderMustNotBeFile() throws Exception {
void directoryMustNotBeFile() throws Exception {
File file = new File(this.tempDir, "file");
file.createNewFile();
assertThatIllegalArgumentException().isThrownBy(() -> new FolderSnapshot(file))
.withMessageContaining("Folder '" + file + "' must not be a file");
assertThatIllegalArgumentException().isThrownBy(() -> new DirectorySnapshot(file))
.withMessageContaining("Directory '" + file + "' must not be a file");
}
@Test
void folderDoesNotHaveToExist() throws Exception {
void directoryDoesNotHaveToExist() throws Exception {
File file = new File(this.tempDir, "does/not/exist");
FolderSnapshot snapshot = new FolderSnapshot(file);
assertThat(snapshot).isEqualTo(new FolderSnapshot(file));
DirectorySnapshot snapshot = new DirectorySnapshot(file);
assertThat(snapshot).isEqualTo(new DirectorySnapshot(file));
}
@Test
void equalsWhenNothingHasChanged() {
FolderSnapshot updatedSnapshot = new FolderSnapshot(this.folder);
DirectorySnapshot updatedSnapshot = new DirectorySnapshot(this.directory);
assertThat(this.initialSnapshot).isEqualTo(updatedSnapshot);
assertThat(this.initialSnapshot.hashCode()).isEqualTo(updatedSnapshot.hashCode());
}
@Test
void notEqualsWhenAFileIsAdded() throws Exception {
new File(new File(this.folder, "folder1"), "newfile").createNewFile();
FolderSnapshot updatedSnapshot = new FolderSnapshot(this.folder);
new File(new File(this.directory, "directory1"), "newfile").createNewFile();
DirectorySnapshot updatedSnapshot = new DirectorySnapshot(this.directory);
assertThat(this.initialSnapshot).isNotEqualTo(updatedSnapshot);
}
@Test
void notEqualsWhenAFileIsDeleted() {
new File(new File(this.folder, "folder1"), "file1").delete();
FolderSnapshot updatedSnapshot = new FolderSnapshot(this.folder);
new File(new File(this.directory, "directory1"), "file1").delete();
DirectorySnapshot updatedSnapshot = new DirectorySnapshot(this.directory);
assertThat(this.initialSnapshot).isNotEqualTo(updatedSnapshot);
}
@Test
void notEqualsWhenAFileIsModified() throws Exception {
File file1 = new File(new File(this.folder, "folder1"), "file1");
File file1 = new File(new File(this.directory, "directory1"), "file1");
FileCopyUtils.copy("updatedcontent".getBytes(), file1);
FolderSnapshot updatedSnapshot = new FolderSnapshot(this.folder);
DirectorySnapshot updatedSnapshot = new DirectorySnapshot(this.directory);
assertThat(this.initialSnapshot).isNotEqualTo(updatedSnapshot);
}
@ -107,30 +107,30 @@ class FolderSnapshotTests {
}
@Test
void getChangedFilesSnapshotMustBeTheSameSourceFolder() throws Exception {
void getChangedFilesSnapshotMustBeTheSameSourceDirectory() throws Exception {
assertThatIllegalArgumentException().isThrownBy(
() -> this.initialSnapshot.getChangedFiles(new FolderSnapshot(createTestFolderStructure()), null))
.withMessageContaining("Snapshot source folder must be '" + this.folder + "'");
() -> this.initialSnapshot.getChangedFiles(new DirectorySnapshot(createTestDirectoryStructure()), null))
.withMessageContaining("Snapshot source directory must be '" + this.directory + "'");
}
@Test
void getChangedFilesWhenNothingHasChanged() {
FolderSnapshot updatedSnapshot = new FolderSnapshot(this.folder);
DirectorySnapshot updatedSnapshot = new DirectorySnapshot(this.directory);
this.initialSnapshot.getChangedFiles(updatedSnapshot, null);
}
@Test
void getChangedFilesWhenAFileIsAddedAndDeletedAndChanged() throws Exception {
File folder1 = new File(this.folder, "folder1");
File file1 = new File(folder1, "file1");
File file2 = new File(folder1, "file2");
File newFile = new File(folder1, "newfile");
File directory1 = new File(this.directory, "directory1");
File file1 = new File(directory1, "file1");
File file2 = new File(directory1, "file2");
File newFile = new File(directory1, "newfile");
FileCopyUtils.copy("updatedcontent".getBytes(), file1);
file2.delete();
newFile.createNewFile();
FolderSnapshot updatedSnapshot = new FolderSnapshot(this.folder);
DirectorySnapshot updatedSnapshot = new DirectorySnapshot(this.directory);
ChangedFiles changedFiles = this.initialSnapshot.getChangedFiles(updatedSnapshot, null);
assertThat(changedFiles.getSourceFolder()).isEqualTo(this.folder);
assertThat(changedFiles.getSourceDirectory()).isEqualTo(this.directory);
assertThat(getChangedFile(changedFiles, file1).getType()).isEqualTo(Type.MODIFY);
assertThat(getChangedFile(changedFiles, file2).getType()).isEqualTo(Type.DELETE);
assertThat(getChangedFile(changedFiles, newFile).getType()).isEqualTo(Type.ADD);
@ -145,12 +145,12 @@ class FolderSnapshotTests {
return null;
}
private File createTestFolderStructure() throws IOException {
private File createTestDirectoryStructure() throws IOException {
File root = new File(this.tempDir, UUID.randomUUID().toString());
File folder1 = new File(root, "folder1");
folder1.mkdirs();
FileCopyUtils.copy("abc".getBytes(), new File(folder1, "file1"));
FileCopyUtils.copy("abc".getBytes(), new File(folder1, "file2"));
File directory1 = new File(root, "directory1");
directory1.mkdirs();
FileCopyUtils.copy("abc".getBytes(), new File(directory1, "file1"));
FileCopyUtils.copy("abc".getBytes(), new File(directory1, "file2"));
return root;
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
@ -51,11 +51,11 @@ class FileSnapshotTests {
}
@Test
void fileMustNotBeAFolder() throws Exception {
void fileMustNotBeADirectory() throws Exception {
File file = new File(this.tempDir, "file");
file.mkdir();
assertThatIllegalArgumentException().isThrownBy(() -> new FileSnapshot(file))
.withMessageContaining("File must not be a folder");
.withMessageContaining("File must not be a directory");
}
@Test

@ -94,70 +94,70 @@ class FileSystemWatcherTests {
}
@Test
void sourceFolderMustNotBeNull() {
assertThatIllegalArgumentException().isThrownBy(() -> this.watcher.addSourceFolder(null))
.withMessageContaining("Folder must not be null");
void sourceDirectoryMustNotBeNull() {
assertThatIllegalArgumentException().isThrownBy(() -> this.watcher.addSourceDirectory(null))
.withMessageContaining("Directory must not be null");
}
@Test
void sourceFolderMustNotBeAFile() throws IOException {
void sourceDirectoryMustNotBeAFile() throws IOException {
File file = new File(this.tempDir, "file");
assertThat(file.createNewFile()).isTrue();
assertThat(file.isFile()).isTrue();
assertThatIllegalArgumentException().isThrownBy(() -> this.watcher.addSourceFolder(file))
.withMessageContaining("Folder '" + file + "' must not be a file");
assertThatIllegalArgumentException().isThrownBy(() -> this.watcher.addSourceDirectory(file))
.withMessageContaining("Directory '" + file + "' must not be a file");
}
@Test
void cannotAddSourceFolderToStartedListener() throws Exception {
void cannotAddSourceDirectoryToStartedListener() throws Exception {
this.watcher.start();
assertThatIllegalStateException().isThrownBy(() -> this.watcher.addSourceFolder(this.tempDir))
assertThatIllegalStateException().isThrownBy(() -> this.watcher.addSourceDirectory(this.tempDir))
.withMessageContaining("FileSystemWatcher already started");
}
@Test
void addFile() throws Exception {
File folder = startWithNewFolder();
File file = touch(new File(folder, "test.txt"));
File directory = startWithNewDirectory();
File file = touch(new File(directory, "test.txt"));
this.watcher.stopAfter(1);
ChangedFiles changedFiles = getSingleChangedFiles();
ChangedFile expected = new ChangedFile(folder, file, Type.ADD);
ChangedFile expected = new ChangedFile(directory, file, Type.ADD);
assertThat(changedFiles.getFiles()).contains(expected);
}
@Test
void addNestedFile() throws Exception {
File folder = startWithNewFolder();
File file = touch(new File(new File(folder, "sub"), "text.txt"));
File directory = startWithNewDirectory();
File file = touch(new File(new File(directory, "sub"), "text.txt"));
this.watcher.stopAfter(1);
ChangedFiles changedFiles = getSingleChangedFiles();
ChangedFile expected = new ChangedFile(folder, file, Type.ADD);
ChangedFile expected = new ChangedFile(directory, file, Type.ADD);
assertThat(changedFiles.getFiles()).contains(expected);
}
@Test
void createSourceFolderAndAddFile() throws IOException {
File folder = new File(this.tempDir, "does/not/exist");
assertThat(folder.exists()).isFalse();
this.watcher.addSourceFolder(folder);
void createSourceDirectoryAndAddFile() throws IOException {
File directory = new File(this.tempDir, "does/not/exist");
assertThat(directory.exists()).isFalse();
this.watcher.addSourceDirectory(directory);
this.watcher.start();
folder.mkdirs();
File file = touch(new File(folder, "text.txt"));
directory.mkdirs();
File file = touch(new File(directory, "text.txt"));
this.watcher.stopAfter(1);
ChangedFiles changedFiles = getSingleChangedFiles();
ChangedFile expected = new ChangedFile(folder, file, Type.ADD);
ChangedFile expected = new ChangedFile(directory, file, Type.ADD);
assertThat(changedFiles.getFiles()).contains(expected);
}
@Test
void waitsForPollingInterval() throws Exception {
setupWatcher(10, 1);
File folder = startWithNewFolder();
touch(new File(folder, "test1.txt"));
File directory = startWithNewDirectory();
touch(new File(directory, "test1.txt"));
while (this.changes.size() != 1) {
Thread.sleep(10);
}
touch(new File(folder, "test2.txt"));
touch(new File(directory, "test2.txt"));
this.watcher.stopAfter(1);
assertThat(this.changes.size()).isEqualTo(2);
}
@ -165,9 +165,9 @@ class FileSystemWatcherTests {
@Test
void waitsForQuietPeriod() throws Exception {
setupWatcher(300, 200);
File folder = startWithNewFolder();
File directory = startWithNewDirectory();
for (int i = 0; i < 10; i++) {
touch(new File(folder, i + "test.txt"));
touch(new File(directory, i + "test.txt"));
Thread.sleep(100);
}
this.watcher.stopAfter(1);
@ -177,39 +177,39 @@ class FileSystemWatcherTests {
@Test
void withExistingFiles() throws Exception {
File folder = new File(this.tempDir, UUID.randomUUID().toString());
folder.mkdir();
touch(new File(folder, "test.txt"));
this.watcher.addSourceFolder(folder);
File directory = new File(this.tempDir, UUID.randomUUID().toString());
directory.mkdir();
touch(new File(directory, "test.txt"));
this.watcher.addSourceDirectory(directory);
this.watcher.start();
File file = touch(new File(folder, "test2.txt"));
File file = touch(new File(directory, "test2.txt"));
this.watcher.stopAfter(1);
ChangedFiles changedFiles = getSingleChangedFiles();
ChangedFile expected = new ChangedFile(folder, file, Type.ADD);
ChangedFile expected = new ChangedFile(directory, file, Type.ADD);
assertThat(changedFiles.getFiles()).contains(expected);
}
@Test
void multipleSources() throws Exception {
File folder1 = new File(this.tempDir, UUID.randomUUID().toString());
folder1.mkdir();
File folder2 = new File(this.tempDir, UUID.randomUUID().toString());
folder2.mkdir();
this.watcher.addSourceFolder(folder1);
this.watcher.addSourceFolder(folder2);
File directory1 = new File(this.tempDir, UUID.randomUUID().toString());
directory1.mkdir();
File directory2 = new File(this.tempDir, UUID.randomUUID().toString());
directory2.mkdir();
this.watcher.addSourceDirectory(directory1);
this.watcher.addSourceDirectory(directory2);
this.watcher.start();
File file1 = touch(new File(folder1, "test.txt"));
File file2 = touch(new File(folder2, "test.txt"));
File file1 = touch(new File(directory1, "test.txt"));
File file2 = touch(new File(directory2, "test.txt"));
this.watcher.stopAfter(1);
Set<ChangedFiles> change = getSingleOnChange();
assertThat(change.size()).isEqualTo(2);
for (ChangedFiles changedFiles : change) {
if (changedFiles.getSourceFolder().equals(folder1)) {
ChangedFile file = new ChangedFile(folder1, file1, Type.ADD);
if (changedFiles.getSourceDirectory().equals(directory1)) {
ChangedFile file = new ChangedFile(directory1, file1, Type.ADD);
assertThat(changedFiles.getFiles()).containsOnly(file);
}
else {
ChangedFile file = new ChangedFile(folder2, file2, Type.ADD);
ChangedFile file = new ChangedFile(directory2, file2, Type.ADD);
assertThat(changedFiles.getFiles()).containsOnly(file);
}
}
@ -217,48 +217,48 @@ class FileSystemWatcherTests {
@Test
void multipleListeners() throws Exception {
File folder = new File(this.tempDir, UUID.randomUUID().toString());
folder.mkdir();
File directory = new File(this.tempDir, UUID.randomUUID().toString());
directory.mkdir();
final Set<ChangedFiles> listener2Changes = new LinkedHashSet<>();
this.watcher.addSourceFolder(folder);
this.watcher.addSourceDirectory(directory);
this.watcher.addListener(listener2Changes::addAll);
this.watcher.start();
File file = touch(new File(folder, "test.txt"));
File file = touch(new File(directory, "test.txt"));
this.watcher.stopAfter(1);
ChangedFiles changedFiles = getSingleChangedFiles();
ChangedFile expected = new ChangedFile(folder, file, Type.ADD);
ChangedFile expected = new ChangedFile(directory, file, Type.ADD);
assertThat(changedFiles.getFiles()).contains(expected);
assertThat(listener2Changes).isEqualTo(this.changes.get(0));
}
@Test
void modifyDeleteAndAdd() throws Exception {
File folder = new File(this.tempDir, UUID.randomUUID().toString());
folder.mkdir();
File modify = touch(new File(folder, "modify.txt"));
File delete = touch(new File(folder, "delete.txt"));
this.watcher.addSourceFolder(folder);
File directory = new File(this.tempDir, UUID.randomUUID().toString());
directory.mkdir();
File modify = touch(new File(directory, "modify.txt"));
File delete = touch(new File(directory, "delete.txt"));
this.watcher.addSourceDirectory(directory);
this.watcher.start();
FileCopyUtils.copy("abc".getBytes(), modify);
delete.delete();
File add = touch(new File(folder, "add.txt"));
File add = touch(new File(directory, "add.txt"));
this.watcher.stopAfter(1);
ChangedFiles changedFiles = getSingleChangedFiles();
Set<ChangedFile> actual = changedFiles.getFiles();
Set<ChangedFile> expected = new HashSet<>();
expected.add(new ChangedFile(folder, modify, Type.MODIFY));
expected.add(new ChangedFile(folder, delete, Type.DELETE));
expected.add(new ChangedFile(folder, add, Type.ADD));
expected.add(new ChangedFile(directory, modify, Type.MODIFY));
expected.add(new ChangedFile(directory, delete, Type.DELETE));
expected.add(new ChangedFile(directory, add, Type.ADD));
assertThat(actual).isEqualTo(expected);
}
@Test
void withTriggerFilter() throws Exception {
File folder = new File(this.tempDir, UUID.randomUUID().toString());
folder.mkdir();
File file = touch(new File(folder, "file.txt"));
File trigger = touch(new File(folder, "trigger.txt"));
this.watcher.addSourceFolder(folder);
File directory = new File(this.tempDir, UUID.randomUUID().toString());
directory.mkdir();
File file = touch(new File(directory, "file.txt"));
File trigger = touch(new File(directory, "trigger.txt"));
this.watcher.addSourceDirectory(directory);
this.watcher.setTriggerFilter((candidate) -> candidate.getName().equals("trigger.txt"));
this.watcher.start();
FileCopyUtils.copy("abc".getBytes(), file);
@ -269,7 +269,7 @@ class FileSystemWatcherTests {
ChangedFiles changedFiles = getSingleChangedFiles();
Set<ChangedFile> actual = changedFiles.getFiles();
Set<ChangedFile> expected = new HashSet<>();
expected.add(new ChangedFile(folder, file, Type.MODIFY));
expected.add(new ChangedFile(directory, file, Type.MODIFY));
assertThat(actual).isEqualTo(expected);
}
@ -278,12 +278,12 @@ class FileSystemWatcherTests {
this.watcher.addListener((changeSet) -> FileSystemWatcherTests.this.changes.add(changeSet));
}
private File startWithNewFolder() throws IOException {
File folder = new File(this.tempDir, UUID.randomUUID().toString());
folder.mkdir();
this.watcher.addSourceFolder(folder);
private File startWithNewDirectory() throws IOException {
File directory = new File(this.tempDir, UUID.randomUUID().toString());
directory.mkdir();
this.watcher.addSourceDirectory(directory);
this.watcher.start();
return folder;
return directory;
}
private ChangedFiles getSingleChangedFiles() {

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
@ -37,7 +37,7 @@ import org.springframework.boot.devtools.filewatch.ChangedFiles;
import org.springframework.boot.devtools.restart.classloader.ClassLoaderFile;
import org.springframework.boot.devtools.restart.classloader.ClassLoaderFile.Kind;
import org.springframework.boot.devtools.restart.classloader.ClassLoaderFiles;
import org.springframework.boot.devtools.restart.classloader.ClassLoaderFiles.SourceFolder;
import org.springframework.boot.devtools.restart.classloader.ClassLoaderFiles.SourceDirectory;
import org.springframework.boot.devtools.test.MockClientHttpRequestFactory;
import org.springframework.http.HttpStatus;
import org.springframework.mock.http.client.MockClientHttpRequest;
@ -91,33 +91,33 @@ class ClassPathChangeUploaderTests {
}
@Test
void sendsClassLoaderFiles(@TempDir File sourceFolder) throws Exception {
ClassPathChangedEvent event = createClassPathChangedEvent(sourceFolder);
void sendsClassLoaderFiles(@TempDir File sourceDirectory) throws Exception {
ClassPathChangedEvent event = createClassPathChangedEvent(sourceDirectory);
this.requestFactory.willRespond(HttpStatus.OK);
this.uploader.onApplicationEvent(event);
assertThat(this.requestFactory.getExecutedRequests()).hasSize(1);
MockClientHttpRequest request = this.requestFactory.getExecutedRequests().get(0);
verifyUploadRequest(sourceFolder, request);
verifyUploadRequest(sourceDirectory, request);
}
@Test
void retriesOnSocketException(@TempDir File sourceFolder) throws Exception {
ClassPathChangedEvent event = createClassPathChangedEvent(sourceFolder);
void retriesOnSocketException(@TempDir File sourceDirectory) throws Exception {
ClassPathChangedEvent event = createClassPathChangedEvent(sourceDirectory);
this.requestFactory.willRespond(new SocketException());
this.requestFactory.willRespond(HttpStatus.OK);
this.uploader.onApplicationEvent(event);
assertThat(this.requestFactory.getExecutedRequests()).hasSize(2);
verifyUploadRequest(sourceFolder, this.requestFactory.getExecutedRequests().get(1));
verifyUploadRequest(sourceDirectory, this.requestFactory.getExecutedRequests().get(1));
}
private void verifyUploadRequest(File sourceFolder, MockClientHttpRequest request)
private void verifyUploadRequest(File sourceDirectory, MockClientHttpRequest request)
throws IOException, ClassNotFoundException {
ClassLoaderFiles classLoaderFiles = deserialize(request.getBodyAsBytes());
Collection<SourceFolder> sourceFolders = classLoaderFiles.getSourceFolders();
assertThat(sourceFolders.size()).isEqualTo(1);
SourceFolder classSourceFolder = sourceFolders.iterator().next();
assertThat(classSourceFolder.getName()).isEqualTo(sourceFolder.getAbsolutePath());
Iterator<ClassLoaderFile> classFiles = classSourceFolder.getFiles().iterator();
Collection<SourceDirectory> sourceDirectories = classLoaderFiles.getSourceDirectories();
assertThat(sourceDirectories.size()).isEqualTo(1);
SourceDirectory classSourceDirectory = sourceDirectories.iterator().next();
assertThat(classSourceDirectory.getName()).isEqualTo(sourceDirectory.getAbsolutePath());
Iterator<ClassLoaderFile> classFiles = classSourceDirectory.getFiles().iterator();
assertClassFile(classFiles.next(), "File1", ClassLoaderFile.Kind.ADDED);
assertClassFile(classFiles.next(), "File2", ClassLoaderFile.Kind.MODIFIED);
assertClassFile(classFiles.next(), null, ClassLoaderFile.Kind.DELETED);
@ -129,21 +129,21 @@ class ClassPathChangeUploaderTests {
assertThat(file.getKind()).isEqualTo(kind);
}
private ClassPathChangedEvent createClassPathChangedEvent(File sourceFolder) throws IOException {
private ClassPathChangedEvent createClassPathChangedEvent(File sourceDirectory) throws IOException {
Set<ChangedFile> files = new LinkedHashSet<>();
File file1 = createFile(sourceFolder, "File1");
File file2 = createFile(sourceFolder, "File2");
File file3 = createFile(sourceFolder, "File3");
files.add(new ChangedFile(sourceFolder, file1, Type.ADD));
files.add(new ChangedFile(sourceFolder, file2, Type.MODIFY));
files.add(new ChangedFile(sourceFolder, file3, Type.DELETE));
File file1 = createFile(sourceDirectory, "File1");
File file2 = createFile(sourceDirectory, "File2");
File file3 = createFile(sourceDirectory, "File3");
files.add(new ChangedFile(sourceDirectory, file1, Type.ADD));
files.add(new ChangedFile(sourceDirectory, file2, Type.MODIFY));
files.add(new ChangedFile(sourceDirectory, file3, Type.DELETE));
Set<ChangedFiles> changeSet = new LinkedHashSet<>();
changeSet.add(new ChangedFiles(sourceFolder, files));
changeSet.add(new ChangedFiles(sourceDirectory, files));
return new ClassPathChangedEvent(this, changeSet, false);
}
private File createFile(File sourceFolder, String name) throws IOException {
File file = new File(sourceFolder, name);
private File createFile(File sourceDirectory, String name) throws IOException {
File file = new File(sourceDirectory, name);
FileCopyUtils.copy(name.getBytes(), file);
return file;
}

@ -46,7 +46,7 @@ class ChangeableUrlsTests {
File tempDir;
@Test
void folderUrl() throws Exception {
void directoryUrl() throws Exception {
URL url = makeUrl("myproject");
assertThat(ChangeableUrls.fromUrls(url).size()).isEqualTo(1);
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
@ -83,25 +83,25 @@ class ClassLoaderFilesResourcePatternResolverTests {
}
@Test
void getResourceWhenDeletedShouldReturnDeletedResource(@TempDir File folder) throws Exception {
File file = createFile(folder, "name.class");
this.files.addFile(folder.getName(), "name.class", new ClassLoaderFile(Kind.DELETED, null));
void getResourceWhenDeletedShouldReturnDeletedResource(@TempDir File directory) throws Exception {
File file = createFile(directory, "name.class");
this.files.addFile(directory.getName(), "name.class", new ClassLoaderFile(Kind.DELETED, null));
Resource resource = this.resolver.getResource("file:" + file.getAbsolutePath());
assertThat(resource).isNotNull().isInstanceOf(DeletedClassLoaderFileResource.class);
}
@Test
void getResourcesShouldReturnResources(@TempDir File folder) throws Exception {
createFile(folder, "name.class");
Resource[] resources = this.resolver.getResources("file:" + folder.getAbsolutePath() + "/**");
void getResourcesShouldReturnResources(@TempDir File directory) throws Exception {
createFile(directory, "name.class");
Resource[] resources = this.resolver.getResources("file:" + directory.getAbsolutePath() + "/**");
assertThat(resources).isNotEmpty();
}
@Test
void getResourcesWhenDeletedShouldFilterDeleted(@TempDir File folder) throws Exception {
createFile(folder, "name.class");
this.files.addFile(folder.getName(), "name.class", new ClassLoaderFile(Kind.DELETED, null));
Resource[] resources = this.resolver.getResources("file:" + folder.getAbsolutePath() + "/**");
void getResourcesWhenDeletedShouldFilterDeleted(@TempDir File directory) throws Exception {
createFile(directory, "name.class");
this.files.addFile(directory.getName(), "name.class", new ClassLoaderFile(Kind.DELETED, null));
Resource[] resources = this.resolver.getResources("file:" + directory.getAbsolutePath() + "/**");
assertThat(resources).isEmpty();
}
@ -179,8 +179,8 @@ class ClassLoaderFilesResourcePatternResolverTests {
return resolver;
}
private File createFile(File folder, String name) throws IOException {
File file = new File(folder, name);
private File createFile(File directory, String name) throws IOException {
File file = new File(directory, name);
FileCopyUtils.copy("test".getBytes(), file);
return file;
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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,7 +25,7 @@ import java.util.Iterator;
import org.junit.jupiter.api.Test;
import org.springframework.boot.devtools.restart.classloader.ClassLoaderFile.Kind;
import org.springframework.boot.devtools.restart.classloader.ClassLoaderFiles.SourceFolder;
import org.springframework.boot.devtools.restart.classloader.ClassLoaderFiles.SourceDirectory;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
@ -79,18 +79,18 @@ class ClassLoaderFilesTests {
}
@Test
void addTwiceInDifferentSourceFolders() {
void addTwiceInDifferentSourceDirectories() {
ClassLoaderFile file1 = new ClassLoaderFile(Kind.ADDED, new byte[10]);
ClassLoaderFile file2 = new ClassLoaderFile(Kind.MODIFIED, new byte[10]);
this.files.addFile("a", "myfile", file1);
this.files.addFile("b", "myfile", file2);
assertThat(this.files.getFile("myfile")).isEqualTo(file2);
assertThat(this.files.getOrCreateSourceFolder("a").getFiles().size()).isEqualTo(0);
assertThat(this.files.getOrCreateSourceFolder("b").getFiles().size()).isEqualTo(1);
assertThat(this.files.getOrCreateSourceDirectory("a").getFiles().size()).isEqualTo(0);
assertThat(this.files.getOrCreateSourceDirectory("b").getFiles().size()).isEqualTo(1);
}
@Test
void getSourceFolders() {
void getSourceDirectories() {
ClassLoaderFile file1 = new ClassLoaderFile(Kind.ADDED, new byte[10]);
ClassLoaderFile file2 = new ClassLoaderFile(Kind.MODIFIED, new byte[10]);
ClassLoaderFile file3 = new ClassLoaderFile(Kind.MODIFIED, new byte[10]);
@ -99,14 +99,14 @@ class ClassLoaderFilesTests {
this.files.addFile("a", "myfile2", file2);
this.files.addFile("b", "myfile3", file3);
this.files.addFile("b", "myfile4", file4);
Iterator<SourceFolder> sourceFolders = this.files.getSourceFolders().iterator();
SourceFolder sourceFolder1 = sourceFolders.next();
SourceFolder sourceFolder2 = sourceFolders.next();
assertThat(sourceFolders.hasNext()).isFalse();
assertThat(sourceFolder1.getName()).isEqualTo("a");
assertThat(sourceFolder2.getName()).isEqualTo("b");
assertThat(sourceFolder1.getFiles()).containsOnly(file1, file2);
assertThat(sourceFolder2.getFiles()).containsOnly(file3, file4);
Iterator<SourceDirectory> sourceDirectories = this.files.getSourceDirectories().iterator();
SourceDirectory sourceDirectory1 = sourceDirectories.next();
SourceDirectory sourceDirectory2 = sourceDirectories.next();
assertThat(sourceDirectories.hasNext()).isFalse();
assertThat(sourceDirectory1.getName()).isEqualTo("a");
assertThat(sourceDirectory2.getName()).isEqualTo("b");
assertThat(sourceDirectory1.getFiles()).containsOnly(file1, file2);
assertThat(sourceDirectory2.getFiles()).containsOnly(file3, file4);
}
@Test
@ -132,13 +132,13 @@ class ClassLoaderFilesTests {
toAdd.addFile("a", "myfile2", file2);
toAdd.addFile("b", "myfile3", file3);
this.files.addAll(toAdd);
Iterator<SourceFolder> sourceFolders = this.files.getSourceFolders().iterator();
SourceFolder sourceFolder1 = sourceFolders.next();
SourceFolder sourceFolder2 = sourceFolders.next();
assertThat(sourceFolders.hasNext()).isFalse();
assertThat(sourceFolder1.getName()).isEqualTo("a");
assertThat(sourceFolder2.getName()).isEqualTo("b");
assertThat(sourceFolder1.getFiles()).containsOnly(file1, file2);
Iterator<SourceDirectory> sourceDirectoryies = this.files.getSourceDirectories().iterator();
SourceDirectory sourceDirectory1 = sourceDirectoryies.next();
SourceDirectory sourceDirectory2 = sourceDirectoryies.next();
assertThat(sourceDirectoryies.hasNext()).isFalse();
assertThat(sourceDirectory1.getName()).isEqualTo("a");
assertThat(sourceDirectory2.getName()).isEqualTo("b");
assertThat(sourceDirectory1.getFiles()).containsOnly(file1, file2);
}
@Test

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
@ -27,11 +27,11 @@ import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link DefaultSourceFolderUrlFilter}.
* Tests for {@link DefaultSourceDirectoryUrlFilter}.
*
* @author Phillip Webb
*/
class DefaultSourceFolderUrlFilterTests {
class DefaultSourceDirectoryUrlFilterTests {
private static final String SOURCE_ROOT = "/Users/me/code/some-root/";
@ -52,33 +52,33 @@ class DefaultSourceFolderUrlFilterTests {
COMMON_POSTFIXES = Collections.unmodifiableList(postfixes);
}
private DefaultSourceFolderUrlFilter filter = new DefaultSourceFolderUrlFilter();
private DefaultSourceDirectoryUrlFilter filter = new DefaultSourceDirectoryUrlFilter();
@Test
void mavenSourceFolder() throws Exception {
void mavenSourceDirectory() throws Exception {
doTest("my-module/target/classes/");
}
@Test
void gradleEclipseSourceFolder() throws Exception {
void gradleEclipseSourceDirectory() throws Exception {
doTest("my-module/bin/");
}
@Test
void unusualSourceFolder() throws Exception {
void unusualSourceDirectory() throws Exception {
doTest("my-module/something/quite/quite/mad/");
}
@Test
void skippedProjects() throws Exception {
String sourceFolder = "/Users/me/code/spring-boot-samples/spring-boot-sample-devtools";
String sourceDirectory = "/Users/me/code/spring-boot-samples/spring-boot-sample-devtools";
URL jarUrl = new URL("jar:file:/Users/me/tmp/spring-boot-sample-devtools-1.3.0.BUILD-SNAPSHOT.jar!/");
assertThat(this.filter.isMatch(sourceFolder, jarUrl)).isTrue();
assertThat(this.filter.isMatch(sourceDirectory, jarUrl)).isTrue();
URL nestedJarUrl = new URL("jar:file:/Users/me/tmp/spring-boot-sample-devtools-1.3.0.BUILD-SNAPSHOT.jar!/"
+ "lib/spring-boot-1.3.0.BUILD-SNAPSHOT.jar!/");
assertThat(this.filter.isMatch(sourceFolder, nestedJarUrl)).isFalse();
assertThat(this.filter.isMatch(sourceDirectory, nestedJarUrl)).isFalse();
URL fileUrl = new URL("file:/Users/me/tmp/spring-boot-sample-devtools-1.3.0.BUILD-SNAPSHOT.jar");
assertThat(this.filter.isMatch(sourceFolder, fileUrl)).isTrue();
assertThat(this.filter.isMatch(sourceDirectory, fileUrl)).isTrue();
}
private void doTest(String sourcePostfix) throws MalformedURLException {
@ -89,11 +89,11 @@ class DefaultSourceFolderUrlFilterTests {
}
private void doTest(String sourcePostfix, String moduleRoot, boolean expected) throws MalformedURLException {
String sourceFolder = SOURCE_ROOT + sourcePostfix;
String sourceDirectory = SOURCE_ROOT + sourcePostfix;
for (String postfix : COMMON_POSTFIXES) {
for (URL url : getUrls(moduleRoot + postfix)) {
boolean match = this.filter.isMatch(sourceFolder, url);
assertThat(match).as(url + " against " + sourceFolder).isEqualTo(expected);
boolean match = this.filter.isMatch(sourceDirectory, url);
assertThat(match).as(url + " against " + sourceDirectory).isEqualTo(expected);
}
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
@ -62,9 +62,9 @@ class HttpRestartServerTests {
}
@Test
void sourceFolderUrlFilterMustNotBeNull() {
assertThatIllegalArgumentException().isThrownBy(() -> new HttpRestartServer((SourceFolderUrlFilter) null))
.withMessageContaining("SourceFolderUrlFilter must not be null");
void sourceDirectoryUrlFilterMustNotBeNull() {
assertThatIllegalArgumentException().isThrownBy(() -> new HttpRestartServer((SourceDirectoryUrlFilter) null))
.withMessageContaining("SourceDirectoryUrlFilter must not be null");
}
@Test

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
@ -43,9 +43,9 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
class RestartServerTests {
@Test
void sourceFolderUrlFilterMustNotBeNull() {
assertThatIllegalArgumentException().isThrownBy(() -> new RestartServer((SourceFolderUrlFilter) null))
.withMessageContaining("SourceFolderUrlFilter must not be null");
void sourceDirectoryUrlFilterMustNotBeNull() {
assertThatIllegalArgumentException().isThrownBy(() -> new RestartServer((SourceDirectoryUrlFilter) null))
.withMessageContaining("SourceDirectoryUrlFilter must not be null");
}
@Test
@ -56,7 +56,7 @@ class RestartServerTests {
URL url4 = new URL("file:/proj/module-d.jar!/");
URLClassLoader classLoaderA = new URLClassLoader(new URL[] { url1, url2 });
URLClassLoader classLoaderB = new URLClassLoader(new URL[] { url3, url4 }, classLoaderA);
SourceFolderUrlFilter filter = new DefaultSourceFolderUrlFilter();
SourceDirectoryUrlFilter filter = new DefaultSourceDirectoryUrlFilter();
MockRestartServer server = new MockRestartServer(filter, classLoaderB);
ClassLoaderFiles files = new ClassLoaderFiles();
ClassLoaderFile fileA = new ClassLoaderFile(Kind.ADDED, new byte[0]);
@ -70,14 +70,14 @@ class RestartServerTests {
}
@Test
void updateSetsJarLastModified(@TempDir File folder) throws Exception {
void updateSetsJarLastModified(@TempDir File directory) throws Exception {
long startTime = System.currentTimeMillis();
File jarFile = new File(folder, "module-a.jar");
File jarFile = new File(directory, "module-a.jar");
new FileOutputStream(jarFile).close();
jarFile.setLastModified(0);
URL url = jarFile.toURI().toURL();
URLClassLoader classLoader = new URLClassLoader(new URL[] { url });
SourceFolderUrlFilter filter = new DefaultSourceFolderUrlFilter();
SourceDirectoryUrlFilter filter = new DefaultSourceDirectoryUrlFilter();
MockRestartServer server = new MockRestartServer(filter, classLoader);
ClassLoaderFiles files = new ClassLoaderFiles();
ClassLoaderFile fileA = new ClassLoaderFile(Kind.ADDED, new byte[0]);
@ -87,15 +87,15 @@ class RestartServerTests {
}
@Test
void updateReplacesLocalFilesWhenPossible(@TempDir File folder) throws Exception {
void updateReplacesLocalFilesWhenPossible(@TempDir File directory) throws Exception {
// This is critical for Cloud Foundry support where the application is
// run exploded and resources can be found from the servlet root (outside of the
// classloader)
File classFile = new File(folder, "ClassA.class");
File classFile = new File(directory, "ClassA.class");
FileCopyUtils.copy("abc".getBytes(), classFile);
URL url = folder.toURI().toURL();
URL url = directory.toURI().toURL();
URLClassLoader classLoader = new URLClassLoader(new URL[] { url });
SourceFolderUrlFilter filter = new DefaultSourceFolderUrlFilter();
SourceDirectoryUrlFilter filter = new DefaultSourceDirectoryUrlFilter();
MockRestartServer server = new MockRestartServer(filter, classLoader);
ClassLoaderFiles files = new ClassLoaderFiles();
ClassLoaderFile fileA = new ClassLoaderFile(Kind.ADDED, "def".getBytes());
@ -106,8 +106,8 @@ class RestartServerTests {
static class MockRestartServer extends RestartServer {
MockRestartServer(SourceFolderUrlFilter sourceFolderUrlFilter, ClassLoader classLoader) {
super(sourceFolderUrlFilter, classLoader);
MockRestartServer(SourceDirectoryUrlFilter sourceDirectoryUrlFilter, ClassLoader classLoader) {
super(sourceDirectoryUrlFilter, classLoader);
}
private Set<URL> restartUrls;

@ -89,7 +89,7 @@ Any dependencies that are required when running embedded but are not required wh
[[executable-jar-war-index-files]]
=== Index Files
Spring Boot Loader-compatible jar and war archives can include additional index files under the `BOOT-INF/` folder.
Spring Boot Loader-compatible jar and war archives can include additional index files under the `BOOT-INF/` directory.
A `classpath.idx` file can be provided for both jars and wars, it provides the ordering that jars should be added to the classpath.
The `layers.idx` file can be used only for jars, it allows a jar to be split into logical layers for Docker/OCI image creation.
@ -101,7 +101,7 @@ These files, however, are _not_ parsed internally as YAML and they must be writt
[[executable-jar-war-index-files-classpath]]
=== Classpath Index
The classpath index file can be provided in `BOOT-INF/classpath.idx`.
It provides a list of jar names (not including the folder) in the order that they should be added to the classpath.
It provides a list of jar names (not including the directory) in the order that they should be added to the classpath.
Each line must start with dash space (`"-&#183;"`) and names must be in double quotes.
For example, given the following jar:
@ -136,9 +136,9 @@ The classpath index file can be provided in `BOOT-INF/layers.idx`.
It provides a list of layers and the parts of the jar that should be contained within them.
Layers are written in the order that they should be added to the Docker/OCI image.
Layers names are written as quoted strings prefixed with dash space (`"-&#183;"`) and with a colon (`":"`) suffix.
Layer content is either a file or folder name written as a quoted string prefixed by space space dash space (`"&#183;&#183;-&#183;"`).
A folder name ends with `/`, a file name does not.
When a folder name is used it means that all files inside that folder are in the same layer.
Layer content is either a file or directory name written as a quoted string prefixed by space space dash space (`"&#183;&#183;-&#183;"`).
A directory name ends with `/`, a file name does not.
When a directory name is used it means that all files inside that directory are in the same layer.
A typical example of a layers index would be:

@ -51,7 +51,7 @@ You need to remember to start Ant using the `-lib` option, as shown in the follo
[indent=0,subs="verbatim,quotes,attributes"]
----
$ ant -lib <folder containing spring-boot-antlib-{spring-boot-version}.jar>
$ ant -lib <directory containing spring-boot-antlib-{spring-boot-version}.jar>
----
TIP: The "`Using Spring Boot`" section includes a more complete example of <<using-spring-boot.adoc#using-boot-ant, using Apache Ant with `spring-boot-antlib`>>.

@ -365,8 +365,8 @@ Before we begin, open a terminal and run the following commands to ensure that y
Java version: 1.8.0_102, vendor: Oracle Corporation
----
NOTE: This sample needs to be created in its own folder.
Subsequent instructions assume that you have created a suitable folder and that it is your current directory.
NOTE: This sample needs to be created in its own directory.
Subsequent instructions assume that you have created a suitable directory and that it is your current directory.
@ -481,7 +481,7 @@ If you run `mvn dependency:tree` again, you see that there are now a number of a
[[getting-started-first-application-code]]
=== Writing the Code
To finish our application, we need to create a single Java file.
By default, Maven compiles sources from `src/main/java`, so you need to create that folder structure and then add a file named `src/main/java/Example.java` to contain the following code:
By default, Maven compiles sources from `src/main/java`, so you need to create that directory structure and then add a file named `src/main/java/Example.java` to contain the following code:
[source,java,indent=0]
----

@ -605,7 +605,7 @@ To enable that support, your application needs to have two additional dependenci
Spring Boot ships by default with Tomcat 9.0.x which supports HTTP/2 out of the box when using JDK 9 or later.
Alternatively, HTTP/2 can be used on JDK 8 if the `libtcnative` library and its dependencies are installed on the host operating system.
The library folder must be made available, if not already, to the JVM library path.
The library directory must be made available, if not already, to the JVM library path.
You can do so with a JVM argument such as `-Djava.library.path=/usr/local/opt/tomcat-native/lib`.
More on this in the https://tomcat.apache.org/tomcat-9.0-doc/apr.html[official Tomcat documentation].
@ -2019,7 +2019,7 @@ Spring Boot supports two higher-level migration tools: https://flywaydb.org/[Fly
To automatically run Flyway database migrations on startup, add the `org.flywaydb:flyway-core` to your classpath.
Typically, migrations are scripts in the form `V<VERSION>__<NAME>.sql` (with `<VERSION>` an underscore-separated version, such as '`1`' or '`2_1`').
By default, they are in a folder called `classpath:db/migration`, but you can modify that location by setting `spring.flyway.locations`.
By default, they are in a directory called `classpath:db/migration`, but you can modify that location by setting `spring.flyway.locations`.
This is a comma-separated list of one or more `classpath:` or `filesystem:` locations.
For example, the following configuration would search for scripts in both the default classpath location and the `/opt/migration` directory:
@ -2036,7 +2036,7 @@ Assume the following:
spring.flyway.locations=classpath:db/migration/{vendor}
----
Rather than using `db/migration`, the preceding configuration sets the folder to use according to the type of the database (such as `db/migration/mysql` for MySQL).
Rather than using `db/migration`, the preceding configuration sets the directory to use according to the type of the database (such as `db/migration/mysql` for MySQL).
The list of supported databases is available in {spring-boot-module-code}/jdbc/DatabaseDriver.java[`DatabaseDriver`].
Migrations can also be written in Java.
@ -2049,7 +2049,7 @@ Spring Boot calls `Flyway.migrate()` to perform the database migration.
If you would like more control, provide a `@Bean` that implements {spring-boot-autoconfigure-module-code}/flyway/FlywayMigrationStrategy.java[`FlywayMigrationStrategy`].
Flyway supports SQL and Java https://flywaydb.org/documentation/callbacks.html[callbacks].
To use SQL-based callbacks, place the callback scripts in the `classpath:db/migration` folder.
To use SQL-based callbacks, place the callback scripts in the `classpath:db/migration` directory.
To use Java-based callbacks, create one or more beans that implement `Callback`.
Any such beans are automatically registered with `Flyway`.
They can be ordered by using `@Order` or by implementing `Ordered`.

@ -447,7 +447,7 @@ Property values can be injected directly into your beans by using the `@Value` a
Spring Boot uses a very particular `PropertySource` order that is designed to allow sensible overriding of values.
Properties are considered in the following order:
. <<using-spring-boot.adoc#using-boot-devtools-globalsettings,Devtools global settings properties>> in the `$HOME/.config/spring-boot` folder when devtools is active.
. <<using-spring-boot.adoc#using-boot-devtools-globalsettings,Devtools global settings properties>> in the `$HOME/.config/spring-boot` directory when devtools is active.
. {spring-framework-api}/test/context/TestPropertySource.html[`@TestPropertySource`] annotations on your tests.
. `properties` attribute on your tests.
Available on {spring-boot-test-module-api}/context/SpringBootTest.html[`@SpringBootTest`] and the <<boot-features-testing-spring-boot-applications-testing-autoconfigured-tests,test annotations for testing a particular slice of your application>>.
@ -2482,11 +2482,11 @@ In the preceding example, if `YourException` is thrown by a controller defined i
[[boot-features-error-handling-custom-error-pages]]
===== Custom Error Pages
If you want to display a custom HTML error page for a given status code, you can add a file to an `/error` folder.
Error pages can either be static HTML (that is, added under any of the static resource folders) or be built by using templates.
If you want to display a custom HTML error page for a given status code, you can add a file to an `/error` directory.
Error pages can either be static HTML (that is, added under any of the static resource directories) or be built by using templates.
The name of the file should be the exact status code or a series mask.
For example, to map `404` to a static HTML file, your folder structure would be as follows:
For example, to map `404` to a static HTML file, your directory structure would be as follows:
[source,indent=0,subs="verbatim,quotes,attributes"]
----
@ -2501,7 +2501,7 @@ For example, to map `404` to a static HTML file, your folder structure would be
+- <other public assets>
----
To map all `5xx` errors by using a FreeMarker template, your folder structure would be as follows:
To map all `5xx` errors by using a FreeMarker template, your directory structure would be as follows:
[source,indent=0,subs="verbatim,quotes,attributes"]
----
@ -2828,11 +2828,11 @@ For a more complete picture, you can also subclass `DefaultErrorWebExceptionHand
[[boot-features-webflux-error-handling-custom-error-pages]]
===== Custom Error Pages
If you want to display a custom HTML error page for a given status code, you can add a file to an `/error` folder.
Error pages can either be static HTML (that is, added under any of the static resource folders) or built with templates.
If you want to display a custom HTML error page for a given status code, you can add a file to an `/error` directory.
Error pages can either be static HTML (that is, added under any of the static resource directories) or built with templates.
The name of the file should be the exact status code or a series mask.
For example, to map `404` to a static HTML file, your folder structure would be as follows:
For example, to map `404` to a static HTML file, your directory structure would be as follows:
[source,indent=0,subs="verbatim,quotes,attributes"]
----
@ -2847,7 +2847,7 @@ For example, to map `404` to a static HTML file, your folder structure would be
+- <other public assets>
----
To map all `5xx` errors by using a Mustache template, your folder structure would be as follows:
To map all `5xx` errors by using a Mustache template, your directory structure would be as follows:
[source,indent=0,subs="verbatim,quotes,attributes"]
----
@ -8157,7 +8157,7 @@ Assuming the above `Dockerfile` is in the current directory, your docker image c
----
This is a multi-stage dockerfile.
The builder stage extracts the folders that are needed later.
The builder stage extracts the directories that are needed later.
Each of the `COPY` commands relates to the layers extracted by the jarmode.
Of course, a Dockerfile can be written without using the jarmode.

@ -578,7 +578,7 @@ TIP: For a complete list of the properties that are applied by the devtools, see
=== Automatic Restart
Applications that use `spring-boot-devtools` automatically restart whenever files on the classpath change.
This can be a useful feature when working in an IDE, as it gives a very fast feedback loop for code changes.
By default, any entry on the classpath that points to a folder is monitored for changes.
By default, any entry on the classpath that points to a directory is monitored for changes.
Note that certain resources, such as static assets and view templates, <<using-boot-devtools-restart-exclude, do not need to restart the application>>.
.Triggering a restart
@ -767,7 +767,7 @@ If you start multiple applications from your IDE, only the first has LiveReload
[[using-boot-devtools-globalsettings]]
=== Global Settings
You can configure global devtools settings by adding any of the following files to the `$HOME/.config/spring-boot` folder:
You can configure global devtools settings by adding any of the following files to the `$HOME/.config/spring-boot` directory:
. `spring-boot-devtools.properties`
. `spring-boot-devtools.yaml`
@ -782,7 +782,7 @@ For example, to configure restart to always use a <<using-boot-devtools-restart-
spring.devtools.restart.trigger-file=.reloadtrigger
----
NOTE: If devtools configuration files are not found in `$HOME/.config/spring-boot`, the root of the `$HOME` folder is searched for the presence of a `.spring-boot-devtools.properties` file.
NOTE: If devtools configuration files are not found in `$HOME/.config/spring-boot`, the root of the `$HOME` directory is searched for the presence of a `.spring-boot-devtools.properties` file.
This allows you to share the devtools global configuration with applications that are on an older version of Spring Boot that does not support the `$HOME/.config/spring-boot` location.
[NOTE]
@ -807,7 +807,7 @@ If you observe such problems constantly, try increasing the `spring.devtools.res
spring.devtools.restart.quiet-period=1s
----
The monitored classpath folders are now polled every 2 seconds for changes, and a 1 second quiet period is maintained to make sure there are no additional class changes.
The monitored classpath directories are now polled every 2 seconds for changes, and a 1 second quiet period is maintained to make sure there are no additional class changes.

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
@ -41,7 +41,7 @@ public class SpringBootMockServletContext extends MockServletContext {
private final ResourceLoader resourceLoader;
private File emptyRootFolder;
private File emptyRootDirectory;
public SpringBootMockServletContext(String resourceBasePath) {
this(resourceBasePath, new FileSystemResourceLoader());
@ -91,16 +91,16 @@ public class SpringBootMockServletContext extends MockServletContext {
// Liquibase assumes that "/" always exists, if we don't have a directory
// use a temporary location.
try {
if (this.emptyRootFolder == null) {
if (this.emptyRootDirectory == null) {
synchronized (this) {
File tempFolder = File.createTempFile("spr", "servlet");
tempFolder.delete();
tempFolder.mkdirs();
tempFolder.deleteOnExit();
this.emptyRootFolder = tempFolder;
File tempDirectory = File.createTempFile("spr", "servlet");
tempDirectory.delete();
tempDirectory.mkdirs();
tempDirectory.deleteOnExit();
this.emptyRootDirectory = tempDirectory;
}
}
return this.emptyRootFolder.toURI().toURL();
return this.emptyRootDirectory.toURI().toURL();
}
catch (IOException ex) {
// Ignore

@ -124,8 +124,8 @@ class Lifecycle implements Closeable {
private Phase detectPhase() {
Phase phase = createPhase("detector");
phase.withArgs("-app", Folder.APPLICATION);
phase.withArgs("-platform", Folder.PLATFORM);
phase.withArgs("-app", Directory.APPLICATION);
phase.withArgs("-platform", Directory.PLATFORM);
phase.withLogLevelArg();
return phase;
}
@ -133,10 +133,10 @@ class Lifecycle implements Closeable {
private Phase restorePhase() {
Phase phase = createPhase("restorer");
phase.withDaemonAccess();
phase.withArgs("-cache-dir", Folder.CACHE);
phase.withArgs("-layers", Folder.LAYERS);
phase.withArgs("-cache-dir", Directory.CACHE);
phase.withArgs("-layers", Directory.LAYERS);
phase.withLogLevelArg();
phase.withBinds(this.buildCacheVolume, Folder.CACHE);
phase.withBinds(this.buildCacheVolume, Directory.CACHE);
return phase;
}
@ -148,18 +148,18 @@ class Lifecycle implements Closeable {
phase.withArgs("-skip-layers");
}
phase.withArgs("-daemon");
phase.withArgs("-layers", Folder.LAYERS);
phase.withArgs("-cache-dir", Folder.CACHE);
phase.withArgs("-layers", Directory.LAYERS);
phase.withArgs("-cache-dir", Directory.CACHE);
phase.withArgs(this.request.getName());
phase.withBinds(this.buildCacheVolume, Folder.CACHE);
phase.withBinds(this.buildCacheVolume, Directory.CACHE);
return phase;
}
private Phase buildPhase() {
Phase phase = createPhase("builder");
phase.withArgs("-layers", Folder.LAYERS);
phase.withArgs("-app", Folder.APPLICATION);
phase.withArgs("-platform", Folder.PLATFORM);
phase.withArgs("-layers", Directory.LAYERS);
phase.withArgs("-app", Directory.APPLICATION);
phase.withArgs("-platform", Directory.PLATFORM);
return phase;
}
@ -168,14 +168,14 @@ class Lifecycle implements Closeable {
phase.withDaemonAccess();
phase.withLogLevelArg();
phase.withArgs("-image", this.runImageReference);
phase.withArgs("-layers", Folder.LAYERS);
phase.withArgs("-app", Folder.APPLICATION);
phase.withArgs("-layers", Directory.LAYERS);
phase.withArgs("-app", Directory.APPLICATION);
phase.withArgs("-daemon");
phase.withArgs("-launch-cache", Folder.LAUNCH_CACHE);
phase.withArgs("-cache-dir", Folder.CACHE);
phase.withArgs("-launch-cache", Directory.LAUNCH_CACHE);
phase.withArgs("-cache-dir", Directory.CACHE);
phase.withArgs(this.request.getName());
phase.withBinds(this.launchCacheVolume, Folder.LAUNCH_CACHE);
phase.withBinds(this.buildCacheVolume, Folder.CACHE);
phase.withBinds(this.launchCacheVolume, Directory.LAUNCH_CACHE);
phase.withBinds(this.buildCacheVolume, Directory.CACHE);
return phase;
}
@ -183,8 +183,8 @@ class Lifecycle implements Closeable {
boolean verboseLogging = this.request.isVerboseLogging()
&& this.lifecycleVersion.isEqualOrGreaterThan(LOGGING_MINIMUM_VERSION);
Phase phase = new Phase(name, verboseLogging);
phase.withBinds(this.layersVolume, Folder.LAYERS);
phase.withBinds(this.applicationVolume, Folder.APPLICATION);
phase.withBinds(this.layersVolume, Directory.LAYERS);
phase.withBinds(this.applicationVolume, Directory.APPLICATION);
return phase;
}
@ -211,7 +211,8 @@ class Lifecycle implements Closeable {
}
try {
TarArchive applicationContent = this.request.getApplicationContent(this.builder.getBuildOwner());
return this.docker.container().create(config, ContainerContent.of(applicationContent, Folder.APPLICATION));
return this.docker.container().create(config,
ContainerContent.of(applicationContent, Directory.APPLICATION));
}
finally {
this.applicationVolumePopulated = true;
@ -229,13 +230,13 @@ class Lifecycle implements Closeable {
}
/**
* Common folders used by the various phases.
* Common directories used by the various phases.
*/
private static class Folder {
private static class Directory {
/**
* The folder used by buildpacks to write their layer contributions. A new layer
* folder is created for each lifecycle execution.
* The directory used by buildpacks to write their layer contributions. A new
* layer directory is created for each lifecycle execution.
* <p>
* Maps to the {@code <layers...>} concept in the
* <a href="https://github.com/buildpacks/spec/blob/master/buildpack.md">buildpack
@ -245,8 +246,8 @@ class Lifecycle implements Closeable {
static final String LAYERS = "/layers";
/**
* The folder containing the original contributed application. A new application
* folder is created for each lifecycle execution.
* The directory containing the original contributed application. A new
* application directory is created for each lifecycle execution.
* <p>
* Maps to the {@code <app...>} concept in the
* <a href="https://github.com/buildpacks/spec/blob/master/buildpack.md">buildpack
@ -255,15 +256,15 @@ class Lifecycle implements Closeable {
* convention of using {@code '/workspace'}.
* <p>
* Note that application content is uploaded to the container with the first phase
* that runs and saved in a volume that is passed to subsequent phases. The folder
* is mutable and buildpacks may modify the content.
* that runs and saved in a volume that is passed to subsequent phases. The
* directory is mutable and buildpacks may modify the content.
*/
static final String APPLICATION = "/workspace";
/**
* The folder used by buildpacks to obtain environment variables and platform
* specific concerns. The platform folder is read-only and is created/populated by
* the {@link EphemeralBuilder}.
* The directory used by buildpacks to obtain environment variables and platform
* specific concerns. The platform directory is read-only and is created/populated
* by the {@link EphemeralBuilder}.
* <p>
* Maps to the {@code <platform>/env} and {@code <platform>/#} concepts in the
* <a href="https://github.com/buildpacks/spec/blob/master/buildpack.md">buildpack
@ -273,7 +274,7 @@ class Lifecycle implements Closeable {
static final String PLATFORM = "/platform";
/**
* The folder used by buildpacks for caching. The volume name is based on the
* The directory used by buildpacks for caching. The volume name is based on the
* image {@link BuildRequest#getName() name} being built, and is persistent across
* invocations even if the application content has changed.
* <p>
@ -283,7 +284,7 @@ class Lifecycle implements Closeable {
static final String CACHE = "/cache";
/**
* The folder used by buildpacks for launch related caching. The volume name is
* The directory used by buildpacks for launch related caching. The volume name is
* based on the image {@link BuildRequest#getName() name} being built, and is
* persistent across invocations even if the application content has changed.
* <p>

@ -19,7 +19,7 @@ package org.springframework.boot.buildpack.platform.io;
import java.io.IOException;
/**
* Interface that can be used to write a file/folder layout.
* Interface that can be used to write a file/directory layout.
*
* @author Phillip Webb
* @since 2.3.0
@ -27,17 +27,17 @@ import java.io.IOException;
public interface Layout {
/**
* Add a folder to the content.
* @param name the full name of the folder to add.
* @param owner the owner of the folder
* Add a directory to the content.
* @param name the full name of the directory to add.
* @param owner the owner of the directory
* @throws IOException on IO error
*/
void folder(String name, Owner owner) throws IOException;
void directory(String name, Owner owner) throws IOException;
/**
* Write a file to the content.
* @param name the full name of the file to add.
* @param owner the owner of the folder
* @param owner the owner of the file
* @param content the content to add
* @throws IOException on IO error
*/

@ -43,8 +43,8 @@ class TarLayoutWriter implements Layout, Closeable {
}
@Override
public void folder(String name, Owner owner) throws IOException {
this.outputStream.putArchiveEntry(createFolderEntry(name, owner));
public void directory(String name, Owner owner) throws IOException {
this.outputStream.putArchiveEntry(createDirectoryEntry(name, owner));
this.outputStream.closeArchiveEntry();
}
@ -55,7 +55,7 @@ class TarLayoutWriter implements Layout, Closeable {
this.outputStream.closeArchiveEntry();
}
private TarArchiveEntry createFolderEntry(String name, Owner owner) {
private TarArchiveEntry createDirectoryEntry(String name, Owner owner) {
return createEntry(name, owner, TarConstants.LF_DIR, 0755, 0);
}

@ -86,7 +86,7 @@ public class BuildRequestTests {
}
@Test
void forJarFileWhenJarFileIsFolderThrowsException() {
void forJarFileWhenJarFileIsDirectoryThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> BuildRequest.forJarFile(this.tempDir))
.withMessage("JarFile must be a file");
}

@ -111,8 +111,8 @@ class EphemeralBuilderTests extends AbstractJsonTests {
@Test
void getArchiveContainsEnvLayer() throws Exception {
EphemeralBuilder builder = new EphemeralBuilder(this.owner, this.image, this.metadata, this.creator, this.env);
File folder = unpack(getLayer(builder.getArchive(), 0), "env");
assertThat(new File(folder, "platform/env/spring")).usingCharset(StandardCharsets.UTF_8).hasContent("boot");
File directory = unpack(getLayer(builder.getArchive(), 0), "env");
assertThat(new File(directory, "platform/env/spring")).usingCharset(StandardCharsets.UTF_8).hasContent("boot");
}
private TarArchiveInputStream getLayer(ImageArchive archive, int index) throws Exception {
@ -126,11 +126,11 @@ class EphemeralBuilderTests extends AbstractJsonTests {
}
private File unpack(TarArchiveInputStream archive, String name) throws Exception {
File folder = new File(this.temp, name);
folder.mkdirs();
File directory = new File(this.temp, name);
directory.mkdirs();
ArchiveEntry entry = archive.getNextEntry();
while (entry != null) {
File file = new File(folder, entry.getName());
File file = new File(directory, entry.getName());
if (entry.isDirectory()) {
file.mkdirs();
}
@ -142,7 +142,7 @@ class EphemeralBuilderTests extends AbstractJsonTests {
}
entry = archive.getNextEntry();
}
return folder;
return directory;
}
}

@ -262,7 +262,7 @@ class DockerApiTests {
ImageReference imageReference = ImageReference.of("ubuntu:bionic");
ContainerConfig config = ContainerConfig.of(imageReference, (update) -> update.withCommand("/bin/bash"));
TarArchive archive = TarArchive.of((layout) -> {
layout.folder("/test", Owner.ROOT);
layout.directory("/test", Owner.ROOT);
layout.file("/test/file", Owner.ROOT, Content.of("test"));
});
ContainerContent content = ContainerContent.of(archive);

@ -44,7 +44,7 @@ class ImageArchiveTests extends AbstractJsonTests {
void fromImageWritesToValidArchiveTar() throws Exception {
Image image = Image.of(getContent("image.json"));
ImageArchive archive = ImageArchive.from(image, (update) -> {
update.withNewLayer(Layer.of((layout) -> layout.folder("/spring", Owner.ROOT)));
update.withNewLayer(Layer.of((layout) -> layout.directory("/spring", Owner.ROOT)));
update.withTag(ImageReference.of("pack.local/builder/6b7874626575656b6162"));
});
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

@ -52,17 +52,17 @@ class LayerTests {
@Test
void ofCreatesLayer() throws Exception {
Layer layer = Layer.of((layout) -> {
layout.folder("/folder", Owner.ROOT);
layout.file("/folder/file", Owner.ROOT, Content.of("test"));
layout.directory("/directory", Owner.ROOT);
layout.file("/directory/file", Owner.ROOT, Content.of("test"));
});
assertThat(layer.getId().toString())
.isEqualTo("sha256:8b8a3cea2ba716da6bbb0a3bf7472f235fa08c71a27cec5fbf2de1cf1baa513f");
.isEqualTo("sha256:d03a34f73804698c875eb56ff694fc2fceccc69b645e4adceb004ed13588613b");
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
layer.writeTo(outputStream);
try (TarArchiveInputStream tarStream = new TarArchiveInputStream(
new ByteArrayInputStream(outputStream.toByteArray()))) {
assertThat(tarStream.getNextTarEntry().getName()).isEqualTo("/folder/");
assertThat(tarStream.getNextTarEntry().getName()).isEqualTo("/folder/file");
assertThat(tarStream.getNextTarEntry().getName()).isEqualTo("/directory/");
assertThat(tarStream.getNextTarEntry().getName()).isEqualTo("/directory/file");
assertThat(tarStream.getNextTarEntry()).isNull();
}
}

@ -46,12 +46,12 @@ class TarArchiveTests {
void ofWritesTarContent() throws Exception {
Owner owner = Owner.of(123, 456);
TarArchive tarArchive = TarArchive.of((content) -> {
content.folder("/workspace", owner);
content.folder("/layers", owner);
content.folder("/cnb", Owner.ROOT);
content.folder("/cnb/buildpacks", Owner.ROOT);
content.folder("/platform", Owner.ROOT);
content.folder("/platform/env", Owner.ROOT);
content.directory("/workspace", owner);
content.directory("/layers", owner);
content.directory("/cnb", Owner.ROOT);
content.directory("/cnb/buildpacks", Owner.ROOT);
content.directory("/platform", Owner.ROOT);
content.directory("/platform/env", Owner.ROOT);
});
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
tarArchive.writeTo(outputStream);

@ -38,21 +38,21 @@ class TarLayoutWriterTests {
void writesTarArchive() throws Exception {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try (TarLayoutWriter writer = new TarLayoutWriter(outputStream)) {
writer.folder("/foo", Owner.ROOT);
writer.directory("/foo", Owner.ROOT);
writer.file("/foo/bar.txt", Owner.of(1, 1), Content.of("test"));
}
try (TarArchiveInputStream tarInputStream = new TarArchiveInputStream(
new ByteArrayInputStream(outputStream.toByteArray()))) {
TarArchiveEntry folderEntry = tarInputStream.getNextTarEntry();
TarArchiveEntry directoryEntry = tarInputStream.getNextTarEntry();
TarArchiveEntry fileEntry = tarInputStream.getNextTarEntry();
byte[] fileContent = new byte[(int) fileEntry.getSize()];
tarInputStream.read(fileContent);
assertThat(tarInputStream.getNextEntry()).isNull();
assertThat(folderEntry.getName()).isEqualTo("/foo/");
assertThat(folderEntry.getMode()).isEqualTo(0755);
assertThat(folderEntry.getLongUserId()).isEqualTo(0);
assertThat(folderEntry.getLongGroupId()).isEqualTo(0);
assertThat(folderEntry.getModTime()).isEqualTo(new Date(TarLayoutWriter.NORMALIZED_MOD_TIME));
assertThat(directoryEntry.getName()).isEqualTo("/foo/");
assertThat(directoryEntry.getMode()).isEqualTo(0755);
assertThat(directoryEntry.getLongUserId()).isEqualTo(0);
assertThat(directoryEntry.getLongGroupId()).isEqualTo(0);
assertThat(directoryEntry.getModTime()).isEqualTo(new Date(TarLayoutWriter.NORMALIZED_MOD_TIME));
assertThat(fileEntry.getName()).isEqualTo("/foo/bar.txt");
assertThat(fileEntry.getMode()).isEqualTo(0644);
assertThat(fileEntry.getLongUserId()).isEqualTo(1);

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
@ -44,9 +44,9 @@ public class MetadataStore {
private static final String ADDITIONAL_METADATA_PATH = "META-INF/additional-spring-configuration-metadata.json";
private static final String RESOURCES_FOLDER = "resources";
private static final String RESOURCES_DIRECTORY = "resources";
private static final String CLASSES_FOLDER = "classes";
private static final String CLASSES_DIRECTORY = "classes";
private final ProcessingEnvironment environment;
@ -122,18 +122,18 @@ public class MetadataStore {
}
}
}
return new File(locateGradleResourcesFolder(standardLocation), ADDITIONAL_METADATA_PATH);
return new File(locateGradleResourcesDirectory(standardLocation), ADDITIONAL_METADATA_PATH);
}
private File locateGradleResourcesFolder(File standardAdditionalMetadataLocation) throws FileNotFoundException {
private File locateGradleResourcesDirectory(File standardAdditionalMetadataLocation) throws FileNotFoundException {
String path = standardAdditionalMetadataLocation.getPath();
int index = path.lastIndexOf(CLASSES_FOLDER);
int index = path.lastIndexOf(CLASSES_DIRECTORY);
if (index < 0) {
throw new FileNotFoundException();
}
String buildFolderPath = path.substring(0, index);
String buildDirectoryPath = path.substring(0, index);
File classOutputLocation = standardAdditionalMetadataLocation.getParentFile().getParentFile();
return new File(buildFolderPath, RESOURCES_FOLDER + '/' + classOutputLocation.getName());
return new File(buildDirectoryPath, RESOURCES_DIRECTORY + '/' + classOutputLocation.getName());
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
@ -203,9 +203,9 @@ class MergeMetadataGenerationTests extends AbstractMetadataGenerationTests {
@Test
void mergingOfAdditionalMetadata() throws Exception {
File metaInfFolder = new File(getCompiler().getOutputLocation(), "META-INF");
metaInfFolder.mkdirs();
File additionalMetadataFile = new File(metaInfFolder, "additional-spring-configuration-metadata.json");
File metaInfDirectory = new File(getCompiler().getOutputLocation(), "META-INF");
metaInfDirectory.mkdirs();
File additionalMetadataFile = new File(metaInfDirectory, "additional-spring-configuration-metadata.json");
additionalMetadataFile.createNewFile();
JSONObject property = new JSONObject();
property.put("name", "foo");
@ -273,9 +273,9 @@ class MergeMetadataGenerationTests extends AbstractMetadataGenerationTests {
}
private File createAdditionalMetadataFile() throws IOException {
File metaInfFolder = new File(getCompiler().getOutputLocation(), "META-INF");
metaInfFolder.mkdirs();
File additionalMetadataFile = new File(metaInfFolder, "additional-spring-configuration-metadata.json");
File metaInfDirectory = new File(getCompiler().getOutputLocation(), "META-INF");
metaInfDirectory.mkdirs();
File additionalMetadataFile = new File(metaInfDirectory, "additional-spring-configuration-metadata.json");
additionalMetadataFile.createNewFile();
return additionalMetadataFile;
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
@ -56,19 +56,21 @@ public class TestProject {
* Contains copies of the original source so we can modify it safely to test
* incremental builds.
*/
private File sourceFolder;
private File sourceDirectory;
private TestCompiler compiler;
private Set<File> sourceFiles = new LinkedHashSet<>();
public TestProject(File tempFolder, Class<?>... classes) throws IOException {
this.sourceFolder = new File(tempFolder, "src");
this.compiler = new TestCompiler(new File(tempFolder, "build")) {
public TestProject(File tempDirectory, Class<?>... classes) throws IOException {
this.sourceDirectory = new File(tempDirectory, "src");
this.compiler = new TestCompiler(new File(tempDirectory, "build")) {
@Override
protected File getSourceFolder() {
return TestProject.this.sourceFolder;
protected File getSourceDirectory() {
return TestProject.this.sourceDirectory;
}
};
Set<Class<?>> contents = new HashSet<>(Arrays.asList(classes));
contents.addAll(Arrays.asList(ALWAYS_INCLUDE));
@ -90,14 +92,14 @@ public class TestProject {
}
public File getSourceFile(Class<?> type) {
return new File(this.sourceFolder, TestCompiler.sourcePathFor(type));
return new File(this.sourceDirectory, TestCompiler.sourcePathFor(type));
}
public ConfigurationMetadata fullBuild() {
TestConfigurationMetadataAnnotationProcessor processor = new TestConfigurationMetadataAnnotationProcessor(
this.compiler.getOutputLocation());
TestCompilationTask task = this.compiler.getTask(this.sourceFiles);
deleteFolderContents(this.compiler.getOutputLocation());
deleteDirectoryContents(this.compiler.getOutputLocation());
task.call(processor);
return processor.getMetadata();
}
@ -110,13 +112,13 @@ public class TestProject {
return processor.getMetadata();
}
private void deleteFolderContents(File outputFolder) {
FileSystemUtils.deleteRecursively(outputFolder);
outputFolder.mkdirs();
private void deleteDirectoryContents(File outputDirectory) {
FileSystemUtils.deleteRecursively(outputDirectory);
outputDirectory.mkdirs();
}
/**
* Retrieve File relative to project's output folder.
* Retrieve File relative to project's output directory.
* @param relativePath the relative path
* @return the output file
*/
@ -183,7 +185,7 @@ public class TestProject {
* code.
*/
private File getOriginalSourceFile(Class<?> type) {
return new File(TestCompiler.SOURCE_FOLDER, TestCompiler.sourcePathFor(type));
return new File(TestCompiler.SOURCE_DIRECTORY, TestCompiler.sourcePathFor(type));
}
private static void putContents(File targetFile, String contents) throws IOException {

@ -174,7 +174,7 @@ include::../gradle/packaging/boot-war-include-devtools.gradle.kts[tags=include-d
Most libraries can be used directly when nested in an executable archive, however certain libraries can have problems.
For example, JRuby includes its own nested jar support which assumes that `jruby-complete.jar` is always directly available on the file system.
To deal with any problematic libraries, an executable archive can be configured to unpack specific nested jars to a temporary folder when the executable archive is run.
To deal with any problematic libraries, an executable archive can be configured to unpack specific nested jars to a temporary directory when the executable archive is run.
Libraries can be identified as requiring unpacking using Ant-style patterns that match against the absolute path of the source jar file:
[source,groovy,indent=0,subs="verbatim,attributes",role="primary"]
@ -268,7 +268,7 @@ include::../gradle/packaging/boot-war-properties-launcher.gradle.kts[tags=proper
[[packaging-layered-jars]]
==== Packaging Layered Jars
By default, the `bootJar` task builds an archive that contains the application's classes and dependencies in `BOOT-INF/classes` and `BOOT-INF/lib` respectively.
For cases where a docker image needs to be built from the contents of the jar, it's useful to be able to separate these folders further so that they can be written into distinct layers.
For cases where a docker image needs to be built from the contents of the jar, it's useful to be able to separate these directories further so that they can be written into distinct layers.
Layered jars use the same layout as regular boot packaged jars, but include an additional meta-data file that describes each layer.
To use this feature, the layering feature must be enabled:

@ -46,9 +46,9 @@ public class BootJar extends Jar implements BootArchive {
private static final String LAUNCHER = "org.springframework.boot.loader.JarLauncher";
private static final String CLASSES_FOLDER = "BOOT-INF/classes/";
private static final String CLASSES_DIRECTORY = "BOOT-INF/classes/";
private static final String LIB_FOLDER = "BOOT-INF/lib/";
private static final String LIB_DIRECTORY = "BOOT-INF/lib/";
private static final String LAYERS_INDEX = "BOOT-INF/layers.idx";
@ -95,8 +95,8 @@ public class BootJar extends Jar implements BootArchive {
@Override
public void copy() {
this.support.configureManifest(getManifest(), getMainClassName(), CLASSES_FOLDER, LIB_FOLDER, CLASSPATH_INDEX,
(this.layered != null) ? LAYERS_INDEX : null);
this.support.configureManifest(getManifest(), getMainClassName(), CLASSES_DIRECTORY, LIB_DIRECTORY,
CLASSPATH_INDEX, (this.layered != null) ? LAYERS_INDEX : null);
super.copy();
}
@ -104,7 +104,7 @@ public class BootJar extends Jar implements BootArchive {
protected CopyAction createCopyAction() {
if (this.layered != null) {
LayerResolver layerResolver = new LayerResolver(getConfigurations(), this.layered, this::isLibrary);
String layerToolsLocation = this.layered.isIncludeLayerTools() ? LIB_FOLDER : null;
String layerToolsLocation = this.layered.isIncludeLayerTools() ? LIB_DIRECTORY : null;
return this.support.createCopyAction(this, layerResolver, layerToolsLocation);
}
return this.support.createCopyAction(this);
@ -265,7 +265,7 @@ public class BootJar extends Jar implements BootArchive {
*/
protected boolean isLibrary(FileCopyDetails details) {
String path = details.getRelativePath().getPathString();
return path.startsWith(LIB_FOLDER);
return path.startsWith(LIB_DIRECTORY);
}
private LaunchScriptConfiguration enableLaunchScriptIfNecessary() {

@ -42,11 +42,11 @@ public class BootWar extends War implements BootArchive {
private static final String LAUNCHER = "org.springframework.boot.loader.WarLauncher";
private static final String CLASSES_FOLDER = "WEB-INF/classes/";
private static final String CLASSES_DIRECTORY = "WEB-INF/classes/";
private static final String LIB_PROVIDED_FOLDER = "WEB-INF/lib-provided/";
private static final String LIB_PROVIDED_DIRECTORY = "WEB-INF/lib-provided/";
private static final String LIB_FOLDER = "WEB-INF/lib/";
private static final String LIB_DIRECTORY = "WEB-INF/lib/";
private final BootArchiveSupport support;
@ -70,7 +70,7 @@ public class BootWar extends War implements BootArchive {
@Override
public void copy() {
this.support.configureManifest(getManifest(), getMainClassName(), CLASSES_FOLDER, LIB_FOLDER, null, null);
this.support.configureManifest(getManifest(), getMainClassName(), CLASSES_DIRECTORY, LIB_DIRECTORY, null, null);
super.copy();
}
@ -194,7 +194,7 @@ public class BootWar extends War implements BootArchive {
*/
protected boolean isLibrary(FileCopyDetails details) {
String path = details.getRelativePath().getPathString();
return path.startsWith(LIB_FOLDER) || path.startsWith(LIB_PROVIDED_FOLDER);
return path.startsWith(LIB_DIRECTORY) || path.startsWith(LIB_PROVIDED_DIRECTORY);
}
private LaunchScriptConfiguration enableLaunchScriptIfNecessary() {

@ -282,7 +282,7 @@ class BootZipCopyAction implements CopyAction {
return;
}
if (isInMetaInf(details)) {
// Don't write loader entries until after META-INF folder (see gh-16698)
// Always write loader entries after META-INF directory (see gh-16698)
return;
}
LoaderZipEntries loaderEntries = new LoaderZipEntries(getTime());

@ -125,13 +125,13 @@ abstract class AbstractBootArchiveTests<T extends Jar & BootArchive> {
}
@Test
void classpathFoldersArePackagedBeneathClassesPath() throws IOException {
void classpathDirectoriesArePackagedBeneathClassesPath() throws IOException {
this.task.setMainClassName("com.example.Main");
File classpathFolder = new File(this.temp, "classes");
File applicationClass = new File(classpathFolder, "com/example/Application.class");
File classpathDirectory = new File(this.temp, "classes");
File applicationClass = new File(classpathDirectory, "com/example/Application.class");
applicationClass.getParentFile().mkdirs();
applicationClass.createNewFile();
this.task.classpath(classpathFolder);
this.task.classpath(classpathDirectory);
executeTask();
try (JarFile jarFile = new JarFile(this.task.getArchiveFile().get().getAsFile())) {
assertThat(jarFile.getEntry(this.classesPath + "com/example/Application.class")).isNotNull();
@ -141,14 +141,14 @@ abstract class AbstractBootArchiveTests<T extends Jar & BootArchive> {
@Test
void moduleInfoClassIsPackagedInTheRootOfTheArchive() throws IOException {
this.task.setMainClassName("com.example.Main");
File classpathFolder = new File(this.temp, "classes");
File moduleInfoClass = new File(classpathFolder, "module-info.class");
File classpathDirectory = new File(this.temp, "classes");
File moduleInfoClass = new File(classpathDirectory, "module-info.class");
moduleInfoClass.getParentFile().mkdirs();
moduleInfoClass.createNewFile();
File applicationClass = new File(classpathFolder, "com/example/Application.class");
File applicationClass = new File(classpathDirectory, "com/example/Application.class");
applicationClass.getParentFile().mkdirs();
applicationClass.createNewFile();
this.task.classpath(classpathFolder);
this.task.classpath(classpathDirectory);
executeTask();
try (JarFile jarFile = new JarFile(this.task.getArchiveFile().get().getAsFile())) {
assertThat(jarFile.getEntry(this.classesPath + "com/example/Application.class")).isNotNull();
@ -386,11 +386,11 @@ abstract class AbstractBootArchiveTests<T extends Jar & BootArchive> {
void allEntriesUseUnixPlatformAndUtf8NameEncoding() throws IOException {
this.task.setMainClassName("com.example.Main");
this.task.setMetadataCharset("UTF-8");
File classpathFolder = new File(this.temp, "classes");
File resource = new File(classpathFolder, "some-resource.xml");
File classpathDirectory = new File(this.temp, "classes");
File resource = new File(classpathDirectory, "some-resource.xml");
resource.getParentFile().mkdirs();
resource.createNewFile();
this.task.classpath(classpathFolder);
this.task.classpath(classpathDirectory);
executeTask();
File archivePath = this.task.getArchiveFile().get().getAsFile();
try (ZipFile zip = new ZipFile(archivePath)) {
@ -406,11 +406,11 @@ abstract class AbstractBootArchiveTests<T extends Jar & BootArchive> {
@Test
void loaderIsWrittenFirstThenApplicationClassesThenLibraries() throws IOException {
this.task.setMainClassName("com.example.Main");
File classpathFolder = new File(this.temp, "classes");
File applicationClass = new File(classpathFolder, "com/example/Application.class");
File classpathDirectory = new File(this.temp, "classes");
File applicationClass = new File(classpathDirectory, "com/example/Application.class");
applicationClass.getParentFile().mkdirs();
applicationClass.createNewFile();
this.task.classpath(classpathFolder, jarFile("first-library.jar"), jarFile("second-library.jar"),
this.task.classpath(classpathDirectory, jarFile("first-library.jar"), jarFile("second-library.jar"),
jarFile("third-library.jar"));
this.task.requiresUnpack("second-library.jar");
executeTask();

@ -93,12 +93,12 @@ class BootWarTests extends AbstractBootArchiveTests<BootWar> {
@Test
void webappResourcesInDirectoriesThatOverlapWithLoaderCanBePackaged() throws IOException {
File webappFolder = new File(this.temp, "src/main/webapp");
webappFolder.mkdirs();
File orgFolder = new File(webappFolder, "org");
orgFolder.mkdir();
new File(orgFolder, "foo.txt").createNewFile();
getTask().from(webappFolder);
File webappDirectory = new File(this.temp, "src/main/webapp");
webappDirectory.mkdirs();
File orgDirectory = new File(webappDirectory, "org");
orgDirectory.mkdir();
new File(orgDirectory, "foo.txt").createNewFile();
getTask().from(webappDirectory);
getTask().setMainClassName("com.example.Main");
executeTask();
try (JarFile jarFile = new JarFile(getTask().getArchiveFile().get().getAsFile())) {

@ -95,8 +95,8 @@ class Context {
return new File(name);
}
private String deduceRelativeDir(File sourceFolder, File workingDir) {
String sourcePath = sourceFolder.getAbsolutePath();
private String deduceRelativeDir(File sourceDirectory, File workingDir) {
String sourcePath = sourceDirectory.getAbsolutePath();
String workingPath = workingDir.getAbsolutePath();
if (sourcePath.equals(workingPath) || !sourcePath.startsWith(workingPath)) {
return null;

@ -100,7 +100,7 @@ class ExtractCommand extends Command {
private void mkDirs(File file) throws IOException {
if (!file.exists() && !file.mkdirs()) {
throw new IOException("Unable to create folder " + file);
throw new IOException("Unable to create directory " + file);
}
}

@ -43,10 +43,10 @@ class ContextTests {
}
@Test
void createWhenSourceIsFolderThrowsException() {
File folder = new File(this.temp, "test");
folder.mkdir();
assertThatIllegalStateException().isThrownBy(() -> new Context(folder, this.temp))
void createWhenSourceIsDirectoryThrowsException() {
File directory = new File(this.temp, "test");
directory.mkdir();
assertThatIllegalStateException().isThrownBy(() -> new Context(directory, this.temp))
.withMessage("Unable to find source JAR");
}
@ -95,13 +95,13 @@ class ContextTests {
@Test
void getRelativePathWhenCannotBeDeducedReturnsNull() throws Exception {
File folder1 = new File(this.temp, "folder1");
folder1.mkdir();
File folder2 = new File(this.temp, "folder1");
folder2.mkdir();
File jar = new File(folder1, "test.jar");
File directory1 = new File(this.temp, "directory1");
directory1.mkdir();
File directory2 = new File(this.temp, "directory2");
directory2.mkdir();
File jar = new File(directory1, "test.jar");
Files.createFile(jar.toPath());
Context context = new Context(jar, folder2);
Context context = new Context(jar, directory2);
assertThat(context.getRelativeJarDir()).isNull();
}

@ -70,10 +70,10 @@ class IndexedLayersTests {
}
@Test
void getLayerWhenMatchesFolderReturnsLayer() throws Exception {
void getLayerWhenMatchesDirectoryReturnsLayer() throws Exception {
IndexedLayers layers = new IndexedLayers(getIndex());
assertThat(layers.getLayer(mockEntry("META-INF/MANIFEST.MF"))).isEqualTo("application");
assertThat(layers.getLayer(mockEntry("META-INF/a/sub/folder/and/a/file"))).isEqualTo("application");
assertThat(layers.getLayer(mockEntry("META-INF/a/sub/directory/and/a/file"))).isEqualTo("application");
}
@Test

@ -239,7 +239,7 @@ public abstract class AbstractJarWriter implements LoaderClassesWriter {
private void writeEntry(JarArchiveEntry entry, EntryWriter entryWriter, UnpackHandler unpackHandler)
throws IOException {
String name = entry.getName();
writeParentFolderEntries(name);
writeParentDirectoryEntries(name);
if (this.writtenEntries.add(name)) {
entry.setUnixMode(name.endsWith("/") ? UNIX_DIR_MODE : UNIX_FILE_MODE);
entry.getGeneralPurposeBit().useUTF8ForNames(true);
@ -254,7 +254,7 @@ public abstract class AbstractJarWriter implements LoaderClassesWriter {
protected abstract void writeToArchive(ZipEntry entry, EntryWriter entryWriter) throws IOException;
private void writeParentFolderEntries(String name) throws IOException {
private void writeParentDirectoryEntries(String name) throws IOException {
String parent = name.endsWith("/") ? name.substring(0, name.length() - 1) : name;
while (parent.lastIndexOf('/') != -1) {
parent = parent.substring(0, parent.lastIndexOf('/'));

@ -36,9 +36,10 @@ import org.springframework.util.MultiValueMap;
* text files that should be read from top to bottom. Each file defines the layers and
* their content. Layer names are written as quoted strings prefixed by a dash space
* ({@code "- "}) and with a colon ({@code ":"}) suffix. Layer content is either a file or
* folder name written as a quoted string prefixed by space space dash space
* ({@code " - "}). A folder name ends with {@code /}, a file name does not. When a
* folder name is used it means that all files inside that folder are in the same layer.
* directory name written as a quoted string prefixed by space space dash space
* ({@code " - "}). A directory name ends with {@code /}, a file name does not. When a
* directory name is used it means that all files inside that directory are in the same
* layer.
* <p>
* Index files are designed to be compatible with YAML and may be read into a list of
* `Map&lt;String, List&lt;String&gt;&gt;` instances.
@ -79,8 +80,8 @@ public class LayersIndex {
String[] segments = name.split("/");
Node node = this.root;
for (int i = 0; i < segments.length; i++) {
boolean isFolder = i < (segments.length - 1);
node = node.updateOrAddNode(segments[i], isFolder, layer);
boolean isDirectory = i < (segments.length - 1);
node = node.updateOrAddNode(segments[i], isDirectory, layer);
}
}
@ -127,8 +128,8 @@ public class LayersIndex {
this.layers = new HashSet<>(Collections.singleton(layer));
}
Node updateOrAddNode(String segment, boolean isFolder, Layer layer) {
String name = segment + (isFolder ? "/" : "");
Node updateOrAddNode(String segment, boolean isDirectory, Layer layer) {
String name = segment + (isDirectory ? "/" : "");
for (Node child : this.children) {
if (name.equals(child.name)) {
child.layers.add(layer);

@ -64,71 +64,71 @@ public abstract class MainClassFinder {
private static final FileFilter CLASS_FILE_FILTER = MainClassFinder::isClassFile;
private static final FileFilter PACKAGE_FOLDER_FILTER = MainClassFinder::isPackageFolder;
private static final FileFilter PACKAGE_DIRECTORY_FILTER = MainClassFinder::isPackageDirectory;
private static boolean isClassFile(File file) {
return file.isFile() && file.getName().endsWith(DOT_CLASS);
}
private static boolean isPackageFolder(File file) {
private static boolean isPackageDirectory(File file) {
return file.isDirectory() && !file.getName().startsWith(".");
}
/**
* Find the main class from a given folder.
* @param rootFolder the root folder to search
* Find the main class from a given directory.
* @param rootDirectory the root directory to search
* @return the main class or {@code null}
* @throws IOException if the folder cannot be read
* @throws IOException if the directory cannot be read
*/
public static String findMainClass(File rootFolder) throws IOException {
return doWithMainClasses(rootFolder, MainClass::getName);
public static String findMainClass(File rootDirectory) throws IOException {
return doWithMainClasses(rootDirectory, MainClass::getName);
}
/**
* Find a single main class from the given {@code rootFolder}.
* @param rootFolder the root folder to search
* Find a single main class from the given {@code rootDirectory}.
* @param rootDirectory the root directory to search
* @return the main class or {@code null}
* @throws IOException if the folder cannot be read
* @throws IOException if the directory cannot be read
*/
public static String findSingleMainClass(File rootFolder) throws IOException {
return findSingleMainClass(rootFolder, null);
public static String findSingleMainClass(File rootDirectory) throws IOException {
return findSingleMainClass(rootDirectory, null);
}
/**
* Find a single main class from the given {@code rootFolder}. A main class annotated
* with an annotation with the given {@code annotationName} will be preferred over a
* main class with no such annotation.
* @param rootFolder the root folder to search
* Find a single main class from the given {@code rootDirectory}. A main class
* annotated with an annotation with the given {@code annotationName} will be
* preferred over a main class with no such annotation.
* @param rootDirectory the root directory to search
* @param annotationName the name of the annotation that may be present on the main
* class
* @return the main class or {@code null}
* @throws IOException if the folder cannot be read
* @throws IOException if the directory cannot be read
*/
public static String findSingleMainClass(File rootFolder, String annotationName) throws IOException {
public static String findSingleMainClass(File rootDirectory, String annotationName) throws IOException {
SingleMainClassCallback callback = new SingleMainClassCallback(annotationName);
MainClassFinder.doWithMainClasses(rootFolder, callback);
MainClassFinder.doWithMainClasses(rootDirectory, callback);
return callback.getMainClassName();
}
/**
* Perform the given callback operation on all main classes from the given root
* folder.
* directory.
* @param <T> the result type
* @param rootFolder the root folder
* @param rootDirectory the root directory
* @param callback the callback
* @return the first callback result or {@code null}
* @throws IOException in case of I/O errors
*/
static <T> T doWithMainClasses(File rootFolder, MainClassCallback<T> callback) throws IOException {
if (!rootFolder.exists()) {
static <T> T doWithMainClasses(File rootDirectory, MainClassCallback<T> callback) throws IOException {
if (!rootDirectory.exists()) {
return null; // nothing to do
}
if (!rootFolder.isDirectory()) {
throw new IllegalArgumentException("Invalid root folder '" + rootFolder + "'");
if (!rootDirectory.isDirectory()) {
throw new IllegalArgumentException("Invalid root directory '" + rootDirectory + "'");
}
String prefix = rootFolder.getAbsolutePath() + "/";
String prefix = rootDirectory.getAbsolutePath() + "/";
Deque<File> stack = new ArrayDeque<>();
stack.push(rootFolder);
stack.push(rootDirectory);
while (!stack.isEmpty()) {
File file = stack.pop();
if (file.isFile()) {
@ -144,7 +144,7 @@ public abstract class MainClassFinder {
}
}
if (file.isDirectory()) {
pushAllSorted(stack, file.listFiles(PACKAGE_FOLDER_FILTER));
pushAllSorted(stack, file.listFiles(PACKAGE_DIRECTORY_FILTER));
pushAllSorted(stack, file.listFiles(CLASS_FILE_FILTER));
}
}

@ -85,7 +85,7 @@ class LayersIndexTests {
}
@Test
void writeToWhenAllFilesInFolderAreInSameLayerUsesFolder() {
void writeToWhenAllFilesInDirectoryAreInSameLayerUsesDirectory() {
LayersIndex index = new LayersIndex(LAYER_A, LAYER_B, LAYER_C);
index.add(LAYER_A, "a1/b1/c1");
index.add(LAYER_A, "a1/b1/c2");
@ -96,7 +96,7 @@ class LayersIndexTests {
}
@Test
void writeToWhenAllFilesInFolderAreInNotInSameLayerUsesFiles() {
void writeToWhenAllFilesInDirectoryAreInNotInSameLayerUsesFiles() {
LayersIndex index = new LayersIndex(LAYER_A, LAYER_B, LAYER_C);
index.add(LAYER_A, "a1/b1/c1");
index.add(LAYER_B, "a1/b1/c2");

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
@ -60,7 +60,7 @@ class MainClassFinderTests {
}
@Test
void findMainClassInJarSubFolder() throws Exception {
void findMainClassInJarSubDirectory() throws Exception {
this.testJarFile.addClass("a/b/c/D.class", ClassWithMainMethod.class);
this.testJarFile.addClass("a/b/c/E.class", ClassWithoutMainMethod.class);
this.testJarFile.addClass("a/b/F.class", ClassWithoutMainMethod.class);
@ -114,7 +114,7 @@ class MainClassFinderTests {
}
@Test
void findMainClassInFolder() throws Exception {
void findMainClassInDirectory() throws Exception {
this.testJarFile.addClass("B.class", ClassWithMainMethod.class);
this.testJarFile.addClass("A.class", ClassWithoutMainMethod.class);
String actual = MainClassFinder.findMainClass(this.testJarFile.getJarSource());
@ -122,7 +122,7 @@ class MainClassFinderTests {
}
@Test
void findMainClassInSubFolder() throws Exception {
void findMainClassInSubDirectory() throws Exception {
this.testJarFile.addClass("a/b/c/D.class", ClassWithMainMethod.class);
this.testJarFile.addClass("a/b/c/E.class", ClassWithoutMainMethod.class);
this.testJarFile.addClass("a/b/F.class", ClassWithoutMainMethod.class);
@ -131,7 +131,7 @@ class MainClassFinderTests {
}
@Test
void usesBreadthFirstFolderSearch() throws Exception {
void usesBreadthFirstDirectorySearch() throws Exception {
this.testJarFile.addClass("a/B.class", ClassWithMainMethod.class);
this.testJarFile.addClass("a/b/c/E.class", ClassWithMainMethod.class);
String actual = MainClassFinder.findMainClass(this.testJarFile.getJarSource());
@ -139,7 +139,7 @@ class MainClassFinderTests {
}
@Test
void findSingleFolderSearch() throws Exception {
void findSingleDirectorySearch() throws Exception {
this.testJarFile.addClass("a/B.class", ClassWithMainMethod.class);
this.testJarFile.addClass("a/b/c/E.class", ClassWithMainMethod.class);
assertThatIllegalStateException()
@ -149,7 +149,7 @@ class MainClassFinderTests {
}
@Test
void findSingleFolderSearchPrefersAnnotatedMainClass() throws Exception {
void findSingleDirectorySearchPrefersAnnotatedMainClass() throws Exception {
this.testJarFile.addClass("a/B.class", ClassWithMainMethod.class);
this.testJarFile.addClass("a/b/c/E.class", AnnotatedClassWithMainMethod.class);
String mainClass = MainClassFinder.findSingleMainClass(this.testJarFile.getJarSource(),
@ -158,7 +158,7 @@ class MainClassFinderTests {
}
@Test
void doWithFolderMainMethods() throws Exception {
void doWithDirectoryMainMethods() throws Exception {
this.testJarFile.addClass("a/b/c/D.class", ClassWithMainMethod.class);
this.testJarFile.addClass("a/b/c/E.class", ClassWithoutMainMethod.class);
this.testJarFile.addClass("a/b/F.class", ClassWithoutMainMethod.class);

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
@ -40,15 +40,15 @@ public class TestJarFile {
private final byte[] buffer = new byte[4096];
private final File temporaryFolder;
private final File temporaryDirectory;
private final File jarSource;
private final List<ZipEntrySource> entries = new ArrayList<>();
public TestJarFile(File temporaryFolder) throws IOException {
this.temporaryFolder = temporaryFolder;
this.jarSource = new File(temporaryFolder, "jar-source");
public TestJarFile(File temporaryDirectory) throws IOException {
this.temporaryDirectory = temporaryDirectory;
this.jarSource = new File(temporaryDirectory, "jar-source");
}
public void addClass(String filename, Class<?> classToCopy) throws IOException {
@ -120,7 +120,7 @@ public class TestJarFile {
}
public File getFile(String extension) throws IOException {
File file = new File(this.temporaryFolder, UUID.randomUUID() + "." + extension);
File file = new File(this.temporaryDirectory, UUID.randomUUID() + "." + extension);
ZipUtil.pack(this.entries.toArray(new ZipEntrySource[0]), file);
return file;
}

@ -139,7 +139,7 @@ public abstract class ExecutableArchiveLauncher extends Launcher {
* Determine if the specified entry is a nested item that should be added to the
* classpath.
* @param entry the entry to check
* @return {@code true} if the entry is a nested item (jar or folder)
* @return {@code true} if the entry is a nested item (jar or directory)
*/
protected abstract boolean isNestedArchive(Archive.Entry entry);

@ -55,7 +55,7 @@ public class ExplodedArchive implements Archive {
/**
* Create a new {@link ExplodedArchive} instance.
* @param root the root folder
* @param root the root directory
*/
public ExplodedArchive(File root) {
this(root, true);
@ -63,15 +63,14 @@ public class ExplodedArchive implements Archive {
/**
* Create a new {@link ExplodedArchive} instance.
* @param root the root folder
* @param root the root directory
* @param recursive if recursive searching should be used to locate the manifest.
* Defaults to {@code true}, folders with a large tree might want to set this to
* {@code
* false}.
* Defaults to {@code true}, directories with a large tree might want to set this to
* {@code false}.
*/
public ExplodedArchive(File root, boolean recursive) {
if (!root.exists() || !root.isDirectory()) {
throw new IllegalArgumentException("Invalid source folder " + root);
throw new IllegalArgumentException("Invalid source directory " + root);
}
this.root = root;
this.recursive = recursive;

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
@ -47,7 +47,7 @@ public class JarFileArchive implements Archive {
private URL url;
private File tempUnpackFolder;
private File tempUnpackDirectory;
public JarFileArchive(File file) throws IOException {
this(file, file.toURI().toURL());
@ -109,31 +109,31 @@ public class JarFileArchive implements Archive {
if (name.lastIndexOf('/') != -1) {
name = name.substring(name.lastIndexOf('/') + 1);
}
File file = new File(getTempUnpackFolder(), name);
File file = new File(getTempUnpackDirectory(), name);
if (!file.exists() || file.length() != jarEntry.getSize()) {
unpack(jarEntry, file);
}
return new JarFileArchive(file, file.toURI().toURL());
}
private File getTempUnpackFolder() {
if (this.tempUnpackFolder == null) {
File tempFolder = new File(System.getProperty("java.io.tmpdir"));
this.tempUnpackFolder = createUnpackFolder(tempFolder);
private File getTempUnpackDirectory() {
if (this.tempUnpackDirectory == null) {
File tempDirectory = new File(System.getProperty("java.io.tmpdir"));
this.tempUnpackDirectory = createUnpackDirectory(tempDirectory);
}
return this.tempUnpackFolder;
return this.tempUnpackDirectory;
}
private File createUnpackFolder(File parent) {
private File createUnpackDirectory(File parent) {
int attempts = 0;
while (attempts++ < 1000) {
String fileName = new File(this.jarFile.getName()).getName();
File unpackFolder = new File(parent, fileName + "-spring-boot-libs-" + UUID.randomUUID());
if (unpackFolder.mkdirs()) {
return unpackFolder;
File unpackDirectory = new File(parent, fileName + "-spring-boot-libs-" + UUID.randomUUID());
if (unpackDirectory.mkdirs()) {
return unpackDirectory;
}
}
throw new IllegalStateException("Failed to create unpack folder in directory '" + parent + "'");
throw new IllegalStateException("Failed to create unpack directory in directory '" + parent + "'");
}
private void unpack(JarEntry entry, File file) throws IOException {

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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,7 +16,7 @@
/**
* Abstraction over logical Archives be they backed by a JAR file or unpacked into a
* folder.
* directory.
*
* @see org.springframework.boot.loader.archive.Archive
*/

@ -55,8 +55,8 @@ class ClassPathIndexFileTests {
}
@Test
void loadIfPossibleWhenRootIsFolderThrowsException() throws Exception {
File root = new File(this.temp, "folder");
void loadIfPossibleWhenRootIsDirectoryThrowsException() throws Exception {
File root = new File(this.temp, "directory");
root.mkdirs();
assertThat(ClassPathIndexFile.loadIfPossible(root.toURI().toURL(), "test.idx")).isNull();
}

@ -102,7 +102,7 @@ class PropertiesLauncherTests {
void testNonExistentHome() {
System.setProperty("loader.home", "src/test/resources/nonexistent");
assertThatIllegalStateException().isThrownBy(PropertiesLauncher::new)
.withMessageContaining("Invalid source folder").withCauseInstanceOf(IllegalArgumentException.class);
.withMessageContaining("Invalid source directory").withCauseInstanceOf(IllegalArgumentException.class);
}
@Test

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
@ -51,7 +51,7 @@ class ExplodedArchiveTests {
@TempDir
File tempDir;
private File rootFolder;
private File rootDirectory;
private ExplodedArchive archive;
@ -71,16 +71,16 @@ class ExplodedArchiveTests {
createArchive(null);
}
private void createArchive(String folderName) throws Exception {
private void createArchive(String directoryName) throws Exception {
File file = new File(this.tempDir, "test.jar");
TestJarCreator.createTestJar(file);
this.rootFolder = (StringUtils.hasText(folderName) ? new File(this.tempDir, folderName)
this.rootDirectory = (StringUtils.hasText(directoryName) ? new File(this.tempDir, directoryName)
: new File(this.tempDir, UUID.randomUUID().toString()));
JarFile jarFile = new JarFile(file);
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
File destination = new File(this.rootFolder.getAbsolutePath() + File.separator + entry.getName());
File destination = new File(this.rootDirectory.getAbsolutePath() + File.separator + entry.getName());
destination.getParentFile().mkdirs();
if (entry.isDirectory()) {
destination.mkdir();
@ -89,7 +89,7 @@ class ExplodedArchiveTests {
FileCopyUtils.copy(jarFile.getInputStream(entry), new FileOutputStream(destination));
}
}
this.archive = new ExplodedArchive(this.rootFolder);
this.archive = new ExplodedArchive(this.rootDirectory);
jarFile.close();
}
@ -106,20 +106,20 @@ class ExplodedArchiveTests {
@Test
void getUrl() throws Exception {
assertThat(this.archive.getUrl()).isEqualTo(this.rootFolder.toURI().toURL());
assertThat(this.archive.getUrl()).isEqualTo(this.rootDirectory.toURI().toURL());
}
@Test
void getUrlWithSpaceInPath() throws Exception {
createArchive("spaces in the name");
assertThat(this.archive.getUrl()).isEqualTo(this.rootFolder.toURI().toURL());
assertThat(this.archive.getUrl()).isEqualTo(this.rootDirectory.toURI().toURL());
}
@Test
void getNestedArchive() throws Exception {
Entry entry = getEntriesMap(this.archive).get("nested.jar");
Archive nested = this.archive.getNestedArchive(entry);
assertThat(nested.getUrl().toString()).isEqualTo(this.rootFolder.toURI() + "nested.jar");
assertThat(nested.getUrl().toString()).isEqualTo(this.rootDirectory.toURI() + "nested.jar");
nested.close();
}
@ -129,7 +129,7 @@ class ExplodedArchiveTests {
Archive nested = this.archive.getNestedArchive(entry);
Map<String, Entry> nestedEntries = getEntriesMap(nested);
assertThat(nestedEntries.size()).isEqualTo(1);
assertThat(nested.getUrl().toString()).isEqualTo("file:" + this.rootFolder.toURI().getPath() + "d/");
assertThat(nested.getUrl().toString()).isEqualTo("file:" + this.rootDirectory.toURI().getPath() + "d/");
}
@Test

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
@ -126,20 +126,20 @@ class HandlerTests {
@Test
void urlWithSpecReferencingParentDirectory() throws MalformedURLException {
assertStandardAndCustomHandlerUrlsAreEqual("file:/test.jar!/BOOT-INF/classes!/xsd/folderA/a.xsd",
"../folderB/c/d/e.xsd");
assertStandardAndCustomHandlerUrlsAreEqual("file:/test.jar!/BOOT-INF/classes!/xsd/directoryA/a.xsd",
"../directoryB/c/d/e.xsd");
}
@Test
void urlWithSpecReferencingAncestorDirectoryOutsideJarStopsAtJarRoot() throws MalformedURLException {
assertStandardAndCustomHandlerUrlsAreEqual("file:/test.jar!/BOOT-INF/classes!/xsd/folderA/a.xsd",
"../../../../../../folderB/b.xsd");
assertStandardAndCustomHandlerUrlsAreEqual("file:/test.jar!/BOOT-INF/classes!/xsd/directoryA/a.xsd",
"../../../../../../directoryB/b.xsd");
}
@Test
void urlWithSpecReferencingCurrentDirectory() throws MalformedURLException {
assertStandardAndCustomHandlerUrlsAreEqual("file:/test.jar!/BOOT-INF/classes!/xsd/folderA/a.xsd",
"./folderB/c/d/e.xsd");
assertStandardAndCustomHandlerUrlsAreEqual("file:/test.jar!/BOOT-INF/classes!/xsd/directoryA/a.xsd",
"./directoryB/c/d/e.xsd");
}
@Test

@ -77,7 +77,7 @@ The `layout` property defaults to a value determined by the archive type (`jar`
[[repackage-layers]]
=== Layered Jars
A repackaged jar contains the application's classes and dependencies in `BOOT-INF/classes` and `BOOT-INF/lib` respectively.
For cases where a docker image needs to be built from the contents of the jar, it's useful to be able to separate these folders further so that they can be written into distinct layers.
For cases where a docker image needs to be built from the contents of the jar, it's useful to be able to separate these directories further so that they can be written into distinct layers.
Layered jars use the same layout as regular repackaged jars, but include an additional meta-data file that describes each layer.
To use this feature, the layering feature must be enabled:

@ -64,7 +64,7 @@ You can restore it at any time by configuring your project:
</build>
----
When `addResources` is enabled, any `src/main/resources` folder will be added to the application classpath when you run the application and any duplicate found in `target/classes` will be removed.
When `addResources` is enabled, any `src/main/resources` directory will be added to the application classpath when you run the application and any duplicate found in `target/classes` will be removed.
This allows hot refreshing of resources which can be very useful when developing web applications.
For example, you can work on HTML, CSS or JavaScript files and see your changes immediately without recompiling your application.
It is also a helpful way of allowing your front end developers to work without needing to download and install a Java IDE.

@ -212,7 +212,7 @@ class MavenBuild {
}
/**
* Action to take on a maven project folder.
* Action to take on a maven project directory.
*/
@FunctionalInterface
public interface ProjectCallback {

@ -176,13 +176,23 @@ public abstract class AbstractRunMojo extends AbstractDependencyFilterMojo {
private String mainClass;
/**
* Additional folders besides the classes directory that should be added to the
* Additional directories besides the classes directory that should be added to the
* classpath.
* @since 1.0.0
* @deprecated since 2.3.0 in favor of {@code directories}
*/
@Deprecated
@Parameter(property = "spring-boot.run.folders")
private String[] folders;
/**
* Additional directories besides the classes directory that should be added to the
* classpath.
* @since 1.0.0
*/
@Parameter(property = "spring-boot.run.directories")
private String[] directories;
/**
* Directory containing the classes and resource files that should be packaged into
* the archive.
@ -440,7 +450,7 @@ public abstract class AbstractRunMojo extends AbstractDependencyFilterMojo {
protected URL[] getClassPathUrls() throws MojoExecutionException {
try {
List<URL> urls = new ArrayList<>();
addUserDefinedFolders(urls);
addUserDefinedDirectories(urls);
addResources(urls);
addProjectClasses(urls);
addDependencies(urls);
@ -451,12 +461,17 @@ public abstract class AbstractRunMojo extends AbstractDependencyFilterMojo {
}
}
private void addUserDefinedFolders(List<URL> urls) throws MalformedURLException {
private void addUserDefinedDirectories(List<URL> urls) throws MalformedURLException {
if (this.folders != null) {
for (String folder : this.folders) {
urls.add(new File(folder).toURI().toURL());
}
}
if (this.directories != null) {
for (String directory : this.directories) {
urls.add(new File(directory).toURI().toURL());
}
}
}
private void addResources(List<URL> urls) throws IOException {

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
@ -41,9 +41,9 @@ import javax.tools.ToolProvider;
public class TestCompiler {
/**
* The default source folder.
* The default source directory.
*/
public static final File SOURCE_FOLDER = new File("src/test/java");
public static final File SOURCE_DIRECTORY = new File("src/test/java");
private final JavaCompiler compiler;
@ -93,15 +93,15 @@ public class TestCompiler {
}
protected File getFile(Class<?> type) {
return new File(getSourceFolder(), sourcePathFor(type));
return new File(getSourceDirectory(), sourcePathFor(type));
}
public static String sourcePathFor(Class<?> type) {
return type.getName().replace('.', '/') + ".java";
}
protected File getSourceFolder() {
return SOURCE_FOLDER;
protected File getSourceDirectory() {
return SOURCE_DIRECTORY;
}
/**

@ -439,8 +439,8 @@ public class ConfigFileApplicationListener implements EnvironmentPostProcessor,
private void load(Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
getSearchLocations().forEach((location) -> {
boolean isFolder = location.endsWith("/");
Set<String> names = isFolder ? getSearchNames() : NO_SEARCH_NAMES;
boolean isDirectory = location.endsWith("/");
Set<String> names = isDirectory ? getSearchNames() : NO_SEARCH_NAMES;
names.forEach((name) -> load(location, name, profile, filterFactory, consumer));
});
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
@ -88,7 +88,7 @@ public class ApplicationPid {
*/
public void write(File file) throws IOException {
Assert.state(this.pid != null, "No PID available");
createParentFolder(file);
createParentDirectory(file);
if (file.exists()) {
assertCanOverwrite(file);
}
@ -97,7 +97,7 @@ public class ApplicationPid {
}
}
private void createParentFolder(File file) {
private void createParentDirectory(File file) {
File parent = file.getParentFile();
if (parent != null) {
parent.mkdirs();

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
@ -87,7 +87,7 @@ public class WebServerPortFileWriter implements ApplicationListener<WebServerIni
File portFile = getPortFile(event.getApplicationContext());
try {
String port = String.valueOf(event.getWebServer().getPort());
createParentFolder(portFile);
createParentDirectory(portFile);
FileCopyUtils.copy(port.getBytes(), portFile);
portFile.deleteOnExit();
}
@ -139,7 +139,7 @@ public class WebServerPortFileWriter implements ApplicationListener<WebServerIni
return true;
}
private void createParentFolder(File file) {
private void createParentDirectory(File file) {
File parent = file.getParentFile();
if (parent != null) {
parent.mkdirs();

@ -1025,7 +1025,7 @@ class ConfigFileApplicationListenerTests {
}
@Test
void whenConfigLocationSpecifiesFolderConfigFileProcessingContinues() {
void whenConfigLocationSpecifiesDirectoryConfigFileProcessingContinues() {
String location = "classpath:application.unknown/";
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.environment,
"spring.config.location=" + location);
@ -1033,7 +1033,7 @@ class ConfigFileApplicationListenerTests {
}
@Test
void locationsWithWildcardFoldersShouldLoadAllFilesThatMatch() {
void locationsWithWildcardDirectoriesShouldLoadAllFilesThatMatch() {
String location = "file:src/test/resources/config/*/";
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.environment,
"spring.config.location=" + location);

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
@ -35,17 +35,17 @@ public abstract class AbstractLoggingSystemTests {
private static final String JAVA_IO_TMPDIR = "java.io.tmpdir";
private String originalTempFolder;
private String originalTempDirectory;
@BeforeEach
void configureTempDir(@TempDir Path temp) {
this.originalTempFolder = System.getProperty(JAVA_IO_TMPDIR);
this.originalTempDirectory = System.getProperty(JAVA_IO_TMPDIR);
System.setProperty(JAVA_IO_TMPDIR, temp.toAbsolutePath().toString());
}
@AfterEach
void reinstateTempDir() {
System.setProperty(JAVA_IO_TMPDIR, this.originalTempFolder);
System.setProperty(JAVA_IO_TMPDIR, this.originalTempDirectory);
}
@AfterEach

@ -59,11 +59,11 @@ class ApplicationBuilder {
}
File buildApplication() throws Exception {
File containerFolder = new File(this.temp.toFile(), this.container);
if (containerFolder.exists()) {
return new File(containerFolder, "app/target/app-0.0.1." + this.packaging);
File containerDirectory = new File(this.temp.toFile(), this.container);
if (containerDirectory.exists()) {
return new File(containerDirectory, "app/target/app-0.0.1." + this.packaging);
}
return doBuildApplication(containerFolder);
return doBuildApplication(containerDirectory);
}
String getPackaging() {
@ -74,15 +74,15 @@ class ApplicationBuilder {
return this.container;
}
private File doBuildApplication(File containerFolder) throws IOException, MavenInvocationException {
private File doBuildApplication(File containerDirectory) throws IOException, MavenInvocationException {
File resourcesJar = createResourcesJar();
File appFolder = new File(containerFolder, "app");
appFolder.mkdirs();
File settingsXml = writeSettingsXml(appFolder);
writePom(appFolder, resourcesJar);
copyApplicationSource(appFolder);
packageApplication(appFolder, settingsXml);
return new File(appFolder, "target/app-0.0.1." + this.packaging);
File appDirectory = new File(containerDirectory, "app");
appDirectory.mkdirs();
File settingsXml = writeSettingsXml(appDirectory);
writePom(appDirectory, resourcesJar);
copyApplicationSource(appDirectory);
packageApplication(appDirectory, settingsXml);
return new File(appDirectory, "target/app-0.0.1." + this.packaging);
}
private File createResourcesJar() throws IOException {
@ -106,23 +106,23 @@ class ApplicationBuilder {
}
}
private void writePom(File appFolder, File resourcesJar) throws IOException {
private void writePom(File appDirectory, File resourcesJar) throws IOException {
Map<String, Object> context = new HashMap<>();
context.put("packaging", this.packaging);
context.put("container", this.container);
context.put("bootVersion", Versions.getBootVersion());
context.put("resourcesJarPath", resourcesJar.getAbsolutePath());
try (FileWriter out = new FileWriter(new File(appFolder, "pom.xml"));
try (FileWriter out = new FileWriter(new File(appDirectory, "pom.xml"));
FileReader templateReader = new FileReader("src/test/resources/pom-template.xml")) {
Mustache.compiler().escapeHTML(false).compile(templateReader).execute(context, out);
}
}
private File writeSettingsXml(File appFolder) throws IOException {
private File writeSettingsXml(File appDirectory) throws IOException {
Map<String, Object> context = new HashMap<>();
context.put("repository", new File("build/test-repository").toURI().toURL());
context.put("localRepository", new File("build/local-m2-repository").getAbsolutePath());
File settingsXml = new File(appFolder, "settings.xml");
File settingsXml = new File(appDirectory, "settings.xml");
try (FileWriter out = new FileWriter(settingsXml);
FileReader templateReader = new FileReader("src/test/resources/settings-template.xml")) {
Mustache.compiler().escapeHTML(false).compile(templateReader).execute(context, out);
@ -130,8 +130,8 @@ class ApplicationBuilder {
return settingsXml;
}
private void copyApplicationSource(File appFolder) throws IOException {
File examplePackage = new File(appFolder, "src/main/java/com/example");
private void copyApplicationSource(File appDirectory) throws IOException {
File examplePackage = new File(appDirectory, "src/main/java/com/example");
examplePackage.mkdirs();
FileCopyUtils.copy(new File("src/test/java/com/example/ResourceHandlingApplication.java"),
new File(examplePackage, "ResourceHandlingApplication.java"));
@ -142,20 +142,20 @@ class ApplicationBuilder {
new File(examplePackage, "JettyServerCustomizerConfig.java"));
}
if ("war".equals(this.packaging)) {
File srcMainWebapp = new File(appFolder, "src/main/webapp");
File srcMainWebapp = new File(appDirectory, "src/main/webapp");
srcMainWebapp.mkdirs();
FileCopyUtils.copy("webapp resource", new FileWriter(new File(srcMainWebapp, "webapp-resource.txt")));
}
copyAutoConfigurationFiles(appFolder);
copyAutoConfigurationFiles(appDirectory);
return;
}
private void copyAutoConfigurationFiles(File appFolder) throws IOException {
File autoConfigPackage = new File(appFolder, "src/main/java/com/autoconfig");
private void copyAutoConfigurationFiles(File appDirectory) throws IOException {
File autoConfigPackage = new File(appDirectory, "src/main/java/com/autoconfig");
autoConfigPackage.mkdirs();
FileCopyUtils.copy(new File("src/test/java/com/autoconfig/ExampleAutoConfiguration.java"),
new File(autoConfigPackage, "ExampleAutoConfiguration.java"));
File srcMainResources = new File(appFolder, "src/main/resources");
File srcMainResources = new File(appDirectory, "src/main/resources");
srcMainResources.mkdirs();
File metaInf = new File(srcMainResources, "META-INF");
metaInf.mkdirs();
@ -165,9 +165,9 @@ class ApplicationBuilder {
new File(srcMainResources, "application.yml"));
}
private void packageApplication(File appFolder, File settingsXml) throws MavenInvocationException {
private void packageApplication(File appDirectory, File settingsXml) throws MavenInvocationException {
InvocationRequest invocation = new DefaultInvocationRequest();
invocation.setBaseDirectory(appFolder);
invocation.setBaseDirectory(appDirectory);
invocation.setGoals(Collections.singletonList("package"));
if (settingsXml != null) {
invocation.setUserSettingsFile(settingsXml);

Loading…
Cancel
Save