/*
 * $Id: ServerSupport.java,v 1.12 2007/08/16 11:11:00 marbobo Exp $
 *
 * Copyright (c) Siemens AG 2008 
 *           (c) Universität Paderborn 2008 
 * Licensed under EUPL 1.1. 
 *
 * $Log: ServerSupport.java,v $
 * Revision 1.12  2007/08/16 11:11:00  marbobo
 * + Bugfixes
 * + Refactorings
 *
 * Revision 1.11  2004/11/19 14:00:33  koppy
 * Kleine Änderungen wegen Converter-Serveränderungen
 *
 */
package vkc.server;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

import vkc.converter.common.Status;
import vkc.db.PersistenceManager;
import vkc.db.common.ServerInfo;
import vkc.exception.DBException;
import vkc.misc.SocketSetter;
import vkc.misc.VKCLogManager;

/**
 * This class represents a support for a server implementations
 * 
 * @created 10.01.2004
 * @author koppy
 */
public abstract class ServerSupport implements Runnable {
    protected ServerSocket server = null;

    protected ServerInfo conf = null;

    protected Object jobsCountLock = new Object();

    protected PersistenceManager pm = PersistenceManager.getInstance();

    private SocketSetter socketSetter = null;

    /**
     * Returns the server socket. Overide this method for using an SSL server
     * 
     * @return
     * @throws Exception
     */
    protected ServerSocket getServerSocket() throws IOException {
        VKCLogManager.infoLog(this, "Opening socket on port " + conf.getPort());
        
        return new ServerSocket(conf.getPort());
    }

    /**
     * Returns the configuration of this server. Every time this method is
     * accessed, the configuration is updated by the current values in the
     * database.
     * 
     * @return the ServerInfo description
     */
    public ServerInfo getConfiguration() {
        try {
            conf.refresh();
        } catch (DBException e) {
            VKCLogManager.errorLog(this, "", "Refresh of info " + conf.getName() + ", ID=" + conf.getId() + " failed!", e);
        }
        
        return this.conf;
    }

    /**
     * Processes the request for the connected client
     * 
     * @param client
     *            the socket to the connected client
     * @throws Exception
     */
    protected abstract void process(Socket client) throws Exception;

    /**
     * Main dispatching loop
     */
    public void run() {
        try {
            server = getServerSocket();
            VKCLogManager.infoLog(this, conf.getName() + " started on " + server.getInetAddress().getCanonicalHostName() + ":" + server.getLocalPort());
            conf.setStatus(Status.SERVER_UP);
            conf.makeStatusPersistent();

            while (true) {
                Socket client = server.accept();

                if (client == null) {
                    continue;
                }
                
                if (socketSetter != null) {
                    socketSetter.setProperties(client);
                }
                
                process(client);
                client = null;
            }

        } catch (Exception e) {
            VKCLogManager.infoLog(this, conf.getName() + " shutdown: " + e.getMessage());
        } finally {
            shutdown();
        }
    }

    /**
     * Shut down the server
     */
    public void shutdown() {
        try {
            if (server != null) {
                server.close();
            }
        } catch (IOException e) {
        } finally {
            server = null;
        }

        try {
            conf.setStatus(Status.SERVER_DOWN);
            conf.makeStatusPersistent();
        } catch (DBException e) {
            VKCLogManager.errorLog(this, "", "Exception occured while setting serverstatus to 'down'", e);
        }

        pm.shutdown();
    }

    /**
     * Increases the job count
     * 
     * @param amount
     *            might be negative
     */
    public void incJobsCount(int amount) {
        synchronized (jobsCountLock) {
            conf.incJobsCount(amount);
            
            try {
                conf.makeJobsCountPersistent();
            } catch (DBException e) {
                VKCLogManager.errorLog(this, "", "Job count could not be updated due to DB-Exception!", e);
            }
        }
    }

    /**
     * Decreases the job count
     */
    public void decJobsCount() {
        this.incJobsCount(-1);
    }

    /**
     * @param setter
     */
    public void setSocketSetter(SocketSetter setter) {
        socketSetter = setter;
    }
}