/*
 * $Header: /home/cvspublic/jakarta-slide/proposals/tamino/src/store/org/apache/slide/store/tamino/tools/Server.java,v 1.1 2004/03/25 16:18:04 juergen Exp $
 * $Revision: 1.1 $
 * $Date: 2004/03/25 16:18:04 $
 *
 * ====================================================================
 *
 * Copyright 1999-2004 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.slide.store.tamino.tools;

import java.io.IOException;
import java.util.List;
import org.apache.slide.store.tamino.tools.Env;
import org.apache.slide.store.tamino.tools.config.Log4jConfig;
import org.apache.slide.store.tamino.tools.config.Service;
import org.apache.slide.store.tamino.tools.stores.XDomainFileHandler;
import org.apache.slide.util.Strings;
import org.apache.slide.util.XAssertionFailed;
import org.apache.slide.util.XException;
import org.apache.slide.util.cli.Abort;
import org.apache.slide.util.cli.Actuals;
import org.apache.slide.util.cli.CommandLine;
import org.apache.slide.util.os.Catalina;
import org.apache.slide.util.os.Platform;
import org.jdom.JDOMException;

/**
 ** @version   $Revision: 1.1 $
 **/
public class Server extends Tws {
    public Server() {
        super(new String[] {
                    CMD_RUN, CMD_START, CMD_STOP,
                        CMD_UP, CMD_DOWN,
                        CMD_SUSPEND, CMD_RESUME, CMD_RELOAD,
                        CMD_STATUS, CMD_TRACE, CMD_PORT
                },
                  "server", "Starts or stops the Tamino WebDAV Server.",
                   new String[] {
                    "This tool provides the core functionality to control Tamino WEBDAV Server. " +
                    "The commands fall into three categories: " +
                    "Server commands start and stop the server, they resemble Tomcat's catalina script. " +
                    "(The catalina script doesn't work for TWS because we need native libraries). " +
                    "Service commands are similar to server commands but affect the service, " +
                    "not the server. They are only available for Windows, and only  if " +
                    "'inodavconfig install service' was used to set up the serivce. " +
                    "Manager commands control the web application that implements the server. To use them, " +
                    "you have to defined a user '" + Catalina.MANAGER_USER + "' with role 'manager' in " +
                    "Tomcat tomcat-user.xml file. " +
                    "Misc commands provide additional server configuration."
            },
                  "Commands:\n" +
                  "  Server\n" +
                  "    run                   Starts the server in this console.\n" +
                  "    start                 Starts the server in a new console.\n" +
                  "    stop                  Stops the server.\n" +
                  "  Service\n" +
                  "    up                    Starts the server service. Only for Windows.\n" +
                  "    down                  Stops the server service. Only for Windows.\n" +
                  "  Manager\n" +
                  "    suspend               Stops the web application.\n" +
                  "    resume                Starts the web application.\n" +
                  "    reload                Stops and starts the web-application.\n" +
                  "    status                Prints the web application status.\n" +
                  "  Misc\n" +
                  "    trace                 Configures tracing.\n" +
                  "    port                  Defines the port Tomcat listens to.\n" +
                  "Options run | start:\n" +
                  "  -trace                  Enable tracing as configured by the trace command.\n" +
                  "                          The trace file is ${catalina.home}/logs/inodav.trace.\n" +
                  "                          If not specified, tracing is disabled.\n" +
                  "Options trace:\n" +
                  "  -category <list>        Specifies a trace categories to enable/disable. \n" +
                  "                          <list> can be a single category which will be enabled,\n" +
                                             "or a list of categories separated by \"+\" (enable) and \"-\" (disable).\n" +
                  "                          If a specified category starts with \".\", it will automatically \n" +
                  "                          be prefixed by \"org.apache.slide.store.tamino\". If the specified\n" +
                  "                          specified category is \"*\", all categories will be enabled/disabled.\n" +
                  "Examples:\n" +
                  "    inodav server start\n" +
                  "    inodavserver trace -category +.servlet+.store+.datastore\n" +
                  "    inodavserver trace -category -.store+.common\n" +
                  "    inodavserver trace -category -*\n");
    }
    
    private static final String ARG_CATEGORY = "category";
    private static final String ARG_TRACE = "trace";
    
    // tomcat-like stuff
    private static final String CMD_RUN = "run";
    private static final String CMD_START = "start";
    private static final String CMD_STOP = "stop";
    
    private static final String CMD_UP = "up";
    private static final String CMD_DOWN = "down";
    
    private static final String CMD_SUSPEND = "suspend";
    private static final String CMD_RESUME = "resume";
    private static final String CMD_RELOAD = "reload";
    private static final String CMD_STATUS = "status";
    
    private static final String CMD_TRACE = "trace";
    private static final String CMD_PORT = "port";
    
    public CommandLine commandLine() {
        CommandLine cl;
        
        cl = new CommandLine(true);
        cl.addOption(ARG_CATEGORY);
        cl.addSwitch(ARG_TRACE);
        return cl;
    }
    
    public Command dispatch(String cmd, Actuals actuals) throws Abort, XException {
        List additionals;
        
        additionals = actuals.getAdditionals();
        if (CMD_PORT.equals(cmd)) {
            return portCommand(additionals);
        } else {
            if (additionals.size() > 0) {
                throw new Abort("unkown action: " + additionals.get(0));
            }
            if (CMD_RUN.equals(cmd)) {
                final Catalina catalina = getCatalina();
                return new Command("run server at " + catalina.getUrl(), out) {
                    public void run() throws Abort {
                        out.normal.println("exit code = " + catalina.run(out.quiet));
                    }
                };
            } else if (CMD_START.equals(cmd)) {
                final Catalina catalina = getCatalina();
                return new Command("start server at " + catalina.getUrl(), out) {
                    public void run() throws Abort {
                        catalina.start(out.normal);
                    }
                };
            } else if (CMD_STOP.equals(cmd)) {
                final Catalina catalina = getCatalina();
                return new Command("stop server at " + catalina.getUrl(), out) {
                    public void run() throws Abort {
                        catalina.stop();
                    }
                };
            } else if (CMD_UP.equals(cmd)) {
                final Service service = new Service();
                checkWindows();
                return new Command("server up", out) {
                    public void run() throws Abort, IOException {
                        service.start();
                    }
                };
            } else if (CMD_DOWN.equals(cmd)) {
                final Service service = new Service();
                checkWindows();
                return new Command("server down", out) {
                    public void run() throws Abort, IOException {
                        service.stop();
                    }
                };
            } else if (CMD_SUSPEND.equals(cmd)) {
                return manager("stop");
            } else if (CMD_RESUME.equals(cmd)) {
                return manager("start");
            } else if (CMD_RELOAD.equals(cmd)) {
                return manager("reload");
            } else if (CMD_TRACE.equals(cmd)) {
                final String category = actuals.getString(ARG_CATEGORY, null);
                return new Command("server trace", out) {
                    public void run() throws Abort, IOException {
                        trace(category);
                    }
                };
            } else if (CMD_STATUS.equals(cmd)) {
                final Catalina catalina = getCatalina();
                return new Command("status", out) {
                    public void run() throws Abort {
                        out.quiet.println("status: " + getStatus(catalina));
                    }
                };
            } else {
                throw new XAssertionFailed(cmd);
            }
        }
    }
    
    private Command portCommand(List additionals) throws Abort {
        String str;
        final int port;
        
        if (additionals.size() != 1) {
            throw new Abort("missing port option");
        }
        str = (String) additionals.get(0);
        try {
            port = Integer.parseInt(str);
        } catch (NumberFormatException e) {
            throw new Abort("invalid port: " + str);
        }
        return new Command("set port " + port, out) {
            public void run() throws Abort, XException, IOException {
                try {
                    getCatalina().setPort(port);
                } catch (JDOMException e) {
                    throw new Abort(e);
                }
            }
        };
    }
    
    public void checkWindows() throws Abort {
        if (!Platform.isWindows()) {
            throw new Abort("This command is available on Windows only.");
        }
    }
    
    public Command manager(final String cmd) throws Abort {
        final Catalina catalina = getCatalina();
        return new Command("manager " + cmd, out) {
            public void run() throws Abort, XException, IOException {
                try {
                    out.verbose.println(catalina.manager("/" + cmd + "?path=" + getPath(catalina)));
                } catch (JDOMException e) {
                    throw new Abort(e);
                }
            }
        };
    }
    private static String getPath(Catalina catalina) {
        return "/" + catalina.getContext();
    }
    
    public static final String RUNNING = "running";
    public static final String SUSPENDED = "suspended";
    public static final String STOPPED = "stopped";
    
    public String getStatus(Catalina catalina) throws Abort {
        String line;
        String status;
        
        try {
            line = getTwsLine(catalina);
        } catch (JDOMException e) {
            throw new Abort("cannot access tomcat", e);
        } catch (XException e) {
            throw new Abort("cannot access tomcat", e);
        } catch (IOException e) {
            if (e.getMessage().startsWith("Connection refused")) {
                // tomcat not running
                return STOPPED;
            } else {
                throw new Abort("cannot access tomcat", e);
            }
        }
        status = getStatus(line);
        if (RUNNING.equals(status)) {
            return status;
        } else if ("stopped".equals(status)) {
            return SUSPENDED;
        } else {
            throw new Abort("unkown status: " + status);
        }
    }
    
    private static String getStatus(String line) throws Abort {
        String[] fields;
        
        fields = Strings.split(line, ":");
        if (fields.length < 2) {
            throw new Abort("invalid status line: " + line);
        }
        return fields[1].trim();
    }
    
    private static String getTwsLine(Catalina catalina)
        throws Abort, IOException, XException, JDOMException {
        String response;
        String[] lines;
        int i;
        
        response = catalina.manager("/list");
        lines = Strings.split(response, "\n");
        if (lines.length == 0) {
            throw new Abort("invalid response: missing ok line: " + response);
        }
        for (i = 1; i < lines.length; i++) {
            if (lines[i].trim().startsWith(getPath(catalina))) {
                return lines[i].trim();
            }
        }
        throw new Abort("web application not found: " + response);
    }
    
    public void trace(String category) throws Abort, IOException {
        Log4jConfig task;
        
        if (category == null) {
            throw new Abort("missing category option");
        }
        task = new Log4jConfig(out.verbose);
        task.setCategory(category);
        task.setOutput("TRACEFILE");
        task.setTaminowebdavserverHome(Env.get().home);
        task.execute();
    }
}






