Add filesystem watcher support
Add a filesystem watcher that can be used to inform listeners of changes (add, delete, modify) to files in specific source folders. The implementation uses a background thread rather than the WatchService API to remain compatible with Java 6 and because WatchService is slow on OSX. See gh-3084
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright 2012-2015 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.developertools.filewatch;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A single file that has changed.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.3.0
|
||||
* @see ChangedFiles
|
||||
*/
|
||||
public final class ChangedFile {
|
||||
|
||||
private final File sourceFolder;
|
||||
|
||||
private final File file;
|
||||
|
||||
private final Type type;
|
||||
|
||||
/**
|
||||
* Create a new {@link ChangedFile} instance.
|
||||
* @param sourceFolder the source folder
|
||||
* @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");
|
||||
Assert.notNull(file, "File must not be null");
|
||||
Assert.notNull(type, "Type must not be null");
|
||||
this.sourceFolder = sourceFolder;
|
||||
this.file = file;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the file that was changed.
|
||||
* @return the file
|
||||
*/
|
||||
public File getFile() {
|
||||
return this.file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the type of change.
|
||||
* @return the type of change
|
||||
*/
|
||||
public Type getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the file relative to the source folder.
|
||||
* @return the relative name
|
||||
*/
|
||||
public String getRelativeName() {
|
||||
String folderName = this.sourceFolder.getAbsoluteFile().getPath();
|
||||
String fileName = this.file.getAbsoluteFile().getPath();
|
||||
Assert.state(fileName.startsWith(folderName), "The file " + fileName
|
||||
+ " is not contained in the source folder " + folderName);
|
||||
return fileName.substring(folderName.length() + 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.file.hashCode() * 31 + this.type.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (obj instanceof ChangedFile) {
|
||||
ChangedFile other = (ChangedFile) obj;
|
||||
return this.file.equals(other.file) && this.type.equals(other.type);
|
||||
}
|
||||
return super.equals(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.file + " (" + this.type + ")";
|
||||
}
|
||||
|
||||
/**
|
||||
* Change types.
|
||||
*/
|
||||
public static enum Type {
|
||||
|
||||
/**
|
||||
* A new file has been added.
|
||||
*/
|
||||
ADD,
|
||||
|
||||
/**
|
||||
* An existing file has been modified.
|
||||
*/
|
||||
MODIFY,
|
||||
|
||||
/**
|
||||
* An existing file has been deleted.
|
||||
*/
|
||||
DELETE
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright 2012-2015 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.developertools.filewatch;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A collections of files from a specific source folder that have changed.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.3.0
|
||||
* @see FileChangeListener
|
||||
* @see ChangedFiles
|
||||
*/
|
||||
public final class ChangedFiles implements Iterable<ChangedFile> {
|
||||
|
||||
private final File sourceFolder;
|
||||
|
||||
private final Set<ChangedFile> files;
|
||||
|
||||
public ChangedFiles(File sourceFolder, Set<ChangedFile> files) {
|
||||
this.sourceFolder = sourceFolder;
|
||||
this.files = Collections.unmodifiableSet(files);
|
||||
}
|
||||
|
||||
/**
|
||||
* The source folder being watched.
|
||||
* @return the source folder
|
||||
*/
|
||||
public File getSourceFolder() {
|
||||
return this.sourceFolder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<ChangedFile> iterator() {
|
||||
return getFiles().iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* The files that have been changed.
|
||||
* @return the changed files
|
||||
*/
|
||||
public Set<ChangedFile> getFiles() {
|
||||
return this.files;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.files.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (obj instanceof ChangedFiles) {
|
||||
ChangedFiles other = (ChangedFiles) obj;
|
||||
return this.sourceFolder.equals(other.sourceFolder)
|
||||
&& this.files.equals(other.files);
|
||||
}
|
||||
return super.equals(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.sourceFolder + " " + this.files;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2012-2015 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.developertools.filewatch;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Callback interface when file changes are detected.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Phillip Webb
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public interface FileChangeListener {
|
||||
|
||||
/**
|
||||
* Called when files have been changed.
|
||||
* @param changeSet a set of the {@link ChangedFiles}
|
||||
*/
|
||||
void onChange(Set<ChangedFiles> changeSet);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2012-2015 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.developertools.filewatch;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A snapshot of a File at a given point in time.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class FileSnapshot {
|
||||
|
||||
private final File file;
|
||||
|
||||
private final boolean exists;
|
||||
|
||||
private final long length;
|
||||
|
||||
private final long lastModified;
|
||||
|
||||
public FileSnapshot(File file) {
|
||||
Assert.notNull(file, "File must not be null");
|
||||
Assert.isTrue(file.isFile() || !file.exists(), "File must not be a folder");
|
||||
this.file = file;
|
||||
this.exists = file.exists();
|
||||
this.length = file.length();
|
||||
this.lastModified = file.lastModified();
|
||||
}
|
||||
|
||||
public File getFile() {
|
||||
return this.file;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (obj instanceof FileSnapshot) {
|
||||
FileSnapshot other = (FileSnapshot) obj;
|
||||
boolean equals = this.file.equals(other.file);
|
||||
equals &= this.exists == other.exists;
|
||||
equals &= this.length == other.length;
|
||||
equals &= this.lastModified == other.lastModified;
|
||||
return equals;
|
||||
}
|
||||
return super.equals(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hashCode = this.file.hashCode();
|
||||
hashCode = 31 * hashCode + (this.exists ? 1231 : 1237);
|
||||
hashCode = 31 * hashCode + (int) (this.length ^ (this.length >>> 32));
|
||||
hashCode = 31 * hashCode + (int) (this.lastModified ^ (this.lastModified >>> 32));
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.file.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* Copyright 2012-2015 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.developertools.filewatch;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Watches specific folders for file changes.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Phillip Webb
|
||||
* @see FileChangeListener
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public class FileSystemWatcher {
|
||||
|
||||
private static final long DEFAULT_IDLE_TIME = 400;
|
||||
|
||||
private static final long DEFAULT_QUIET_TIME = 200;
|
||||
|
||||
private List<FileChangeListener> listeners = new ArrayList<FileChangeListener>();
|
||||
|
||||
private final boolean daemon;
|
||||
|
||||
private final long idleTime;
|
||||
|
||||
private final long quietTime;
|
||||
|
||||
private Thread watchThread;
|
||||
|
||||
private AtomicInteger remainingScans = new AtomicInteger(-1);
|
||||
|
||||
private Map<File, FolderSnapshot> folders = new LinkedHashMap<File, FolderSnapshot>();
|
||||
|
||||
/**
|
||||
* Create a new {@link FileSystemWatcher} instance.
|
||||
*/
|
||||
public FileSystemWatcher() {
|
||||
this(true, DEFAULT_IDLE_TIME, DEFAULT_QUIET_TIME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link FileSystemWatcher} instance.
|
||||
* @param daemon if a daemon thread used to monitor changes
|
||||
* @param idleTime the amount of time to wait between checking for changes
|
||||
* @param quietTime the amount of time required after a change has been detected to
|
||||
* ensure that updates have completed
|
||||
*/
|
||||
public FileSystemWatcher(boolean daemon, long idleTime, long quietTime) {
|
||||
this.daemon = daemon;
|
||||
this.idleTime = idleTime;
|
||||
this.quietTime = quietTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add listener for file change events. Cannot be called after the watcher has been
|
||||
* {@link #start() started}.
|
||||
* @param fileChangeListener the listener to add
|
||||
*/
|
||||
public synchronized void addListener(FileChangeListener fileChangeListener) {
|
||||
Assert.notNull(fileChangeListener, "FileChangeListener must not be null");
|
||||
checkNotStarted();
|
||||
this.listeners.add(fileChangeListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a source folder to monitor. Cannot be called after the watcher has been
|
||||
* {@link #start() started}.
|
||||
* @param folder the folder to monitor
|
||||
*/
|
||||
public synchronized void addSourceFolder(File folder) {
|
||||
Assert.notNull(folder, "Folder must not be null");
|
||||
Assert.isTrue(folder.isDirectory(), "Folder must not be a file");
|
||||
checkNotStarted();
|
||||
this.folders.put(folder, null);
|
||||
}
|
||||
|
||||
private void checkNotStarted() {
|
||||
Assert.state(this.watchThread == null, "FileSystemWatcher already started");
|
||||
}
|
||||
|
||||
/**
|
||||
* Start monitoring the source folder for changes.
|
||||
*/
|
||||
public synchronized void start() {
|
||||
saveInitalSnapshots();
|
||||
if (this.watchThread == null) {
|
||||
this.watchThread = new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
int remainingScans = FileSystemWatcher.this.remainingScans.get();
|
||||
while (remainingScans > 0 || remainingScans == -1) {
|
||||
try {
|
||||
if (remainingScans > 0) {
|
||||
FileSystemWatcher.this.remainingScans.decrementAndGet();
|
||||
}
|
||||
scan();
|
||||
remainingScans = FileSystemWatcher.this.remainingScans.get();
|
||||
}
|
||||
catch (InterruptedException ex) {
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
this.watchThread.setName("File Watcher");
|
||||
this.watchThread.setDaemon(this.daemon);
|
||||
this.remainingScans = new AtomicInteger(-1);
|
||||
this.watchThread.start();
|
||||
}
|
||||
}
|
||||
|
||||
private void saveInitalSnapshots() {
|
||||
for (File folder : this.folders.keySet()) {
|
||||
this.folders.put(folder, new FolderSnapshot(folder));
|
||||
}
|
||||
}
|
||||
|
||||
private void scan() throws InterruptedException {
|
||||
Thread.sleep(this.idleTime - this.quietTime);
|
||||
Set<FolderSnapshot> previous;
|
||||
Set<FolderSnapshot> current = new HashSet<FolderSnapshot>(this.folders.values());
|
||||
do {
|
||||
previous = current;
|
||||
current = getCurrentSnapshots();
|
||||
Thread.sleep(this.quietTime);
|
||||
}
|
||||
while (!previous.equals(current));
|
||||
updateSnapshots(current);
|
||||
}
|
||||
|
||||
private Set<FolderSnapshot> getCurrentSnapshots() {
|
||||
Set<FolderSnapshot> snapshots = new LinkedHashSet<FolderSnapshot>();
|
||||
for (File folder : this.folders.keySet()) {
|
||||
snapshots.add(new FolderSnapshot(folder));
|
||||
}
|
||||
return snapshots;
|
||||
}
|
||||
|
||||
private void updateSnapshots(Set<FolderSnapshot> snapshots) {
|
||||
Map<File, FolderSnapshot> updated = new LinkedHashMap<File, FolderSnapshot>();
|
||||
Set<ChangedFiles> changeSet = new LinkedHashSet<ChangedFiles>();
|
||||
for (FolderSnapshot snapshot : snapshots) {
|
||||
FolderSnapshot previous = this.folders.get(snapshot.getFolder());
|
||||
updated.put(snapshot.getFolder(), snapshot);
|
||||
ChangedFiles changedFiles = previous.getChangedFiles(snapshot);
|
||||
if (!changedFiles.getFiles().isEmpty()) {
|
||||
changeSet.add(changedFiles);
|
||||
}
|
||||
}
|
||||
if (!changeSet.isEmpty()) {
|
||||
fireListeners(Collections.unmodifiableSet(changeSet));
|
||||
}
|
||||
this.folders = updated;
|
||||
}
|
||||
|
||||
private void fireListeners(Set<ChangedFiles> changeSet) {
|
||||
for (FileChangeListener listener : this.listeners) {
|
||||
listener.onChange(changeSet);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop monitoring the source folders.
|
||||
*/
|
||||
public synchronized void stop() {
|
||||
stopAfter(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop monitoring the source folders.
|
||||
* @param remainingScans the number of scans remaming
|
||||
*/
|
||||
synchronized void stopAfter(int remainingScans) {
|
||||
Thread thread = this.watchThread;
|
||||
if (thread != null) {
|
||||
this.remainingScans.set(remainingScans);
|
||||
try {
|
||||
thread.join();
|
||||
}
|
||||
catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
this.watchThread = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright 2012-2015 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.developertools.filewatch;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.boot.developertools.filewatch.ChangedFile.Type;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A snapshot of a folder at a given point in time.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class FolderSnapshot {
|
||||
|
||||
private static final Set<String> DOT_FOLDERS = Collections
|
||||
.unmodifiableSet(new HashSet<String>(Arrays.asList(".", "..")));
|
||||
|
||||
private final File folder;
|
||||
|
||||
private final Date time;
|
||||
|
||||
private Set<FileSnapshot> files;
|
||||
|
||||
/**
|
||||
* Create a new {@link FolderSnapshot} for the given folder.
|
||||
* @param folder the source folder
|
||||
*/
|
||||
public FolderSnapshot(File folder) {
|
||||
Assert.notNull(folder, "Folder must not be null");
|
||||
Assert.isTrue(folder.isDirectory(), "Folder must not be a file");
|
||||
this.folder = folder;
|
||||
this.time = new Date();
|
||||
Set<FileSnapshot> files = new LinkedHashSet<FileSnapshot>();
|
||||
collectFiles(folder, files);
|
||||
this.files = Collections.unmodifiableSet(files);
|
||||
}
|
||||
|
||||
private void collectFiles(File source, Set<FileSnapshot> result) {
|
||||
File[] children = source.listFiles();
|
||||
if (children != null) {
|
||||
for (File child : children) {
|
||||
if (child.isDirectory() && !DOT_FOLDERS.contains(child.getName())) {
|
||||
collectFiles(child, result);
|
||||
}
|
||||
else if (child.isFile()) {
|
||||
result.add(new FileSnapshot(child));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ChangedFiles getChangedFiles(FolderSnapshot snapshot) {
|
||||
Assert.notNull(snapshot, "Snapshot must not be null");
|
||||
File folder = this.folder;
|
||||
Assert.isTrue(snapshot.folder.equals(folder), "Snapshot source folder must be '"
|
||||
+ folder + "'");
|
||||
Set<ChangedFile> changes = new LinkedHashSet<ChangedFile>();
|
||||
Map<File, FileSnapshot> previousFiles = getFilesMap();
|
||||
for (FileSnapshot currentFile : snapshot.files) {
|
||||
FileSnapshot previousFile = previousFiles.remove(currentFile.getFile());
|
||||
if (previousFile == null) {
|
||||
changes.add(new ChangedFile(folder, currentFile.getFile(), Type.ADD));
|
||||
}
|
||||
else if (!previousFile.equals(currentFile)) {
|
||||
changes.add(new ChangedFile(folder, currentFile.getFile(), Type.MODIFY));
|
||||
}
|
||||
}
|
||||
for (FileSnapshot previousFile : previousFiles.values()) {
|
||||
changes.add(new ChangedFile(folder, previousFile.getFile(), Type.DELETE));
|
||||
}
|
||||
return new ChangedFiles(folder, changes);
|
||||
}
|
||||
|
||||
private Map<File, FileSnapshot> getFilesMap() {
|
||||
Map<File, FileSnapshot> files = new LinkedHashMap<File, FileSnapshot>();
|
||||
for (FileSnapshot file : this.files) {
|
||||
files.put(file.getFile(), file);
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (obj instanceof FolderSnapshot) {
|
||||
FolderSnapshot other = (FolderSnapshot) obj;
|
||||
return this.folder.equals(other.folder) && this.files.equals(other.files);
|
||||
}
|
||||
return super.equals(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hashCode = this.folder.hashCode();
|
||||
hashCode = 31 * hashCode + this.files.hashCode();
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the source folder of this snapshot.
|
||||
* @return the source folder
|
||||
*/
|
||||
public File getFolder() {
|
||||
return this.folder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.folder + " snaphost at " + this.time;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright 2012-2015 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class to watch the local filesystem for changes.
|
||||
*/
|
||||
package org.springframework.boot.developertools.filewatch;
|
||||
|
||||
Reference in New Issue
Block a user