Реализация переименования и удаления в java watchservice

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

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

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

Ниже приведены мои коды:

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, зависят от ОС)


person theRoot    schedule 31.03.2015    source источник
comment
Для меня это кажется невозможным, если вы не смотрите сами файлы (возможно, сохраняя fileKey() и сравнивая его с перехваченными событиями). С точки зрения каталога, как бы вы определили разницу между delete file1 + create file2 и delete file1 + create file1_with_new_name? В Linux индексный дескриптор может сказать вам, является ли это одним и тем же файлом. Что касается javadoc, то fileKey() будет использовать индекс для идентификации.   -  person SubOptimal    schedule 31.03.2015
comment
@SubOptimal Моя цель - просмотреть один каталог ... Извините, я не понял, можете ли вы уточнить это?   -  person theRoot    schedule 31.03.2015
comment
@SubOptimal кажется, что inode зависит от ОС. Я хочу универсальное решение, возможно ли это?   -  person theRoot    schedule 31.03.2015
comment
Посмотрите на мой отредактированный ответ. С простыми классами среды выполнения Java невозможно достичь того, что вы ищете.   -  person SubOptimal    schedule 02.04.2015


Ответы (1)


Ниже вы найдете объяснение, почему независимое от ОС решение не будет работать. И почему степень детализации событий 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 действием.

В то время как mv bar foobar создает следующие события.

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

Теперь то же самое для Windows-машины

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

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.

Тогда как 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;
}
person SubOptimal    schedule 31.03.2015
comment
Кто-нибудь понял, почему они просто сократили эту информацию и не передавали ее, чтобы у нас была лучшая степень детализации? - person JayC667; 23.08.2018
comment
@ JayC667 JayC667 Я считаю, что не каждое событие поддерживается в каждой файловой системе. - person SubOptimal; 03.12.2020