在java watchservice中实现重命名和删除 [英] Implementing renaming and deletion in java watchservice

查看:188
本文介绍了在java watchservice中实现重命名和删除的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图在Java WatchService中实现简单的重命名。



我的假设是:当文件被重命名时,执行三个操作




  • 删除文件xxx

  • 创建文件yyy

  • 修改文件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){

路径myWatchPath = Paths.get(D:\\log4j);
long preventDuplicateTime = 0;
FileDelete onDelete = new FileDelete(); //此对象必须是线程安全
列表< String> notifications = new ArrayList< String>();

WatchService myPathWatchService = null;
尝试{
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(); //持续时间不近(似乎没有重命名)
}
} else {
//这不应该来这里!
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(文件修改:+ 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(文件重命名为+ 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,都是依赖于操作系统)

解决方案

请在下面找到解释为什么独立于操作系统的解决方案不会工作。为什么Java WatchService 的事件粒度对于您想要实现的程度来说太弱了。



基于可用的事件(CREATE,MODIFY,DELETE),您无法确定发生了哪个操作。



在Linux机器上执行以下示例



创建一些测试文件

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

执行以下命令

  rm foo&& cp foo2吧&&回声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

在您假定事件顺序之后,这将是一个重命名操作。



Wheras mv bar foobar 创建以下事件。 p>

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

现在对于Window机器来说相同



创建一些测试文件

  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

在您假定事件后,将会是重命名动作。



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吧&&回声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被关闭
[31 /Mär/ 2015 20:25:40] IN_CLOSE_WRITE / tmp / stackoverflow // bar
[31 /Mär/ 2015 20:25:40 ] * / tmp / stackoverflow // bar被关闭

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被移动到/ tmp / stackoverflow // foobar

编辑相关的Java源代码来确认我的解释。没有办法找到一个文件是否被重命名,一个文件被删除,另一个文件被创建(同时)。



< a href =http://hg.openjdk.java.net/jdk7/jdk7/jdk/file/9b8c96f96a0f/src/windows/classes/sun/nio/fs/WindowsWatchService.java =nofollow noreferrer> WindowsWatchService .java 行462

  //将文件更改操作转换为watch事件
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;

默认值:
返回null; // action not recognized
}
}

LinuxWatchService.java 第376行

  / ** 
*将inotify事件映射到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;
返回null;
}


I tried to implement simple renaming in java WatchService.

My assumption is: when files are renamed, three operations are performed

  • Deletion of file xxx
  • creation of file yyy
  • modification of file yyy

Below are my codes:

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 ;
}

It's showing perfectly on renaming operations but the problem is I can't figure how to show for onDelete. Because each delete is pushed into notifications!! Or else help me implement renaming!!

*NOTE please don't suggest third party jars! (Since most of them, like JNotify, are OS dependent)

解决方案

Please find below some explanation why an OS independent solution will not work. And why the event granularity of the Java WatchService is too weak for what you want to achieve.

Based on the available events (CREATE, MODIFY, DELETE) you cannot determine which action happened.

Take following example on a Linux machine

create some test files

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

execute the following commands

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

This will create following events (monitored with 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

Following your assumption of the events order this would have been a rename action.

Wheras mv bar foobar creates following events.

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

Now the same for a Window machine

create some test files

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

execute the following commands

del foo
copy foo2 bar

This will create following events

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

Following your assumption of the events order this would have been a rename action.

Wheras ren bar foobar creates in this case the same events order.

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

Contrary with iwatch /tmp/stackoverflow/ on a Linux machine you can determine exactly what happen.

Executing the commands rm foo && cp foo2 bar && echo foo > bar produces following inotify events.

[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

whereas mv bar foobar creates following inotify events

[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

edit The related Java source code to confirm my explanation. There is no way (in plain Java) to find out if one file is renamed or one file is deleted and another one is created (at the same time).

WindowsWatchService.java line 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 line 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;
}

这篇关于在java watchservice中实现重命名和删除的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆