RWHandler.java

package fr.umlv.ji.tcp.redirector;
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.util.logging.*;
/**
 * Classe de gestion des lectures-écritures.
 */
public class RWHandler implements Handler {
  /** Journaliseur. */
  private final static Logger logger =
    Logger.getLogger("fr.umlv.ji.tcp.redirector");
  /** Etat de la communication côté lecture. */
  private Connection readC;
  /** Etat de la communication côté écriture. */
  private Connection writeC;
  /** Clef de sélection du canal opposé (par rapport au redirecteur). */
  private SelectionKey otherSK;
  /** Constructeur. */
  public RWHandler(Connection writeC, Connection readC, 
                                      SelectionKey otherSK) {
    this.readC = readC;
    this.writeC = writeC;
    this.otherSK = otherSK;
  }
  /** Réalise les lectures-écritures de la redirection. */
  public void performs(SelectionKey sk) {
    try {
      // Récupération du canal correspondant à la clef sélectionnée
      SocketChannel sc = (SocketChannel) sk.channel();
      // Si opération de lecture possible
      if (sk.isReadable()) {
    // Récupération du tampon de l'objet Connection côté lecture
    ByteBuffer buffer = readC.getBuffer();
    // Lecture depuis le canal dans le buffer 
    int res = sc.read(buffer);
    if (res==-1) {
      // Si fin du flux atteinte, ne plus s'intéresser à la lecture
      sk.interestOps(SelectionKey.OP_WRITE);
    } else {
      // Si lecture mettre à jour la vue pour l'écriture
      readC.updateWriteView();
    }
    // Placer le canal vers lequel se fait le
    // transfert en attente d'écriture
    otherSK.interestOps(otherSK.interestOps()|SelectionKey.OP_WRITE);
      }
      // Si opération d'écriture possible
      if (sk.isWritable()) {
    // S'il y a des choses à écrire
    if (writeC.readyToWrite()) {
      // Récupérer un tampon et l'écrire dans le canal 
      ByteBuffer buffer = writeC.getWriteView();
      sc.write(buffer);
      writeC.tryCompact();
      return;
    }
    // Si plus de données à écrire et fournisseur des données encore
    // succeptible d'en recevoir, se désenregistrer pour l'écriture
    if ((otherSK.isValid()) &&
       ((otherSK.interestOps()&SelectionKey.OP_READ)!=0)) {
      sk.interestOps(sk.interestOps() & ~SelectionKey.OP_WRITE);
      return;
    }
    // Si plus de données et fournisseur des données fermé
    // ou n'a plus de données, fermer le canal
    try { sk.channel().close(); } catch (Exception e) { }
    // Si le fournisseur est encore ouvert
    if (otherSK.isValid()) {
      // Préparer l'autre côté à la fermeture
      otherSK.interestOps(SelectionKey.OP_WRITE);
    }
    return;
      }
    } catch (Exception e) {
      logger.log(Level.WARNING,this.getClass().getName(),e);
      // En cas de problème on ne se pose pas de question
      try { sk.channel().close(); } catch (IOException ex) { }
      try { otherSK.channel().close(); } catch (IOException ex) { }
    }
  }
}