11package com .kttdevelopment .simplehttpserver .handler ;
22
3- import java .io .* ;
4- import java .nio . file . Files ;
5- import java .nio .file .Path ;
3+ import java .io .File ;
4+ import java .io . IOException ;
5+ import java .nio .file .* ;
66import java .util .*;
77import java .util .concurrent .ConcurrentHashMap ;
88
9+ import static java .nio .file .StandardWatchEventKinds .*;
10+
911/**
1012 * Represents a directory in the {@link FileHandler}. Applications do not use this class.
1113 *
@@ -22,79 +24,176 @@ class DirectoryEntry {
2224 private final ByteLoadingOption loadingOption ;
2325 private final boolean isWalkthrough ;
2426
25- private final Map <String ,FileEntry > files = new ConcurrentHashMap <>(); // preload/watchload only
26-
27+ private final Map <String ,FileEntry > preloadedFiles = new ConcurrentHashMap <>(); // preload/watchload only
2728
28- DirectoryEntry (final File directory , final FileHandlerAdapter adapter , final ByteLoadingOption loadingOption , final boolean isWalkthrough ) throws IOException {
29+ /**
30+ * Create a directory entry.
31+ *
32+ * @param directory directory to represent
33+ * @param adapter how to process the bytes in {@link #getBytes(String)}
34+ * @param loadingOption how to handle the initial file loading
35+ * @param isWalkthrough whether to use sub-directories or not
36+ * @throws RuntimeException failure to walk through directory or failure to start watch service
37+ *
38+ * @see FileBytesAdapter
39+ * @see ByteLoadingOption
40+ * @since 03.05.00
41+ * @author Ktt Development
42+ */
43+ DirectoryEntry (final File directory , final FileHandlerAdapter adapter , final ByteLoadingOption loadingOption , final boolean isWalkthrough ){
2944 this .directory = directory ;
3045 this .adapter = adapter ;
3146 this .loadingOption = loadingOption ;
3247 this .isWalkthrough = isWalkthrough ;
3348
3449 if (loadingOption == ByteLoadingOption .WATCHLOAD ){
50+ {
51+ final File [] listFiles = Objects .requireNonNullElse (directory .listFiles (),new File [0 ]);
52+ for (final File file : listFiles )
53+ if (!file .isDirectory ())
54+ try {
55+ preloadedFiles .put (
56+ getContext (adapter .getName (file )),
57+ new FileEntry (file , adapter , ByteLoadingOption .WATCHLOAD , true )
58+ );
59+ }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+
92+ }catch (final IOException e ){
93+ throw new RuntimeException (e );
94+ }
95+ }
96+ if (isWalkthrough ){
97+ final Path dirPath = directory .toPath ();
98+ try {
99+ Files .walk (dirPath ).filter (path -> path .toFile ().isDirectory ()).forEach (path -> {
100+ final File p2f = path .toFile ();
101+ final String rel = dirPath .relativize (path ).toString ();
102+
103+ final File [] listFile = Objects .requireNonNullElse (p2f .listFiles (),new File [0 ]);
104+ for (final File file : listFile ){
105+ try {
106+ preloadedFiles .put (
107+ (rel .isEmpty () || rel .equals ("/" ) || rel .equals ("\\ " ) ? "" : getContext (rel )) + getContext (adapter .getName (file )),
108+ new FileEntry (file ,adapter ,loadingOption ,true )
109+ );
110+ }catch (final RuntimeException ignored ){ }
35111
112+ // watch service
113+ try {
114+ final WatchService service = FileSystems .getDefault ().newWatchService ();
115+ final Path dpath = file .toPath ();
116+ dpath .register (service , ENTRY_CREATE , ENTRY_DELETE , ENTRY_MODIFY );
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 = (Path ) event .context ();
125+ final File targFile = target .toFile ();
126+ final WatchEvent .Kind <?> type = event .kind ();
127+
128+ final String context = 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+ }
142+ }catch (final InterruptedException ignored ){ }
143+ }).start ();
144+ }catch (final IOException e ){
145+ throw new RuntimeException (e );
146+ }
147+ }
148+ });
149+ }catch (final IOException e ){
150+ throw new RuntimeException (e );
151+ }
152+ }
36153 }else if (loadingOption == ByteLoadingOption .PRELOAD ){
37- if (!isWalkthrough ){
38- final File [] listFiles = Objects .requireNonNullElse (directory .listFiles (),new File [0 ]);
39- for (final File file : listFiles )
40- if (!file .isDirectory ())
41- files .put (
154+ {
155+ final File [] listFiles = Objects .requireNonNullElse (directory .listFiles (),new File [0 ]);
156+ for (final File file : listFiles )
157+ if (!file .isDirectory ())
158+ try {
159+ preloadedFiles .put (
42160 getContext (adapter .getName (file )),
43- new FileEntry (file ,adapter ,ByteLoadingOption .PRELOAD )
161+ new FileEntry (file , adapter , ByteLoadingOption .PRELOAD )
44162 );
45- }else {
46- final Path dirPath = directory .toPath ();
163+ }catch (final RuntimeException ignored ){ }
164+ }
165+ if (isWalkthrough ){
166+ final Path dirPath = directory .toPath ();
47167
168+ try {
48169 Files .walk (dirPath ).filter (path -> path .toFile ().isDirectory ()).forEach (path -> {
49170 final File p2f = path .toFile ();
50171 final String rel = dirPath .relativize (path ).toString ();
51172
52173 final File [] listFiles = Objects .requireNonNullElse (p2f .listFiles (), new File [0 ]);
53174 for (final File file : listFiles )
54- files .put (
55- (rel .isEmpty () || rel .equals ("/" ) || rel .equals ("\\ " ) ? "" : getContext (rel )) + getContext (adapter .getName (file )),
56- new FileEntry (file ,adapter ,loadingOption )
57- );
175+ try {
176+ preloadedFiles .put (
177+ (rel .isEmpty () || rel .equals ("/" ) || rel .equals ("\\ " ) ? "" : getContext (rel )) + getContext (adapter .getName (file )),
178+ new FileEntry (file , adapter , loadingOption )
179+ );
180+ }catch (final RuntimeException ignored ){ }
58181 });
182+ }catch (final IOException e ){
183+ throw new RuntimeException (e );
59184 }
185+ }
60186 }
61187 }
62188
63- //
64-
65- /**
66- * Returns if the directory uses inner folders and files
67- *
68- * @return if directory uses inner folders and files
69- *
70- * @since 02.00.00
71- * @author Ktt Development
72- */
73- public final boolean isWalkthrough (){
74- return isWalkthrough ;
75- }
76-
77- /**
78- * Returns if the files were preloaded.
79- *
80- * @return if the files were preloaded
81- *
82- * @since 02.00.00
83- * @author Ktt Development
84- */
85- @ Deprecated
86- public final boolean isFilesPreloaded (){
87- return loadingOption != ByteLoadingOption .LIVELOAD ;
88- }
89-
90189//
91190
92191 /**
93192 * Returns the directory being referenced
94193 *
95194 * @return referenced directory
96195 *
97- * @see #getFiles ()
196+ * @see #getPreloadedFiles ()
98197 * @see #getFile(String)
99198 * @since 02.00.00
100199 * @author Ktt Development
@@ -113,8 +212,8 @@ public final File getDirectory(){
113212 * @since 02.00.00
114213 * @author Ktt Development
115214 */
116- public Map <String , FileEntry > getFiles (){
117- return files ;
215+ public Map <String , FileEntry > getPreloadedFiles (){
216+ return Collections . unmodifiableMap ( preloadedFiles ) ;
118217 }
119218
120219 /**
@@ -124,18 +223,18 @@ public Map<String, FileEntry> getFiles(){
124223 * @return file associated with that context
125224 *
126225 * @see #getDirectory()
127- * @see #getFiles ()
226+ * @see #getPreloadedFiles ()
128227 * @since 02.00.00
129228 * @author Ktt Development
130229 */
131230 public final File getFile (final String path ){
132231 final String rel = getContext (path );
133232 if (loadingOption != ByteLoadingOption .LIVELOAD ){
134233 String match = "" ;
135- for (final String key : files .keySet ())
234+ for (final String key : preloadedFiles .keySet ())
136235 if (rel .startsWith (key ) && key .startsWith (match ))
137236 match = key ;
138- return !match .isEmpty () ? files .get (match ).getFile () : null ;
237+ return !match .isEmpty () ? preloadedFiles .get (match ).getFile () : null ;
139238 }else {
140239 if (isWalkthrough ){
141240 final File parent = new File (directory .getAbsolutePath () + path ).getParentFile ();
@@ -171,11 +270,11 @@ public final byte[] getBytes(final String path){
171270 final String rel = getContext (path );
172271 if (loadingOption != ByteLoadingOption .LIVELOAD ){
173272 String match = "" ;
174- for (final String key : files .keySet ())
273+ for (final String key : preloadedFiles .keySet ())
175274 if (rel .startsWith (key ) && key .startsWith (match ))
176275 match = key ;
177276 if (!match .isEmpty ()){
178- return files .get (match ).getBytes ();
277+ return preloadedFiles .get (match ).getBytes ();
179278 }else {
180279 return null ;
181280 }
@@ -199,8 +298,20 @@ public final byte[] getBytes(final String path){
199298 * @author Ktt Development
200299 */
201300 public final ByteLoadingOption getLoadingOption (){
202- return loadingOption ;
203- }
301+ return loadingOption ;
302+ }
303+
304+ /**
305+ * Returns if the directory uses inner folders and files
306+ *
307+ * @return if directory uses inner folders and files
308+ *
309+ * @since 02.00.00
310+ * @author Ktt Development
311+ */
312+ public final boolean isWalkthrough (){
313+ return isWalkthrough ;
314+ }
204315
205316//
206317
@@ -217,13 +328,13 @@ private static String getContext(final String path){
217328 @ Override
218329 public String toString (){
219330 return
220- "DirectoryEntry" + '{' +
221- "directory" + '=' + directory + ", " +
222- "adapter" + '=' + adapter + ", " +
223- "loadingOption" + '=' + loadingOption + ", " +
224- "isWalkthrough" + '=' + isWalkthrough + ", " +
225- "files" + '=' + files +
226- '}' ;
331+ "DirectoryEntry" + '{' +
332+ "directory" + '=' + directory + ", " +
333+ "adapter" + '=' + adapter + ", " +
334+ "loadingOption" + '=' + loadingOption + ", " +
335+ "isWalkthrough" + '=' + isWalkthrough + ", " +
336+ "files" + '=' + preloadedFiles +
337+ '}' ;
227338 }
228339
229340}
0 commit comments