package ar.com.javacuriosities.nio;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
/*
* Java NIO (New IO) fue introducido a partir de Java 1.4, luego en Java 1.7 se extendió este modelo con
* lo que se conoce como NIO 2.
*
* IO vs NIO:
*
* IO NIO
* Stream Oriented Buffer Oriented
* Blocking IO Non blocking IO
* Selectors
*
* Stream Oriented: Leemos los datos de a uno o mas bytes y estos no son cacheados, tampoco podemos retroceder en el flujo de datos
* Blocking IO: Las operaciones bloquean al hilo actual hasta que ejecuta el system call de lectura
*
* Buffer Oriented: Los datos son almacenados en un buffer y podemos movernos en el mismo
* Non blocking IO: Permite realizar operaciones sobre el channel sin necesidad de esperar que esa operación termine, dependiendo el channel puede ser
* que se pueda habilitar o no el soporte para esto
* Selectors: Un selector puede monitorear multiples channels esperando determinados eventos
*
* NIO:
* - Channels
* - Buffers
* - Selectors
*
* NIO 2:
* - File System API
* - Socket y Asynchronous I/O Channel
* - SCTP (Stream Control Transport Protocol)
* - SDP (Socket Direct Protocol)
*
* Channels and Buffers:
* Los datos son siempre leídos desde un channel al buffer o escritos del buffer al channel
*
* Channels:
* - FileChannel: No soporta el modo Non-blocking, dado que Unix no soporta non-locking I/O para archivos, pero en Java 7 se agrego AsynchronousFileChannel, el cual provee soporte ya sea de forma nativa si lo soporta o con multiples threads
* - DatagramChannel
* - SocketChannel
* - ServerSocketChannel
*
* Buffers:
* - ByteBuffer
* - CharBuffer
* - DoubleBuffer
* - FloatBuffer
* - IntBuffer
* - LongBuffer
* - ShortBuffer
*
* Los Buffers cuentan con tres atributos de importancia y algunos métodos
*
* - capacity: Capacidad total del buffer
* - position: Posición actual en el buffer
* - limit: Cantidad de datos escritos en el buffer
*
* - flip(): Mueve la posición al inicio y deja el limit asignado a la posición actual para pasar al modo lectura
* - rewind(): Mueve la posición al inicio
* - clear(): Vuelve la posición a cero para sobreescribir el buffer
* - compact(): Los datos no leídos se mueven al comienzo del buffer
* - mark(): Se guarda la posición actual para luego poder hacer un reset
* - reset(): Vuelve la posición a la ultima posición donde se hizo mark
*/
public class Lesson01Introduction {
public static void main(String[] args) {
try {
readChannel();
writeChannel();
readIntoMultiplesBuffers();
writeChannelFromMultiplesBuffers();
} catch (IOException e) {
// Log and Handle exception
e.printStackTrace();
}
}
private static void readChannel() throws FileNotFoundException, IOException {
// Usamos la clase File perteneciente al paquete IO, luego pasaremos a usar Path que pertenece a NIO
File file = new File("message.txt");
FileInputStream fis = new FileInputStream(file);
FileChannel channel = fis.getChannel();
// El buffer puede estar almacenado en el Heap o fuera del mismo si usamos el método "allocateDirect"
ByteBuffer buffer = ByteBuffer.allocate(5);
int bytesRead = channel.read(buffer);
while (bytesRead != -1) {
System.out.println("Number of bytes read " + bytesRead);
// El método flip modifica el estado interno para asignar los indices de forma correcta
buffer.flip();
System.out.println("Current data");
while(buffer.hasRemaining()){
System.out.print((char) buffer.get());
}
System.out.println();
// Posiciona el índice al principio para sobreescribir los datos, otra opción es usar el método compact el cual mueve al principio los datos que no han sido leídos
buffer.clear();
bytesRead = channel.read(buffer);
}
// Si cerramos el IS también se cierra el channel asociado
fis.close();
}
private static void writeChannel() throws FileNotFoundException, IOException {
File file = new File("output.txt");
if (file.exists()) {
file.delete();
}
FileOutputStream fos = new FileOutputStream(file);
FileChannel channel = fos.getChannel();
String message = "Content written using FileChannel";
channel.write(ByteBuffer.wrap(message.getBytes()));
fos.close();
}
private static void readIntoMultiplesBuffers() throws FileNotFoundException, IOException {
File file = new File("data.txt");
FileInputStream fis = new FileInputStream(file);
FileChannel channel = fis.getChannel();
ByteBuffer header = ByteBuffer.allocate(6);
ByteBuffer body = ByteBuffer.allocate(4);
ByteBuffer[] bufferArray = { header, body };
// Este método recibe multiples buffers y los va llenando de acuerdo al orden en que los recibió
channel.read(bufferArray);
fis.close();
}
private static void writeChannelFromMultiplesBuffers() throws FileNotFoundException, IOException {
File file = new File("output_multiples_buffers.txt");
if (file.exists()) {
file.delete();
}
FileOutputStream fos = new FileOutputStream(file);
FileChannel channel = fos.getChannel();
byte[] headerBytes = ("Header" + System.lineSeparator()).getBytes();
byte[] bodyBytes = ("Body" + System.lineSeparator()).getBytes();
ByteBuffer header = ByteBuffer.allocate(headerBytes.length);
ByteBuffer body = ByteBuffer.allocate(bodyBytes.length);
header.put(headerBytes);
body.put(bodyBytes);
ByteBuffer[] bufferArray = { header, body };
header.flip();
body.flip();
channel.write(bufferArray);
fos.close();
}
}