Skip to content
This repository was archived by the owner on Jul 31, 2022. It is now read-only.

Commit e162cc8

Browse files
authored
Merge pull request #62 from Ktt-Development/watchload-fix@193c9ea
Fixed #59 - Add/remove watch service on folder add/remove
2 parents 193c9ea + d04ec6d commit e162cc8

File tree

3 files changed

+86
-86
lines changed

3 files changed

+86
-86
lines changed

src/main/java/com/kttdevelopment/simplehttpserver/handler/DirectoryEntry.java

+83-83
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
package com.kttdevelopment.simplehttpserver.handler;
22

3-
import java.io.File;
4-
import java.io.IOException;
3+
import java.io.*;
54
import java.nio.file.*;
65
import java.util.*;
76
import java.util.concurrent.ConcurrentHashMap;
7+
import java.util.function.Consumer;
88

99
import static java.nio.file.StandardWatchEventKinds.*;
1010

@@ -14,7 +14,7 @@
1414
* @see FileHandler
1515
* @see FileEntry
1616
* @since 02.00.00
17-
* @version 03.05.00
17+
* @version 03.05.01
1818
* @author Ktt Development
1919
*/
2020
class DirectoryEntry {
@@ -25,6 +25,7 @@ class DirectoryEntry {
2525
private final boolean isWalkthrough;
2626

2727
private final Map<String,FileEntry> preloadedFiles = new ConcurrentHashMap<>(); // preload/watchload only
28+
private final Path directoryPath;
2829

2930
/**
3031
* Create a directory entry.
@@ -33,7 +34,7 @@ class DirectoryEntry {
3334
* @param adapter how to process the bytes in {@link #getBytes(String)}
3435
* @param loadingOption how to handle the initial file loading
3536
* @param isWalkthrough whether to use sub-directories or not
36-
* @throws RuntimeException failure to walk through directory or failure to start watch service
37+
* @throws UncheckedIOException failure to walk through directory or failure to start watch service
3738
*
3839
* @see FileBytesAdapter
3940
* @see ByteLoadingOption
@@ -46,114 +47,56 @@ class DirectoryEntry {
4647
this.loadingOption = loadingOption;
4748
this.isWalkthrough = isWalkthrough;
4849

50+
directoryPath = directory.toPath();
51+
4952
if(loadingOption == ByteLoadingOption.WATCHLOAD){
50-
{
53+
/* load top level directory */ {
5154
final File[] listFiles = Objects.requireNonNullElse(directory.listFiles(),new File[0]);
52-
for(final File file : listFiles)
53-
if(!file.isDirectory())
55+
for(final File file : listFiles) // initial population
56+
if(file.isFile())
5457
try{
5558
preloadedFiles.put(
5659
getContext(adapter.getName(file)),
5760
new FileEntry(file, adapter, ByteLoadingOption.WATCHLOAD, true)
5861
);
5962
}catch(final RuntimeException ignored){ }
60-
try{
61-
final WatchService service = FileSystems.getDefault().newWatchService();
62-
final Path path = directory.toPath();
63-
path.register(service, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
64-
65-
new Thread(() -> {
66-
WatchKey key;
67-
try{
68-
while((key = service.take()) != null){
69-
for(WatchEvent<?> event : key.pollEvents()){
70-
try{
71-
final Path target = (Path) event.context();
72-
final File file = target.toFile();
73-
final WatchEvent.Kind<?> type = event.kind();
74-
75-
final String context = getContext(adapter.getName(file));
76-
77-
if(type == ENTRY_CREATE)
78-
preloadedFiles.put(
79-
context,
80-
new FileEntry(file, adapter, loadingOption,true)
81-
);
82-
else if(type == ENTRY_DELETE)
83-
preloadedFiles.remove(context);
84-
else if(type == ENTRY_MODIFY)
85-
preloadedFiles.get(context).reloadBytes();
86-
}catch(final ClassCastException ignored){ }
87-
}
88-
}
89-
}catch(final InterruptedException ignored){ }
90-
}).start();
91-
63+
try{ // create watch service for top level directory
64+
createWatchService(directoryPath, createWatchServiceConsumer());
9265
}catch(final IOException e){
93-
throw new RuntimeException(e);
66+
throw new UncheckedIOException(e);
9467
}
9568
}
96-
if(isWalkthrough){
97-
final Path dirPath = directory.toPath();
69+
if(isWalkthrough){ /* load sub directories */
9870
try{
99-
Files.walk(dirPath).filter(path -> path.toFile().isDirectory()).forEach(path -> {
71+
Files.walk(directoryPath).filter(path -> path.toFile().isDirectory()).forEach(path -> {
10072
final File p2f = path.toFile();
101-
final String rel = dirPath.relativize(path).toString();
73+
final String rel = directoryPath.relativize(path).toString();
10274

10375
final File[] listFile = Objects.requireNonNullElse(p2f.listFiles(),new File[0]);
104-
for(final File file : listFile)
76+
for(final File file : listFile) // initial population
10577
try{
10678
preloadedFiles.put(
10779
(rel.isEmpty() || rel.equals("/") || rel.equals("\\") ? "" : getContext(rel)) + getContext(adapter.getName(file)),
10880
new FileEntry(file,adapter,loadingOption,true)
10981
);
11082
}catch(final RuntimeException ignored){ }
11183

112-
// watch service
84+
// create watch service for directory
11385
try{
114-
final WatchService service = FileSystems.getDefault().newWatchService();
115-
path.register(service, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
116-
final String relative = getContext(dirPath.relativize(path).toString());
117-
118-
new Thread(() -> {
119-
WatchKey key;
120-
try{
121-
while((key = service.take()) != null){
122-
for(WatchEvent<?> event : key.pollEvents()){
123-
try{
124-
final Path target = dirPath.resolve((Path) event.context());
125-
final File targFile = target.toFile();
126-
final WatchEvent.Kind<?> type = event.kind();
127-
128-
final String context = relative + getContext(adapter.getName(targFile));
129-
130-
if(type == ENTRY_CREATE)
131-
preloadedFiles.put(
132-
context,
133-
new FileEntry(targFile,adapter,loadingOption,true)
134-
);
135-
else if(type == ENTRY_DELETE)
136-
preloadedFiles.remove(context);
137-
else if(type == ENTRY_MODIFY)
138-
preloadedFiles.get(context).reloadBytes();
139-
}catch(final ClassCastException ignored){ }
140-
}
141-
key.reset();
142-
}
143-
}catch(final InterruptedException ignored){ }
144-
}).start();
86+
createWatchService(path, createWatchServiceConsumer());
14587
}catch(final IOException e){
146-
throw new RuntimeException(e);
88+
throw new UncheckedIOException(e);
14789
}
90+
14891
});
14992
}catch(final IOException e){
15093
throw new RuntimeException(e);
15194
}
15295
}
15396
}else if(loadingOption == ByteLoadingOption.PRELOAD){
154-
{
97+
/* load top level directory */ {
15598
final File[] listFiles = Objects.requireNonNullElse(directory.listFiles(),new File[0]);
156-
for(final File file : listFiles)
99+
for(final File file : listFiles) // initial population
157100
if(!file.isDirectory())
158101
try{
159102
preloadedFiles.put(
@@ -171,21 +114,78 @@ else if(type == ENTRY_MODIFY)
171114
final String rel = dirPath.relativize(path).toString();
172115

173116
final File[] listFiles = Objects.requireNonNullElse(p2f.listFiles(), new File[0]);
174-
for(final File file : listFiles)
117+
for(final File file : listFiles) // populate sub files
175118
try{
176119
preloadedFiles.put(
177120
(rel.isEmpty() || rel.equals("/") || rel.equals("\\") ? "" : getContext(rel)) + getContext(adapter.getName(file)),
178-
new FileEntry(file, adapter, loadingOption)
121+
new FileEntry(file, adapter, ByteLoadingOption.PRELOAD)
179122
);
180123
}catch(final RuntimeException ignored){ }
181124
});
182125
}catch(final IOException e){
183-
throw new RuntimeException(e);
126+
throw new UncheckedIOException(e);
184127
}
185128
}
186129
}
187130
}
188131

132+
133+
private final Map<Path,Thread> watchService = new ConcurrentHashMap<>();
134+
135+
private void createWatchService(final Path path, final Consumer<WatchEvent<?>> consumer) throws IOException{
136+
final WatchService service = FileSystems.getDefault().newWatchService();
137+
path.register(service,ENTRY_CREATE,ENTRY_DELETE,ENTRY_MODIFY);
138+
139+
final Thread th =
140+
new Thread(() -> {
141+
WatchKey key;
142+
try{
143+
while((key = service.take()) != null){
144+
for(WatchEvent<?> event : key.pollEvents()){
145+
consumer.accept(event);
146+
key.reset();
147+
}
148+
}
149+
}catch(final InterruptedException ignored){ }
150+
});
151+
152+
watchService.put(path,th);
153+
}
154+
155+
@SuppressWarnings("StatementWithEmptyBody")
156+
private Consumer<WatchEvent<?>> createWatchServiceConsumer(){
157+
158+
return (WatchEvent<?> event) -> {
159+
try{
160+
final Path target = (Path) event.context();
161+
final File file = target.toFile();
162+
final WatchEvent.Kind<?> type = event.kind();
163+
164+
final String relative = getContext(directoryPath.relativize(target).toString());
165+
final String context = relative + getContext(adapter.getName(file));
166+
167+
if(file.isFile())
168+
if(type == ENTRY_CREATE)
169+
preloadedFiles.put(context, new FileEntry(file,adapter,ByteLoadingOption.WATCHLOAD,true));
170+
else if(type == ENTRY_DELETE)
171+
preloadedFiles.remove(context);
172+
else if(type == ENTRY_MODIFY)
173+
preloadedFiles.get(context).reloadBytes();
174+
else; // prevent ambiguous else with below
175+
else
176+
if(type == ENTRY_CREATE)
177+
try{
178+
createWatchService(target, createWatchServiceConsumer());
179+
}catch(final IOException ignored){ }
180+
else if(type == ENTRY_DELETE){
181+
watchService.get(target).stop();
182+
watchService.remove(target);
183+
}
184+
185+
}catch(final ClassCastException ignored){ }
186+
};
187+
}
188+
189189
//
190190

191191
/**

src/main/java/com/kttdevelopment/simplehttpserver/handler/FileEntry.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
* @see ByteLoadingOption
1111
* @see FileHandler
1212
* @since 02.00.00
13-
* @version 03.05.00
13+
* @version 03.05.01
1414
* @author Ktt Development
1515
*/
1616
class FileEntry {
@@ -75,7 +75,7 @@ class FileEntry {
7575
try{
7676
final Path modified = path.resolve((Path) event.context());
7777
try{
78-
if(Files.isSameFile(target, modified))
78+
if(!modified.toFile().isDirectory() && Files.isSameFile(target, modified))
7979
preloadedBytes = adapter.getBytes(file,Files.readAllBytes(target));
8080
}catch(final IOException ignored){ } // don't overwrite if corrupt
8181
}catch(final ClassCastException ignored){ }

src/main/java/com/kttdevelopment/simplehttpserver/handler/FileHandler.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -625,7 +625,7 @@ public final void addDirectory(final String context, final File directory, final
625625
target.isEmpty() ? "/" : target,
626626
new DirectoryEntry(directory, adapter,loadingOption,walk)
627627
);
628-
}catch(final Exception ignored){}
628+
}catch(final UncheckedIOException ignored){}
629629
}
630630

631631
//

0 commit comments

Comments
 (0)