package org.wikiwebserver.core;

import java.net.Socket;

import org.wikiwebserver.core.interfaces.ConnectionHandler;
import org.wikiwebserver.core.interfaces.HandlerConfiguration;

/**
 * A ConcurrentHandler is a wrapper for a ConnectionHandler that
 * makes it possible to handle many connections at the same time.
 * 
 * The wrapper requires a handler class name and an optional
 * configuration class name the handler is to use.
 * 
 * The socket on which to handle communications is specified by 
 * calling the handle() method.
 * 
 * To start handling the connection the run method must be called
 * which is typically achieved by wrapping this class in a new Thread
 * and calling start(). 
 * 
 * The context class loader is used for creating a new instance of the
 * ConnectionHandler together with the HandlerConfiguration, this provides 
 * isolation between handlers and makes it possible to use multiple class
 * loaders for reloading classes that have changed.
 * 
 * @author Dr Michael Gardiner 
 **/
public class ConcurrentHandler implements Runnable, ConnectionHandler {
    
    private final long startTime = System.currentTimeMillis();
    private final String handlerClassName;
    private final String configurationClassName;
    
    private Socket sock;    
    
    private HandlerConfiguration configuration;
    private ConnectionHandler handler;
    private Thread handlerThread;   
    
    public ConcurrentHandler(String handlerClassName) {
        this(handlerClassName, null);
    }     

    public ConcurrentHandler(String handlerClassName,
                             String configurationClassName) {
        
        this.handlerClassName = handlerClassName;
        this.configurationClassName = configurationClassName;
    }  
    
    public void configure(HandlerConfiguration config) {
        String msg = "The concurrent handler can not be configured";
        throw new IllegalArgumentException(msg);
    }    

    public void handle(Socket sock) {
        this.sock = sock;
    }
    
    public void run() {
        handlerThread = Thread.currentThread();
        try {
            ClassLoader cl = Thread.currentThread().getContextClassLoader();
            
            handler = (ConnectionHandler)
                cl.loadClass(handlerClassName).newInstance();
  
            if (configurationClassName != null) {
                configuration = (HandlerConfiguration) 
                    cl.loadClass(configurationClassName).newInstance();
                
                handler.configure(configuration);
            }
            
            handler.handle(sock);
        }
        catch (ClassNotFoundException ex) {
            System.err.println("Failed to find and configure handler: " + handlerClassName);
            ex.printStackTrace();
        } 
        catch (InstantiationException ex) {
            System.err.println("Failed to create instance of handler: " + handlerClassName);
            ex.printStackTrace();
        } 
        catch (IllegalAccessException ex) {
            System.err.println("Failed access handler: " + handlerClassName);
            ex.printStackTrace();
        } 
        catch (Throwable ex) {
            System.err.println("Unhandled exception in handler: " + handlerClassName);
            ex.printStackTrace();
        }
    }
    
    public void forceClose() {
        if (handler != null) handler.forceClose();
    }
    
    public void forceStop() {
        if (handler != null) handler.forceStop();
    }
    
    public void gracefulClose() {
        if (handler != null) handler.gracefulClose();
    }    
    
    public long getStartTime() {
        return startTime;
    }
    
    public long getExecutionTime() {
        return (handler != null) ? handler.getExecutionTime() : 0;
    }    
    
    public boolean isRunning() {
        return (handlerThread != null) ? handlerThread.isAlive() : false;
    }    
    
    public void setPriority(int priority) {
        if (handlerThread != null) handlerThread.setPriority(priority);
    }
}

