2016年12月12日月曜日

ディレクトリの監視

みなさん。 こんばんは。

大変お久しぶりです。

先日はディレクトリの監視対象の登録について見ましたが、今回は登録したディレクトリの監視対象にイベントが発生した時の処理について見てみましょう。

ディレクトリの監視の登録をすると、ディレクトリの監視が開始されます。

ディレクトリの監視は、発生しているイベントを取得し、イベントに応じた処理を行うという形式で行います。

通常は、イベントを取得し続けるために、無限ループを使用します。

WatchServiceインターフェースから、発生しているイベントをまとめたWatchKeyオブジェクトを取得するには、3種類の方法があります。

takeメソッドはイベントが発生するまでブロックするメソッドです。

イベント発生までブロックするため、InterruptedException例外がスローされることがあります。

takeメソッド以外に引数なしのpollメソッドと、ひきすうとして待ち時間を指定するpollメソッドがあります。

pollメソッドはイベントが発生していない場合、nullを返します。

待ち時間を指定するpollメソッドは、イベントの発生を指定した時間だけ待ちますが、それでもイベントが発生しないばあはnullを返します。

待ち時間を指定する場合もInterruptedException例外がスローされることがあります。

WatchKeyインターフェースには、発生したイベントをリスト形式で返すpollEventsメソッドが定義されています。

イベントはWatchEventインターフェースで表されます。

そこで、拡張for文を使用して、個々のイベントに対する処理を記述していきます。

イベントの種類はWatchEventインターフェースのkindメソッドで取得できます。

kindメソッドの返り値の型は、監視の登録時に使用したWatchEvent.Kindクラスです。

こちらも実際に使用されるのはStandardWatchEventKindsクラスです。

イベントの処理を行った後、WatchKeyインターフェースのresetメソッドをコールします。

resetメソッドをコールすることで、次のイベントを扱うことが可能になります。

では、ディレクトリの監視を行ってみましょう。

// ディレクトリの監視例while(true) {       try{           // 現在発生しているキーを取得           WatchKey key = watchService.take();
           // キーからイベントを取得           for(WatchEvent<?> event : key.pollEvents()) {                if(event.kend() == StandardWatchEventKinds.OVERFLOW) {                   // イベントがオーバーフローしていまった場合                   key.reset():                   continue;                } else {                   // それ以外の場合、                   // イベントの種類と対象となるパスを出力                   System.out.println(event.kind() + " " + event.context());                }
                // キーをリセット                if(!key.reset()) {                   break;                }             }         } catch(InterruptedException ex) {             break;         }}
// ディレクトリ監視の終了watchService.close();

上記ソースは、無限ループで監視イベントを取得し続けています。

まず、takeメソッドを使用してWatchKeyオブジェクトを取得します。

そして、WatchKeyオブジェクトからWatchKeyオブジェクトを取得しています。

監視の登録時に使用しなかった定数として、OVERFLOWがあります。

これはイベントがオーバーフローを起こし、処理できていないことを示します。

ここでは、WatchKeyインターフェースのresetメソッドをコールして、リセットを行っています。

オーバーフローしているので、あふれてしまったイベントは扱えません。

OVERFLOW以外のイベントの場合、kindメソッドでイベントの種類、contextメソッドでイベントの対象となったパスを出力しています。

ディレクトリの監視を終了させる場合、WatchServiceインターフェースのcloseメソッドをコールします。

今回はディレクトリの監視について見てみました。

いままで煩雑だったファイルに関する大幅の操作がNIO2によって簡易化したのが分かりますね!

次回はJava8で導入されたラムダ式について見ていきたいと思います。

それではまた!

人気ブログランキングへ

2016年11月30日水曜日

ディレクトリの監視(監視ディレクトリの登録方法)

みなさん。 こんばんは。

今日はディレクトリの監視について見ていきましょう。

ディレクトリの監視とは、指定したディレクトリに関する変更があるかどうかチェックする機能です。

標準では、ファイルやディレクトリの作成、削除、変更の監視を行うことができます。

既存のFileクラスでは、監視機能は提供されていませんでした。

このため、定期的にファイル一覧を取得するなどして監視を行う必要がありましたが、NIO.2を使用すれば簡単に監視を行うことができます。

ディレクトリの監視を行うのはWatchServiceインターフェースです。

これはFilesSystemクラスから取得します。

WatchServiceオブジェクトを取得するのはFileSystemクラスのnewWatchServiceメソッドです。

ファイルシステムがディレクトリの監視に対応していない場合、UnsupportedOperationException例外がスローされます。

ターゲットとなるディレクトリの監視を開始するには、Pathインターフェースのregisterメソッドを使用します。

registerメソッドの第1引数がWatchServiceオブジェクト、第2引数は可変長引数で、ここにイベントの種類を列挙します。

第2引数の型はWatchEvent.Kindインターフェースです。

しかし、実際に使用されるのはWatchService.Kindインターフェースを実装したStandardWatchEventKindsクラスになります。

StandartWatchEventKindsクラスには4種類の定数が定義されていますが、registerメソッドで使用できるのは以下の3種類です。


  • ENTRY_CREATE : ファイル/ディレクトリの作成
  • ENTRY_DELETE : ファイル/ディレクトリの削除
  • ENTRY_MODIFY : ファイル/ディレクトリの更新


なお、registerメソッドの第1引数にディレクトリではないPathオブジェクトを指定すると、NotDirectoryException例外がスローされます。

では実際に監視対象となるディレクトリの登録例を見てみましょう。

// WatchServiceオブジェクトの生成
WatchService watchService = FileSystems.getDefault().newWatchService();
// 監視の対象となるディレクトリ
Path target = Paths.get(targetDirectory);
// ディレクトリの監視を登録
target.register(watchService,
                    StandardWatchEventKinds.ENTRY_CREATE,
                    StandardWatchEventKinds.ENTRY_DELETE,
                    StandardWatchEventKinds.ENTRY_MODIFY);

上記のソースでは、デフォルトのファイルシステムを使用してWatchServiceオブジェクトを取得しています。

そして、ディレクトリに対して、ENTRY_CREATE、ENTRY_DELETE、ENTRY_MODIFYの3種類の監視イベントを登録しています。

ディレクトリ監視の登録をすると、ディレクトリの監視が開始されます。

ディレクトリの監視の登録が終わったので、実際にどのようにディレクトリの監視するのかは次回に記述していきます。

今日はこの辺にしておきましょう。

それではまた!
人気ブログランキングへ

2016年11月21日月曜日

ディレクトリのコピー

みなさん。 こんばんは。

前回はファイルツリーの走査のサンプルを見ましたが、今回はもう少し役に立つサンプルとしてディレクトリのコピーについて見てみましょう。

ディレクトリのコピーを行うには、次に示す手順で行います。

  1. ディレクトリをコピーする。このとき、ディレクトリに存在するファイル/ディレクトリはコピーされない。
  2. ディレクトリに存在するファイルをコピーする。
  3. サブディレクトリが存在したら、①に戻る。サブディレクトリがなくなるまで①と②を繰り返す。
ファイルツリーの走査を使用して、この手順を実現していきます。

①はpreVisitDirectoryメソッドで処理可能です。
次の②はvisitFileメソッドで実装できます。
③に関してはwalkFileTreeメソッドの内部で制御する処理なので、実装はありません。

ということは、preVisitDirectoryメソッドとvisitFileメソッドを実装したFileVisitorオブジェクトを生成すればよいことになります。

postVisitDirectoryメソッドなどは実装する必要がないため、ここではSimpleFileVisitorクラスの匿名クラスとして実装してみます。

// ディレクトリのコピー
// コピー元のディレクトリ
Path source = Paths.get(sourceDirectory);
// コピー先のディレクトリ
Path dest = Paths.get(destDirectory);
// ファイルツリーをたどって、ファイル/ディレクトリをコピーするFileVisitor
FileVisitor<Path> visitor = new SimpleFileVisitor<Path>() {
          @Override
          public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
                    throws IOException {
                 // ディレクトリのコピー
                 Files.copy(dir, dest.resolve(dir), StandardCopyOption.COPY_ATTRIBUTES);
                 return FileVisitResult.CONTINUE;
          }
          @Override
          public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
                     throws IOException {
                  // ファイルをコピー
          Files.copy(file, dest.resolve(file), StandardCopyOption.COPY_ATTRIBUTES);
                  return FileVisitResult.CONTINUE;
          }
};

// ディレクトリのコピーを実行!!Files.walkFileTree(source, visitor);

上記のpreVisitDirectoryでは、ディレクトリのコピーをcopyメソッドを使用して行っています。

コピー先のディレクトリ名を解決するために使用しているのが、Pathインターフェースのresolveメソッドです。

resolveメソッドはディレクトリに対するコピーはディレクトリだけをコピーし、ディレクトリにあるファイルはコピーしません。

ファイルツリーに存在するファイルに到達するとvisitFileメソッドがコールされるので、このときにファイルのコピーを行います。

このときも、resolveメソッドを使用してファイル名の解決を行っています。

今回はファイルツリーの走査をしてディレクトリのコピーをするサンプルを記述しました。

では今日はこの辺で!

人気ブログランキングへ

2016年11月17日木曜日

ファイルツリーの走査例

みなさん。 こんばんは。

前回はファイルツリーの走査の概要について記述しました。

今回は実際の実装例について見ていきましょう。

まず、FileVisitorインターフェースの匿名クラスを示します。

それぞれのメソッドはすべて、第1引数の型はPathインターフェースになります。

preVisitDirectoryメソッドとvisitFileメソッドは、第2引数の型がBasicFileAttributesクラスになります。

postVisitDirectoryメソッドとvisitFileFailedメソッドの第2引数の型はIOException例外となり、

走査中に発生した例外が引数となります。ただし、走査が正常に行われた場合、

postVisitDirectoryメソッドの第2引数の値はnullになります。

すべてのメソッドの返り値の型はFileVisitResult列挙型です。

FileVisitResult列挙型は以下の4種類の値を取ります。


  • CONTINUE
  • SKIP_SIBLINGS
  • SKIP_SUBTREE
  • TERMINATE


走査を続けるのであればCONTINUEを返します。

SKIP_SUBLINGSはディレクトリに依存するそれ以降のファイル/ディレクトリの走査をスキップするために使用します。

同様にSKIP_SUBTREEもサブツリーの走査をスキップするために使用します。

最後のTERMINATEは走査を中止させます。

// ファイルツリーを走査するFileVisitor
FileVisitor<Path> visitor = new FileVisitor<Path>() {
@Override
public FileVisitResult preVisitDIrectory(Path dir, BasicFileAttributes attrs) {
         System.out.println("Pre visit Directory; " + dir);
         return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
         System.out.println("Visit File: " + file);
         return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
         System.out.println("Post Visit Directory: " + dir);
         return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) {
         System.out.println("Visit File Failed: " + file);
         return FileVisitResult.TERMINATE;
}
};
// ファイルツリーの走査実行
Path path = Paths.get("alpha");
Files.walkFileTree(path, visitor);

上記のソースでは、4種類のメソッドとともに第1引数を標準出力に出力する処理だけを行っています。

これで、どのメソッドがどの順番にコールされるかわかるはずです。

visitFileFailedメソッド以外のメソッドは正常に走査が行われているので、返り値としてCONTINUEを返しています。

visitFileFailedメソッドだけは、何らかの異常が発生したことを示しているので、走査を中止するためTERMINATEを返すようにしています。

Paths.getメソッドの引数に起点のディレクトリを指定し、FilesクラスのwalkFileTreeメソッドが実際にファイルツリーの走査を行います。

今回はFileVisitorインターフェースの匿名クラスを使用しましたが、空のメソッドが定義されている
SimpleFileVisitorクラスを使用して必要なメソッドだけオーバーライドすることもできます。

今回はコンソールに出力するだけでしたが、次回はもう少し役にたつ実装例を示していきたいと思います。

それでは今日はこの辺で!

人気ブログランキングへ

2016年11月15日火曜日

ファイルツリーの走査

みなさん。 お久しぶりです。

今日はファイルツリーの走査について見てみましょう。

Filesクラスのcopyメソッドを使用してディレクトリのコピーを行う場合、

ディレクトリに存在するファイルなどはコピーされません。

ディレクトリのすべてのファイル/ディレクトリのコピーを行うにはディレクトリの構造をたどり、

ファイルツリーの末端までファイルをコピーしていく必要があります。

しかし、いままでのFileクラスではファイルツリーをたどることはできるものの、

煩雑になりがちでした。

そこで、NIO2ではデザインパターンのVisitorパターンを利用して、

ファイルツリーを走査する機能が提供されました。

ここでは、Visitorパターンについての詳細については述べませんが、簡単に動作を説明しましょう。

Visitorは、指定されたディレクトリからファイルツリーをだどっていきます。

ファイルツリーのディレクトリ/ファイルに到達するとVisitorのコールバックメソッドがコールされます。

Visitorはファイルツリーの末端までたどっていくことで、ファイルツリーの存在するすべての

ディレクトリやファイルを走査することができます。

NIO2でVisitorとして使用されるのが、java.nio.file.FileVisitorインターフェースです。

FileVisitorインターフェースには以下の4種類のメソッドが定義されています。


  • previsitDirectoryメソッド

  • postVisitDirectoryメソッド

  • visitFileメソッド

  • visitFIleFailedメソッド


preVisitDirectoryメソッドはディレクトリに入ったときにコールされるメソッドです。

preVisitDirectoryメソッドがコールされた後、そのディレクトリに存在するファイルがあると

visitFileメソッドがコールされます。

そして、ディレクトリから抜けるとき、postVisitDirectoryメソッドがコールされます。

また、ファイルツリーの走査に失敗すると、visitFileFailedメソッドがコールされます。

文章では解りづらいので、次回はこれらの実装について見ていきます。

それでは今日はこの辺で!

人気ブログランキングへ

2016年11月3日木曜日

アトリビュートの設定

みなさん。 こんばんは。

今日はファイルのアトリビュートをまとめて設定してみましょう。

設定を行うインターフェース群は、すべて java.nio.file.attribute.AttributeView インターフェースの

継承関係にあります。

ファイルやディレクトリに対するアトリビュートを設定するために使用されるのがFileAttributeView

インターフェースです。

しかし、FileAttributeViewインターフェースにはメソッドが定義されていません。

実際には、用途ごとに定義されているFileAttributeViewインターフェースのサブインターフェースを

使用します。

BasicFileAttributeViewインターフェースはOSに依存しない共通のアトリビュートを設定するための

インターフェースです。

アトリビュートの取得に使用したBasicFileAttributesインターフェースに対応しており、

BasicFileAttributesインターフェースで取得できるアトリビュートを設定することができます。

そして、OSに依存したアトリビュートの設定を行うには、Windows用のDosFileAttributeView

インターフェース、POSIXに準拠したOS用のPosixFileAttributeViewインターフェースを使用します。

それぞれDosFileAttributesインターフェース、PosixFileAttributesインターフェースに対応してます。

PosixFileAttributeViewインターフェースはBasicFileAttributeViewインターフェースだけでなく、

FileOwnerAttributeViewインターフェースも継承しています。

FileOwnerAttributeViewインターフェースはオーナを設定するためのインターフェースです。

FileOwnerAttributeViewインターフェースのサブインターフェースはもう1つ、

AclFileAttributeViewインターフェースが提供されています。

インターフェース名のACLはAccess Control List (アクセス制御リスト)の略称です。

NIO.2のACLはRFC 3350: Network File System(NFS) version4 Protocol に基づいています。

これらのインターフェースのオブジェクトを取得するには、FilesクラスのgetFileAttributeView

メソッドを使用します。

getFileAttributeViewメソッドは、第1引数の型がPathインターフェース、

第2引数の型がClassクラス、第3引数は可変長引数でLinkOption列挙型になります。

取得したいインターフェースを第2引数に指定することで、オブジェクトを取得できます。

今回はDosFileAttributeViewインターフェースとPosixFileAttributeViewインターフェースの

オブジェクトを取得して、アトリビュートを設定してみます。

BasicFileAttributeViewインターフェースは最終更新日時、最終アクセス日、作成日時を設定する

setTimeメソッドを定義しています。

同様に、DosFileAttributeViewインターフェースはファイルをリードオンリーに設定する

setReadOnlyメソッド、隠しファイルに設定するsetHiddenメソッド、アーカイブファイルに設定する

setArchiveメソッド、そしてシステムファイルに設定するsetSystemメソッドなどを定義しています。

PosixFileAttributeViewインターフェースはパーミッションを設定するsetPermissionsメソッドと

グループを設定するsetGroupメソッドを定義しています。

また、PosixFileAttributeViewインターフェースのスーパーインターフェースである

FileOwnerAttributeViewインターフェースではオーナを設定するsetOwnerメソッドを定義してます。

では、まずWindowsで複数のアトリビュートをまとめて設定してみましょう。

// 対象となるパスPath path = Paths.get("var.txt");
// Windows用のDosFileAttributeViewオブジェクトを取得DosFileAttributeView view = Files.getFileAttributeView(path, DosFileAttributeView.class);
// BasicFileAttributeViewインターフェースで定義されたメソッドlong now = System.currentTimeMillis();view.setTime(FileTime.fromMillis(now), // 最終更新日時                   FileTime.fromMillis(now), // 最終アクセス日時                   FileTime.fromMillis(now)); // 作成日時
// DosFileAttributeViewインターフェースで定義されたメソッド// システムファイルとして設定するview.setSystem(true);

setTimeメソッドは、第1引数が最終更新日時、第2引数が最終アクセス日時、第3引数が

作成日時になります。これらの値を設定しない場合は、引数にnullを指定します。

そして、trueを引数にしてsetSystemメソッドをコールすることで、

ファイルをしsてむファイルとして設定しています。

同様に、PosixFileAttributeViewインターフェースを使用して、アトリビュートを設定してみます。

// 対象となるバスPath path = Paths.get("var.txt");
// POSIX準拠OS用のPosixFileAttributeViewオブジェクトを取得PosixFileAttributeView view  = Files.getFileAttributeView(path, PosixFileAttributeView.class);
// BasicFileAttributeViewインターフェースで定義されたメソッドlong now = System.currentTimeMillis();view.setTimes(FileTime.fromMillis(now), // 最終更新日時                    FileTime.fromMillis(now); // 最終アクセス日時                    FileTime.fromMillis(now)); // 作成日時
// アカウントの情報を取得UserPrincipalLookupService lookupService = FileSystems.getDefault().getUserPrincipalLookupService();UserPrincipal owner = LookupService.loolupPrincipalByName("sakuraba");
// PosixFileAttributeViewインターフェースで定義されたメソッドview.setOwner(owner);

PosixFileAttributeViewインターフェースでは、オーナやグループを設定することができます。

オーナはUserPrincipalインターフェース、グループはGroupPrincipalインターフェースで表します。

いずれもUserPrincipalLookupServiceクラスを使用し、既存アカウントやグループから検索します。

UserPrincipalLookupServiceオブジェクトを取得するためには、

FileSystemクラスのgetUserPrincipalLookupServiceメソッドを使用します。

そして、UserPrincipalオブジェクトを検索するにはlookupPrincipalByNameメソッドを使用します。

引数は文字列でアカウント名を指定します。

GroupPricipalオブジェクトを検索するにはlookupPrincipalByGroupNameを使用します。

引数はグループ名です。

そして、得られたUserPrincipalオブジェクトをsetOwnerメソッドで指定します。

このようにAttributeViewインターフェースのサブインターフェース群を使用することで、

アトリビュートをまとめて設定することが可能です。

それでは今日はこの辺にしておきましょう。

それではまた!
人気ブログランキングへ

2016年10月26日水曜日

複数のアトリビュートの取得

みなさん。 こんばんは。

今日はファイルの複数アトリビュートの取得について見ていきましょう。

FilesクラスのgetAttributeメソッドではアトリビュートを1つずつ取得しなくてはなりません。

ファイルの作成日時と最終更新日時などの関連するアトリビュートでも、

まとめて取得することができません。

この問題はアトリビュートの取得だけでなく、設定する場合にもあてはまります。

そこで今回は複数のアトリビュートをまとめて取得する方法について見てみましょう。

取得するアトリビュートはOSごとに異なるため、3種類のインターフェースが提供されています。

BasicFileAttributesインターフェースはOSによらない、

共通のアトリビュートを取得するために使用します。

OSに依存するアトリビュートは、BasicFileAttributesインターフェースのサブインターフェースとして

定義されています。

Windows用に使用するのがDosFileAttributesインターフェース、LinuxやMac OS Xのような

POSIX準拠のOS用に使用するのがPosixFileAttributesインターフェースです。

では、これらのインターフェースを使用してアトリビュートを取得してみましょう。

取得するのはFilesクラスのreadAttributesメソッドを使用します。

readAttributesメソッドの第1引数はPathインターフェース、第2引数はClassクラスです。

第2引数でどのインターフェースを取得するかを指定します。

取得できないインターフェースを指定した場合、nullが返ります。

たとえば、WindowsでPosixFileAttributesオブジェクトを取得しようとしても、nullが返ります。

第3引数は可変長引数で、引数の型はLinkOption列挙型です。

リンクを追わないようにする場合、第3引数にNOFOLLOW_LINKSを指定します。

まず、Windowsのアトリビュートを取得してみます。

// 対象となるバス
Path path = Paths.get("var.txt");
// Windows用のDosFileAttributesオブジェクトの取得
DosFileAttributes attributes = Files.readAttributes(path, DosFileAttributeView.class);
// BasicFileAttributesインターフェースで定義されたメソッド
System.out.println("Creation Time: " + attributes.creationTime());
System.out.println("Last Modified Time: " + attributes.lastModifiedTime());
System.out.println("Last Access Time: " + attributes.lastAccessTime());
System.out.println("File: " + attributes.isRegularFile());
System.out.println("Directory; " + attributes.isDirectory()):
System.out.println("Symbolic Link: " + attributes.isSymbolicLink());
// DosFileAttributesインターフェースで定義されたメソッド
System.out.println("System File: " + attributes.isSystem());

readAttributesの第2引数にDosFileAttribute.classを指定することで、

DosFileAttributesオブジェクトを取得できます。

DosFileAttributesインターフェースでは、スーパーインターフェースのBasicFileAttributes

インターフェースで定義されたOSに共通のアトリビュートを取得するメソッドと、

Windowsだけで取得可能なアトリビュートを取得できるメソッドを使用することができます。

ファイルの作成時を取得するcreationTimeメソッドなどはBasicFileAttributesインターフェース

で定義されたメソッドです。

これに対し、isSystemメソッドがDosFileAttributesインターフェースで定義されたメソッドになります。

同様にLinuxやMac OS X のようなPOSIX準拠OSでもアトリビュートをまとめて取得してみましょう。

// 対象となるパス
Path path = Paths.get("var.txt");
// POSIX準拠OS用のPosixFileAttributesオブジェクトの取得
PosixFileAttributes attributes = Files.readAttributes(path, PosixFileAttributeView.class);
// BasicFileAttributesインターフェースで定義されたメソッド
System.out.println("Creation Time: " + attributes.creationTime());
System.out.println("Last Modified Time: " + attributes.lasoModifiedTime());
System.out.println("Last Access TIme: " + attributes.lastAccessTime());
System.out.println("File: " + attributes.isRegularFile());
System.out.println("Directory: " + attributes.isDirectory());
System.out.println("Symbolic Link: " + attributes.isSymbolicLink());
// PosixFileAttributesインターフェースで定義されたメソッド
System.out.println("Owner: " + attributes.owner());
System.out.println("Permission: " + PosixFilePermissions.toString(attributes.permissions()));

DosFileAttributesインターフェースの代わりにPosixFileAttributesインターフェースを使用

していること以外はほぼ同じです。

ファイルの属性をまとめて取得できることでプログラミングでできる幅が広がりましたね!

では今日はこの辺で!

人気ブログランキングへ