2727import java .time .ZoneOffset ;
2828import java .time .format .DateTimeFormatter ;
2929import java .util .List ;
30+ import java .util .Optional ;
3031import java .util .concurrent .ConcurrentHashMap ;
3132import java .util .concurrent .ConcurrentMap ;
3233import java .util .concurrent .ExecutionException ;
@@ -114,7 +115,7 @@ public void scanAllDirectories() {
114115 Future <List <File >> futureFiles = scanDirectoryAsync (dir .getPath ());
115116 activeScanTasks .put (dir .getPath (), new ScanTaskState (futureFiles , scan ));
116117 } else { // If not, scan only the top-level files
117- // TODO: Handle non subdirectory scans in a nice manner :D
118+ scanTopLevelDirectory ( scan );
118119 }
119120
120121 }
@@ -123,14 +124,75 @@ public void scanAllDirectories() {
123124 }
124125 }
125126
127+ @ Override
128+ public void scanSingleDirectory (MonitoredDirectory dir ) {
129+ if (!dir .getIsActive ()) {
130+ log .warn ("Monitored directory {} is not active. Skipping scan." , dir .getPath ());
131+ return ;
132+ }
133+
134+ Scan scan = new Scan ();
135+ scan .setMonitoredDirectory (dir );
136+ scan .setScanTime (OffsetDateTime .now ());
137+ scan .setStatus (ScanStatus .IN_PROGRESS .toString ());
138+
139+ repository .save (scan );
140+
141+ // If scan should include monitored directory subdirectories
142+ if (dir .getIncludeSubdirectories ()) {
143+ // Add the scan to active tasks for monitoring
144+ Future <List <File >> futureFiles = scanDirectoryAsync (dir .getPath ());
145+ activeScanTasks .put (dir .getPath (), new ScanTaskState (futureFiles , scan ));
146+ } else { // If not, scan only the top-level files
147+ scanTopLevelDirectory (scan );
148+ }
149+ }
150+
126151 /**
127- * Asynchronously scans a directory and its subdirectories( if specified)to retrieve a list of files.
152+ * Scans the top-level directory for files and processes them.
153+ * <p>
154+ * This method retrieves the top-level files in the monitored directory associated
155+ * with the given scan instance. If files are found, it processes them and finalizes
156+ * the scan task. If no files are found, the scan is marked as failed. Any exceptions
157+ * during the process are logged.
128158 *
129- * @param directoryPath the path of the directory to scan
159+ * @param scanInstance the scan instance associated with the directory to be scanned
160+ */
161+ @ Async
162+ private void scanTopLevelDirectory (Scan scanInstance ) {
163+ final MonitoredDirectory mDirectory = scanInstance .getMonitoredDirectory ();
164+ final File file ;
165+
166+ try {
167+ file = new File (mDirectory .getPath ());
168+ Optional <List <File >> topLevelFiles = directoryTraverser .collectTopLevelFiles (file );
169+ if (topLevelFiles .isPresent ()) {
170+ log .info ("Found {} top-level files in directory: {}" , topLevelFiles .get ().size (), mDirectory .getPath ());
171+ if (finalizeScanTask (scanInstance , topLevelFiles .get ())) {
172+ log .info ("Scan top-level completed successfully for directory: {}" , mDirectory .getPath ());
173+ } else {
174+ log .warn ("Scan top-level was not completed successfully for directory: {}" , mDirectory .getPath ());
175+ }
176+ } else {
177+ log .warn ("No top-level files found in directory: {}" , mDirectory .getPath ());
178+ scanInstance .setStatus (ScanStatus .FAILED .toString ());
179+ repository .save (scanInstance );
180+ }
181+ } catch (ExecutionException e ) {
182+ log .error ("ExecutionException while scanning top-level directory {}: {}" , mDirectory .getPath (), e .getMessage ());
183+ } catch (InterruptedException e ) {
184+ log .error ("InterruptedException while scanning top-level directory {}: {}" , mDirectory .getPath (), e .getMessage ());
185+ }// Shouldn't reach here under normal execution
186+ }
187+
188+ /**
189+ * Asynchronously scans a directory and its subdirectories to retrieve a list of files.
190+ *
191+ * @param directoryPath the path of the directory to scan
130192 * @return a Future containing the list of files found in the directory
131193 */
132194 @ Async
133- private Future <List <File >> scanDirectoryAsync (String directoryPath ) throws ExecutionException , InterruptedException {
195+ private Future <List <File >> scanDirectoryAsync (String directoryPath ) {
134196 return directoryTraverser .collectFilesInDirectory (directoryPath );
135197 }
136198
@@ -144,7 +206,7 @@ private Future<List<File>> scanDirectoryAsync(String directoryPath) throws Execu
144206 * If a scan is still in progress, it logs the status.
145207 */
146208 @ Scheduled (fixedDelay = 5000 )
147- public void monitorAsyncScans () {
209+ public void monitorOngoingScanTasks () {
148210 if (activeScanTasks .isEmpty ()) {
149211 log .info ("No active scan tasks to process." );
150212 return ;
@@ -156,10 +218,17 @@ public void monitorAsyncScans() {
156218
157219 if (future .isDone ()) {
158220 log .info ("Traversing completed for directory: {}" , dirPath );
159- if (completeScanTask (task )) {
160- log .info ("Scan completed successfully for directory: {}" , dirPath );
161- } else {
162- log .warn ("Scan was not completed successfully for directory: {}" , dirPath );
221+ try {
222+ List <File > files = future .get (); // Non-blocking since we check if the future is done
223+ if (finalizeScanTask (task .scan (), files )) {
224+ log .info ("Scan recursive completed successfully for directory: {}" , dirPath );
225+ } else {
226+ log .warn ("Scan recursive was not completed successfully for directory: {}" , dirPath );
227+ }
228+ } catch (InterruptedException e ) {
229+ log .error ("Scan interrupted for directory {}: {}" , dirPath , e .getMessage ());
230+ } catch (ExecutionException e ) {
231+ log .error ("Execution exception while completing scan for directory {}: {}" , dirPath , e .getMessage ());
163232 }
164233 activeScanTasks .remove (dirPath );
165234 } else {
@@ -169,44 +238,40 @@ public void monitorAsyncScans() {
169238 }
170239
171240 /**
172- * Completes the scan process for a given scan task state .
241+ * Finalizes the scan task for a given scan instance and list of files .
173242 * <p>
174- * This method retrieves the list of files from the completed scan task and processes each file.
175- * If a stop request is detected, the scan is marked as cancelled. Otherwise, the scan is marked
176- * as completed, and the baseline is established for the monitored directory if it has not been set.
177- * <p>
178- * Any errors during processing are logged, and the scan is marked as failed. The updated scan
179- * status is saved to the repository at the end of the method. The task is removed from the active
180- * scan tasks map regardless of success or failure.
243+ * This method processes each file in the provided list, updates the scan status,
244+ * and establishes a baseline for the monitored directory if necessary. If a stop
245+ * request is detected, the scan is marked as cancelled. In case of errors during
246+ * processing, the scan is marked as failed. Regardless of the outcome, the task
247+ * is removed from the active scan tasks map.
181248 *
182- * @param scanTaskState the state of the scan task to process, containing the scan and its future result
249+ * @param scanInstance the scan instance associated with the task
250+ * @param files the list of files to process
183251 * @return true if the scan was successfully completed, false otherwise
184252 */
185253 @ Async
186- private boolean completeScanTask (ScanTaskState scanTaskState ) {
187- String dirPath = scanTaskState .scan ().getMonitoredDirectory ().getPath ();
188- Scan scan = scanTaskState .scan ();
254+ private boolean finalizeScanTask (Scan scanInstance , List <File > files ) {
255+ String dirPath = scanInstance .getMonitoredDirectory ().getPath ();
189256
190257 try {
191258 // Retrieve the list of files from the completed scan
192- List <File > files = scanTaskState .future ().get (); // Non-blocking since the future is done.
193-
194259 for (File file : files ) {
195260 if (stopRequested ) {
196261 break ;// Exit if stop is requested
197262 }
198263 // Process each file found in the directory and its subdirectories
199- processFile (file , scanTaskState . scan () );
264+ processFile (file , scanInstance );
200265 }
201266
202267 if (stopRequested ) {
203268 // Mark the scan as cancelled if a stop was requested
204- scan .setStatus (ScanStatus .CANCELLED .toString ());
205- repository .save (scan );
269+ scanInstance .setStatus (ScanStatus .CANCELLED .toString ());
270+ repository .save (scanInstance );
206271 return false ; // Scan Stopped / Not complete (no baseline)
207272 } else {
208- MonitoredDirectory dir = scan .getMonitoredDirectory ();
209- scan .setStatus (ScanStatus .COMPLETED .toString ());
273+ MonitoredDirectory dir = scanInstance .getMonitoredDirectory ();
274+ scanInstance .setStatus (ScanStatus .COMPLETED .toString ());
210275
211276 // If the Baseline has not yet been established and the Scan was Successful
212277 if (!dir .getBaselineEstablished ()) {
@@ -217,23 +282,25 @@ private boolean completeScanTask(ScanTaskState scanTaskState) {
217282 log .info ("Baseline already established for directory: {}" , dirPath );
218283 }
219284 log .info ("Completed scan for directory {}" , dirPath );
220- repository .save (scan );
285+ repository .save (scanInstance );
221286
222287 return true ; // Successful Scan
223288 }
224289
225290 } catch (Exception e ) {
226291 // Handle errors during scan processing
227292 log .error ("Scan Failed {} - Start Time {} - End Time {}" , e .getMessage (),
228- scan .getScanTime ().format (timeAndDateStringForLogFormat ),
293+ scanInstance .getScanTime ().format (timeAndDateStringForLogFormat ),
229294 OffsetDateTime .now ().format (timeAndDateStringForLogFormat ));
230295
231- scan .setStatus (ScanStatus .FAILED .toString ());
232- repository .save (scan );
296+ scanInstance .setStatus (ScanStatus .FAILED .toString ());
297+ repository .save (scanInstance );
233298 } finally {
234299 // Remove the task from active tasks regardless of success or failure
235- activeScanTasks .remove (dirPath );
236- log .info ("Removed scan task for directory: {}" , dirPath );
300+ if (activeScanTasks .containsKey (dirPath )) {
301+ activeScanTasks .remove (dirPath );
302+ log .info ("Removed scan task for directory: {}" , dirPath );
303+ }
237304 }
238305 return false ; // Shouldn't reach here under normal execution
239306 }
@@ -314,89 +381,4 @@ public void stopScan() {
314381 stopRequested = true ;
315382 log .info ("Scan stop requested. Will stop after current file processing." );
316383 }
317-
318- @ Override
319- public Boolean scanMonitoredDirectory (Scan scanInstance ) {
320- return scanMonitoredDirectory (scanInstance , true );
321- }
322-
323- @ Override
324- public Boolean scanMonitoredDirectory (Scan scanInstance , boolean includeSubFolders ) {
325- //
326- //final MonitoredDirectory mDirectory;
327- //
328- //mDirectory = scanInstance.getMonitoredDirectory();
329- //if (includeSubFolders) {
330- //
331- // // Regular scan
332- //
333- // if (completeScan(scanInstance)) {
334- //
335- // log.info("Scan Successful for Monitored Directory {}", mDirectory.getPath());
336- // scanInstance.setStatus(ScanStatus.COMPLETED.toString());
337- //
338- // // If the Baseline has not yet been established and the Scan was Successful
339- // if (!mDirectory.getBaselineEstablished()) {
340- // mDirectory.setBaselineEstablished(true);
341- // monitoredDirectoryService.save(mDirectory);
342- // }
343- //
344- // // Save scanInstance in the persistence layer
345- // this.repository.save(scanInstance);
346- // return true; // OK Scan :)
347- //
348- // } else {
349- //
350- // log.error("Scan failed for Monitored Directory {}", mDirectory.getPath());
351- //
352- // // An added Note that is saved to the persistence layer
353- // mDirectory.setNotes(ScanStatus.FAILED.toString());
354- // monitoredDirectoryService.save(mDirectory);
355- // this.repository.save(scanInstance);
356- // return false;
357- // }
358- //
359- //}
360- //
361- //// No Subfolders
362- //else {
363- //
364- // final File file;
365- //
366- // try {
367- //
368- // file = new File(mDirectory.getPath());
369- // log.debug("Created new entity - scanMonitoredDirectory - In No Subfolders If Block - Path {}",
370- // file.getAbsolutePath());
371- // Optional<List<File>> topLevelFiles = directoryTraverser.collectTopLevelFiles(file);
372- //
373- // if (topLevelFiles.isPresent()) {
374- //
375- // topLevelFiles.get().stream().forEach(tp -> processFile(tp, scanInstance));
376- //
377- // scanInstance.setStatus(ScanStatus.COMPLETED.toString());
378- // // Save scanInstance in the persistence layer
379- // this.repository.save(scanInstance);
380- // return true; // OK Scan :)
381- // } else {
382- // scanInstance.setStatus(ScanStatus.FAILED.toString());
383- // // Save scanInstance in the persistence layer
384- // this.repository.save(scanInstance);
385- // return false; // No Top Files Present - Scan Failed
386- // }
387- // } catch (ExecutionException executingException) {
388- //
389- // log.error("ExecutionException in scanMonitoredDirectory - {}", executingException);
390- // } catch (InterruptedException interruptedException) {
391- //
392- // log.error("InterruptedException in scanMonitoredDirectory", interruptedException.getMessage());
393- // } catch (Exception exception) {
394- //
395- // log.error("Generic Exception in scanMonitoredDirectory", exception.getMessage());
396- // }
397- //
398- //}
399- //// Fallback return - this should not be reached under normal execution
400- return false ;
401- }
402384}
0 commit comments