/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 2005-2009 Bull S.A.S.
 * Contact: jonas-team@ow2.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 * Initial developer: Benoit Pelletier
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */

package org.ow2.jonas.ant.cluster;

import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.ow2.jonas.ant.jonasbase.JEcho;
import org.ow2.jonas.ant.jonasbase.JMkdir;
import org.ow2.jonas.ant.jonasbase.JTouch;
import org.ow2.jonas.ant.jonasbase.Tasks;



/**
 * Allow to configure the mod_jk load balancer for Apache
 * @author Benoit Pelletier
 */
public class ModJk extends Tasks {

    /**
     * Info for the logger
     */
    private static final String INFO = "[ModJk] ";

    /**
     * Name of the worker file
     */
    private static final String MOD_JK_WORKER_FILE = "workers.properties";

    /**
     * Name of the tomcat_jk file
     */
    private static final String MOD_JK_TOMCAT_FILE = "tomcat_jk.conf";

    /**
     * Name of the httpd.conf file
     */
    private static final String HTTPD_CONF_FILE = "httpd.conf";

    /**
     * Worker file
     */
    private File fileWorker = null;

    /**
     * Tomcat config file
     */
    private File fileTomcat = null;

    /**
     * Httpd config file
     */
    private File fileHttpd = null;

    /**
     * Workers list
     */
    private List<Worker> workersList = null;

    /**
     * sticky session
     */
    private boolean stickySession = false;

    /**
     * root dir
     */
    private String rootDir = null;

    /**
     * mod_jk dir
     */
    private String dir = null;

    /**
     * List of worker files to put in configuration dirs
     * of web instances.
     */
    // Disabling, wondering the use case of JONAS-67 
    //private List<File> workerFiles = null;

    /**
     * Default constructor
     */
    public ModJk() {
        super();
        workersList = new ArrayList<Worker>();
        // Disabling, wondering the use case of JONAS-67 
        //workerFiles = new ArrayList<File>();
    }

    /**
     * Creation of the mod_jk files
     */
    public void createFiles() {
        String modJkDir = rootDir + File.separator + dir;
        JMkdir mkdir = new JMkdir();
        mkdir.setDestDir(new File(modJkDir));
        addTask(mkdir);

        JTouch touchWorker = new JTouch();
        fileWorker = new File(modJkDir + "/" + MOD_JK_WORKER_FILE);
        touchWorker.setDestDir(fileWorker);
        addTask(touchWorker);

        JTouch touchTomcat = new JTouch();
        fileTomcat = new File(modJkDir + "/" + MOD_JK_TOMCAT_FILE);
        touchTomcat.setDestDir(fileTomcat);
        addTask(touchTomcat);

//        JTouch touchHttpd = new JTouch();
//        fileHttpd = new File(modJkDir + "/" + HTTPD_CONF_FILE);
//        touchHttpd.setDestDir(fileHttpd);
//        addTask(touchHttpd);

    }

    /**
     * @return the webConfDirs
     */
    // Disabling, wondering the use case of JONAS-67 
    //public List<File> getWorkerFiles() {
    //    return workerFiles;
    //}

    /**
     * @param webConfDirs the webConfDirs to set
     */
    // Disabling, wondering the use case of JONAS-67 
    //public void setWorkerFiles(final List<File> workerFiles) {
    //    this.workerFiles = workerFiles;
    //}

    /**
     * Add a worker
     * @param portNumber port number
     * @param lbFactor load balancing factor
     * @return worker name
     */
    public String addWorker(final String portNumber, final String lbFactor) {
        Worker worker = new Worker();
        worker.setPortNumber(portNumber);
        worker.setLbFactor(lbFactor);
        int index =  workersList.size() + 1;
        worker.setName("worker" + index);
        workersList.add(worker);
        return worker.getName();
    }

    /**
     * get worker definition
     * @param worker worker to define
     * @return definition of the worker
     */
    private String getWorkerDef(final Worker worker) {
        String preferedWorker = choosePreferedFailOverNode(worker.getName());
        String workerDef = "\n"
            +
            "# -----------------------" + "\n"
            +
            "# " + worker.getName() + "\n"
            +
            "# -----------------------" + "\n"
            +
            "worker." + worker.getName() + ".port=" + worker.getPortNumber() + "\n"
            +
            "worker." + worker.getName() + ".host=localhost" + "\n"
            +
            "worker." + worker.getName() + ".type=ajp13" + "\n"
            +
            "# Load balance factor" + "\n"
            +
            "worker." + worker.getName() + ".lbfactor=" + worker.getLbFactor() + "\n"
            +
            "# Define prefered failover node for " + worker.getName() + "\n"
            +
            "worker." + worker.getName() + ".redirect=" + preferedWorker + "\n"
            +
            "# Disable " + worker.getName() + " for all requests except failover" + "\n"
            +
            "#worker." + worker.getName() + ".disabled=True" + "\n";
        return workerDef;
    }
    /**
     * Choose the prefered fail over node for the given worker.
     * @param name the worker's name.
     * @return the name of the prefered fail over node.
     */
    private String choosePreferedFailOverNode(final String name) {
        String ret = name;
        for(Worker worker: workersList) {
            if (!worker.getName().equals(name)) {
                ret = worker.getName();
                break;
            }
        }
        return ret;
    }

    /**
     * creation of the worker file
     */
    private void flushWorkerFile() {
        JEcho echo = new JEcho();
        echo.setDestDir(fileWorker);
        String workersNameList = "";
        String workersDefs = "";
        for (Iterator it = this.workersList.iterator(); it.hasNext();) {
            Worker worker = (Worker) it.next();

            if (workersNameList.compareTo("") == 0) {
                workersNameList = workersNameList + worker.getName();
            } else {
                workersNameList = workersNameList + "," + worker.getName();
            }
            workersDefs = workersDefs + getWorkerDef(worker);
        }

        String contentFile = "\n"
            +
            "# -----------------------" + "\n"
            +
            "# List the workers name" + "\n"
            +
            "# -----------------------" + "\n"
            +
            "worker.list=loadbalancer,jkstatus" + "\n"
            +
            workersDefs + "\n"
            +
            "# -----------------------" + "\n"
            +
            "# Load Balancer worker" + "\n"
            +
            "# -----------------------" + "\n"
            +
            "worker.loadbalancer.type=lb" + "\n"
            +
            "worker.loadbalancer.balance_workers=" + workersNameList + "\n"
            +
            "worker.loadbalancer.sticky_session=" + stickySession + "\n"
            +
            "# -----------------------" + "\n"
            +
            "# jkstatus worker" + "\n"
            +
            "# -----------------------" + "\n"
            +
            "worker.jkstatus.type=status" + "\n";

        echo.setMessage(contentFile);
        echo.setLogInfo(INFO + "Flushing Configuration in '" + fileWorker + "'");
        addTask(echo);

        // Disabling, wondering the use case of JONAS-67 
        //try {
        //    for (Iterator<File> iterator = workerFiles.iterator(); iterator.hasNext();) {
        //        File workerFile = iterator.next();
        //        JEcho cpyWebBase = (JEcho)echo.clone();
        //        cpyWebBase.setDestDir(workerFile);
        //        cpyWebBase.setLogInfo(INFO + "Flushing Configuration in '" + workerFile + "'");
        //        addTask(cpyWebBase);
        //    }
        //} catch (Exception e) {
        //    e.printStackTrace();
        //}

    }

    /**
     * creation of the tomcat-jk file
     */
    private void flushTomcatFile() {
        JEcho echo = new JEcho();
        echo.setDestDir(fileTomcat);

        String contentFile = "\n"
            +
            "LoadModule jk_module modules/mod_jk.so" + "\n"
            +
            "# Location of the worker file" + "\n"
            +
            "JkWorkersFile " + this.dir + File.separator + MOD_JK_WORKER_FILE + "\n"
            +
            "# Location of the log file" + "\n"
            +
            "JkLogFile " + this.dir + File.separator + "mod_jk.log" + "\n"
            +
            "# Log level : debug, info, error or emerg" + "\n"
            +
            "JkLogLevel emerg" + "\n"
            +
            "# Shared Memory Filename ( Only for Unix platform ) required by loadbalancer"  + "\n"
            +
            "JkShmFile " + this.dir + File.separator + "jk.shm" + "\n"
            +
            "# Assign specific URL to Tomcat workers" + "\n"
            +
            "JkMount /sampleCluster2 loadbalancer" + "\n"
            +
            "JkMount /sampleCluster2/* loadbalancer" + "\n"
            +
            "JkMount /sampleCluster3 loadbalancer" + "\n"
            +
            "JkMount /sampleCluster3/* loadbalancer" + "\n"
            +
            "# A mount point to the status worker" + "\n"
            +
            "JkMount /jkmanager jkstatus" + "\n"
            +
            "JkMount /jkmanager/* jkstatus" + "\n"
            +
            "# Copy mount points into all the virtual hosts" + "\n"
            +
            "JkMountCopy All" + "\n"
            +
            "# Enable the Jk manager access only from localhost" + "\n"
            +
            "<Location /jkmanager/>" + "\n"
            +
            "    JkMount jkstatus" + "\n"
            +
            "    Order deny,allow" + "\n"
            +
            "    Deny from all" + "\n"
            +
            "    Allow from 127.0.0.1" + "\n"
            +
            "</Location>" + "\n";

        echo.setMessage(contentFile);
        echo.setLogInfo(INFO + "Flushing Configuration in '" + fileTomcat + "'");
        addTask(echo);
    }

    /**
     * creation of the httpd.conf file
     */
    private void flushHttpdFile() {
        JEcho echo = new JEcho();
        echo.setDestDir(fileHttpd);

        String contentFile = "\n"
            +
            "Include " + this.dir + File.separator + MOD_JK_TOMCAT_FILE + "\n";

        echo.setMessage(contentFile);
        echo.setLogInfo(INFO + "Flushing Configuration in '" + fileHttpd + "'");
        addTask(echo);
    }

    /**
     * Generation of the config files
     */
    public void flushFiles() {
        flushWorkerFile();
        flushTomcatFile();
//        flushHttpdFile();
    }

    /**
     * Set sticky Session
     * @param stickySession to set
     **/
    public void setStickySession(final boolean stickySession) {
        this.stickySession = stickySession;
    }

    /**
     * Define an inner class for workers
     * @author Benoit Pelletier
     */
    public class Worker {

        /**
         * port number
         */
        private String portNumber = null;

        /**
         * load balancing factor
         */
        private String lbFactor = null;

        /**
         * name
         */
        private String name = null;

        /**
         * get port number
         * @return port number
         */
        public String getPortNumber() {
            return portNumber;
        }

        /**
         * set port number
         * @param portNumber port number
         */
        public void setPortNumber(final String portNumber) {
            this.portNumber = portNumber;
        }

        /**
         * get load balancing factor
         * @return load balancing factor
         */
        public String getLbFactor() {
            return lbFactor;
        }

        /**
         * set load balancing factor
         * @param lbFactor load balancing factor
         */
        public void setLbFactor(final String lbFactor) {
            this.lbFactor = lbFactor;
        }

        /**
         * get name
         * @return name
         */
        public String getName() {
            return name;
        }

        /**
         * set name
         * @param name name to set
         */
        public void setName(final String name) {
            this.name = name;
        }
    }


    /**
     * Set the mod_jk directory
     * @param dir directory
     */
    public void setDir(final String dir) {
        this.dir = dir;
    }

    /**
     * Set the root directory
     * @param dir directory
     */
    public void setRootDir(final String dir) {
        this.rootDir = dir;
    }
    /**
     * Add configuration dir for a web instance.
     * @param webConfDir the configuration dir to add.
     */
    // Disabling, wondering the use case of JONAS-67 
    //public void addWorkerFile(final String webConfDir) {
    //    JTouch touchWorker = new JTouch();
    //    File fileWorker = new File(webConfDir + File.separator + MOD_JK_WORKER_FILE);
    //    touchWorker.setDestDir(fileWorker);
    //    addTask(touchWorker);
    //    workerFiles.add(fileWorker);
    //}
}
