2015-03-31 1 views
1

Я попытался реализовать простое переименование в Java WatchService.Внедрение переименования и удаления в java watchservice

Мое предположение: когда файлы переименованы, три операций выполняются

  • Удаления файла хого
  • создания файла их
  • модификации файл их

Ниже мои коды:

MyWatcher.java

import java.io.IOException; 
    import java.nio.file.FileSystems; 
    import java.nio.file.Path; 
    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.ArrayList; 
    import java.util.List; 

    public class MyWatcher { 

     @SuppressWarnings("rawtypes") 
     public static void main(String[] strings) { 

      Path myWatchPath = Paths.get("D:\\log4j"); 
      long preventDuplicateTime = 0; 
      FileDelete onDelete = new FileDelete();//this object must be thread safe 
      List<String> notifications = new ArrayList<String>(); 

      WatchService myPathWatchService = null; 
      try { 
       myPathWatchService = FileSystems.getDefault().newWatchService(); 
      } catch (IOException e) { 
       e.printStackTrace(); 
      } 
      try { 
       myWatchPath.register(myPathWatchService, 
         StandardWatchEventKinds.ENTRY_CREATE, 
         StandardWatchEventKinds.ENTRY_DELETE, 
         StandardWatchEventKinds.ENTRY_MODIFY); 
      } catch (IOException e) { 
       e.printStackTrace(); 
      } 
      boolean isKeyValid = true; 
      while (isKeyValid) { 
       WatchKey myPathWatchKey = null; 
       try { 
        myPathWatchKey = myPathWatchService.take(); 
       } catch (InterruptedException e) { 
        e.printStackTrace();// throw 
       } 
        for (WatchEvent watchEvent : myPathWatchKey.pollEvents()) { 
         //WatchEvent.Kind kind = watchEvent.kind(); 
         if (StandardWatchEventKinds.ENTRY_CREATE.equals(watchEvent 
           .kind())) { 
          String fileName = watchEvent.context().toString(); 
          if(onDelete.status == -1) 
          System.out.println("File Created:" + fileName + " " 
            + watchEvent.context()); 
          else{ 
           if(onDelete.status == 0){ 
            onDelete.createdTime = System.nanoTime(); 
           if (onDelete.deletedTime/10000000 == onDelete.createdTime/10000000) { 
            onDelete.createdFile = watchEvent.context().toString(); 
            onDelete.status++; 
            notifications.add("File Created:" + fileName); 
           }else{ 
            for (String string : notifications) { 
             System.out.println(string); 
            } 
            notifications = new ArrayList<String>(); 
            System.out.println("File Created:" + fileName + " " 
              + watchEvent.context()); 
            onDelete = new FileDelete(); //Time duration not close (seems not renamed) 
           } 
           }else{ 
            //this should never come here!! 
            onDelete = new FileDelete(); 
           } 
          } 
         } 
         if (StandardWatchEventKinds.ENTRY_DELETE.equals(watchEvent 
           .kind())) { 
          String fileName = watchEvent.context().toString(); 
          if(onDelete.status == -1){ 
           onDelete = new FileDelete(); 
           onDelete.status++; 
           onDelete.deletedFile = watchEvent.context().toString(); 
           onDelete.deletedTime = System.nanoTime(); 
           notifications.add("File deleted:" + fileName); 
          } 
          //System.out.println("File deleted:" + fileName); // push to notfication to array for later use 
         } 
         if (StandardWatchEventKinds.ENTRY_MODIFY.equals(watchEvent 
           .kind())) { 
          long current = System.nanoTime(); 
          String fileName = watchEvent.context().toString(); 
          if(!(preventDuplicateTime/10000000 == current/10000000)) 
           notifications.add("File modified:" + fileName); 
          preventDuplicateTime = (System.nanoTime()); 
          onDelete.modifiedFile= fileName; 
          onDelete.modifiedTime =System.nanoTime(); 
          if(onDelete.status != 1){ 
           for (String messages : notifications) { 
            System.out.println(messages); 
           } 
          onDelete= new FileDelete(); 
          notifications = new ArrayList<String>(); 
          } 
          else if(onDelete.createdFile.equals(onDelete.modifiedFile)) 
            if(onDelete.createdTime /10000000 == onDelete.modifiedTime/10000000){ 
             System.out.println("File renamed:" + fileName); 
             onDelete = new FileDelete(); 
             notifications = new ArrayList<String>(); 
          } 
         } 
        /*}*/ 

       } 
       isKeyValid = myPathWatchKey.reset(); 
      } 
     } 
    } 

FileRename.java

public class FileRename { 
    int status =-1; 
    String deletedFile = ""; 
    long deletedTime = 0 ; 
    String createdFile = ""; 
    long createdTime =0 ; 
    String modifiedFile = ""; 
    long modifiedTime = 0 ; 
} 

Это прекрасно показывает на переименование операций, но проблема в том, что я не могу понять, как показать на OnDelete. Потому что каждое удаление вставляется в уведомления! Или помогите мне реализовать переименование !!

* ПРИМЕЧАНИЕ, пожалуйста, не предлагайте сторонние банки! (Поскольку большинство из них, например JNotify, зависят от ОС)

+0

Для меня это, кажется, не представляется возможным, если не смотреть сам файлы (возможно, сохраняя fileKey() и сравнить это в пределах уловленных событий). С точки зрения каталога, как вы могли бы сделать разницу между 'delete file1 + create file2' и' delete file1 + create file1_with_new_name'? В Linux inode может сказать вам, является ли он одним и тем же файлом. Что касается файла javadoc fileKey(), он будет использовать индекс для идентификации. – SubOptimal

+0

@SubOptimal Моя цель - смотреть один каталог ... Извините, я не получил вас, вы можете его разработать – theRoot

+0

@SubOptimal кажется inode - это специфичная ОС. Я хочу, чтобы общее решение, возможно ли это? – theRoot

ответ

5

Ниже приведено некоторое объяснение, почему автономное решение ОС не будет работать. И почему гранулярность событий Java WatchService слишком слаба для того, чего вы хотите достичь.

Основываясь на доступных событиях (CREATE, MODIFY, DELETE), вы не можете определить, какое действие произошло.

Примите следующий пример на машине Linux

создать несколько тестовых файлов

touch /tmp/stackoverflow/foo /tmp/stackoverflow/foo2 

выполнить следующие команды

rm foo && cp foo2 bar && echo foo > bar 

Это создаст следующие события (мониторинг с WatchDir.java)

ENTRY_DELETE ..:..:.. [.........]: /tmp/stackoverflow/foo 
ENTRY_CREATE 20:09:37 [rw.rw.rw.]: /tmp/stackoverflow/bar 
ENTRY_MODIFY 20:09:37 [rw.rw.rw.]: /tmp/stackoverflow/bar 

После вашего предположения о заказе событий это было бы действием rename.

Wheras mv bar foobar создает следующие события.

ENTRY_DELETE ..:..:.. [.........]: /tmp/stackoverflow/bar 
ENTRY_CREATE 19:55:37 [rw.rw.rw.]: /tmp/stackoverflow/foobar 

Теперь же для окна машины

создают некоторые тестовые файлы

rem.>c:/temp/stackoverflow/foo 
rem.>c:/tmp/stackoverflow/foo2 

выполнить следующие команды

del foo 
copy foo2 bar 

Это создаст следующие события

ENTRY_DELETE ..:..:.. [.........]: c:\temp\stackoverflow\foo 
ENTRY_CREATE 19:59:10 [.........]: c:\temp\stackoverflow\bar 
ENTRY_MODIFY 19:59:10 [.........]: c:\temp\stackoverflow\bar 

После Вашего предположения о событиях заказывали было бы rename действия.

Wheras ren bar foobar создает в этом случае тот же порядок событий.

ENTRY_DELETE ..:..:.. [.........]: c:\temp\stackoverflow\bar 
ENTRY_CREATE 20:02:41 [.........]: c:\temp\stackoverflow\foobar 
ENTRY_MODIFY 20:02:41 [.........]: c:\temp\stackoverflow\foobar 

В отличие от iwatch /tmp/stackoverflow/ на машине Linux вы можете точно определить, что случилось.

Выполнение команд rm foo && cp foo2 bar && echo foo > bar производит следующие события inotify.

[31/Mär/2015 20:25:40] IN_DELETE /tmp/stackoverflow//foo 
[31/Mär/2015 20:25:40] * /tmp/stackoverflow//foo is deleted 
[31/Mär/2015 20:25:40] IN_CREATE /tmp/stackoverflow//bar 
[31/Mär/2015 20:25:40] IN_CLOSE_WRITE /tmp/stackoverflow//bar 
[31/Mär/2015 20:25:40] * /tmp/stackoverflow//bar is closed 
[31/Mär/2015 20:25:40] IN_CLOSE_WRITE /tmp/stackoverflow//bar 
[31/Mär/2015 20:25:40] * /tmp/stackoverflow//bar is closed 

mv bar foobar тогда создает следующие Inotify события

[31/Mär/2015 20:27:10] IN_MOVED_FROM /tmp/stackoverflow//bar 
[31/Mär/2015 20:27:10] IN_MOVED_TO /tmp/stackoverflow//foobar 
[31/Mär/2015 20:27:10] * /tmp/stackoverflow//bar is moved to /tmp/stackoverflow//foobar 

редактировать Связанная Java исходный код, чтобы подтвердить свое объяснение. Нет способа (в простой Java) узнать, переименован ли один файл или удаляется один файл, а другой создается (в то же время).

WindowsWatchService.java линия 462

// Translate file change action into watch event 
private WatchEvent.Kind<?> translateActionToEvent(int action) 
{ 
    switch (action) { 
     case FILE_ACTION_MODIFIED : 
      return StandardWatchEventKinds.ENTRY_MODIFY; 

     case FILE_ACTION_ADDED : 
     case FILE_ACTION_RENAMED_NEW_NAME : 
      return StandardWatchEventKinds.ENTRY_CREATE; 

     case FILE_ACTION_REMOVED : 
     case FILE_ACTION_RENAMED_OLD_NAME : 
      return StandardWatchEventKinds.ENTRY_DELETE; 

     default : 
      return null; // action not recognized 
    } 
} 

LinuxWatchService.java линия 376

/** 
* map inotify event to WatchEvent.Kind 
*/ 
private WatchEvent.Kind<?> maskToEventKind(int mask) { 
    if ((mask & IN_MODIFY) > 0) 
     return StandardWatchEventKinds.ENTRY_MODIFY; 
    if ((mask & IN_ATTRIB) > 0) 
     return StandardWatchEventKinds.ENTRY_MODIFY; 
    if ((mask & IN_CREATE) > 0) 
     return StandardWatchEventKinds.ENTRY_CREATE; 
    if ((mask & IN_MOVED_TO) > 0) 
     return StandardWatchEventKinds.ENTRY_CREATE; 
    if ((mask & IN_DELETE) > 0) 
     return StandardWatchEventKinds.ENTRY_DELETE; 
    if ((mask & IN_MOVED_FROM) > 0) 
     return StandardWatchEventKinds.ENTRY_DELETE; 
    return null; 
} 
+0

спасибо за ваше объяснение со ссылкой !! – theRoot