Probleme mit WatchService Java 7

Hallo,

ich lade mir eine Datei via Firefox auf den Rechner und verarbeite die Datei nach dem Download weiter. Dazu habe ich mir den WatchService angeschaut. Aufgefallen ist mir, dass durch den Download immer 2 mal der Event “Create” ausgelöst wird.
Da ich diesen Event für weitere Aktionen nutze hab ich “quick and dirty” den Zähler count in mein Programm eingebaut.

Mein Problem ist aber, dass ich den Poller nur auf key = watcher.take() setzen kann.

key = watcher.poll(2000, TimeUnit.MILLISECONDS); liefert einen Runtime Error.
Kann mir jemand sagen, was ich da falsch mache?

ich bekomme beim Start des Programmes eine NullPointerException. Im Verzeichnis findet sich beim Programmstart keine Datei.

import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
 
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.concurrent.TimeUnit;
 

public class TestWatcherService {
 
    public static void main(String[] args) throws InterruptedException {
        try {
            WatchService watcher = FileSystems.getDefault().newWatchService();
            Path dir = Paths.get("H:/Inspool");
            dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
             
            System.out.println("Watch Service registered for dir: " + dir.getFileName());
            WatchKey key;
            int count = 0;
            while (true) {
                
                //*key = watcher.take();
            	key = watcher.poll(2000, TimeUnit.MILLISECONDS);
                
                for (WatchEvent<?> event : key.pollEvents()) {
                    
                	
                	WatchEvent.Kind<?> kind = event.kind();
                     
                    @SuppressWarnings("unchecked")
                    WatchEvent<Path> ev = (WatchEvent<Path>) event;
                    Path fileName = ev.context();
                    if (kind == ENTRY_CREATE) {
                    	count ++;
                    	if (count == 2) {
                    		System.out.println(kind.name() + ": "+count + ". " + fileName);
                    	}
                    	
                    }
                    
                     
                    if (kind == ENTRY_MODIFY) {
                    	
                    	key.reset();
                        
                    }
                }
                 
                boolean valid = key.reset();
                if (!valid) {
                    break;
                }
            }
             
        } catch (IOException ex) {
            System.err.println(ex);
        }
    }
}```

Danke

GGK

Nun, das liegt vermutlich an der Arbeitsweise moderner Browser : TEMP-Daten.
Einfach ausgedrückt : die Daten werden nicht direkt in die Ziel-Datei gespeichert sondern in eine temporäre Datei die eine weitere Endung wie .partial (IE11) hat und erst nach vollständigem Download umkopiert.
Naiv würde ich behaupten dies hat zwei Hintergründe : Wiederaufnahme von abgebrochenen Downloads und Sicherheit. Als Beispiel nehmen wir mal EXE > eine ausführbare Datei. Der Explorer würde direkt versuchen das Programm-Icon zu lesen was bei einer verseuchten oder beschädigten Datei zu einer Infektion oder zum Absturz führen kann. Die temp-Erweiterung ist in der Regel unbelegt so das der Explorer nicht weis was es ist und auch nicht versucht die Datei zu lesen. So hat z.B. ein Virenscanner Zeit die Daten zu analysieren bevor es zu einer gefährlichen Ausführen kommen kann. Daher solltest du nicht nur zwei mal CREATE und MODIFY bekommen sondern auch einmal sogar DELETE.

Ja, du hast natürlich recht…ich dachte, wenn ich mir den Polling-Intervall größer setze, dann kann ich das Problem umgehen.
Dabei erhalte ich jedoch den Runtime-Error.

Hast du dazu einen Rat…?

GGK

Ganz erlich : statt über den Browser und einen WatchService das File einfach direkt mit Java laden > Problem erledigt.

Ansonsten : wenn es wirklich über den Browser sein muss mit nem WatchService wäre noch möglich nicht nur CREATE sondern halt auch MODIFY und DELETE zu prüfen, denn wenn auf das TEMP-File DELETE gecallt wird sollte der Kopiervorgang in die eigentliche Ziel-Datei abgeschlossen sein.

du hast uneingeschränkt recht.
Den Poll-Intervall auf das Verzeichnis kann ich aber immer noch nicht ändern…hast du dazu eine Erklärung?

GGK

Gut, du sagtest das watcher.poll(…) eine RuntimeException liefert (wenn es wirklich ein Error sein sollte läuft bei dir was gewaltig schief (siehe dazu Unterschied Exception <-> Error)). Dann wäre es auch sinnvoll uns diesen Fehler zu posten.
Dazu soltlest du (und das solltest du dir eigentlich grundsätzlich angewöhen) deinen catch-Block so ändern das dort nicht nur ein sysout steht sondern korrekt ein .printStackTrace() damit auch klar wird wo was warum geknallt hat. Dann kann man weiter analysieren warum das passiert und was man dagegen machen kann.

OK…ich werde diesen noch nachliefern.
Bisweilen habe ich noch eine andere Frage…für die ich einen neuen Threat aufmache.

GGK

Ok, aber ohne weiteren StackTrace kann ich hier nicht weiterhelfen.

es gibt eine NullPointerException…

genau da:

                
                for (WatchEvent<?> event : key.pollEvents()) {```

wo ich auf den "leeren" key zugreife. Was auch logisch ist. Aber dann sollte das abgefangen werden bzw. müsste das doch auch beim watcher.take() auftreten.

GGK

Wie gesgt : bitte den KOMPLETTEN StackTrace !

Und warum bei take() keine NPE kommt verrät die DOC : poll() versucht einen Key zu grabben, returnt aber im Fehlerfall einfach NULL. take() hingegen ist ein blockierender Call bis ein Key vorliegt.
Ergo : dein Timeout ist zu kurz und es liegt schlicht kein Key vor. Ich würde jedoch bei diesem Vorhaben grundsätzlich take() nutzen da es halt solange blockt bis ein Event ausgelöst wird.

OK…danke dir…
So hauts auch hin…

GGK