
/**
 * Abstract class AbstractServer.
 * La classe AbstractServer  réalise une abstraction/encapsulation
 * de la classe java.net.ServerSocket, un utilisateur de cette classe
 * doit seulement préciser le port et le protocole de ce serveur.
 * La stratégie du serveur est fonction de l'implémentation de la
 * méthode executeConnexion  laissée abstraite. 
 * 
 * @author Laurent Dehoey I177416
 * @version 23/01/2003
 */

//import java.io.*;

//The Runnable interface should be implemented by any class whose
//instances are intended to be executed by a thread. The class must
//define a method of no arguments called run
//...
//In addition, Runnable provides the means for a class to be active
//while not subclassing Thread. A class that implements Runnable
//can run without subclassing Thread by instantiating a Thread
//instance and passing itself in as the target. In most cases, the
//Runnable interface should be used if you are only planning to
//override the run() method and no other Thread methods. This is
//important because classes should not be subclassed unless the
//programmer intends on modifying or enhancing the fundamental behavior
//of the class. 
//...
//public void run()
//When an object implementing interface Runnable is used to create
//a thread, starting the thread causes the object's run method to be
//called in that separately executing thread. 
//The general contract of the method run is that it may take any action whatsoever. 


public abstract class AbstractServer implements Runnable{
   //déclarations des données d'instance à compléter

   /**
    * Le nombre de connection
    * protected ca veut dire que ce sont
    * nos classes filles qui soit lisent
    * soit mettent à jour cet attibut ?
    * le nombre de requete que ce
    * serveur a servi ? ou le nombre
    * de requetes courantes ? qu'il est
    * en train de servir ?
    * 
    */
   protected long connections;

    /**
     * compteur statistique static du nombre
     * de connections effectuées avec
     * AbstractServer
     */
   protected static int globalStaticConnections;
   
    /**
     * compteur statistique du nombre
     * de connections effectuées avec
     * AbstractServer
     */
   protected int globalInstanceConnections;
   
   /**
    * Le Thread associé en local
    * on va lui donner un petit nom pour les affichages
    */
   private java.lang.Thread local;

   /**Le port associé au server*/
   protected int port;

   /*Le protocol de communication*/
   protected Protocol protocol;

   /**Le serveur de "socket"*/
   private java.net.ServerSocket server;

   /**
    * Delai de garde en millisecondes afin que le serveur
    * puisse s'arreter d'apres la doc de AbstractServer
    * sur le site des TP
    * Private et pas de setTIME_OUT(int tmo) methode ?
    * donc y a pas le choix ?!!
    */
   
   private int TIME_OUT=6000;//6 secondes

   /**Reflete l'etat du mode trace*/
   protected boolean verbose=true;
   

   /**
    * Constructeur d'AbstractServer
    * @param protocol Protocol le protocol que vont "causer" le serveur et le client
    * @param port int le numéro de port sur lequel il vont se brancher
    */
   
   public AbstractServer(Protocol protocol, int port) throws java.io.IOException
   {
   
     this.protocol = protocol;
     this.port = port;
     /**
      * Creates a server socket, bound to the specified port.
      * A port of 0 creates a socket on any free port.
      * The maximum queue length for incoming connection indications
      * (a request to connect) is set to 50. If a connection indication
      * arrives when the queue is full, the connection is refused. 
      */
     server = new java.net.ServerSocket(port);
     /**
      * Ici il s'agit de n'ecouter que pendant un
      * certain laps de temps les clients arriver
      * car on a fixé TIME_OUT à une valeur /= de zero(infini)
      * passé ce delai on sort du accept...et on
      * fait quoi alors ? le serveur s'arrete ? le thread
      * s'arrete ? le serveur interrompt le thread pendant
      * TIME_OUT milliseconde et le relance ?
      */
     server.setSoTimeout(TIME_OUT); 
     /**
      * c'est dans le thead que ca va se passer la connection
      */
     local = new Thread(this,"DaAbstracServer/Id:"+(int)(1000*java.lang.Math.random()));
     local.setPriority(Thread.MAX_PRIORITY);
     local.start();
  }

  /**
   * On feeeeerme !
   * c'est à dire quu'on interrompt le thread ?
   * qu'on ferme le socket ouvert ? qu'on ferme
   * le socket en blockage d'accept (lui meme en
   * attente pendant TIME_OUT ms)
   * interrompre ca ne veut pas dire tuer ?
   * ca veux juste dire mettre en pause pour
   * laisser reprendre par la suite non ?
   * @return void
   */
  public void interrupt(){
    //est-ce qu'on peut interrompre le serveur
    //s'il est train de servir des clients ?
    //non evidemment !!
    //mais on pourrait essayer...en random 1 fois sur 2?
    if (connections>0){
        if (getVerbose())
        {
            System.out.println("Qui a osé tenter de m'interrompre moi : " +local.toString()+"?!!!\n");
            System.out.println("Alors qu'il y a encore "+connections+" connections actives !\n");
            }
            int i=(int)((int)100*java.lang.Math.random())%2;
            if(getVerbose())System.out.println(i);
        if( 0 == i){
            if (getVerbose()){
                System.out.println("Oui on va t'interrompre si on veut....glups");
                System.out.println("mais on va couper les socket");
                System.out.println("et on ne te fais pas dormir sur ce coup là...");
                System.out.println("..on va te tuer tout simplement...");
                }
                try{
                server.close();
                local.dumpStack();
                local.destroy();
                }catch(java.io.IOException ioe)
                {
                    System.out.println("Difficile à fermer le server ?!");
                }
                local.interrupt();
       }else  if (getVerbose()){
            System.out.println("Ok on va te laisser en continuer :-)");
       }

    }else {
        if(getVerbose()){
            System.out.println("Ok on va se coucher: " +local.toString()+"\n");
        }
        try{
/**
 * http://java.sun.com/docs/books/tutorial/essential/threads/index.html
 * http://java.sun.com/products/jdk/1.2/docs/guide/misc/threadPrimitiveDeprecation.html
 * What should I use instead of Thread.stop?
 * Most uses of stop should be replaced by code that simply modifies some variable to indicate
 * that the target thread should stop running. The target thread should check this variable
 * regularly, and return from its run method in an orderly fashion if the variable indicates
 * that it is to stop running. (This is the approach that JavaSoft's Tutorial has always recommended.)
 * To ensure prompt communication of the stop-request, the variable must be volatile (or access
 * to the variable must be synchronized).
 */

        //on s'endore un peu le temps de laisser accept "time_outer"
        if (getVerbose()){
         System.out.println(local.toString()+" Will going to sleep for "+TIME_OUT+"ms before being interrupted");
        }
        local.sleep(TIME_OUT);
        local.interrupt();

        
        }catch(InterruptedException ie){
         System.out.println(local.toString()+" : Hahum j'ai dormi longtemps ?");

        }catch(SecurityException se){
          System.out.println(local.toString()+" : Humppf quoi ?!!");
        }
   }
    
    //à completer
    

  }
  public boolean getVerbose(){
    return verbose;
  }
  public void setVerbose(boolean verbose){
  if (verbose)
   System.out.println("Hoho U want a verbose port "+port+" !\n");
  this.verbose = verbose;
 }
  public Protocol getProtocol(){
    return protocol;
 }
  public void setProtocol(Protocol protocol){
    this.protocol = protocol;
 }

    /**
     * le corps de thread d'ecoute et qui va
     * lancer le type de connection...
     */
    
  public void run(){
    if(local==Thread.currentThread()){
      while(true){//on Choisi de boucler indéfiniment quelque soir le timeout
                  //de l'accept() ?
                  //on va donc pouvoir se faire arreter ?
                  //il faudrait qu'on puisse nous repérer pour cela...
      try{
       //normalement getVerbose devrait etre à vrai meme au premier appel
       //juste apres le start()
       //mais non ... ca c'est un mystère pour moi 
        if(getVerbose())
            System.out.println("Accepting request\n");
            //il semble qu'il ne soit pas possible de fixer le mode
            //verbose avant cette ligne...
            else System.out.println("!getVerbose()?!?\n");
          
         if (local.interrupted()){
         //Le serveur a ete interrompu
         System.out.println(local.toString()+" bon bah voila je reessaye un accept pendant "+TIME_OUT+"ms...");
         }
          
            /**
             * le serveur se met à l'ecoute des clients
             * potentiel pendant TIME_OUT ms
             * si tout se passe bien on recupere alors
             * un socket de "connection" client en sortie
             */
            
            if (server.isClosed())throw(new java.net.SocketException());
             java.net.Socket client=server.accept();
            
        //il y a pas eu de time out
        //on va servir un client
        if(getVerbose())
            System.out.println("Haaa a client on port : "+port+"\n");
        if(getVerbose())
            System.out.println("Fireing subclass executeConnection\n");

            /**
             * Socket qu'on transmet à notre methode, ici abstraite,
             * mais implémentée dans nos sub-classes
             */
            //un thread va s'occuper d'un client connecté
            //on incremente la connection ici ?
        connections++;
        //et c'est la methode executeConnection(client) implémentée
        //qui décremente à la sortie ?
        //humpf ?! pas terrible non ?
        //finalement ca marche assez bien :-)
        executeConnection(client);
        //executeConnection doit nous rendre la main assez vite
        //en particulier si elle est aussi multithreadée non ?
        //meme si on ne sait pas si oui ou nous elle l'est
        //et comment elle l'est...Pattern Strategy
        if(getVerbose())
            System.out.println("Was that " + ++globalInstanceConnections + "/" + ++globalStaticConnections +"th executeConnection fine ?\n");
       }catch(java.net.SocketTimeoutException st){
            System.out.println("TimeOut: " + st +"\n");
            System.out.println("Trying to interrupt server thread: "+local.toString()+"\n");
            this.interrupt();
       }catch(java.io.IOException e){
            System.out.println("Argaa " + e);
       }
     }
    }
  }
  /**
   * 
   * da methode que nos serveur se doivent d'implementer dans
   * le pattern strategy
   */
  
   public abstract void executeConnection(java.net.Socket s);
}
   

