자바

자바 NIO 파일과 디렉토리 - WatchService 와치 서비스

알통몬_ 2017. 4. 16. 17:52
반응형


안녕하세요 알통몬입니다.

공감 및 댓글은 포스팅 하는데 아주아주 큰 힘이 됩니다!!

포스팅 내용이 찾아주신 분들께 도움이 되길 바라며

더 깔끔하고 좋은 포스팅을 만들어 나가겠습니다^^

 


2017/04/16 - [자바] - 자바 NIO 파일과 디렉토리 - Path, 파일 시스템 정보, 파일의 속성 읽기, 파일과 디렉토리 생성과 삭제


WatchService - 와치서비스

: 파일 변경 통지 매커니즘으로 알려져 있습니다. 자바 버전 7에서 처음 나왔고

디렉토리 내부에서 파일 생성, 삭제, 수정 등의 내용 변화를 감시하는데 사용됩니다.

예를 들면 에디터에서 파일을 편집하고 있을 때 에디터 바깥에서 파일 내용을 수정하게 되면

파일 내용이 변경되었으니 파일을 다시 불러올 것인지 묻는 대화상자를 띄우는 것이 있습니다.


WatchService watchService = FileSystems.getDefault().newWatchService();


와치서비스를 생성했다면 감시가 필요한 디렉토리의 Path 객체에서 register()로 WatchService()를

등록합니다.

어떤 변화를 감시할 것인지 standardWatchEventKinds 상수로 지정가능합니다.

path.register( watch,  StandardWatchEventKinds.ENTRY_CREATE, // 생성 감시

                               StandardWatchEventKinds.ENTRY_MODIFY, // 수정 감시

                               StandardWatchEventKinds.ENTRY_DELETE); // 삭제 감시


Path에 와치 서비스 (WatchService)를 등록한 순간부터 디렉토리 내부에서 변경이 발생하면

와치 이벤트 (WacthEvent)가 발생하고, 와치서비스는 해당 이벤트 정보를 가진 와치키(WatchKey)를

생성하고 큐에 넣어줍니다. 프로그램은 무한 루프를 돌며 WatchService의 take()를 호출하여

와치키가 큐에 들어올 때까지 대기하다가 와치키가 큐에 들어오면 와치키를 얻어 처리합니다.


while(true) {

    WatchKey watchKey = watchService.take();

}


와치키를 얻은 후 pollEvents()를 호출해 WatchEvent 리스트를 얻어내야 합니다.

List<WatchEvent<?>>로 리턴하는 이유는 여러 개의 파일이 동시에 삭제 , 수정, 생성될 수 있기 때문입니다.

List<WatchEvent<?>> watchList = watchKey.pollEvents();


프로그램은 WatchEvent 리스트에서 WatchEvent 를 하나씩 꺼내어 이벤트의 정류와 Path 객체를

얻어낸 후에 적절히 처리하면 됩니다.

for(WatchEvent watchEvent : list) {

  //이벤트 종류 얻기

  Kind kind = watchEvent.kind();

  //감지된 Path 얻기

  Path path = (Path)watchEvent.context();

  if(kind == StandardWatchEventKinds.ENTRY_CREATE) {

  //생성되었을 경우, 실행할 코드

 } else if(kind == StandardWatchEventKinds.ENTRY_DELETE) {

  //삭제되었을 경우, 실행할 코드

 } else if(kind == StandardWatchEventKinds.ENTRY_MODIFY) {

  //변경되었을 경우, 실행할 코드

  } else if(kind == StandardWatchEventKinds.OVERFLOW) {

 }


OVERFLOW 는 이벤트 운영 체제에서 이벤트가 소실되었거나 버려진 경우에 알아서 발생합니다.

CREATE, DELETE, MODIFY 이벤트만 처리해주면 됩니다.

한 번 사용된 WatchKey 는 reset() 으로 초기화 해야합니다.

새로운 WatchEvent 가 발생하면 큐에 다시 들어가기 때문입니다.

초기화에 성공하면 reset() 은 true 를 리턴합니다.

감시하는 디렉토리가 삭제되었다거나 키가 더 이상 유효하지 않으면 false 를 리턴합니다.

WatchKey 가 더 이상 유효하지 않게 되면 무한 루프를 빠져나와 

WatchService의 close()를 호출하고 종료합니다.

while(true) {

WatchKey watchKey = watchService.take();

List<WatchEvent<?>> list = watchKey.pollEvents();

for(WatchEvent watchEvent : list) {

..........

}

boolean valid = watchKey.reset();

if(!valid) { break; }

 

}

      watchService.close(); 


예제) C:\Temp를 감시 디렉토리로 설정, C:\Temp\file.txt파일 생성 후 내용을 수정한 다음 저장해보면 됩니다.

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.WatchEvent.Kind;

import java.nio.file.WatchKey;

import java.nio.file.WatchService;

import java.util.List;


import javafx.application.Application;

import javafx.application.Platform;

import javafx.scene.Scene;

import javafx.scene.control.TextArea;

import javafx.scene.layout.BorderPane;

import javafx.stage.Stage;


public class Example extends Application {

 class WatchServiceThread extends Thread {

  @Override

  public void run() {

   try {

    WatchService watchService = FileSystems.getDefault().newWatchService();

    Path directory = Paths.get("C:/Temp");

    directory.register(watchService, StandardWatchEventKinds.ENTRY_CREATE,

                               StandardWatchEventKinds.ENTRY_DELETE,

                               StandardWatchEventKinds.ENTRY_MODIFY);

    while(true) {

     WatchKey watchKey = watchService.take();

     List<WatchEvent<?>> list = watchKey.pollEvents();

     for(WatchEvent watchEvent : list) {

      //이벤트 종류 얻기

      Kind kind = watchEvent.kind();

      //감지된 Path 얻기

      Path path = (Path)watchEvent.context();

      if(kind == StandardWatchEventKinds.ENTRY_CREATE) {

       //생성되었을 경우, 실행할 코드

       Platform.runLater(()->textArea.appendText("파일 생성됨 -> " + path.getFileName() + "\n"));

      } else if(kind == StandardWatchEventKinds.ENTRY_DELETE) {

       //삭제되었을 경우, 실행할 코드

       Platform.runLater(()->textArea.appendText("파일 삭제됨 -> " + path.getFileName() + "\n"));

      } else if(kind == StandardWatchEventKinds.ENTRY_MODIFY) {

       //변경되었을 경우, 실행할 코드

       Platform.runLater(()->textArea.appendText("파일 변경됨 -> " + path.getFileName() + "\n"));

      } else if(kind == StandardWatchEventKinds.OVERFLOW) {

      }

     }

     boolean valid = watchKey.reset();

     if(!valid) { break; }

    }

   } catch (Exception e) {}

  }

 } 

 

 TextArea textArea;

 

 @Override

 public void start(Stage primaryStage) throws Exception {

  BorderPane root = new BorderPane();

  root.setPrefSize(500, 300);

  

  textArea = new TextArea();

  textArea.setEditable(false);

  root.setCenter(textArea);

  

  Scene scene = new Scene(root);

  primaryStage.setScene(scene);

  primaryStage.setTitle("WatchServiceExample");

  primaryStage.show();

  

  WatchServiceThread wst = new WatchServiceThread();

  wst.start();

 }

 

 public static void main(String[] args) {

  launch(args);

 }

}


이상입니다.


반응형