Introducción a Java NIO



Hola y buen día a todos mis lectores, hoy, les tengo un artículo (o más bien un tutorial) de 2 partes muy interesante de la revista Java, como ya habrán leído en el título, se trata del nuevo sistema de archivos :D NIO.2, espero les guste, aquí les va...



Java SE 7 provee nuevas adiciones a los paquetes NIOlas llamadas NIO.2 APIs de JSR 203—muchas de las cuales se concentran en las APIs del sistema de archivos y canales asincrónicos.
En el pasado, la mayoría de las operaciones del sistema de archivos en Java eran realizadas usando las clases del paquete java.io que proveen una abstracción de características dependientes del sistema.
Estas clases eran débiles, por ejemplo, en tratar con permisos de archivo y manejando links simbólicos. Muchas librerías eran desarrolladas para superar estas debilidades, pero tienen el gasto adicional de contar ya sea con librerías nativas o invocaciones a procesos externos. Otras librerías eran además desarrolladas para tratar mejor con operaciones comunes del sistema de archivos (como copiar, mover y exploración) o para proveer sistemas de archivos virtuales (como archivos comprimidos, FTP y HTTP).
En este artículo, vamos a concentrarnos en las adiciones de NIO relacionadas al sistema de archivos: operaciones, manipulación de metadatos, y cambiar el monitoreo. Como veremos, las APIs NIO.2 proveen elegantes adiciones al set de herramientas de Java.

Primeros pasos
Las adiciones a NIO.2 están contenidas en su mayoría en los paquetes java.nio.file. Vamos a empezar con Path (ruta en español), una interface clave de las nuevas APIs.

Paths y archivos. La interface Path es una abstracción de un archivo o directorio dentro de un dado sistema de archivo. Instancias de Path pueden ser obtenidas usando la clase Path, que comprende dos métodos get(): uno que toma una URI y uno que toma un número variable de elementos Path. Un ejemplo sería obtener una referencia Path al archivo /etc/hosts, como se muestra a continuación:
Una referencia Path ofrece métodos para comparar rutas, resolver rutas contra otras rutas, y demás. La clase Files es comúnmente usada para realizar operaciones comunes en las rutas, incluyendo lo siguiente:


  • Crear archivos, carpetas, y links simbólicos
  • Copiar, mover y borrar
  • Consultando atributos
  • Iterar sobre un árbol del sistema de archivos
  • Obtener flujos de lectura y escritura y canales
  • Realizar operaciones de escritura y lectura directamente.


Listar cada método en Path y Files sería aburrido, así que en su lugar vamos a mirar algunos
ejemplos.

Operaciones simples. Vamos a crear un directorio temporal y copiar un archivo en él.
A continuación (en el código de abajo) copiamos el archivo asm a un archivo asm-copy.jar bajo una carpeta temporal como /var/folders/cj/h0x5ghcd1tz5s7shyj8txxf00000gn/T/nio2134767717145250358. Respectivos parámetros CopyOptions o no pueden ser provistos más probablemente de la enumeración StandardCopyOption:
En el código de arriba pedimos preservar los atributos del sistema de archivos, como el dueño del archivo, grupo y permisos. Otras opciones incluyen la habilidad realizar un movimiento atómico y reemplazar los archivos existentes. Hay además una enumeración LinkOption para prevenir la operación de copia de los siguientes links simbólicos.
Hablando de links simbólicos, si un link simbólico al archivo de librería original Java (JAR) ASM no existe, podemos crear uno y consultar el tamaño del JAR, como se muestra a continuación:
Lecturas y escrituras rápidas. Leer y escribir contenidos de archivo tradicionalmente ha requerido mucho código. La clase Files provee métodos ayudantes rápidos para lectura y escritura de los contenidos de un archivo como operaciones de “un tiro”. Ellas operan ya sea en Strings o en arreglos de bytes “crudos”.

A continuación se muestra cómo podemos leer los contenidos de un archivo:
Integración con flujos existentes y APIs NIO. Las APIs NIO.2 no están destinadas a vivir en una isla desierta, así que proveen medios para integración con existentes flujos y APIs NIO.
Los flujos (Streams) estilo I/O java.io y los búfers pueden ser obtenidos usando la clase Files
y sus métodos newInputStream(), newOutputStream(), newBufferedReader() y
newBufferedWriter().
Los canales NIO pueden ser obtenidos usando el método newByteChannel().
Curiosamente, hay una versión del método copy() que puede ser usada cuando el “blanco” es una instancia de java.io.OutputStream. Un ejemplo de uso sería arrojar el contenido de un archivo al flujo de salida estándar:
Tratando con los sistemas de archivos
Como el nombre lo sugiere, la clase FileSystem representa una abstracción de un sistema de
archivos.
Provee muchos servicios útiles, incluyendo la habilidad para accesar a archivos, carpetas,
usuarios, grupos, o notificaciones de cambio del sistema de archivos.

Obteniendo y usando una referencia al sistema de archivos. FileSystem es una
clase abstracta.
Instancias pueden ser obtenidas usado el correspondiente objeto de fábrica FileSystems.
FileSystems contiene un método getDefault() que regresa el sistema de archivos por default,
que es, el sistema de archivos desde el cual la Máquina Virtual de Java fue invocada.
Podemos obtener una referencia a este sistema de archivos por default así: No todos los sistemas de archivos comparten las mismas propiedades. Mientras los sistemas operativos derivativos de UNIX comparten una raíz de sistema de archivos unificada (/), Microsoft Windows tiene una raíz por partición y disco (C:\, D:\ y demás). Podemos revisar cuántas raíces tiene un dado sistema de archivos invocando el método getRootDirectories(), el cual regresa un Iterable<Path>, como se muestra a continuación:
Una instancia de FileSystem puede ser además usada para devolver instancias de Path. Los métodos en Path incluyen manipulación de métodos para comparar rutas, extraer sub-rutas, computar rutas relativas entre rutas y demás.

Obteniendo instancias de Path desde un objeto FileSystem se hace invocando al método getPath(), que toma un número variable de argumentos String. Cada String representa una parte de la ruta como se muestra a continuación:
Podemos comparar estas rutas así:
compareTo() devuelve los valores usuales -1, 0 y 1 para “menor que”, “igual a” y “más que” respectivamente.

A continuación de muestra como una ruta puede ser convertida a una URI o representación de archivo:
Además podemos realizar algunas manipulaciones de ruta, como se muestra a continuación:
Escuchando a los proveedores del sistema de archivos

Hay otros métodos de fábrica en FileSystems que pueden construir instancias de FileSystem
de una URI o ruta en un sistema de archivos. Esto es especialmente útil cuando proveedores
concretos actúan como sistemas de archivo virtuales sobre archivos zip, conexiones HTTP o
FTP, o unidades de red SMB.
Usando esta capacidad, puedes uniformemente manejar sistemas de archivos “reales” y
virtuales.

Es posible consultar el proveedor del sistema de archivos disponible usando la clase 
FileSystemProvideruna interfaz de proveedor de servicio (SPI) que es parte del paquete
java.nio.spi.

El código mostrado a continuación (abajo) debe regresar tanto el archivo como los esquemas
JAR URI para muchas instalaciones de Java SE 7:
Aunque la especificación no requiera que un proveedor de esquema JAR esté disponible, 
JDK 7 y OpenJDK proveen uno. Puedes tomar ventaja de la habilidad de los SPI’s para
aportar sus propios proveedores de sistema de archivos (HTTP, FTP, y demás), aunque
hacer eso está más allá de la mira de este artículo.

Vamos ahora más lejos a investigar las APIs NIO.2 abriendo un archivo zip como un archivo
de proveedor de sistema y “caminarlo”.

Caminando un proveedor de sistema de archivo
La clase java.io.File tiene métodos list() y listFiles(). Ellos pueden ser usados para instancias
de File que representan una carpeta, y regresan una lista de archivos y carpetas que son
“hijos directos” (que están dentro de la carpeta).
Un mecanismo un poco más poderoso es provisto con la clase java.nio.file.Files: visitores de
archivos y flujos de carpeta, los cuales pueden realizar filtros selectivos y combinaciones
(matching) para elementos descubiertos.
Veremos estas capacidades en el remanente de esta sección.
Abriendo un sistema de archivo zip. Vamos a abrir un JAR como sistema de archivo,
como se muestra a continuación:
La clase FileSystems provee otros métodos de fábrica newFileSystem, incluyendo uno que
toma un mapa de configuración como parámetro y usa claves específicas del proveedor. El
formato mostrado en el código anterior (arriba), el cual toma un Path y un ClassLoader, es el
formato preferido para abrir un sistema de archivo sobre un archivo.

A causa de que FileSystem implementa AutoCloseable, podemos usarlo como parte de una
sentencia try-with-resources que asegura que el método FileSystem.close() sea llamado, sin
importar si una excepción es lanzada.
Combinaciones de ruta. En este ejemplo, estamos interesados en imprimir todos los
archivos .class contenidos dentro del archivo. Podemos tomar ventaja de PathMatcher para
combinar (match) objetos Path ya sea contra una expresión regular o un archivo de sintaxis
glob usando el método match(), como se muestra a continuación:
Una expresión path matcher tiene la forma “glob:<glob-expression>” o “regexp:
<regular-expression>”.
Una expresión como “glob:/foo/*.{java,class}” combina cualquier archivo .java o .class
dentro de /foo.
Visitantes de archivos. El patrón de diseño Visitor encaja bien con la necesidad de
“caminar” un árbol de sistema archivos y tomar acciones en archivos. La interface
FileVisitor<T> define varios métodos para las siguientes circunstancias:
  • Un visitor que es notificado antes y después de entrar a una carpeta
  • Un visitor que es notificado cuando se visita un archivo
  • Un visitor que es notificado cuando se visita un archivo a través de una excepción

Además, los métodos de FileVisitor regresan un valor como parte de la enumeración
FileVisitResult para encargar que el “caminar” sea continuado, sea terminado, se salte los
hermanos (siblings), o salte un subtree.
Hay además una implementación por default llamada SimpleFileVisitor<T> que puede ser
sobreescrita. En nuestro caso, podemos caminar el fichero del sistema de archivos desde su
raíz e imprimir rutas de archivo .class, como se muestra a continuación:
Podemos con seguridad ignorar el parámetro attrs del método visitiFile() por el momento;
vamos a examinar como los atributos de archivos pueden ser manipulados después.

Y bueno, eso es todo por hoy, en la próxima entrada viene la segunda parte de esto, por
cierto, si notan las cajas de código algo raras o el texto, creo que es culpa de Blogger, anda
raro hoy :S, o quizá me metí demasiado en el HTML :P. Ya saben, suscríbanse ya sea por
correo o por RSS o Atom, dudas, peticiones, comentarios o criticas constructivas son bien
recibidas en la parte de abajo, saludos.

Comentarios