Membuat Sebuah DirectoryStream Di Java 7

Kali ini meliputi antarmuka DirectoryStream. Dalam posting ini saya akan mengimplementasikan DirectoryStream kita sendiri yang akan iterate atas file dalam seluruh struktur direktori, bukan hanya sebuah direktori tunggal. Tujuan saya pada akhirnya adalah untuk memiliki sesuatu yang bekerja mirip dengan metode Dir.glob Ruby.


Berikut adalah persyaratannya :

  1. Mulai dari benda Jalan diberikan, secara rekursif harus melalui setiap direktori untuk mencari file yang cocok dengan pola tertentu.
  2. Perlu panggilan metode tunggal, sesuatu seperti DirUtils.glob (Jalan jalan, pola String).
  3. Kami ingin pencarian menjadi asynchronous, sehingga kami dapat iterate atas file saat ditemukan.

Rencana untuk memenuhi tujuan kami adalah lurus ke depan :D :
  1. Buat objek DirectoryStream.Filter dari pola yang diberikan.
  2. Gunakan metode Files.walkFileTree untuk secara rekursif mencari file yang cocok dengan pola kita.
  3. Jalankan pencarian di thread terpisah, dan ketika file yang cocok ditemukan, menempatkannya dalam antrian.
  4. Iterator DirectoryStream akan menarik file dari antrian saat tersedia

Pengungkapan: Posting ini hanyalah upaya untuk melihat apakah membuat DirectorySteam, asynchronous rekursif akan bekerja, saya membuat ada jaminan tentang kode disajikan di sini, YMMV.
Filter
DirectoryStream memiliki antarmuka statis, Filter, yang digunakan untuk menentukan apakah objek jalan harus diterima. Obyek filter dibangun dengan terlebih dahulu menciptakan objek PathMatcher kemudian menggunakan PathMatcher di Filter itu menerima metode :

public static DirectoryStream.Filter buildGlobFilter(String pattern) {
        final PathMatcher pathMatcher = getPathMatcher("glob:"+pattern);
        return new DirectoryStream.Filter() {
            @Override
            public boolean accept(Path entry) throws IOException {
                return pathMatcher.matches(entry);
            }
        };
    }
 Search Rekursif

Untuk melakukan pencarian kita akan penggunaan metode Files.walkFileTree dengan benda FunctionVisitor. FunctionVisitor adalah subclass dari SimpleFileVisitor yang membutuhkan Fungsi Guava sebagai argumen konstruktor. Fungsi yang diberikan disebut sebagai setiap file dikunjungi dan diperiksa oleh objek DirectoryStream.Filter untuk pertandingan. Kami membungkus penciptaan obyek Fungsi dalam panggilan metode :


private Function getFunction(final Filter filter) {
        return new Function() {
            @Override
            public FileVisitResult apply(Path input) {
                try {
                    if (filter.accept(input.getFileName())) {
                        pathsBlockingQueue.offer(input);
                    }
                } catch (IOException e) {
                    throw new RuntimeException(e.getMessage());
                }
                return (pathTask.isCancelled()) ? FileVisitResult.TERMINATE : FileVisitResult.CONTINUE;
            }
        };
    }
 Top 6 Hapus adalah memeriksa untuk pertandingan pada nama file. Jika kecocokan ditemukan, objek jalan ditempatkan dalam LinkedBlockingQueue,, pathsBlockingQueue on line 7. Pada baris 12, kita melihat variabel contoh pathTask, yang merupakan pegangan FutureTask ke thread pencarian. Jika pathTask telah dibatalkan kami mengakhiri pencarian, jika tidak melanjutkan.
 
Thread Pencarian

Untuk menjalankan pencarian, kita membungkus metode Files.walkFileTree dalam sebuah objek Callable, yang digunakan sebagai argumen konstruktor untuk pathTask, objek FutureTask. Dengan menggunakan FutureTask, kita memiliki hook ke membatalkan pencarian serta mampu memeriksa status itu berjalan.


 private void findFiles(final Path startPath, final Filter filter) {
        pathTask = new FutureTask(new Callable() {
            @Override
            public Void call() throws Exception {
                Files.walkFileTree(startPath, new FunctionVisitor(getFunction(filter)));
                return null;
            }
        });
        start(pathTask);
    }
Pada baris 5 kita melihat panggilan ke metode getFunction dari contoh sebelumnya. Top 9 awal pemanggilan metode digunakan untuk spin off pencarian di thread sendiri itu:

private void start(FutureTask futureTask) {
        new Thread(futureTask).start();
    }
 Kami menggunakan objek Thread bukan ExecutorService karena kita hanya perlu sebuah thread tunggal untuk mengeksekusi sekali. Karena kita tidak menyampaikan tugas-tugas berikutnya, ExecutorService sebenarnya tidak diperlukan.

Pelaksana DirectoryStream A

Untuk menerapkan sebuah DirectoryStream ada dua metode yang perlu diganti - iterator dan dekat. DirectoryStream memperluas antarmuka AutoCloseable, sehingga bila digunakan dengan percobaan-dengan-sumber konstruk, metode dekat secara otomatis dipanggil, melepaskan sumber daya yang mendasari. Bagian yang paling menarik dari kode ini override metode iterator:
 

public Iterator iterator() {
        confirmNotClosed();
        findFiles(startPath, filter);
        return new Iterator() {
            Path path;
            @Override
            public boolean hasNext() {
                try {
                    path = pathsBlockingQueue.poll();
                    while (!pathTask.isDone() && path == null) {
                        path = pathsBlockingQueue.poll(5, TimeUnit.MILLISECONDS);
                    }
                    return (path != null) ? true : false;
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
                return false;
            }

            @Override
            public Path next() {
                return path;
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException("Removal not supported");
            }
        };
    }
 Top 2 kita periksa untuk melihat apakah iterator sebelumnya telah ditutup dan melemparkan UnsupportedOperationException jika itu terjadi. Jalur 3 kicks off benang pencarian seperti yang kita lihat dari contoh sebelumnya. Metode hasNext adalah tempat nyata "otak" dari kelas berada. Sejak pencarian adalah asynchronous DirectoryStream akan mencoba untuk iterate atas file segera, tetapi perlu ada koordinasi dengan beberapa benang pencarian karena menemukan objek jalan pencocokan dan menempatkan mereka ke dalam antrian. On line 9 pertama kita sebut polling (panggilan memblokir non) dan mengatur hasil untuk variabel path didefinisikan pada baris 5. Jika objek adalah null jalan kita drop ke dalam while loop yang memanggil polling lagi, tapi kali ini itu panggilan menghalangi dengan batas waktu 5 milidetik. Kami akan tinggal di loop yang sampai jalur non-null dikembalikan atau benang pencarian telah selesai atau dibatalkan. Pada baris 13 kita memeriksa null untuk menentukan apakah kita memiliki hasil yang valid atau jika ada benda jalan lagi untuk kembali. Jika hasNext kembali benar, metode selanjutnya mengembalikan objek jalan yang diambil oleh panggilan hasNext sebelumnya. Proses ini akan berlanjut sampai semua hasil telah kembali.

Conclusion

Putting this all together we now have the AsynchronousRecursiveDirectoryStream class. By combining our new class with DirUtils we can iterate over an entire directory tree:
try(DirectoryStream directoryStream = DirUtils.glob(path,"*")){
   for(Path path : directoryStream){
      ....
    }
}
It is hoped that the reader found this useful. As always, comments and suggestions are welcomed. Thanks for your time.

Previous
Next Post »
0 Komentar

Terimakasih telah berkomentar