-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #22 from vania-pooh/master
Added ResourcesWatcher implementation
- Loading branch information
Showing
6 changed files
with
376 additions
and
49 deletions.
There are no files selected for viewing
165 changes: 165 additions & 0 deletions
165
stecker-plugin-loader/src/main/java/ru/meridor/stecker/ResourcesWatcher.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
package ru.meridor.stecker; | ||
|
||
import ru.meridor.stecker.impl.PluginUtils; | ||
import ru.meridor.stecker.interfaces.ResourceChangedHandler; | ||
|
||
import java.io.Closeable; | ||
import java.io.IOException; | ||
import java.nio.file.FileSystems; | ||
import java.nio.file.Files; | ||
import java.nio.file.Path; | ||
import java.nio.file.WatchKey; | ||
import java.nio.file.WatchService; | ||
import java.nio.file.attribute.FileTime; | ||
import java.util.ArrayList; | ||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.concurrent.CountDownLatch; | ||
import java.util.stream.Collectors; | ||
|
||
import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY; | ||
|
||
/** | ||
* Allows you to watch for resource files changes | ||
*/ | ||
public class ResourcesWatcher implements Closeable { | ||
|
||
private volatile List<ResourceChangedHandler> handlers = new ArrayList<>(); | ||
|
||
private volatile boolean isStarted; | ||
|
||
private Thread watcherThread; | ||
|
||
public ResourcesWatcher(List<Path> resources) { | ||
this.watcherThread = getWatcherThread(resources); | ||
} | ||
|
||
private Thread getWatcherThread(List<Path> resources) { | ||
return isMacOs() ? getScanningWatcherThread(resources) : getWatchServiceWatcherThread(resources); | ||
} | ||
|
||
private static boolean isMacOs() { | ||
//WatchService doesn't work correctly on MacOS | ||
return System.getProperty("os.name").toLowerCase().contains("mac"); | ||
} | ||
|
||
private Thread getWatchServiceWatcherThread(List<Path> resources) { | ||
return new Thread() { | ||
@Override | ||
public void run() { | ||
super.run(); | ||
try (WatchService watcher = FileSystems.getDefault().newWatchService()) { | ||
final List<Path> alreadyRegisteredDirectories = new ArrayList<>(); | ||
List<Path> regularFilesAndDirectories = resources.stream() | ||
.filter(r -> Files.isDirectory(r) || Files.isRegularFile(r)) | ||
.collect(Collectors.toList()); | ||
for (Path resource : regularFilesAndDirectories) { | ||
Path pathToRegister = resource; | ||
if (Files.isRegularFile(resource)) { | ||
pathToRegister = resource.getParent(); | ||
} | ||
if (!alreadyRegisteredDirectories.contains(pathToRegister)) { | ||
pathToRegister.register(watcher, ENTRY_MODIFY); | ||
alreadyRegisteredDirectories.add(pathToRegister); | ||
} | ||
} | ||
while (true) { | ||
WatchKey key; | ||
try { | ||
key = watcher.take(); | ||
} catch (InterruptedException e) { | ||
break; | ||
} | ||
Path directory = (Path) key.watchable(); | ||
key.pollEvents() | ||
.stream() | ||
.filter(event -> ENTRY_MODIFY == event.kind()) | ||
.forEach(event -> { | ||
for (ResourceChangedHandler handler : handlers) { | ||
Path filePath = directory.resolve((Path) event.context()); | ||
handler.onResourceChanged(filePath); | ||
} | ||
}); | ||
if (!key.reset()){ | ||
break; | ||
} | ||
} | ||
} catch (IOException e) { | ||
throw new IllegalStateException(e); | ||
} | ||
} | ||
}; | ||
} | ||
|
||
private Thread getScanningWatcherThread(List<Path> resources) { | ||
final long SCAN_DELAY = 1000; | ||
return new Thread(){ | ||
@Override | ||
public void run() { | ||
super.run(); | ||
try { | ||
Map<Path, FileTime> lastModifiedTimes = new HashMap<>(); | ||
for (Path resource : resources) { | ||
lastModifiedTimes.put(resource, PluginUtils.getLastModificationTime(resource)); | ||
} | ||
|
||
while (true) { | ||
try { | ||
for (Path resource : resources) { | ||
FileTime previousLastModifiedTime = lastModifiedTimes.get(resource); | ||
FileTime currentLastModifiedTime = PluginUtils.getLastModificationTime(resource); | ||
if (currentLastModifiedTime.compareTo(previousLastModifiedTime) > 0) { | ||
for (ResourceChangedHandler handler : handlers) { | ||
handler.onResourceChanged(resource); | ||
} | ||
} | ||
} | ||
sleep(SCAN_DELAY); | ||
} catch (InterruptedException e) { | ||
break; | ||
} | ||
} | ||
|
||
} catch (IOException e) { | ||
throw new IllegalStateException(e); | ||
} | ||
} | ||
}; | ||
} | ||
|
||
public void addChangedHandler(ResourceChangedHandler handler) { | ||
handlers.add(handler); | ||
} | ||
|
||
public void start() { | ||
watcherThread.start(); | ||
isStarted = true; | ||
} | ||
|
||
public void stop() { | ||
watcherThread.interrupt(); | ||
} | ||
|
||
public void await(Path resource) throws InterruptedException { | ||
if (!isStarted) { | ||
start(); | ||
} | ||
final CountDownLatch countDownLatch = new CountDownLatch(1); | ||
addChangedHandler(r -> { | ||
if (resource == null || r.equals(resource)) { | ||
countDownLatch.countDown(); | ||
} | ||
}); | ||
countDownLatch.await(); | ||
} | ||
|
||
public void await() throws InterruptedException { | ||
await(null); | ||
} | ||
|
||
@Override | ||
public void close() throws IOException { | ||
stop(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
9 changes: 9 additions & 0 deletions
9
...ker-plugin-loader/src/main/java/ru/meridor/stecker/interfaces/ResourceChangedHandler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package ru.meridor.stecker.interfaces; | ||
|
||
import java.nio.file.Path; | ||
|
||
public interface ResourceChangedHandler { | ||
|
||
void onResourceChanged(Path resource); | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.