在 java watchservice 中实现重命名和删除
Implementing renaming and deletion in 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) {
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,都是 OS 依赖的)
请在下面找到为什么 OS 独立解决方案不起作用的一些解释。以及为什么 Java WatchService
的事件粒度对于您想要实现的目标来说太弱了。
根据可用事件(创建、修改、删除),您无法确定发生了哪个操作。
以 Linux 机器上的以下示例为例
创建一些测试文件
touch /tmp/Whosebug/foo /tmp/Whosebug/foo2
执行以下命令
rm foo && cp foo2 bar && echo foo > bar
这将创建以下事件(使用 WatchDir.java 监控)
ENTRY_DELETE ..:..:.. [.........]: /tmp/Whosebug/foo
ENTRY_CREATE 20:09:37 [rw.rw.rw.]: /tmp/Whosebug/bar
ENTRY_MODIFY 20:09:37 [rw.rw.rw.]: /tmp/Whosebug/bar
根据您对事件顺序的假设,这将是一个 rename
操作。
Wheras mv bar foobar
创建了以下事件。
ENTRY_DELETE ..:..:.. [.........]: /tmp/Whosebug/bar
ENTRY_CREATE 19:55:37 [rw.rw.rw.]: /tmp/Whosebug/foobar
现在 Window 机器也是如此
创建一些测试文件
rem.>c:/temp/Whosebug/foo
rem.>c:/tmp/Whosebug/foo2
执行以下命令
del foo
copy foo2 bar
这将创建以下事件
ENTRY_DELETE ..:..:.. [.........]: c:\temp\Whosebug\foo
ENTRY_CREATE 19:59:10 [.........]: c:\temp\Whosebug\bar
ENTRY_MODIFY 19:59:10 [.........]: c:\temp\Whosebug\bar
根据您对事件顺序的假设,这将是一个 rename
操作。
Wheras ren bar foobar
在这种情况下创建相同的事件顺序。
ENTRY_DELETE ..:..:.. [.........]: c:\temp\Whosebug\bar
ENTRY_CREATE 20:02:41 [.........]: c:\temp\Whosebug\foobar
ENTRY_MODIFY 20:02:41 [.........]: c:\temp\Whosebug\foobar
与 iwatch /tmp/Whosebug/
在 Linux 机器上相反,您可以准确地确定发生了什么。
正在执行命令rm foo && cp foo2 bar && echo foo > bar
产生以下 inotify 事件。
[31/Mär/2015 20:25:40] IN_DELETE /tmp/Whosebug//foo
[31/Mär/2015 20:25:40] * /tmp/Whosebug//foo is deleted
[31/Mär/2015 20:25:40] IN_CREATE /tmp/Whosebug//bar
[31/Mär/2015 20:25:40] IN_CLOSE_WRITE /tmp/Whosebug//bar
[31/Mär/2015 20:25:40] * /tmp/Whosebug//bar is closed
[31/Mär/2015 20:25:40] IN_CLOSE_WRITE /tmp/Whosebug//bar
[31/Mär/2015 20:25:40] * /tmp/Whosebug//bar is closed
而 mv bar foobar
创建以下 inotify 事件
[31/Mär/2015 20:27:10] IN_MOVED_FROM /tmp/Whosebug//bar
[31/Mär/2015 20:27:10] IN_MOVED_TO /tmp/Whosebug//foobar
[31/Mär/2015 20:27:10] * /tmp/Whosebug//bar is moved to /tmp/Whosebug//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;
}
我尝试在 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) {
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,都是 OS 依赖的)
请在下面找到为什么 OS 独立解决方案不起作用的一些解释。以及为什么 Java WatchService
的事件粒度对于您想要实现的目标来说太弱了。
根据可用事件(创建、修改、删除),您无法确定发生了哪个操作。
以 Linux 机器上的以下示例为例
创建一些测试文件
touch /tmp/Whosebug/foo /tmp/Whosebug/foo2
执行以下命令
rm foo && cp foo2 bar && echo foo > bar
这将创建以下事件(使用 WatchDir.java 监控)
ENTRY_DELETE ..:..:.. [.........]: /tmp/Whosebug/foo
ENTRY_CREATE 20:09:37 [rw.rw.rw.]: /tmp/Whosebug/bar
ENTRY_MODIFY 20:09:37 [rw.rw.rw.]: /tmp/Whosebug/bar
根据您对事件顺序的假设,这将是一个 rename
操作。
Wheras mv bar foobar
创建了以下事件。
ENTRY_DELETE ..:..:.. [.........]: /tmp/Whosebug/bar
ENTRY_CREATE 19:55:37 [rw.rw.rw.]: /tmp/Whosebug/foobar
现在 Window 机器也是如此
创建一些测试文件
rem.>c:/temp/Whosebug/foo
rem.>c:/tmp/Whosebug/foo2
执行以下命令
del foo
copy foo2 bar
这将创建以下事件
ENTRY_DELETE ..:..:.. [.........]: c:\temp\Whosebug\foo
ENTRY_CREATE 19:59:10 [.........]: c:\temp\Whosebug\bar
ENTRY_MODIFY 19:59:10 [.........]: c:\temp\Whosebug\bar
根据您对事件顺序的假设,这将是一个 rename
操作。
Wheras ren bar foobar
在这种情况下创建相同的事件顺序。
ENTRY_DELETE ..:..:.. [.........]: c:\temp\Whosebug\bar
ENTRY_CREATE 20:02:41 [.........]: c:\temp\Whosebug\foobar
ENTRY_MODIFY 20:02:41 [.........]: c:\temp\Whosebug\foobar
与 iwatch /tmp/Whosebug/
在 Linux 机器上相反,您可以准确地确定发生了什么。
正在执行命令rm foo && cp foo2 bar && echo foo > bar
产生以下 inotify 事件。
[31/Mär/2015 20:25:40] IN_DELETE /tmp/Whosebug//foo
[31/Mär/2015 20:25:40] * /tmp/Whosebug//foo is deleted
[31/Mär/2015 20:25:40] IN_CREATE /tmp/Whosebug//bar
[31/Mär/2015 20:25:40] IN_CLOSE_WRITE /tmp/Whosebug//bar
[31/Mär/2015 20:25:40] * /tmp/Whosebug//bar is closed
[31/Mär/2015 20:25:40] IN_CLOSE_WRITE /tmp/Whosebug//bar
[31/Mär/2015 20:25:40] * /tmp/Whosebug//bar is closed
而 mv bar foobar
创建以下 inotify 事件
[31/Mär/2015 20:27:10] IN_MOVED_FROM /tmp/Whosebug//bar
[31/Mär/2015 20:27:10] IN_MOVED_TO /tmp/Whosebug//foobar
[31/Mär/2015 20:27:10] * /tmp/Whosebug//bar is moved to /tmp/Whosebug//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;
}