# Problem mit einem FileWatcher



## G@st (9. Nov 2012)

Moin Moin,

ich hab folgendes Problem: 

Die Aufgabenstellung vom Chef lautet: Lass dein Programm in den Ordner 1 reinschauen. Wenn dort ein neues File angelegt wird, lass das Programm prüfen, ob dieses File in Ordner 2 vorhanden ist. Wenn ja, sende eine Email an den zuständigen Bearbeiter, wenn nein, tue nichts. 
Das ganze habe ich über einen Watcher implementiert und lokal funktioniert es einwandfrei. Die GUI hat keine Probleme bei der Verarbeitung und die Syntax \\\\srv1\\test\\bla wird von mir eingehalten. Aber offenbar kommt Java nicht mit dem Netzwerk klar, denn sobald ich auf Netzwerkordner zugreifen will, reagiert der Watcher kein Stück mehr. Hier der Quelltext dazu: 


```
import java.io.File;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Paths;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.Properties;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;


public class NavWatcherMain {
	
	public static void watch(String watchpath, String checkpath, String mail_sender, String mail_receiver) throws IOException, InterruptedException{
		WatchService watcher = FileSystems.getDefault().newWatchService();
		
		Paths.get(watchpath).register( watcher, StandardWatchEventKinds.ENTRY_CREATE,
		                                      StandardWatchEventKinds.ENTRY_DELETE,
		                                      StandardWatchEventKinds.ENTRY_MODIFY );
		try {
			while ( true ) {
				
			  WatchKey key = watcher.take();
			  
			  for ( WatchEvent<?> event : key.pollEvents() ){
			    System.out.println( "Kind: " + event.kind() + ", File: " +  event.context() );
			  
				  if(event.kind() == StandardWatchEventKinds.ENTRY_CREATE) {
					  
					  System.out.println(event.context());
				
					  if(dateivorhanden(event.context().toString(), checkpath)){
						  
						  String file = event.context().toString();
						  
						  try{
							  
							  	/** Definition des Mailversands
							  	 *  Absender: Jeweiliger Absender der automatischen Nachricht
							  	 *  Empfänger: Empfänger und Bearbeiter der Nachricht
							  	 *  Betreff: Kurze Info 
							  	 *  Nachricht: Genau Nennung des betreffeneden Files
							  	 */
							  
							  	String absender = mail_sender;
							  	String empfaenger = mail_receiver;
							  	
							  	String betreff = "Doppelter Import im zzz Ordner";
							  	String inhalt = "Das File \"" + file + "\" wurde doppelt abgelegt!";
							  
							    Properties props = new Properties();
							    props.setProperty( "mail.smtp.host", "mail.zzz.de" );
	
							    System.out.println("Properties set.");
							    
							    Session session = Session.getDefaultInstance( props );
							    
							    Message msg = new MimeMessage( session );
							    
							    InternetAddress adresseAbsender = new InternetAddress( absender );
							    msg.setFrom( adresseAbsender );
							    System.out.println("Absender set.");
							    
							    InternetAddress addressEmpfaenger = new InternetAddress( empfaenger );
							    msg.setRecipient( Message.RecipientType.TO, addressEmpfaenger );
							    System.out.println("Empfänger set.");
							    
							    msg.setSubject( betreff );
							    msg.setContent( inhalt, "text/plain" );
							    Transport.send( msg );
							    System.out.println("Nachricht versandt.");
								
								}
							  
								catch (MessagingException msgex){
									String errormessage = "";
									String stacktrace = "";
									
									errormessage = msgex.getMessage() + "\n\n";
								
									for (int i=0; i < msgex.getStackTrace().length; i++){
								       stacktrace += msgex.getStackTrace()[i] + "\n";
									}
									
									System.out.println("Error:" + errormessage);
									System.out.println("Stacktrace: " + stacktrace);
									
								}
					  } else {
						  System.out.println("Neuer Artikel");
					  }
					  
				  }
			  }
			  key.reset();
			}
		} catch (Exception ex) {
			//Einflicken eines Warnfensters oder Labels! 			
		}
	}
	
	public static boolean dateivorhanden(String newfile, String checkpath) throws IOException{
		System.out.println("Neues File wird überprüft.");
		
		//Prüft den angegebenen Ordner auf das eingegebene File
		//Sollte es bereits existieren, wird eine Mail versandt,
		//ansonsten wird nicht auf die Eingabe reagiert. 
		if (new File(checkpath + "\\" + newfile).exists()){
			return true;
		}
		
		return false;
	}


}
```

Hat jemand dazu eine Idee oder einen Hinweis? Unser Administrator meinte, dass es daran liegen könnte, wie Java den UNC Path auslegt o.ä. Berechtigung ist nicht das Problem, der Server sei für schreibenden und lesenden Zugriff auf diese Art völlig frei.


----------



## tagedieb (9. Nov 2012)

Der WatchService kann (wahrscheinlich) nur file events von eigenen system abfangen.
Wird auf einem Remote System ein File Event ausgeloest bekommt dies der WatchService nicht mit.

Als alternative koennte ich dir Spring Integration empfehlen, welche deine Anforderung erfuellen sollte.
Du kannst ein FileListener auf ein (Remote)Folder setzen und bei einem Event gleich ein Email ausloesen - all done by Spring Magic 

Hier findest du ein kleines Beispiel.


Sonst gibt es noch JNotify. Das hab ich aber noch nie ausprobiert.


----------



## trööhööt (10. Nov 2012)

sorry für die dumme frage ... aber sie muss gestellt werden

hast du es denn schon mal mit einem netzlaufwerk anstatt dem UNC-path versucht ?

auch sind es ein paar wenige infos ...

z.b. was für ein typ ist der server ? UNIX mit SMB-emulation oder doch ein richtiger windows-server ?

auch kann es natürlich mit dem von tagedieb angemerkten problem zusammenhängen das übers netz einfach die info nicht übertragen wird ...
müsste man einfach mal testen in dem man ein netzlaufwerk nutzt und dann von einem rechner mal über den explorer guckt ob und was passiert wenn man mit nem anderen rechner was in dem verzeichnis macht ... und gleiches natürlich auch mit UNC ... (wobei ich glaube zu meinen das es über UNC selbst unter Win7-netzen KEIN update gibt)


----------



## Bernd Hohmann (11. Nov 2012)

G@st hat gesagt.:


> Aber offenbar kommt Java nicht mit dem Netzwerk klar, denn sobald ich auf Netzwerkordner zugreifen will, reagiert der Watcher kein Stück mehr. [...] Unser Administrator meinte, dass es daran liegen könnte, wie Java den UNC Path auslegt



Weder hat Java damit ein Problem noch liegt es am UNC-Path.

Es ist schlichtweg so, dass sich der Watcher auf Umwegen als Liste in die Routinen des Dateisystems einhängt und daher entsprechend mit wenig Kosten über Änderungen informiert werden kann.

Bei Remotefilesystems müsste die Liste der zu überwachenden Dateien an das Dateisystem des gegenüberliegenden Servers übertragen werden welcher wiederum bei Änderungen den Client informiert - diesem Wahnsinn hat sich bislang noch keiner angenommen 

Es ist durchaus möglich, dass es Implementierungen gibt welche bei entfernten Dateisystemen in einen Polling-Betrieb umschalten, das erzeugt aber ganz ordentlich Traffic und darum lässt man es lieber ehe das Netz mit Beschwerden zugemüllt wird.

Gut - wenn Dein Chef möchte, dass da eine E-Mail generiert wird scheint das ja nicht sonderlich Zeitkritisch zu sein. Du kannst also mit Deinem Programm 1x pro Minute die Verzeichnisse abgrasen und das gemütlich auswerten lassen.

Bernd


----------



## G@st (12. Nov 2012)

@Bernd Stimmt, das ist weniger zeitkritisch, der Ordner wird täglich etwa drei, vier Mal geleert und die Daten in unser ERP-System übertragen. Wenn ein Mitarbeiter ein File aber doppelt einliest, verbuchen wir die selbe Rechnung zweimal an den Kunden, daher die kleine Überprüfung bis unsere Entwickler mit den Anpassungen nachkommen, die sich momentan ergeben. 

Danke für das Update, vor allem wie der Watcher funktioniert, das bedeutet, dass ich momentan einfach einen völlig falschen Ansatz habe. Wenn ich das Programm abgeändert habe, setze ich den neuen Quellcode noch hier rein. 

@tröööhöööt Der Fileserver liegt auf einem Windows Server. Mit den Pfadangaben hab ich alles mögliche probiert, ich bin sogar, weil ich selber an meine Blödheit gedacht hab, zum Admin gelaufen und wir haben noch ein, zwei Sachen probiert, blieb aber alles ohne Erfolg.


----------



## Bernd Hohmann (12. Nov 2012)

G@st hat gesagt.:


> @Bernd Stimmt, das ist weniger zeitkritisch, der Ordner wird täglich etwa drei, vier Mal geleert und die Daten in unser ERP-System übertragen. Wenn ein Mitarbeiter ein File aber doppelt einliest, verbuchen wir die selbe Rechnung zweimal an den Kunden, daher die kleine Überprüfung bis unsere Entwickler mit den Anpassungen nachkommen, die sich momentan ergeben.



Dann würde es eigentlich ausreichen, wenn der Dupe-Check von dem Mitarbeiter gestartet wird der den Rechnungsimport auslöst (also davor).

Wobei ich jetzt irritiert bin: wenn man die Dubletten schon am Dateinamen erkennt (so Dein Mustersource), warum kippt man vor dem Import die beiden Verzeichnisse nicht einfach zusammen - oder warum gibt es überhaupt mehrere Verzeichnisse?

Bernd


----------



## G@st (13. Nov 2012)

Das Problem dabei war, dass die Importe von fast allen Mitarbeitern durchgeführt werden. Der eigentliche Import wird von der Verwaltung durchgeführt, aber Dokumente kommen aus der ganzen Firma. 

Es gibt zwei Ordner, weil das eine die Historie, sprich die Daten sind, die bereits bearbeitet wurden. Der zweite Ordner ist der Importordner ins ERP. Heißt, wenn ich eine bereits bearbeitete Rechnung dann wieder in den Import-Ordner legen will, muss das System Alarm geben, damit der Kunde die Rechnung nicht doppelt erhält. Daher geht es definitiv nicht einen Ordner zu machen. 

Was natürlich auch gegangen wäre, ist, dass der Verwaltungsmitarbeiter der den eigentlichen Import macht, den Check vorher durchlaufen lässt, aber automatisiert ist die Chance, das es vergessen wird verringert.


----------



## G@st (13. Nov 2012)

Hier nochmal der gesamte fertige Quellcode:



```
package navwatch;
import java.io.File;
import java.io.IOException;
import java.util.Properties;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;


public class NavWatcherMain {

        /**

         * Dies ist der NavWatcher-Dienst.
         * Er wurde entwickelt, um die Importe in Navision ERP zu überprüfen
         * und gegebenenfalls einen Alarm auszulösen, sollte ein TB doppelt
         * eingelesen werden. In diesem Fall würde der Kunde zweimal mit einer
         * Rechnung belangt werden, die eigentlich nur einmal erforderlich ist. 
         * 
         * Erste fertige Version 1.0 am 12.11.2012 einsatzbereit
         * Programm sendet eine Mail an die Verwaltung
         * Abgestimmt wurde, dass das Programm sich die Daten jede
         * Stunde ansieht und doppelte TB's mit Namen per Mail weiterleitet.
*/
        //Start über die Main-Methode

        public static void main (String[] args) throws IOException, InterruptedException{

                watch();

        }

        public static void watch() throws IOException, InterruptedException{

                //Belegung der festen Pfade und Empfänger. Sollte sich daran etwas ändern, müssen sie hier
                //geändert werden. -> Vorgabe durch die Diensteigentschaft des Programms 

                String watchpath = "***path***";

                String checkpath = "***path***";

                String mail_sender = "navwatcher@mail.de";

                String mail_receiver = "some.body@mail.de";

                while(true){    

                        //File läuft alle Dateien im Ordner ab und gibt diese 
                        //an das Array zurück. 

                        File f = new File(watchpath);

                        File[] fileArray = f.listFiles();

                        for(int i = 0; i < fileArray.length; i++){

                                //Liest die Dateinamen aus dem zu prüfenden Pfad aus 

                                String s = fileArray[i].toString();

                                //schneidet den absoluten Pfad zu den Datennamen zusammen

                                s = s.substring(s.lastIndexOf("\\") + 1);

                                System.out.println(s);

                                
                                //Ist die Datei schon in der Historie vorhanden, 
                                //wird eine Mail an die Verantwortlichen geschickt. 

                                if (dateivorhanden(s, checkpath)){

                                        sendmail(mail_sender, mail_receiver, s);

                                }

                        }
                        //viertelstündliche Überprüfung des Systems

                        Thread.sleep(900000);

                }

        }

        public static boolean dateivorhanden(String newfile, String checkpath) throws IOException{

                //Prüft den angegebenen Ordner auf das eingegebene File
                //Sollte es bereits existieren, wird eine Mail versandt,
                //ansonsten wird nicht auf die Eingabe reagiert. 

                if (new File(checkpath + "\\" + newfile).exists()){

                        System.out.println("Die Datei ist bereits eingelesen!");

                        return true;

                }

                System.out.println("Die Datei ist neu.");

                return false;

        }

        public static void sendmail(String mail_sender, String mail_receiver, String file){

                 try{

                                /** Definition des Mailversands
                                 *  mail_sender: Jeweiliger Absender der automatischen Nachricht
                                 *  mail_receiver: Empfänger und Bearbeiter der Nachricht
                                 *  betreff: Kurze Info 
                                 *  inhalt: Genau Nennung des betreffenden Files
                                 */

                                String absender = mail_sender;
                                String empfaenger = mail_receiver;

                                String betreff = "Doppelter Import im Navision Ordner ";
                                String inhalt = "Die Datei \"" + file + "\" wurde doppelt in den Import abgelegt!";

                            Properties props = new Properties();
                            props.setProperty( "mail.smtp.host", "mail.***hoster***.de" );

                            System.out.println("Properties set.");

                            Session session = Session.getDefaultInstance( props );

                            Message msg = new MimeMessage( session );

                            InternetAddress adresseAbsender = new InternetAddress( absender );

                            msg.setFrom( adresseAbsender );

                            System.out.println("Absender set.");

                            InternetAddress addressEmpfaenger = new InternetAddress( empfaenger );

                            msg.setRecipient( Message.RecipientType.TO, addressEmpfaenger );

                            System.out.println("Empfänger set.");

                            
                            msg.setSubject( betreff );

                            msg.setContent( inhalt, "text/plain" );

                            Transport.send( msg );

                            System.out.println("Nachricht versandt.");                   
       

                   } catch (MessagingException msgex){

                                        String errormessage = "";

                                        String stacktrace = "";

                                        errormessage = msgex.getMessage() + "\n\n";

                                

                                        for (int i=0; i < msgex.getStackTrace().length; i++){

                                       stacktrace += msgex.getStackTrace()[i] + "\n";

                                        }  

                                        System.out.println("Error:" + errormessage);

                                        System.out.println("Stacktrace: " + stacktrace); 

                                }

        }


}
```


----------

