/*
* @(#) WorkerClassServer.java - Classe che gestisce la comunicazione con il NetworkClassLoader.
*/
package loader;

import java.io.*;
import java.net.*;
import java.util.Hashtable;


/**
* Classe che gestisce la comunicazione
* con il NetworkClassLoader.<br>
* <br>
* La classe attende che gli venga richiesta una classe (mediante il nome in formato String).<br>
* Accettata la richiesta, la classe viene cercata prima nella cache (cache di array di bytes), poi
* nel CLASSPATH della macchina server su cui questa classe e' in esecuzione. <br>
* <br>
*	Se la classe viene trovata allora e' caricata come array di byte.<br>
* <br>
* Al client - NetworkClassLoader - viene inviata prima la dimensione dell'array di byte
* dopodiche' l'array.<br>
* <br>
* Se la classe non viene trovata al client viene inviata la dimensione simbolica -1. <br>
*
* @version		1.0 Gennaio 1998
* @author 		Donato Cappetta - cappetta@diiie.unisa.it
*/

public class WorkerClassServer extends Thread  {

	private Socket clientSocket = null;
	private DataInputStream is = null;
	private DataOutputStream os = null;
   private Hashtable classesCache = null;

	/**
	* Costruttore. Inizializza gli stream di I/O associati al socket client.
	* @param       clientSocket     Socket client per la connessione.
	* @param 			 classesCahce			Cache delle classi.	
	* @exception   IOException      Errore di I/O stream.
	*/
	public WorkerClassServer(Socket clientSocket, Hashtable classesCache)  throws IOException {
		super();
		this.clientSocket = clientSocket;
		this.classesCache = classesCache;

 		try {
			is = new DataInputStream(new BufferedInputStream(clientSocket.getInputStream()));
 			os = new DataOutputStream(new BufferedOutputStream(clientSocket.getOutputStream()));
		} catch (IOException ioe) {
			try {
				this.clientSocket.close();
			} catch (IOException ioex){}
			throw ioe;
		}
	}	//end costruttore()

	/**
	* Un ciclo che sta in attesa del nome di una classe.
	* Appena soddisfatto carica la classe come array di byte e la
	* invia al client.<br>
	* Viene inviato -1 se la classe non viene trovata.
	*/
	public void run() {
		byte[] classBytes;
		String nameClass;
		String fileSeparator = System.getProperty( "file.separator" ) ;
		try {
			while (true) {
				nameClass = null;
				classBytes = null;
				//legge il nome della classe
				nameClass = is.readUTF();
				//Se la classe e' in un package sostituisce il . con il separatore di file (es. /)
				nameClass = nameClass.replace('.', fileSeparator.charAt(0));
				System.out.println("Classe richiesta: " + nameClass);
	      		//prova ad acquisire la classe dalla "cache"
            	classBytes = (byte[])classesCache.get(nameClass);
            	//se la classe non e in cache la carica da file system del server
            	if (classBytes == null) {
	      			try {
    	  					//carica la classe, come array di bytes,  dal classpath del server.
							classBytes = loadClassFromFile(nameClass);
							// Aggiunge la classe/bytes nella cache
                			classesCache.put(nameClass, classBytes);
					} catch (FileNotFoundException fnfe){
							System.out.println("Classe  non trovata!!");
							os.writeInt(-1);	// -1 la classe non esiste
							os.flush();
							continue;
					}	//end try-catch (interno al  while)
				}	//end if (classBytes == null)

				//Se tutto va a buon fine, invia la dimensione della classe al client.
				os.writeInt(classBytes.length);
				//invia la classe al client
				os.write(classBytes, 0, classBytes.length);
				os.flush();
			}	//end while(true);

		//se ci sono problemi di connessione chiude il socket.
		} catch(SocketException se) {
			return;
		} catch(IOException ioe) {
			return;
		} catch(Exception e){
			return;
		} catch(Throwable t) {
			return;
		} finally {
			System.out.println("Client disconnesso!");
			try {
				is.close();
				os.close();
				clientSocket.close();
				this.stop();
			} catch (IOException ioe){
			} catch (Exception e) {}
		} //end finally	(end try-finally esterno al while)
	}	//end run()
		
	/**
	* Metodo che legge un file .class e lo restituisce
	* come  array di byte.
	* @exception   FileNotFoundException File .class non trovato nel CLASSPATH.
	* @exception   IOException Errore nella lettura del file.
	* @return 	byte[]	array di byte contenente il file.
	*/
	protected byte[] loadClassFromFile(String nameClazz) throws FileNotFoundException, IOException {
		int size = 0;
		byte[] classBytes=null;
		InputStream is = null;
		String nameClass = nameClazz + ".class";
		try {
		    //Cerca la classe (file.class)  nel CLASSPATH del server. 
   		   is = ClassLoader.getSystemResourceAsStream(nameClass);
   		   //se is == null il file non e' stato trovato
		   if (is == null) throw new FileNotFoundException(nameClass);
			//se trovato il file legge la dimensione
    		size = is.available();
			//crea l'array di byte data destinato a contenere il file
			classBytes = new byte[size];
			//legge il file
			is.read(classBytes);
			is.close();
		} catch(FileNotFoundException fnfe){
   			throw fnfe;
		} catch(IOException ioe){
			throw ioe;
		}	//end try catch()
		return classBytes;
	}	//end loadClassFromFile()

}	//end class WorkerClassServer.