"Input/Ouput, 16 ans après" à Devoxx France 2012

Preview:

DESCRIPTION

Ma présentation "Input/Ouput, 16 ans après" a Devoxx France 2012

Citation preview

1

Abstract

• Revue rapide des I/O avec Java

• Vue d’ensemble de l’API NIO2

• Fournie avec Java SE 7

• Pour les développeurs que nous sommes

2

Speaker

http://www.jmdoudoux.fr

@jmdoudoux

Auteur de 2 didacticiels

sous licence GNU FDL

Développons en Java (2400 pages)

Développons en Java avec Eclipse (600 pages)

Membre du et du CA du

3

Agenda

• Les entrées / sorties avec Java

• NIO 2

• L’API Filesystem

• Lectures / écritures : API mises à jour

• Les E/S asynchrones (Asynchronous I/O)

4

5

Les entrées / sorties avec Java

L’API java.io

• Depuis Java 1.0

• Flux : octets (Input/OutputStream)

caractères (Reader/Writer)

• Fichiers, In Memory, Pipes, Sérialisation, …

• Filtre, Buffer, … (adaptateur, décorateur)

• La classe File

6

7

Java 1.1 getCanonicalPath()

Java 1.2 getParentFile(), getAbsoluteFile(), getCanonicalFile(),

toURL(), isHidden(), createNewFile(), deleteOnExit(),

listFiles(), setLastModified(), setReadOnly(), listRoots(),

createTempFile(), compareTo()

Java 1.4 création à partir d’une URI, toURI()

Java 6 setWritable(), setReadable(), setExecutable(), canExecute(),

getTotalSpace(), getFreeSpace(), getUsableSpace()

Les évolutions de la classe File

Les défauts de java.io

• Le manque d’opérations basiques (copie, déplacement de

fichiers, …)

• Ne fonctionne pas de la même manière sur toutes les

plateformes

• Support limité des liens symboliques et des méta-données

• …

8

Les défauts de la classe File

• Encapsule le chemin et les opérations

• Gestion de certaines erreurs : certaines méthodes renvoient

un booléen sans lever d’exceptions (delete() par exemple)

• Manque de performance de certaines méthodes

• …

9

New IO (NIO)

• Java 1.4

• utilisation de blocs et non de flux

• les canaux et sélecteurs (channels et selectors),

• les tampons (buffers),

• encodage et décodage de caractères (charset)

10

11

NIO 2

NIO 2

Inclus dans Java SE 7

JSR 203 : ajout de fonctionnalités à l’API NIO

• Accès et manipulation du système de fichiers

• Mise à jour de l’API existante

• Canaux asynchrones (asynchronous channels)

• …

12

L’API FileSystem

• API moderne et complète

• Accès et gestion des systèmes de fichiers (fichiers,

répertoires, liens symboliques, …)

• Support des méta-datas

• Parcours des répertoires, notifications

• Extensible

13

L’API FileSystem

14

15

Les chemins

L’interface Path

• Immuable, encapsule tout ou partie du chemin d’un élément

du système de fichiers

• Dépendant du système de fichier

• Chemin encapsulé absolu ou relatif existe ou non

• Pas de gestion des extensions des fichiers

16

Obtenir une instance de type Path

17

Paths.get("monfichier.txt"); Paths.get("jm/AppData/Local/Temp/monfichier.txt");

Paths.get("C:/Users/jm/AppData/Local/Temp/monfichier.txt");

Paths.get("C:\\Users\\jm\\AppData\\Local\\Temp\\monfichier.txt");

Paths.get(URI

.create("file:///C:/Users/jm/AppData/Local/Temp/monfichier.txt"));

Paths.get(System.getProperty("java.io.tmpdir"), "monfichier.txt");

FileSystems.getDefault().getPath("logs", "access.log");

// Sur Unix

// Path path = Paths.get("/home/jm/temp/monfichier.txt");

Path : gérer les éléments

Méthodes pour gérer les éléments hiérarchiques du chemin

18

Path getFileName() nom du dernier élément

Path getParent() chemin parent ou null

Path getRoot() racine du chemin ou null

Path subPath(int, int) sous chemin

Path getName(int) élément à l’index fourni

int getNameCount() nombre d’éléments dans le chemin

Path : gérer les éléments

19

toString() = C:\Users\jm\AppData\Local\Temp\monfichier.txt

getFileName() = monfichier.txt

getRoot() = C:\

getName(0) = Users

getNameCount() = 6

getParent() = C:\Users\jm\AppData\Local\Temp

subpath(0,3) = Users\jm\AppData

toString() = jm\AppData\Local\Temp\monfichier.txt

getFileName() = monfichier.txt

getRoot() = null

getName(0) = jm

getNameCount() = 5

getParent() = jm\AppData\Local\Temp

subpath(0,3) = jm\AppData\Local

Path : manipuler le chemin

• Path normalize() :

supprime les éléments redondants (par exemple . et ..),

purement syntaxique

• Path resolve(Path) :

combiner deux chemins

• Path relativize(Path) :

chemin relatif entre le chemin et celui fourni

20

Manipuler un Path

21

path = Paths.get("C:/Users/admin/

./../jm/AppData/Local/Temp/./monfichier.txt");

System.out.println("normalize() = " + path.normalize());

normalize() = C:\Users\jm\AppData\Local\Temp\monfichier.txt

Path path = Paths.get("C:/Users/jm/AppData/Local/");

Path nouveauPath = path.resolve("Temp/monfichier.txt");

System.out.println(nouveauPath);

nouveauPath = path.resolve("C:/Temp");

System.out.println(nouveauPath);

C:\Users\jm\AppData\Local\Temp\monfichier.txt

C:\Temp

Path : comparer des chemins

Plusieurs méthodes pour comparer les chemins

• int compareTo(Path other)

• boolean endsWith(Path other)

boolean endsWith(String other)

• boolean startsWith(Path other)

boolean startsWith(String other)

• …

22

Path : convertir des chemins

Plusieurs méthodes pour convertir les chemins

• Path toAbsolutePath() :

retourner le chemin absolu du chemin

• Path toRealPath(LinkOption…) :

retourner le chemin physique avec résolution des liens

symboliques selon les options fournies

23

Path : intégration dans l’existant

• Path File.toPath()

• File Path.toFile()

• URI Path.toUri() : retourner le chemin sous la forme d’une

URI

24

25

La manipulation des fichiers

et des répertoires

Glob

• Pattern à appliquer sur un élément du système de fichiers

• Sous ensemble des expressions régulières :

* ** ? [] {} \

Exemples :

26

*.java éléments dont le nom fini par .java

??? éléments dont le nom est composé de trois

alphanumériques

A*.java éléments dont le nom commence par un a et se

termine par .java

*[0-9]* éléments dont le nom contient au moins un

chiffre

*.{htm,html} éléments dont le nom se termine par htm ou html

Glob

27

final Path file1 = Paths.get("C:/java/test/test.java");

final Path file2 = Paths.get("C:/java/test/test.txt");

final Path file3 = file1.getFileName();

String pattern = "glob:**/*.{java,class}";

System.out.println("Pattern " + pattern);

PathMatcher matcher = FileSystems.getDefault().getPathMatcher(pattern);

System.out.println(file1 + " " + matcher.matches(file1));

System.out.format("%-22s %b\n", file2, matcher.matches(file2));

System.out.format("%-22s %b\n", file3, matcher.matches(file3));

System.out.println("");

pattern = "glob:*.java";

System.out.println("Pattern " + pattern);

matcher = FileSystems.getDefault().getPathMatcher(pattern);

System.out.println(file1 + " " + matcher.matches(file1));

System.out.format("%-22s %b\n", file3, matcher.matches(file3));

La classe Files

• Création : createDirectory(), createFile(), createLink(),

createSymbolicLink(), createTempFile(), createTempDirectory(), …

• Manipulation : delete(), move(), copy(), …

• Type d’un élément : isRegularFile(), isDirectory(),

probeContentType(), …

• Méta-données et permissions : getAttributes(),

getPosixFilePermissions(), isReadable(), isWriteable(), size(),

getFileAttributeView(), …

28

Copier un fichier

29

Path source = Paths.get("c:/java/fichier.txt");

Path cible = Paths.get("c:/java/fichier_copie.txt");

try {

Files.copy(source, cible, REPLACE_EXISTING,

COPY_ATTRIBUTES);

// java.lang.UnsupportedOperationException

// Files.move(source, cible, REPLACE_EXISTING,

// COPY_ATTRIBUTES, ATOMIC_MOVE);

} catch(IOException ioe) {

// traitement en cas d’erreur

}

Copie d’un répertoire

Déplacer ou renommer un fichier

30

Path source = Paths.get("c:/java/fichier.txt");

Path cible = Paths.get("c:/java/fichier_copie.txt");

try {

Files.copy(source, cible, REPLACE_EXISTING,

COPY_ATTRIBUTES);

// java.lang.UnsupportedOperationException

// Files.move(source, cible, REPLACE_EXISTING,

// COPY_ATTRIBUTES, ATOMIC_MOVE);

} catch(IOException ioe) {

// traitement en cas d’erreur

}

Déplacement d’un répertoire

Supprimer un fichier

31

Path path = Paths.get("C:/java/test/monfichier_copie.txt");

try {

Files.delete(path);

} catch (NoSuchFileException nsfee) {

// traitement en cas d’erreur

} catch (DirectoryNotEmptyException dnee) {

// traitement en cas d’erreur

} catch (IOException ioe) {

// traitement en cas d’erreur

}

Path path = Paths.get("C:/java/test/monfichier_copie.txt");

try {

Files.deleteIfExists(path);

} catch (DirectoryNotEmptyException dnee) {

// traitement en cas d’erreur

} catch (IOException ioe) {

// traitement en cas d’erreur

}

Suppression d’un répertoire vide

Les liens symboliques

• Support optionnel selon le FS (Unix)

• Suivis par défaut avec quelques exceptions :

delete(), move(), walkFileTree()

• Files.isSameFile()

• Files.isSymbolicLink()

• Files.readSymbolicLink()

32

Créer un lien / un lien symbolique

33

Path lien = Paths.get("C:/java/test/monlien");

Path cible = Paths.get("C:/java/test/monfichier.txt");

Files.createLink(lien, cible);

if (Files.isSameFile(lien, cible)) {

System.out.println("Identique");

} else {

System.out.println("Non identique");

}

Path lien = Paths.get("/home/jm/monlien");

Path cible = Paths.get("/home/jm/monfichier.txt");

Files.createSymbolicLink(lien, cible);

if (Files.isSameFile(lien, cible)) {

System.out.println("Identique");

} else {

System.out.println("Non identique");

}

34

Les événements sur un répertoire

L’interface WatchService

• Notifications de changements dans un répertoire

• Abonnement à des événements lors de la création,

modification, suppression de fichiers

• Evite d’avoir à écrire du code de type pooling ou une API

open source (JNotify ou JPathWatch)

• Non récursif, performant

35

L’utilisation de WatchService

Mise en œuvre particulière :

• Instanciation avec FileSystem.newWatchService()

• Enregistrement avec Path.register()

(Path implémente Watchable)

• Obtenir et traiter les événements

36

WatchService

37

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

Path dir = Paths.get("c:/java/test");

WatchKey key = dir.register(watcher, ENTRY_CREATE)

for (;;) {

try {

key = watcher.take();

} catch (InterruptedException ex) {

ex.printStackTrace();

}

for (WatchEvent<?> event : key.pollEvents()) {

if (event.kind() == StandardWatchEventKinds.ENTRY_CREATE) {

Path name = (Path) event.context();

System.out.format(event.kind() + " " + "%s created", name);

}

}

key.reset();

}

38

Les méta-données

Les attributs

• Gestion complète des attributs qui sont dépendants du

système sous jacent

• Taille, type d’éléments, caché, …

• Attributs communs (BasicFileAttributes) et spécifiques

(DosFileAttributes, PosixFileAttributes)

• Méthodes de la classe Files

39

Les attributs DOS

40

Path fichier = Paths.get("c:/java/test/test.txt");

FileTime now = FileTime.fromMillis(System.currentTimeMillis());

Files.setLastModifiedTime(fichier, now);

Files.setAttribute(fichier, "dos:hidden", true);

DosFileAttributes attr = Files.readAttributes(fichier, DosFileAttributes.class);

System.out.println(attr.isReadOnly());

System.out.println(attr.isHidden());

System.out.println(attr.isRegularFile());

System.out.println(attr.isSystem());

System.out.println(attr.lastModifiedTime());

Les attributs Posix

• Énumération PosixFilePermission

• PosixFilePermissions : helper

• FileAttribute : encapsule les attributs

41

Attention aux restrictions de droits via umask

ou via le répertoire parent

Les attributs Posix

42

Path fichier = Paths.get("/home/jm/test.txt");

PosixFileAttributes attrs = Files.readAttributes(fichier, PosixFileAttributes.class);

UserPrincipal owner = attrs.owner();

GroupPrincipal group = attrs.group();

System.out.println(owner);

System.out.println(group);

Path fichier = Paths.get("/home/jm/test.txt");

Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rw-rw-rw-");

FileAttribute<Set<PosixFilePermission>> attr =

PosixFilePermissions.asFileAttribute(perms);

Files.createFile(fichier, attr);

Les attributs avec les vues

• Les vues permettent d’obtenir les attributs d’un même

groupe en bloc -> performance

• Les vues sont spécialisées

• Readonly ou mutable

• Création possible de vues personnalisées

43

Les différentes vues

AttributeView

FileAttributeView

BasicFileAttributeView

DosFileAttributeView

PosixFileAttributeView

FileOwnerAttributeView

AclFileAttributeView

PosixFileAttributeView

UserDefinedFileAttributeView

FileStoreAttributeView

44

BasicFileAttributView

45

Path fichier = Paths.get("c:/java/test.txt");

BasicFileAttributeView view = Files.getFileAttributeView(fichier,

BasicFileAttributeView.class);

BasicFileAttributes attributes = view.readAttributes();

FileTime creationTime = attributes.creationTime();

FileTime lastAccessTime = attributes.lastAccessTime();

boolean isRegularFile = attributes.isRegularFile();

La classe FileStore

• Encapsule un système de stockage : un disque dur, une

partition, …

• Obtenir des informations

• Connaître les AttributViews supportées

• FileStore Files.getFileStore(Path)

46

FileStore

47

for (final FileStore store : FileSystems.getDefault().getFileStores()) {

System.out.println(store);

System.out.println("nom : " + store.name() + ", type : " + fileStore.type());

System.out.println("Support BasicFileAttribute : "

+ store.supportsFileAttributeView(BasicFileAttributeView.class));

System.out.println("Support DosFileAttribute : "

+ store.supportsFileAttributeView(DosFileAttributeView.class));

System.out.println("Support PosixFileAttribute : "

+ store.supportsFileAttributeView(PosixFileAttributeView.class));

}

final int UN_GIGA = 1024 * 1024 * 1024;

for (final FileStore store : FileSystems.getDefault().getFileStores()) {

try {

final long total = store.getTotalSpace() / UN_GIGA;

final long used = (store.getTotalSpace() - store.getUnallocatedSpace())

/ UN_GIGA;

final long avail = store.getUsableSpace() / UN_GIGA;

System.out.format("%-20s total=%5dGo used=%5dGo avail=%5dGo%n",

store, total, used, avail);

} catch (final IOException e) {

e.printStackTrace();

}

}

La classe FileSystem

• Encapsule un système de fichiers

• Fabrique pour des objets de l’API

• Obtenir l’instance du système par défaut

FileSystem fs = FilesSystems.getDefault();

48

La classe FileSystem

• Extensible

• Peut permettre d’offrir différentes vues d’un système de

fichiers

(cacher des fichiers sensibles, accès en lecture seule, …)

• N’a pas besoin d’être lié à un « vrai » système de fichiers

49

Les providers de FileSystem

• Invoquer une fabrique (FileSystems) pour obtenir une

implémentation spécifique

• Utiliser ou de créer des fournisseurs de FileSystem

class MonFileSystem extends FileSystem;

• java.nio.file.spi.FileSystemProvider

50

Un provider pour les zip

• Permet de traiter le contenu d’un zip comme un système de

fichiers

• Facilite l’utilisation des archives de type zip

• Fourni en standard

51

Afficher un fichier d’un zip

52

Path jarfile = Paths.get("c:/java/archive.jar");

FileSystem fs = FileSystems.newFileSystem(jarfile, null);

Path mf = fs.getPath("META-INF", "MANIFEST.MF");

try (BufferedReader readBuffer =

Files.newBufferedReader(mf, Charset.defaultCharset())) {

String ligne = "";

while ((ligne = readBuffer.readLine()) != null) {

System.out.println(ligne);

}

}

Extraire et ajouter un fichier à un zip

53

Path jarfile = Paths.get("c:/java/archive.jar");

FileSystem fs = FileSystems.newFileSystem(jarfile, null);

Path cible = Paths.get("c:/java/MANIFEST.MF");

Files.deleteIfExists(cible);

// extaire un élément de l'archive

Files.copy(fs.getPath("/META-INF/MANIFEST.MF"), cible);

54

Parcourir le contenu d’un répertoire

DirectoryStream et WalkFileTree

L’interface DirectoryStream

• Itération sur le contenu d’un répertoire

• Performance sur de gros répertoires, consomme moins de

ressources

• Possibilité d’appliquer un filtre avec un glob

• Invoquer la méthode close() après utilisation

55

DirectoryStream

56

Path dir = Paths.get("C:/java/projets");

try (DirectoryStream<Path> stream =

Files.newDirectoryStream(dir, "*.java")) {

for (Path entry : stream) {

System.out.println(entry.getFileName());

}

}

WalkFileTree avec FileVisitor

• Parcourir une arborescence en utilisant le design pattern

Visiteur (opérations récursives)

• Files.walkFileTree()

• FileVisitor invoqué

sur chaque fichier (visitFile() / visitFileFailed())

sur chaque répertoire (preVisitDirectory()/postVisitDirectory())

57

WalkFileTree avec FileVisitor

• SimpleFileVisitor : contrôle du parcours par la valeur de

retour (CONTINUE, SKIP_SUBTREE, TERMINATE, …)

• Liens symboliques non suivis par défaut

(FileVisitOption.FOLLOW_LINKS)

• détection des références circulaires (méthode

visitFileFailed() avec FileSystemLoopException)

58

WalkFileTree

59

Path dir = Paths.get("C:/java/projets");

Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {

public FileVisitResult visitFile(Path file,

BasicFileAttributes attrs) throws IOException {

String nom = file.getFileName().toString();

if (nom.endsWith(".java")) {

System.out.println("Fichier : " + nom);

}

return FileVisitResult.CONTINUE;

}

public FileVisitResult preVisitDirectory(Path dir,

BasicFileAttributes attrs) throws IOException {

System.out.println("Repertoire : " + dir);

return FileVisitResult.CONTINUE;

}

});

60

Lectures / écritures

Mises à jour de l’API

Gestion des IO avec Java

61

IO

Java 1.0

Synchrone

Bloquant

File InputStream OutputStream Reader Writer Socket RandomAccessFile

NIO

Java 1.4 (JSR 51)

Synchrone

Non bloquant

FileChannel SocketChannel ServerSocketChannel (Charset, Selector, ByteBuffer)

NIO 2

Java 7 (JSR 203)

ASynchrone

Non bloquant

Path AsynchronousFileChannel AsynchronousByteChannel AsynchronousSocketChannel AsynchronousServerSocketChannel SeekableByteChannel

Les méthodes de la classe Files

62

Besoins

avancés

Besoins

simples

Lire et écrire tout le contenu d’un fichier

63

byte[] bytes = Files.readAllBytes(path);

List<String> lignes = Files.readAllLines(path,

Charset.defaultCharset());

Files.write(path, bytes);

Files.write(path, lignes, Charset.defaultCharset());

Ecrire dans un fichier texte

64

final Path fichier =

Paths.get("c:/java/test/donnees.txt");

final Charset charset = Charset.forName("UTF8");

final String contenu = "Prix 10€";

Files.deleteIfExists(fichier);

try (BufferedWriter writer =

Files.newBufferedWriter(fichier, charset)) {

writer.write(contenu, 0, contenu.length());

} catch (IOException ioe) {

// traitement de l'erreur

}

}

L’interface SeekableByteChannel

• ByteChannel qui gère une position

• Channel équivalent à RandomAccessFile

• Files.newSeekableByteChannel()

• CREATE, CREATE_NEW, READ, WRITE, APPEND,

TRUNCATE_EXISTING, NOFOLLOW_LINKS, SYNC,

DSYNC, …

65

SeekableByteChannel

66

ByteBuffer donneesBonjour =

ByteBuffer.wrap("Bonjour".getBytes());

ByteBuffer donneesBonsoir =

ByteBuffer.wrap("Bonsoir".getBytes());

Path path = Paths.get("C:/java/test/fichier.bin");

Files.deleteIfExists(path);

try (FileChannel fileChannel = FileChannel.open(path,

StandardOpenOption.CREATE, StandardOpenOption.WRITE,

StandardOpenOption.SYNC)) {

fileChannel.position(100);

fileChannel.write(donneesBonjour);

}

try (SeekableByteChannel sbc = Files.newByteChannel(path,

StandardOpenOption.WRITE, StandardOpenOption.SYNC)) {

sbc.position(200);

sbc.write(donneesBonsoir);

}

67

Lectures / écritures

asynchrones

Les canaux asynchrones

• Channel : opérations non bloquantes (lecture, écriture)

• AsynchronousChannel : opérations asynchrones (exécutées

dans un thread)

• Contrôle des opérations après leur initialisation via deux

solutions :

java.util.concurrent.Future

java.nio.channels.CompletionHandler

68

Canaux async avec CompletionHandler

• Callback qui sera invoqué lorsque l’opération se termine

(bien ou mal)

• Paramétré avec le type de résultat et un objet en

attachement (contexte qui peut être null)

69

interface CompletionHandler<V, A> {

void completed(V result, A attachment);

void failed(Throwable t, A attachment);

}

Canaux async : les groupes

• Les CompletionHandler sont invoqués par les threads

• AsynchronousChannelGroup encapsule un pool de threads

• FixedThreadPool

• CachedThreadPool

70

71

Conclusion

Conclusion

• NIO2 apporte de nombreuses fonctionnalités, attendues

depuis longtemps

• Implémentation de bas niveau de certaines, mais utilisables

dans différents contextes

• Pour allez plus loin : Javadoc, Java tutorials (Basic I/O),

exemples du JDK (sample/nio/file)

72

73

Recommended