我的代码正常工作。我只是从设计角度有一个问题。
您可以复制/粘贴课程和测试课程-它们应该是开箱即用的。
问题描述:
我有一个类,该类监视目录中的新文件/已修改文件/已删除文件,并在发生任何更改时触发事件。这是使用观察者模式实现的。
interface IFileObserver
{
void onFileChange(String filename, String action);
}
public class FileWatcher implements Runnable
{
private Path _directory;
private WatchService _watchService;
private List<IFileObserver> _fileObserverList;
public FileWatcher(Path directory)
{
_directory = directory;
try
{
_watchService = FileSystems.getDefault().newWatchService();
_directory.register(_watchService, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE);
_fileObserverList = new ArrayList<>();
}
catch (IOException e)
{
System.err.println("Unable to create FileWatcher object!");
}
}
public void register(IFileObserver fileObserver)
{
_fileObserverList.add(fileObserver);
}
private void updatesEvent(String filename, String action)
{
for (IFileObserver observer : _fileObserverList)
{
observer.onFileChange(filename, action);
}
}
private void startWatching()
{
WatchKey key = null;
boolean reset = true;
while(reset)
{
try
{
key = _watchService.take();
outerloop:
for (WatchEvent<?> event : key.pollEvents())
{
String filename = event.context().toString();
WatchEvent.Kind<?> kind = event.kind();
switch(kind.name())
{
case "ENTRY_CREATE":
updatesEvent(filename, kind.name());
// Files on create will normally trigger ENTRY_CREATE and ENTRY_MODIFY
// break to outerloop will prevent catching ENTRY_MODIFY when create is expected
break outerloop;
case "ENTRY_MODIFY":
case "ENTRY_DELETE":
updatesEvent(filename, kind.name());
break;
}
}
}
catch (InterruptedException e)
{
System.out.println("Retrieving watch key interrupted!");
}
reset = key.reset();
}
}
@Override
public void run()
{
startWatching();
}
问题是,要使用事件发送的数据,我必须先进入Thread.sleep。正如您在下面的测试中看到的那样,我在每个assert块之前使用Thread.sleep(100)-如果没有测试失败。
public class FileWatcherTest implements IFileObserver
{
private int _updatesCount = 0;
private String _filename;
private String _action;
@Test
public void FileWatcher() throws IOException, InterruptedException
{
File directory = new File(System.getProperty("user.dir"));
assertTrue(directory.exists());
assertSame(0, _updatesCount);
Path path = Paths.get(directory.getPath());
FileWatcher fileWatcher = new FileWatcher(path);
fileWatcher.register(this);
Thread thread = new Thread(fileWatcher);
thread.start();
String filename = "the-file-name.txt";
File file = new File(filename);
if (file.exists())
{
file.delete();
}
assertTrue(createFile(file));
Thread.sleep(100);
assertSame(1, _updatesCount);
assertEquals(filename, _filename);
assertEquals("ENTRY_CREATE", _action);
assertTrue(modifyFile(file));
Thread.sleep(100);
assertSame(2, _updatesCount);
assertEquals(filename, _filename);
assertEquals("ENTRY_MODIFY", _action);
Thread.sleep(250);
assertTrue(deleteFile(file));
Thread.sleep(100);
assertSame(3, _updatesCount);
assertEquals(filename, _filename);
assertEquals("ENTRY_DELETE", _action);
}
private boolean deleteFile(File file)
{
return file.delete();
}
private boolean modifyFile(File file) throws IOException
{
FileWriter writer = new FileWriter(file, true);
writer.append("another line appended by ENTRY_MODIFY");
writer.close();
return true;
}
private boolean createFile(File file) throws FileNotFoundException, UnsupportedEncodingException
{
PrintWriter writer = new PrintWriter(file, "UTF-8");
writer.println("The first line");
writer.println("The second line");
writer.close();
return file.exists();
}
@Override
public void onFileChange(String filename, String action)
{
_filename = filename;
_action = action;
_updatesCount++;
}
但是我觉得这不好,因为如果其他人使用此类,他们将不知道您需要睡觉,这会引起问题。
可以以任何方式(即在IFileObserver接口的每个实现上)强制使用Thread.sleep(100)吗?
答案 0 :(得分:0)
你说的是对的。当您在系统中的100毫秒内收到创建通知时,我的通知大约需要8400。因此,更好的测试设计将是等待逻辑状态改变一定的时间,而不是仅仅Thread.sleep
。
private void waitUntilUpdateCountChangesFrom(int count) throws InterruptedException {
int sleepTime = 10;
int maxPolls = 10000/sleepTime;
int i=0;
for (; i<maxPolls; i++) {
if (_updatesCount != count) {
break;
}
Thread.sleep(sleepTime); // sleeps a little to give a turn to file watcher thread
};
System.out.println("waited " + (i*sleepTime) + " milliseconds");
}
然后称呼它
assertTrue(modifyFile(file));
waitUntilUpdateCountChangesFrom(1);
assertSame(2, _updatesCount);
assertEquals(filename, _filename);
assertEquals("ENTRY_MODIFY", _action);
我注意到的另一件事是,有时在我的系统中,我也会收到ENTRY_MODIFY,也首先被接收到用于文件创建。因此,可能希望对文件创建使用MODIFY或CREATE如下所示:-
assertTrue(createFile(file));
waitUntilUpdateCountChangesFrom(0);
assertSame(1, _updatesCount);
assertEquals(filename, _filename);
List<String> createActions = Arrays.asList("ENTRY_CREATE", "ENTRY_MODIFY");
assertTrue(createActions.contains(_action));
我的处理流程几乎如下所示:-
Going to create ....
Received event: ENTRY_CREATE on file: the-file-name.txt
waited 8440 milliseconds
Going to modify ....
Received event: ENTRY_MODIFY on file: the-file-name.txt
waited 8430 milliseconds
Going to delete ....
Received event: ENTRY_DELETE on file: the-file-name.txt
waited 8420 milliseconds