A Java7 megjelenését megelőzően nagy várakozás övezte a NIO.2 újdonságait, aztán elmúlt a hype. Ennek oka valószínűleg az lehet, hogy a Java jelenleg két erős területén – az Android és a Java EE platformon – nincs igazán szükség a fájlműveletek támogatására. Az Android esetén megtűrt dolognak számít a fájlrendszer, az SQLite alapú tárterület a támogatott; Java EE esetén pedig deklaráltan nem alapozunk a fájlrendszerre.
Nem mindenki lehet Android vagy Java EE fejlesztő – szükséges az IDE-k fejlesztése is :), ezért nézzük át, milyen előnyöket ad a NIO.2, amely alapvetően a Java eddigi fájlrendszer modelljét bővíti ki az alábbiakkal:
- Platform független fájlrendszer modell
- Fájlrendszer-fa feltérképezés
- Az állományműveletek atomi támogatása (másolás, törlés, mozgatás)
- Soft- és hard-link támogatás
- Fájl attribútumok kezelése
- Változás követés
- SPI támogatás
Nézzük ezeket egyenként...
Platform független fájlrendszer modell
A Java7 NIO.2 kapcsán került bevezetésre a Path osztály, amely az eddigi megoldások mellett lehetővé teszi az elérési útvonalak platformra való fordítását. Emlékezzünk: egy csomó problémánk volt abból, hogy Unix like rendszereken a fájlrendszer kiinduló pontja a / volt, míg Windows és Windows fájlkezelését emuláló rendszerek (emlékeim szerint például a Symbian) esetén voltak meghajtók, így több gyökere is volt a fájlrendszereknek, illetve az elérési útvonalon a szinteket elválasztó jel a / helyett a \ volt, így a multiplatformos programjaink tele voltak platformfüggő elágazásokkal, illetve File.separator összefűzésekkel. Nem beszélve a soft- és hard-link támogatásról, a valódi elérési út felderítéséről és a többi apróságról... Na, ennek végre vége...
Fájlrendszer-fa feltérképezés
A fájlrendszer-fa feltérképezése azon igények egyike, amelyre eddig még nem volt szükségem, de örülök, hogy a NIO.2 ezt már támogatja, az alkalmazás szerverek, illetve a különféle szerver oldali Java megoldások fejlesztői nyilván örülnek egy ilyen lehetőségnek. A módszer a Visitor minta alapján épül fel, ezért hozzunk létre egy saját Visitor-t, amely egyszerűen csak kiírja a kapott Path példányt:
Code Block | ||||
---|---|---|---|---|
| ||||
public class FileVisitor extends SimpleFileVisitor<Path> { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { System.out.println(file); return FileVisitResult.CONTINUE; } } |
Ezek után már csak meg kell hívnunk:
Code Block | ||||
---|---|---|---|---|
| ||||
Path basePath = Paths.get("."); try { Files.walkFileTree(basePath, new FileVisitor()); } catch (IOException ex) { Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex); } |
Az eredmény nem marad el:
Code Block |
---|
./pom.xml ./src/test/java/hu/javaforum/testproject/AppTest.java ./src/main/java/hu/javaforum/testproject/App.java ./target/classes/hu/javaforum/testproject/App$FileVisitor.class ./target/classes/hu/javaforum/testproject/App$1.class ./target/classes/hu/javaforum/testproject/App.class |
Az állományműveletek atomi támogatása
Sajnos a Java hetes verzióig kellett várnunk arra, hogy egy fájlt át tudjunk mozgatni vagy akár csak átnevezni... ez is már a múlté...
Code Block |
---|
> ls -l /tmp/test* -rw-r--r-- 1 auth.gabor users 5 aug 12 12.30 /tmp/test |
Tekintsük a fenti egy darab állományt, amelyet szeretnénk átmásolni test2 névre, majd átnevezni test3 névre:
Code Block | ||||
---|---|---|---|---|
| ||||
Path srcFile = Paths.get("/tmp/test"); Path dstFile = Paths.get("/tmp/test2"); Path moveFile = Paths.get("/tmp/test3"); Files.copy(srcFile, dstFile, StandardCopyOption.COPY_ATTRIBUTES); Files.move(srcFile, moveFile, StandardCopyOption.ATOMIC_MOVE); |
És az eredmény nem marad el:
Code Block |
---|
> ls -l /tmp/test* -rw-r--r-- 1 auth.gabor users 5 aug 12 12.30 /tmp/test2 -rw-r--r-- 1 auth.gabor users 5 aug 12 12.30 /tmp/test3 |
Ez Java7 előtt egy több tucat soros varázslatra volt szükség egy ilyen igény teljesítéséhez.
Soft- és hard-link támogatás
A NIO.2 előtt erre nem volt lehetőség, nem lehetett megállapítani egy elérési útról, hogy az tartalmaz-e linkeket vagy sem, most már vannak eszközeink.
Code Block |
---|
> ls -l /tmp/test* -rw-r--r-- 1 auth.gabor users 5 aug 12 12.30 /tmp/test |
Hozzunk létre egy soft- (symbolic-) és egy hard-linket:
Code Block | ||||
---|---|---|---|---|
| ||||
Path srcFile = Paths.get("/tmp/test"); Path softLinkFile = Paths.get("/tmp/test2"); Path hardLinkFile = Paths.get("/tmp/test3"); Files.createSymbolicLink(softLinkFile, srcFile); Files.createLink(hardLinkFile, srcFile); System.out.println("isSymbolicLink:"); System.out.println(srcFile + ": " + Files.isSymbolicLink(srcFile)); System.out.println(softLinkFile + ": " + Files.isSymbolicLink(softLinkFile)); System.out.println(hardLinkFile + ": " + Files.isSymbolicLink(hardLinkFile)); |
Az eredmény nem marad el:
Code Block |
---|
isSymbolicLink: /tmp/test: false /tmp/test2: true /tmp/test3: false |
A fájlrendszernél is látszik az átlinkelés, illetve a link számláló a hard-link esetén:
Code Block |
---|
> ls -l /tmp/test* -rw-r--r-- 2 auth.gabor users 5 aug 12 12.30 /tmp/test lrwxrwxrwx 1 auth.gabor users 9 aug 12 12.44 /tmp/test2 -> /tmp/test -rw-r--r-- 2 auth.gabor users 5 aug 12 12.30 /tmp/test3 |
Fájl attribútumok kezelése
Ha le szeretnénk kérdezni egy adott fájl attribútumait, akkor a legtöbb példában megemlítésre kerül a JNI vagy a széttárt kéz, mivel erre túl sok eszközünk nem volt a Java platformot tekintve, s sajnos a megfelelő megoldásra egészen a Java7 megjelenéséig várnunk kellett. A fentiekhez hasonlóan a megoldás egyszerű, csak azt kell eldöntenünk, hogy milyen attribútumokat szeretnénk kiolvasni (a teljesség igénye nélkül):
- BasicFileAttributeView
- DosFileAttributeView
- PosixFileAttributeView
- UserDefinedFileAttributeView
- AclFileAttributeView
- FileOwnerAttributeView
Fogjunk egy megfelelő áldozatot és vallassuk ki:
Code Block | ||||
---|---|---|---|---|
| ||||
BasicFileAttributes bfa = Files.readAttributes(srcFile, BasicFileAttributes.class); DosFileAttributes dfa = Files.readAttributes(srcFile, DosFileAttributes.class); PosixFileAttributes pfa = Files.readAttributes(srcFile, PosixFileAttributes.class); AclFileAttributeView afa = Files.getFileAttributeView(srcFile, AclFileAttributeView.class); FileOwnerAttributeView foa = Files.getFileAttributeView(srcFile, FileOwnerAttributeView.class); System.out.println("BasicFileAttributes:"); System.out.println(srcFile + ":creationTime " + bfa.creationTime()); System.out.println(srcFile + ":isDirectory " + bfa.isDirectory()); System.out.println(srcFile + ":isOther " + bfa.isOther()); System.out.println(srcFile + ":isRegularFile " + bfa.isRegularFile()); System.out.println(srcFile + ":isSymbolicLink " + bfa.isSymbolicLink()); System.out.println(srcFile + ":lastAccessTime " + bfa.lastAccessTime()); System.out.println(srcFile + ":lastModifiedTime " + bfa.lastModifiedTime()); System.out.println(srcFile + ":size " + bfa.size()); System.out.println("DosFileAttributes:"); System.out.println(srcFile + ":isArchive " + dfa.isArchive()); System.out.println(srcFile + ":isHidden " + dfa.isHidden()); System.out.println(srcFile + ":isReadOnly " + dfa.isReadOnly()); System.out.println(srcFile + ":isSystem " + dfa.isSystem()); System.out.println("PosixFileAttributes:"); System.out.println(srcFile + ":group " + pfa.group()); System.out.println(srcFile + ":owner " + pfa.owner()); System.out.println(srcFile + ":permissions " + pfa.permissions()); System.out.println("AclFileAttributeView:"); if (afa != null) { System.out.println(srcFile + ":getAcl " + afa.getAcl()); } System.out.println("FileOwnerAttributeView:"); if (foa != null) { System.out.println(srcFile + ":getOwner " + foa.getOwner()); } |
Majd nézzük meg, mit mesélt magáról:
Code Block |
---|
BasicFileAttributes: /tmp/test:creationTime 2012-08-12T10:30:39Z /tmp/test:isDirectory false /tmp/test:isOther false /tmp/test:isRegularFile true /tmp/test:isSymbolicLink false /tmp/test:lastAccessTime 2012-08-12T10:33:09Z /tmp/test:lastModifiedTime 2012-08-12T10:30:39Z /tmp/test:size 5 DosFileAttributes: /tmp/test:isArchive false /tmp/test:isHidden false /tmp/test:isReadOnly false /tmp/test:isSystem false PosixFileAttributes: /tmp/test:group users /tmp/test:owner auth.gabor /tmp/test:permissions [GROUP_READ, OTHERS_READ, OWNER_WRITE, OWNER_READ] AclFileAttributeView: FileOwnerAttributeView: /tmp/test:getOwner auth.gabor |
Változás követés
A fájlrendszerben történt változások figyelése fontos feladat, gondoljunk csak a kedvenc fejlesztőkörnyezetünkre, amely szeretne értesülni arról, hogy a háta mögött módosítunk egy állományt. A Java7 előtt erre különféle praktikákra volt szükség, de ez is a múlté...
Tegyük fel, hogy szeretnénk figyelni a /tmp/nio.2/ könyvtár tartalmában beálló módosulásokat:
Code Block | ||||
---|---|---|---|---|
| ||||
Path tmpDir = Paths.get("/tmp/nio.2/"); WatchService watcher = FileSystems.getDefault().newWatchService(); tmpDir.register(watcher, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_MODIFY); while (true) { WatchKey key = null; try { key = watcher.take(); } catch (InterruptedException ex) { Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex); } for (WatchEvent event : key.pollEvents()) { System.out.println(event.context() + ": " + event.kind()); } key.reset(); } |
A fenti program szerkezete viszonylag egyszerű, megmondjuk, hogy mit szeretnénk figyelni, regisztráljuk a figyelő szolgáltatásba, majd egy végtelen ciklusban figyeljük az események bekövetkeztét. Próbáljuk ki:
Code Block |
---|
> touch /tmp/nio.2/test > rm /tmp/nio.2/test > touch /tmp/nio.2/test2 > echo "abc" >/tmp/nio.2/test2 > rm /tmp/nio.2/test2 |
Ezzel egy időben az alábbit kell lássuk a programunk kimenetén:
Code Block |
---|
test: ENTRY_CREATE test: ENTRY_MODIFY test: ENTRY_DELETE test2: ENTRY_CREATE test2: ENTRY_MODIFY test2: ENTRY_MODIFY test2: ENTRY_DELETE |
Úgy gondolom, hogy sok Java fejlesztő nem fogja használni a fenti megoldásokat, mivel ahhoz az kellene, hogy a Java platform desktop környezetben is terjedjen el, de jelenleg nem tudunk túl sok példát mondani multiplatformos programra.
Viewtracker |
---|