com.adito.server.Main.java Source code

Java tutorial

Introduction

Here is the source code for com.adito.server.Main.java

Source

/*
*  Adito
*
*  Copyright (C) 2003-2006 3SP LTD. All Rights Reserved
*
*  This program is free software; you can redistribute it and/or
*  modify it under the terms of the GNU General Public License
*  as published by the Free Software Foundation; either version 2 of
*  the License, or (at your option) any later version.
*  This program 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 General Public License for more details.
*
*  You should have received a copy of the GNU General Public
*  License along with this program; if not, write to the Free Software
*  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

package com.adito.server;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.net.Authenticator;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.NetworkInterface;
import java.net.Socket;
import java.net.SocketException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.TreeMap;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;

import javax.net.ssl.TrustManager;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.mortbay.http.HttpContext;
import org.mortbay.http.NCSARequestLog;
import org.mortbay.http.ResourceCache;
import org.mortbay.http.SocketListener;
import org.mortbay.http.handler.MsieSslHandler;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.servlet.ServletHandler;
import org.mortbay.jetty.servlet.ServletHttpRequest;
import org.mortbay.jetty.servlet.ServletHttpResponse;
import org.mortbay.jetty.servlet.SessionManager;
import org.mortbay.util.LifeCycleEvent;
import org.mortbay.util.LifeCycleListener;
import org.mortbay.util.Password;
import org.tanukisoftware.wrapper.WrapperListener;
import org.tanukisoftware.wrapper.WrapperManager;

import com.adito.boot.BootProgressMonitor;
import com.adito.boot.Branding;
import com.adito.boot.BrowserLauncher;
import com.adito.boot.Context;
import com.adito.boot.ContextConfig;
import com.adito.boot.ContextHolder;
import com.adito.boot.ContextKey;
import com.adito.boot.ContextListener;
import com.adito.boot.DefaultPropertyDefinition;
import com.adito.boot.KeyStoreManager;
import com.adito.boot.LogBootProgressMonitor;
import com.adito.boot.PropertyClass;
import com.adito.boot.PropertyClassManager;
import com.adito.boot.PropertyDefinition;
import com.adito.boot.PropertyList;
import com.adito.boot.PropertyPreferences;
import com.adito.boot.RequestHandler;
import com.adito.boot.RequestHandlerRequest;
import com.adito.boot.RequestHandlerResponse;
import com.adito.boot.StopContextListenerThread;
import com.adito.boot.SystemProperties;
import com.adito.boot.Util;
import com.adito.boot.VersionInfo;
import com.adito.boot.XMLPropertyDefinition;
import com.adito.boot.VersionInfo.Version;
import com.adito.server.jetty.CustomHttpContext;
import com.adito.server.jetty.CustomJsseListener;
import com.adito.server.jetty.CustomWebApplicationContext;
import com.adito.server.jetty.HTTPRedirectHandler;

/**
 * <p>
 * Provides an entry point and a default environment for starting the
 * Adito service.
 * 
 * <p>
 * Adito is primarily a standard Java web application. However, it
 * requires a few additional services from the container that it is running in.
 * This environment is called the {@link com.adito.boot.Context} (see this
 * interfaces Javadoc for more information about this environment) and this
 * class implements that interface.
 * 
 * <p>
 * This class currently provides an implementation that uses Jetty for the
 * servlet / JSP container.
 * 
 * <p>
 * The <i>Context Properties</b> are stored using the Java Preferences API so
 * will likely end up in the Windows register on Win32 platforms or XML files
 * everywhere else.
 * 
 * @see com.adito.boot.Context
 */
public class Main implements WrapperListener, Context {
    // Private statics

    private static File DB_DIR = new File("db");
    private static File CONF_DIR = new File("conf");
    private static File TMP_DIR = new File("tmp");
    private static File LOG_DIR = new File("logs");
    private static File appDir = null;

    static Log log;
    static Preferences PREF;
    static ClassLoader bootLoader;

    // Private instance variables
    private Server server;
    private long startupStarted;
    private HashMap<URL, ResourceCache> resourceCaches;
    private String hostAddress;
    private static boolean useWrapper = false;
    private boolean install;
    private boolean gui;
    private Throwable startupException;
    // a list of listeners holding the http and https socket listeners.
    private List<SocketListener> listeners;
    private CustomWebApplicationContext webappContext;
    private CustomHttpContext httpContext;
    private String jettyLog;
    private int defaultPort;
    private int actualPort;
    private boolean useDevConfig;
    private String hostname;
    private ServerLock serverLock;
    private Thread mainThread;
    private Thread insecureThread;
    private ThreadGroup threadGroup;
    private TreeMap<String, PropertyDefinition> contextPropertyDefinitions;
    private List<ContextListener> contextListeners;
    private boolean shuttingDown;
    private ContextConfig contextConfiguration;
    private BootProgressMonitor bootProgressMonitor;
    private boolean logToConsole;
    private boolean restarting;
    private Server insecureServer;
    private ServletHandler servletHandler;

    public static void setBootLoader(ClassLoader bootLoader) {
        Main.bootLoader = bootLoader;
    }

    /**
     * Entry point
     * 
     * @param args
     * @throws Throwable
     */
    public static void main(String[] args) throws Throwable {

        // This is a hack to allow the Install4J installer to get the java
        // runtime that will be used
        if (args.length > 0 && args[0].equals("--jvmdir")) {
            System.out.println(SystemProperties.get("java.home"));
            System.exit(0);
        }
        useWrapper = System.getProperty("wrapper.key") != null;
        final Main main = new Main();
        ContextHolder.setContext(main);

        if (useWrapper) {
            WrapperManager.start(main, args);
        } else {
            Integer returnCode = main.start(args);
            if (returnCode != null) {
                if (main.gui) {
                    if (main.startupException == null) {
                        main.startupException = new Exception("An exit code of " + returnCode + " was returned.");
                    }
                    try {
                        if (SystemProperties.get("os.name").toLowerCase().startsWith("windows")) {
                            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                        }
                    } catch (Exception e) {
                    }
                    String mesg = main.startupException.getMessage() == null ? "No message supplied."
                            : main.startupException.getMessage();
                    StringBuffer buf = new StringBuffer();
                    int l = 0;
                    char ch = ' ';
                    for (int i = 0; i < mesg.length(); i++) {
                        ch = mesg.charAt(i);
                        if (l > 50 && ch == ' ') {
                            buf.append("\n");
                            l = 0;
                        } else {
                            if (ch == '\n') {
                                l = 0;
                            } else {
                                l++;
                            }
                            buf.append(ch);
                        }
                    }
                    mesg = buf.toString();
                    final String fMesg = mesg;
                    SwingUtilities.invokeAndWait(new Runnable() {
                        public void run() {
                            JOptionPane.showMessageDialog(null, fMesg, "Startup Error", JOptionPane.ERROR_MESSAGE);
                        }
                    });
                }
                System.exit(returnCode.intValue());
            } else {
                Runtime.getRuntime().addShutdownHook(new Thread() {
                    public void run() {
                        if (!main.shuttingDown) {
                            main.stop(0);
                        }
                    }
                });
            }
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.tanukisoftware.wrapper.WrapperListener#start(java.lang.String[])
     */
    public Integer start(String[] args) {
        startupStarted = System.currentTimeMillis();

        // Inform the wrapper the startup process may take a while
        if (useWrapper) {
            WrapperManager.signalStarting(60000);
        }

        // Parse the command line
        Integer returnCode = parseCommandLine(args);
        if (returnCode != null) {
            if (returnCode.intValue() == 999) {
                return null;
            }
            return returnCode;
        }

        // Create the boot progress monitor
        if (gui) {
            try {
                UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
            } catch (Exception e) {
            }
            bootProgressMonitor = new SwingBootProgressMonitor();
        } else {
            bootProgressMonitor = new LogBootProgressMonitor();
        }

        //
        resourceCaches = new HashMap<URL, ResourceCache>();
        contextListeners = new ArrayList<ContextListener>();

        loadSystemProperties();
        initialiseLogging();

        /*
         * Migrate preferences.
         */
        File newPrefDir = new File(ContextHolder.getContext().getConfDirectory(), "prefs");
        PREF = PropertyPreferences.SYSTEM_ROOT;
        try {
            if (!newPrefDir.exists() && Preferences.systemRoot().node("/com").nodeExists("adito")) {
                Preferences from = Preferences.systemRoot().node("/com/adito");
                log.warn("Migrating preferences");
                try {
                    copyNode(from.node("core"), PREF.node("core"));
                    from.node("core").removeNode();
                    copyNode(from.node("plugin"), PREF.node("plugin"));
                    from.node("plugin").removeNode();
                    copyNode(from.node("extensions"), PREF.node("extensions"));
                    from.node("extensions").removeNode();
                    copyNode(from.node("dbupgrader"), PREF.node("dbupgrader"));
                    from.node("dbupgrader").removeNode();
                } catch (Exception e) {
                    log.error("Failed to migrate preferences.", e);
                }
                try {
                    from.flush();
                } catch (BackingStoreException bse) {
                    log.error("Failed to flush old preferences");
                }
                try {
                    PREF.flush();
                } catch (BackingStoreException bse) {
                    log.error("Failed to flush new preferences");
                }
                if (log.isInfoEnabled()) {
                    log.info("Flushing preferences");
                }

            }
        } catch (BackingStoreException bse) {
            log.error("Failed to migrate preferences.", bse);
        }

        // Inform the wrapper the startup process is going ok
        if (useWrapper) {
            WrapperManager.signalStarting(60000);
        }

        try {
            clearTemp();
            try {
                hostname = Inet4Address.getLocalHost().getCanonicalHostName();
                hostAddress = Inet4Address.getLocalHost().getHostAddress();
            } catch (Exception ex) {
                // This should be fatal, we now rely on the hostname being
                // available
                throw new Exception("The host name or address on which this service is running could not "
                        + "be determined. Check you network configuration. One possible cause is "
                        + "a misconfigured 'hosts' file (e.g. on UNIX-like systems this would be "
                        + "/etc/hosts, on Windows XP it would be "
                        + "C:\\Windows\\System32\\Drivers\\Etc\\Hosts).");
            }

            PropertyClassManager.getInstance()
                    .registerPropertyClass(contextConfiguration = new ContextConfig(getClass().getClassLoader()));

            // Display some information about the system we are running on
            displaySystemInfo();

            // Load the context property definitions
            loadContextProperties();

            // Inform the wrapper the startup process is going ok
            if (useWrapper) {
                WrapperManager.signalStarting(60000);
            }

            // Configure any HTTP / HTTPS / SOCKS proxy servers
            configureProxyServers();

            PropertyList l = contextConfiguration.retrievePropertyList(new ContextKey("webServer.bindAddress"));
            getBootProgressMonitor().updateMessage("Creating server lock");
            getBootProgressMonitor().updateProgress(6);
            serverLock = new ServerLock((String) l.get(0));
            if (serverLock.isLocked()) {
                if (!isSetupMode()) {
                    if (serverLock.isSetup()) {
                        throw new Exception("The installation wizard is currently running. "
                                + "Please shut this down by pointing your browser " + "to http://" + getHostname()
                                + ":" + serverLock.getPort()
                                + "/showShutdown.do before attempting to start the server again.");
                    } else {
                        throw new Exception("The server is already running.");
                    }
                } else {
                    if (!serverLock.isSetup()) {
                        throw new Exception("The server is currently already running. "
                                + "Please shut this down by pointing your browser " + "to https://" + getHostname()
                                + ":" + serverLock.getPort()
                                + "/showShutdown.do before attempting to start the server again.");
                    } else {
                        throw new Exception("The installation wizard is running..");
                    }

                }

            }

            // Inform the wrapper the startup process is going ok
            if (useWrapper) {
                WrapperManager.signalStarting(60000);
            }

            Runtime.getRuntime().addShutdownHook(new Thread() {
                public void run() {
                    serverLock.stop();
                }
            });

            //
            registerKeyStores();

            //
            threadGroup = new ThreadGroup("MainThreadGroup");

            if (install) {
                setupMode();

            } else {
                normalMode();
                startHttpServer();
            }
        } catch (Throwable t) {
            startupException = t;
            log.error("Failed to start the server. " + t.getMessage(), t);
            return new Integer(1);
        }

        return null;
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.adito.boot.Context#getConfig()
     */
    public PropertyClass getConfig() {
        return contextConfiguration;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.tanukisoftware.wrapper.WrapperListener#controlEvent(int)
     */
    public void controlEvent(int evt) {
        if (evt == WrapperManager.WRAPPER_CTRL_C_EVENT) {
            if (log.isInfoEnabled())
                log.info("Got CTRL+C event");
            WrapperManager.stop(0);
        } else if (evt == WrapperManager.WRAPPER_CTRL_CLOSE_EVENT) {
            if (log.isInfoEnabled())
                log.info("Got windows close event, ignoring.");
        } else if (evt == WrapperManager.WRAPPER_CTRL_LOGOFF_EVENT) {
            if (log.isInfoEnabled())
                log.info("Got windows logoff event, ignoring.");
        } else if (evt == WrapperManager.WRAPPER_CTRL_SHUTDOWN_EVENT) {
            if (log.isInfoEnabled())
                log.info("Got shutdown event");
            WrapperManager.stop(0);
        } else {
            if (log.isInfoEnabled())
                log.info("Got unknown control event " + evt + ", ignoring.");
        }

    }

    /*
     * (non-Javadoc)
     * 
     * @see org.tanukisoftware.wrapper.WrapperListener#stop(int)
     */
    public int stop(int exitCode) {
        if (log != null) {
            if (log.isInfoEnabled()) {
                if (restarting)
                    log.info("Restarting the server.");
                else
                    log.info("Shutting down the server.");
            }
        }
        if (useWrapper) {
            WrapperManager.signalStopping(20000);
        }

        // TODO This really screws up wrapper on windows - no idea why
        if (server != null && server.isStarted()) {
            try {
                server.stop(false);
            } catch (InterruptedException e) {
                if (log != null) {
                    if (log.isInfoEnabled())
                        log.info("Failed to stop server.", e);
                }
            }
        }

        // Inform all context listeners of what is happening
        for (ContextListener l : contextListeners) {
            new StopContextListenerThread(l).waitForStop();
        }

        // 
        if (log.isInfoEnabled()) {
            log.info("Flushing preferences");
        }
        try {
            ContextHolder.getContext().getPreferences().flush();
        } catch (BackingStoreException bse) {
            log.error("Failed to flush context preferences.", bse);
        }
        try {
            Preferences.systemRoot().flush();
        } catch (IllegalStateException ise) {
        } catch (BackingStoreException bse) {
            log.error("Failed to flush system preferences");
        }

        return exitCode;
    }

    void loadContextProperties() throws IOException, JDOMException {
        getBootProgressMonitor().updateMessage("Loading context properties");
        getBootProgressMonitor().updateProgress(4);
        for (Enumeration<URL> e = getClass().getClassLoader()
                .getResources("META-INF/contextConfig-definitions.xml"); e.hasMoreElements();) {
            URL u = e.nextElement();
            log.info("Loading context property definitions from " + u);
            SAXBuilder build = new SAXBuilder();
            Element root = build.build(u).getRootElement();
            if (!root.getName().equals("definitions")) {
                throw new JDOMException("Root element in " + u + " should be <definitions>");
            }
            for (Iterator i = root.getChildren().iterator(); i.hasNext();) {
                Element c = (Element) i.next();
                if (c.getName().equals("definition")) {
                    DefaultPropertyDefinition def = new XMLPropertyDefinition(c);
                    contextConfiguration.registerPropertyDefinition(def);
                } else {
                    throw new JDOMException(
                            "Expect root element of <definitions> with child elements of <definition>. Got <"
                                    + c.getName() + ">.");
                }
            }
        }

    }

    void copyNode(Preferences from, Preferences to) throws BackingStoreException {
        String[] keys = from.keys();
        for (int i = 0; i < keys.length; i++) {
            to.put(keys[i], from.get(keys[i], ""));
        }
        String childNodes[] = from.childrenNames();
        for (int i = 0; i < childNodes.length; i++) {
            Preferences cn = from.node(childNodes[i]);
            Preferences tn = to.node(childNodes[i]);
            copyNode(cn, tn);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.adito.boot.Context#isSetupMode()
     */
    public boolean isSetupMode() {
        return install;
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.adito.boot.Context#isRestartAvailableMode()
     */
    public boolean isRestartAvailableMode() {
        return (useDevConfig || useWrapper) && !isSetupMode();
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.adito.boot.Context#shutdown(boolean)
     */
    public void shutdown(boolean restart) {
        shuttingDown = true;
        restarting = restart;
        if (useWrapper) {
            if (restart) {
                WrapperManager.restart();
            } else {
                WrapperManager.stop(0);
            }
        } else {
            stop(0);
            System.exit(0);
        }

    }

    /*
     * (non-Javadoc)
     * 
     * @see com.adito.boot.Context#getVersion()
     */
    public Version getVersion() {
        return VersionInfo.getVersion();
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.adito.boot.Context#getConfDirectory()
     */
    public File getConfDirectory() {
        return CONF_DIR;
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.adito.boot.Context#getTempDirectory()
     */
    public File getTempDirectory() {
        return TMP_DIR;
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.adito.boot.Context#getLogDirectory()
     */
    public File getLogDirectory() {
        return LOG_DIR;
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.adito.boot.Context#getApplicationDirectory()
     */
    public File getApplicationDirectory() {
        return appDir == null ? new File(getTempDirectory(), "extensions") : appDir;
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.adito.boot.Context#getDBDirectory()
     */
    public File getDBDirectory() {
        return DB_DIR;
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.adito.boot.Context#getMainThread()
     */
    public Thread getMainThread() {
        return mainThread;
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.adito.boot.Context#setResourceAlias(java.lang.String,
     *      java.lang.String)
     */
    public void setResourceAlias(String uri, String location) {
        webappContext.setResourceAlias(uri, location);
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.adito.boot.Context#addResourceBase(java.net.URL)
     */
    public void addResourceBase(URL base) {
        if (log.isInfoEnabled())
            log.info("Adding new resource base " + base.toExternalForm());
        ResourceCache cache = new ResourceCache();
        cache.setMimeMap(webappContext.getMimeMap());
        cache.setEncodingMap(webappContext.getEncodingMap());
        cache.setResourceBase(base.toExternalForm());
        try {
            cache.start();
            webappContext.addResourceCache(cache);
            if (httpContext != null) {
                httpContext.addResourceCache(cache);
            }
            resourceCaches.put(base, cache);
        } catch (Exception e) {
            log.error("Failed to add new resource base " + base.toExternalForm() + ".", e);
        }

    }

    /*
     * (non-Javadoc)
     * 
     * @see com.adito.boot.Context#addResourceBase(java.net.URL)
     */
    public void removeResourceBase(URL base) {
        if (log.isInfoEnabled())
            log.info("Removing resource base " + base.toExternalForm());
        ResourceCache cache = (ResourceCache) resourceCaches.get(base);
        webappContext.removeResourceCache(cache);
        if (httpContext != null) {
            httpContext.removeResourceCache(cache);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.adito.boot.Context#getHostname()
     */
    public String getHostname() {
        return hostname;
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.adito.boot.Context#getPort()
     */
    public int getPort() {
        return actualPort;
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.adito.boot.Context#addContextLoaderURL(java.net.URL)
     */
    public void addContextLoaderURL(URL url) {
        doAddContextLoaderURL(url);
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.adito.boot.Context#registerRequestHandler(com.adito.boot.RequestHandler)
     */
    public void registerRequestHandler(RequestHandler requestHandler) {
        registerRequestHandler(requestHandler, HandlerProtocol.HTTPS_PROTOCOL);
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.adito.boot.Context#deregisterRequestHandler(com.adito.boot.RequestHandler)
     */
    public void deregisterRequestHandler(RequestHandler requestHandler) {
        if (httpContext != null) {
            httpContext.deregisterRequestHandler(requestHandler);
        }
        HTTPRedirectHandler.registerHandler(requestHandler);
    }

    private void registerKeyStores() throws Exception {
        getBootProgressMonitor().updateMessage("Registering key stores");
        getBootProgressMonitor().updateProgress(7);
        String defaultKeyStorePassword = contextConfiguration
                .retrieveProperty(new ContextKey("webServer.keystore.sslCertificate.password"));
        KeyStoreManager.registerKeyStore(KeyStoreManager.DEFAULT_KEY_STORE, "keystore", true,
                defaultKeyStorePassword, KeyStoreManager.getKeyStoreType(
                        contextConfiguration.retrieveProperty(new ContextKey("webServer.keyStoreType"))));

        KeyStoreManager.registerKeyStore(KeyStoreManager.SERVER_AUTHENTICATION_CERTIFICATES_KEY_STORE, "keystore",
                true, "adito", KeyStoreManager.TYPE_JKS);
        KeyStoreManager.registerKeyStore(KeyStoreManager.TRUSTED_SERVER_CERTIFICATES_KEY_STORE, "keystore", true,
                "adito", KeyStoreManager.TYPE_JKS);

    }

    private void clearTemp() {
        String currVer = PREF.get("lastTempClear", "");
        if (currVer.equals("") || !currVer.equals(getVersion().toString())
                || "true".equalsIgnoreCase(SystemProperties.get("adito.clearTemp"))) {
            getBootProgressMonitor().updateMessage("Clearing temporary files");
            getBootProgressMonitor().updateProgress(3);
            if (log.isInfoEnabled())
                log.info("Clearing temporary directory");

            /* We have to leave the server.run and server.pid files alone as these are
             * used by external components to determine service state 
             */

            File[] files = getTempDirectory().listFiles();
            if (files != null) {
                for (File file : files) {
                    if (!file.getName().equals(ServerLock.LOCK_NAME)
                            && !file.getName().equals(Branding.SERVICE_NAME + ".pid")) {
                        Util.delTree(file);
                    }
                }
            } else {
                if (!getTempDirectory().mkdirs()) {
                    log.error("CRITICAL. Failed to create the temporary directory " + getTempDirectory() + ".");
                }
            }
        }
        PREF.put("lastTempClear", getVersion().toString());
    }

    private void initialiseLogging() {
        URL resource = bootLoader.getResource("log4j.properties");
        getBootProgressMonitor().updateMessage("Intialising logging");
        getBootProgressMonitor().updateProgress(2);
        LOG_DIR.mkdirs();
        InputStream in = null;
        try {
            if (resource == null) {
                throw new IOException("Could not locate log4j.properties");
            }
            in = resource.openStream();
            Properties p = new Properties();
            p.load(in);
            p.setProperty("log4j.rootCategory",
                    p.getProperty("log4j.rootCategory", "WARN,logfile") + (logToConsole ? ",stdout" : ""));
            Class.forName("org.apache.log4j.PropertyConfigurator", true, bootLoader)
                    .getMethod("configure", new Class[] { Properties.class }).invoke(null, new Object[] { p });
        } catch (Exception e) {
        } finally {
            Util.closeStream(in);
        }
        log = LogFactory.getLog(Main.class);
    }

    private void startHttpServer() throws Exception {

        int port = contextConfiguration.retrievePropertyInt(new ContextKey("webServer.httpRedirectPort"));
        if (port <= 0) {
            if (log.isInfoEnabled())
                log.info("HTTP redirect port " + port + " is invalid");
            return;
        }

        String bind = contextConfiguration.retrieveProperty(new ContextKey("webServer.bindAddress"));
        PropertyList l = new PropertyList(bind.equals("") ? "0.0.0.0" : bind);
        insecureServer = new Server();
        for (Iterator<String> i = l.iterator(); i.hasNext();) {
            String address = i.next();
            if (log.isInfoEnabled())
                log.info("Adding listener on " + address + ":" + port);
            SocketListener listener = new SocketListener();
            listener.setHost(address);
            listener.setPort(port);
            listener.setMinThreads(
                    contextConfiguration.retrievePropertyInt(new ContextKey("webServer.minThreads")));
            listener.setMaxThreads(
                    contextConfiguration.retrievePropertyInt(new ContextKey("webServer.maxThreads")));
            listener.setMaxIdleTimeMs(0);
            listener.setLowResourcePersistTimeMs(2000);
            listener.setAcceptQueueSize(0);
            listener.setPoolName("P2");
            insecureServer.addListener(listener);
        }

        // Create the webapp
        HttpContext context = new HttpContext();
        context.setContextPath("/");
        context.setResourceBase("./dummy/");
        context.addHandler(new HTTPRedirectHandler());
        insecureServer.addContext(context);

        // Configure the server
        insecureServer.setRequestsPerGC(2000);

        insecureThread = new Thread(threadGroup, "InsecureWebServer") {
            public void run() {
                // Start the server
                try {
                    insecureServer.start();
                } catch (Exception e) {
                    log.warn("Failed to start HTTP Jetty. " + e.getMessage(), e);
                }
            }
        };

        if (log.isInfoEnabled())
            log.info("Starting HTTP redirect server");
        insecureThread.start();

    }

    private void normalMode() throws Exception {

        getBootProgressMonitor().updateMessage("Creating server");
        getBootProgressMonitor().updateProgress(8);

        if (log.isInfoEnabled())
            log.info("Starting Jetty Web Server");

        server = createServer();

        // SunJsseListener listener = new SunJsseListener();
        String keystorePassword = contextConfiguration
                .retrieveProperty(new ContextKey("webServer.keystore.sslCertificate.password"));
        if (keystorePassword.equals("")) {
            throw new Exception(
                    "Private key / certificate password has not been set. Please run the Installation Wizard.");
        }

        actualPort = defaultPort == -1 ? contextConfiguration.retrievePropertyInt(new ContextKey("webServer.port"))
                : defaultPort;
        String bind = contextConfiguration.retrieveProperty(new ContextKey("webServer.bindAddress"));
        listeners = new ArrayList<SocketListener>();
        PropertyList l = new PropertyList(bind.equals("") ? "0.0.0.0" : bind);
        for (Iterator<String> i = l.iterator(); i.hasNext();) {
            String address = i.next();
            if (log.isInfoEnabled())
                log.info("Adding listener on " + address + ":" + actualPort);
            if (!serverLock.isStarted()) {
                serverLock.start(actualPort);
            }
            SocketListener listener = null;
            if (contextConfiguration.retrieveProperty(new ContextKey("webServer.protocol")).equals("http")) {
                listener = new SocketListener();
                log.warn("The server is configured to listen for plain HTTP connections.");
            } else {
                listener = new CustomJsseListener(keystorePassword);
                MsieSslHandler sslHandler = new MsieSslHandler();
                sslHandler.setUserAgentSubString("MSIE 5");
                listener.setHttpHandler(sslHandler);
            }
            listener.setPort(actualPort);
            listener.setMinThreads(
                    contextConfiguration.retrievePropertyInt(new ContextKey("webServer.minThreads")));
            listener.setMaxThreads(
                    contextConfiguration.retrievePropertyInt(new ContextKey("webServer.maxThreads")));
            listener.setMaxIdleTimeMs(
                    contextConfiguration.retrievePropertyInt(new ContextKey("webServer.maxIdleTimeMs")));
            listener.setHost(address);
            listener.setBufferSize(
                    contextConfiguration.retrievePropertyInt(new ContextKey("webServer.bufferSize")));
            listener.setBufferReserve(
                    contextConfiguration.retrievePropertyInt(new ContextKey("webServer.bufferReserve")));
            listener.setTcpNoDelay(
                    contextConfiguration.retrievePropertyBoolean(new ContextKey("webServer.tcpNoDelay")));
            listener.setThreadsPriority(
                    contextConfiguration.retrievePropertyInt(new ContextKey("webServer.threadPriority")));
            listeners.add(listener);

            listener.setLowResourcePersistTimeMs(
                    contextConfiguration.retrievePropertyInt(new ContextKey("webServer.lowResourcePersistTimeMs")));
            listener.setPoolName("main");
            server.addListener(listener);
        }

        // Add the context
        getBootProgressMonitor().updateMessage("Creating web application");
        getBootProgressMonitor().updateProgress(9);
        httpContext = new CustomHttpContext(server, "/", useDevConfig, bootLoader);
        httpContext.setRedirectNullPath(false);
        server.addContext(httpContext);

        // Dunny servlet handler for faking HttpServletRequest,
        // HttpServletResponse
        servletHandler = new ServletHandler();
        servletHandler.initialize(httpContext);
        servletHandler.start();

        // Add the webapp
        webappContext = new CustomWebApplicationContext(useDevConfig, bootLoader);
        addLifecycleListener(webappContext);

        server.addContext(webappContext);
        webappContext.setRedirectNullPath(false);

        // Configure the server
        server.setRequestsPerGC(
                contextConfiguration.retrievePropertyInt(new ContextKey("webServer.requestsPerGC")));
        server.setTrace(false);

        // Set the request log
        if (contextConfiguration.retrievePropertyBoolean(new ContextKey("webServer.requestLog"))) {
            NCSARequestLog requestLog = new NCSARequestLog(jettyLog);
            requestLog.setRetainDays(90);
            requestLog.setAppend(true);
            requestLog.setExtended(false);
            requestLog.setBuffered(false);
            requestLog.setLogTimeZone("GMT");
            server.setRequestLog(requestLog);
        }

        // Inform the wrapper the startup process is going ok
        if (useWrapper) {
            WrapperManager.signalStarting(60000);
        }

        mainThread = new Thread(threadGroup, "WebServer") {
            public void run() {
                // Start the server
                try {
                    server.start();
                    if (useDevConfig) {
                        log.warn("Server startup took " + ((System.currentTimeMillis() - startupStarted) / 1000)
                                + " seconds");
                    }
                    getBootProgressMonitor().updateMessage("Server is now running");
                    getBootProgressMonitor().updateProgress(100);
                    Thread.sleep(2000);
                } catch (Exception e) {
                    log.error("Failed to start Jetty. " + e.getMessage(), e);
                    if (useWrapper) {
                        WrapperManager.stop(1);
                    } else {
                        System.exit(1);
                    }
                } finally {
                    getBootProgressMonitor().dispose();
                }
            }
        };
        mainThread.start();
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.adito.boot.Context#addWebApp(java.lang.String,
     *      java.lang.String)
     */
    public void addWebApp(String path, String warFile) throws Exception {
        log.info("Adding webapp '" + path + "' using path / war '" + warFile + "'");
        HttpContext context = server.addWebApplication(path, warFile);
        context.start();
    }

    private void setupMode() throws Exception {

        // Ensure that https redirect is not turned on in jetty
        System.setProperty("jetty.force.HTTPSRedirect", "false");

        getBootProgressMonitor().updateMessage("Creating server");
        getBootProgressMonitor().updateProgress(8);

        actualPort = defaultPort == -1 ? 28080 : defaultPort;
        serverLock.start(actualPort);

        server = createServer();

        SocketListener socketListener = new SocketListener();
        socketListener.setPort(actualPort);
        socketListener.setMinThreads(10);
        socketListener.setMaxThreads(200);
        socketListener.setMaxIdleTimeMs(0);
        socketListener.setLowResourcePersistTimeMs(2000);
        socketListener.setAcceptQueueSize(0);
        socketListener.setPoolName("P1");
        server.addListener(socketListener);

        // // Add the context
        // HttpContext context = new CustomHttpContext(server, "/",
        // useDevConfig);
        // server.addContext(context);

        // Create the webapp

        getBootProgressMonitor().updateMessage("Creating web application");
        getBootProgressMonitor().updateProgress(9);

        webappContext = new CustomWebApplicationContext(useDevConfig, bootLoader);
        webappContext.setRedirectNullPath(false);
        addLifecycleListener(webappContext);
        server.addContext(webappContext);

        // Configure the server
        server.setRequestsPerGC(2000);

        String realHostname = hostname == null ? InetAddress.getLocalHost().getHostName() : hostname;

        /*
         * If the 'Active DNS' feature is enabled, the DNS server may return the
         * wild-card name. This will probably fail. As a work-around, if the
         * hostname looks like a wildcard, then it is simply changed to
         * 'localhost'.
         */
        if (realHostname.startsWith("*.")) {
            realHostname = "localhost";
        }

        //
        final String fRealHostname = realHostname;
        final int realPort = defaultPort == -1 ? 28080 : defaultPort;

        // Inform the wrapper the startup process is going ok
        if (useWrapper) {
            WrapperManager.signalStarting(60000);
        }

        mainThread = new Thread(threadGroup, "WebServer") {
            public void run() {
                // Start the server
                try {
                    server.start();

                    if (!useWrapper && !"true".equals(SystemProperties.get("adito.noBrowserLaunch"))) {
                        try {
                            BrowserLauncher.openURL("http://" + fRealHostname + ":" + realPort);
                            System.out.println("A browser has been opened and pointed to http://" + fRealHostname
                                    + ":" + realPort + ". ");
                        } catch (Exception ex) {
                            System.out.println(
                                    "Point your browser to http://" + fRealHostname + ":" + realPort + ". ");
                        }
                    } else {
                        System.out.println("Point your browser to http://" + fRealHostname + ":" + realPort + ". ");
                    }
                    System.out.println(
                            "\nPress CTRL+C or use the 'Shutdown' option from the web interface to leave the installation wizard.");
                    getBootProgressMonitor().updateMessage("Server is now running");
                    getBootProgressMonitor().updateProgress(100);
                    Thread.sleep(2000);
                } catch (Exception e) {
                    log.error("Failed to start Jetty. " + e.getMessage(), e);
                    if (useWrapper) {
                        WrapperManager.stop(1);
                    } else {
                        System.exit(1);
                    }
                } finally {
                    getBootProgressMonitor().dispose();
                }
            }
        };

        System.out.print("Starting installation wizard");
        mainThread.start();

        /*
         * Wait for up to 5 minutes for the server to become available we need
         * to wait this long because precompilation can take a while!
         */

        int waitFor = 60 * 5;

        boolean running = false;

        if (!"true".equals(SystemProperties.get("adito.disableStartupCheck", "false"))) {
            int i = 0;
            for (; i < waitFor && !running; i++) {
                try {
                    System.out.print(".");
                    Socket s = new Socket(realHostname, realPort);
                    s.close();
                    running = true;
                } catch (Exception ex) {
                    try {
                        Thread.sleep(1000);
                    } catch (Exception ex2) {
                    }
                }
            }
            System.out.println();
        } else {
            running = true;
        }
        if (!running) {
            System.out.println("Failed to start installation wizard. Check the logs for more detail.");
            if (useWrapper) {
                WrapperManager.stop(1);
            } else {
                System.exit(1);
            }
        }
    }

    private void configureProxyServers() throws Exception {
        getBootProgressMonitor().updateMessage("Configuring proxy servers");
        getBootProgressMonitor().updateProgress(5);

        String httpProxyHost = contextConfiguration.retrieveProperty(new ContextKey("proxies.http.proxyHost"));
        if (!httpProxyHost.equals("")) {
            if (log.isInfoEnabled())
                log.info("Configuring outgoing HTTP connections to use a proxy server.");
            System.setProperty("http.proxyHost", httpProxyHost);
            System.setProperty("com.maverick.ssl.https.HTTPProxyHostname", httpProxyHost);
            String httpProxyPort = contextConfiguration.retrieveProperty(new ContextKey("proxies.http.proxyPort"));

            String httpProxyUsername = contextConfiguration
                    .retrieveProperty(new ContextKey("proxies.http.proxyUser"));
            String httpProxyPassword = contextConfiguration
                    .retrieveProperty(new ContextKey("proxies.http.proxyPassword"));

            System.setProperty("http.proxyPort", httpProxyPort);
            System.setProperty("com.maverick.ssl.https.HTTPProxyPort", httpProxyPort);

            if (!httpProxyUsername.trim().equals(""))
                System.setProperty("com.maverick.ssl.https.HTTPProxyUsername", httpProxyUsername.trim());

            if (!httpProxyPassword.trim().equals(""))
                System.setProperty("com.maverick.ssl.https.HTTPProxyPassword", httpProxyPassword.trim());

            System.setProperty("com.maverick.ssl.https.HTTPProxySecure", "false");

            PropertyList list = contextConfiguration
                    .retrievePropertyList(new ContextKey("proxies.http.nonProxyHosts"));
            StringBuffer hosts = new StringBuffer();
            for (Iterator i = list.iterator(); i.hasNext();) {
                if (hosts.length() != 0) {
                    hosts.append("|");
                }
                hosts.append(i.next());
            }
            System.setProperty("http.nonProxyHosts", hosts.toString());
            System.setProperty("com.maverick.ssl.https.HTTPProxyNonProxyHosts", hosts.toString());
        }
        String socksProxyHost = contextConfiguration.retrieveProperty(new ContextKey("proxies.socksProxyHost"));
        if (!socksProxyHost.equals("")) {
            if (log.isInfoEnabled())
                log.info("Configuring outgoing TCP/IP connections to use a SOCKS proxy server.");
            System.setProperty("socksProxyHost", httpProxyHost);
            System.setProperty("socksProxyPort",
                    contextConfiguration.retrieveProperty(new ContextKey("proxies.socksProxyPort")));
        }
        if (!socksProxyHost.equals("") || !httpProxyHost.equals("")) {
            Authenticator.setDefault(new ProxyAuthenticator());
        }
    }

    private Server createServer() throws MalformedURLException {
        Server server = new Server();
        if (contextConfiguration.retrievePropertyBoolean(new ContextKey("webServer.stats"))) {
            new StatsLogger(server,
                    contextConfiguration.retrievePropertyInt(new ContextKey("webServer.statsUpdate")));
        }
        return server;
    }

    private void addLifecycleListener(final CustomWebApplicationContext context) {
        context.addEventListener(new LifeCycleListener() {
            public void lifeCycleFailure(LifeCycleEvent arg0) {
            }

            public void lifeCycleStarted(LifeCycleEvent arg0) {
                getBootProgressMonitor().updateMessage("Server is now running");
                getBootProgressMonitor().updateProgress(100);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                }
            }

            public void lifeCycleStarting(LifeCycleEvent arg0) {
            }

            public void lifeCycleStopped(LifeCycleEvent arg0) {
            }

            public void lifeCycleStopping(LifeCycleEvent arg0) {
            }
        });
    }

    private void displaySystemInfo() throws SocketException {

        //

        if (useDevConfig) {
            log.warn("Development environment enabled. Do not use this on a production server.");
        }

        if (log.isInfoEnabled()) {
            log.info("Version is " + ContextHolder.getContext().getVersion());
            log.info("Java version is " + SystemProperties.get("java.version"));
            log.info("Server is installed on " + hostname + "/" + hostAddress);
            log.info("Configuration: " + CONF_DIR.getAbsolutePath());
        }

        if (SystemProperties.get("java.vm.name", "").indexOf("GNU") > -1
                || SystemProperties.get("java.vm.name", "").indexOf("libgcj") > -1) {
            System.out.println("********** WARNING **********");
            System.out.println("The system has detected that the Java runtime is GNU/GCJ");
            System.out.println("Adito does not work correctly with this Java runtime");
            System.out.println("you should reconfigure with a different runtime");
            System.out.println("*****************************");

            log.warn("********** WARNING **********");
            log.warn("The system has detected that the Java runtime is GNU/GCJ");
            log.warn("Adito may not work correctly with this Java runtime");
            log.warn("you should reconfigure with a different runtime");
            log.warn("*****************************");

        }

        Enumeration e = NetworkInterface.getNetworkInterfaces();

        while (e.hasMoreElements()) {
            NetworkInterface netface = (NetworkInterface) e.nextElement();
            if (log.isInfoEnabled())
                log.info("Net interface: " + netface.getName());

            Enumeration e2 = netface.getInetAddresses();

            while (e2.hasMoreElements()) {
                InetAddress ip = (InetAddress) e2.nextElement();
                if (log.isInfoEnabled())
                    log.info("IP address: " + ip.toString());
            }
        }

        if (log.isInfoEnabled())
            log.info("System properties follow:");
        Properties sysProps = System.getProperties();
        for (Iterator i = sysProps.entrySet().iterator(); i.hasNext();) {
            Map.Entry entry = (Map.Entry) i.next();
            int idx = 0;
            String val = (String) entry.getValue();
            while (true) {
                if (entry.getKey().equals("java.class.path")) {
                    StringTokenizer t = new StringTokenizer(entry.getValue().toString(),
                            SystemProperties.get("path.separator", ","));
                    while (t.hasMoreTokens()) {
                        String s = t.nextToken();
                        if (log.isInfoEnabled())
                            log.info("java.class.path=" + s);
                    }
                    break;
                } else {
                    if ((val.length() - idx) > 256) {
                        if (log.isInfoEnabled())
                            log.info("  " + entry.getKey() + "=" + val.substring(idx, idx + 256));
                        idx += 256;
                    } else {
                        if (log.isInfoEnabled())
                            log.info("  " + entry.getKey() + "=" + val.substring(idx));
                        break;
                    }
                }
            }
        }
    }

    private void loadSystemProperties() {
        getBootProgressMonitor().updateMessage("Loading system properties");
        getBootProgressMonitor().updateProgress(1);

        /*
         * Read in system properties from a resource, more a debugging aid than
         * anything else
         */
        InputStream in = null;
        try {
            File f = new File(CONF_DIR, "system.properties");
            in = new FileInputStream(f);
            Properties p = new Properties();
            p.load(in);
            for (Enumeration e = p.keys(); e.hasMoreElements();) {
                String k = (String) e.nextElement();
                System.getProperties().setProperty(k, p.getProperty(k).trim());
            }
        } catch (IOException e) {
            // Dont care
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException ioe) {

                }
            }
        }

        /**
         * Set the prefix if any. 
         */
        SystemProperties.setPrefix(System.getProperty("boot.propertyPrefix"));

        /**
         * Are we in development mode?
         */
        useDevConfig = "true".equalsIgnoreCase(SystemProperties.get("adito.useDevConfig"));

        if (!"".equals(SystemProperties.get("adito.extensions", ""))) {
            appDir = new File(SystemProperties.get("adito.extensions"));
        }

        //
        System.setProperty("org.mortbay.jetty.servlet.SessionCookie",
                SystemProperties.get("adito.cookie", "JSESSIONID"));
        System.setProperty("org.mortbay.jetty.servlet.SessionURL",
                SystemProperties.get("adito.cookie", "JSESSIONID").toLowerCase());
    }

    private Integer parseCommandLine(String[] args) {
        defaultPort = -1;
        logToConsole = false;
        jettyLog = "logs/yyyy_mm_dd.request.log";
        boolean fullReset = false;
        String os = System.getProperty("os.name").toLowerCase();
        gui = "true".equals(System.getProperty("adito.useDevConfig")) && os.startsWith("windows");
        try {
            for (int i = 0; i < args.length; i++) {
                if (args[i].equals("--manager")) {
                    System.err.println(
                            "The database manager can no longer be started via the server command line. Run it directly from hsqldb.jar ensuring you have the webapp classes included in your class path.");
                    return new Integer(1);
                } else if (args[i].equals("--setup")) {
                    System.err.println("WARNING: --setup is deprecated, please use --install");
                    install = true;
                } else if (args[i].equals("--install")) {
                    install = true;
                } else if (args[i].equals("--logToConsole")) {
                    logToConsole = true;
                } else if (args[i].equals("--gui")) {
                    if (os.startsWith("windows")) {
                        gui = true;
                    } else if (os.equals("linux") || os.equals("solaris") || os.endsWith("aix")) {
                        String displaySysProp = SystemProperties.get("adito.display", "");
                        String display = null;
                        try {
                            display = displaySysProp.equals("") ? System.getenv("DISPLAY") : displaySysProp;
                        } catch (Throwable t) {
                        }
                        gui = display != null && display.length() > 0;
                    }
                } else if (args[i].startsWith("--db")) {
                    DB_DIR = new File(args[i].substring(5));
                    if (DB_DIR.exists() && !DB_DIR.isDirectory()) {
                        throw new Exception(
                                "--db option specifies an existing file, must either not exist or be a directory");
                    }
                } else if (args[i].startsWith("--applications")) {
                    appDir = new File(args[i].substring(15));
                    if (appDir.exists() && !appDir.isDirectory()) {
                        throw new Exception(
                                "--db option specifies an existing file, must either not exist or be a directory");
                    }
                } else if (args[i].startsWith("--temp")) {
                    TMP_DIR = new File(args[i].substring(7));
                    if (TMP_DIR.exists() && !TMP_DIR.isDirectory()) {
                        throw new Exception(
                                "--temp option specifies an existing file, must either not exist or be a directory");
                    }
                } else if (args[i].startsWith("--conf")) {
                    CONF_DIR = new File(args[i].substring(7));
                    if (!CONF_DIR.exists() || !CONF_DIR.isDirectory()) {
                        throw new Exception("--conf option does not specify a valid directory");
                    }
                } else if (args[i].startsWith("--port")) {
                    defaultPort = Integer.parseInt(args[i].substring(7));
                } else if (args[i].startsWith("--jettyLog")) {
                    jettyLog = args[i].substring(11);
                } else if (args[i].equals("--full-reset")) {
                    fullReset = true;
                } else if (args[i].startsWith("start")) {
                    // For compatibility with the install4j launcher
                } else {
                    System.err.println("Starts / configures the server.\n");
                    System.err.println("Usage: adito [OPTION]...");
                    System.err.println("\nThe server may be started in setup or normal mode. When setup.\n");
                    System.err.println("mode is enabled a plain http server will be started on port 28080\n");
                    System.err.println("allowing you configure using a browser.\n\n");
                    System.out.println("Options:\n");
                    System.out.println(" --install         Start the server in installation mode.");
                    System.out.println(" --full-reset      Deletes *all* configuration data and resets");
                    System.out.println("                   the server to its initial state. Use with");
                    System.out.println("                   greate caution.");
                    System.out.println(" --db=DIR          Set the directory where the configuration");
                    System.out.println("                   database is stored.");
                    System.out.println(" --conf=DIR        Set the directory where the configuration");
                    System.out.println("                   files are stored.");
                    System.out.println(" --temp=DIR        Set the directory where the temporary");
                    System.out.println("                   files are stored.");
                    System.out.println(" --port=NUMBER     The port on which the server will start.");
                    System.out.println("                   Note that this applies to both setup and");
                    System.out.println("                   normal mode and will overide whatever port");
                    System.out.println("                   been configured.");
                    System.out.println(" --jettyLog=LOG    The location of the Jetty NCSA request log.");
                    System.out.println("\nInvalid option: " + args[i] + ".\n");
                    return new Integer(2);
                }
            }

            // Create the temporary directory
            if (!TMP_DIR.exists()) {
                if (!TMP_DIR.mkdirs()) {
                    throw new Exception("Could not create temporary directory " + TMP_DIR.getAbsolutePath() + ".");
                }
            }
        } catch (Exception e) {
            System.err.println(e.getMessage());
            return new Integer(2);
        }

        // Perform a full reset
        if (fullReset) {
            if (fullReset()) {
                System.err.println("Configuration has been fully reset");
                return new Integer(0);
            } else {
                System.err.println("Aborted full reset.");
                return new Integer(1);
            }
        }

        // Another way for external processes to force starting installation
        // wizard
        if (new File(TMP_DIR, "setup.run").exists()) {
            install = true;
        }

        return null;

    }

    /*
     * Perform a full reset
     */
    private boolean fullReset() {
        if (gui) {
            if (JOptionPane.showConfirmDialog(null, "The embedded configuration database will be\n"
                    + "completely deleted and re-created the next\ntime you run the server. Are you absolutely\n"
                    + "sure you wish to do this?", "Full Reset", JOptionPane.WARNING_MESSAGE,
                    JOptionPane.YES_NO_OPTION) != JOptionPane.OK_OPTION) {
                return false;
            }
        } else {
            System.out.println("The embedded configuration database will be");
            System.out.println("completely deleted and re-created the next");
            System.out.println("time you run the server. Are you absolutely");
            System.out.println("sure you wish to do this?");
            System.out.println();
            BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
            while (true) {
                System.out.print("(Y)es or (N)o: ");
                String s;
                try {
                    s = br.readLine();
                    if (s == null) {
                        return false;
                    }
                    if (s.toLowerCase().equals("y") || s.toLowerCase().equals("yes")) {
                        break;
                    }
                    if (s.toLowerCase().equals("n") || s.toLowerCase().equals("no")) {
                        return false;
                    }
                    System.out.println(
                            "\nPlease answer 'y' or 'yes' to perform the reset, or 'n' or 'no' to abort the reset.");
                } catch (IOException e) {
                    return false;
                }
            }
        }

        // Start the reset
        System.out.println("Resetting all configuration");
        File[] f = getDBDirectory().listFiles();
        if (f != null) {
            for (int i = 0; i < f.length; i++) {
                if (!f[i].getName().equals("CVS") && !f[i].equals(".cvsignore")) {
                    System.out.println("    Deleting " + f[i].getPath());
                    if (!f[i].delete()) {
                        System.out.println("        Failed to remove");
                    }
                }
            }
        }

        return true;
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.adito.boot.Context#deobfuscatePassword(java.lang.String)
     */
    public String deobfuscatePassword(String val) {
        try {
            return Password.deobfuscate(val);
        } catch (Exception e) {
            return "";
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.adito.boot.Context#obfuscatePassword(java.lang.String)
     */
    public String obfuscatePassword(String val) {
        return Password.obfuscate(val);
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.adito.boot.Context#setTrustManager(javax.net.ssl.TrustManager)
     */
    public void setTrustManager(TrustManager trustManager, boolean require) {
        if (listeners == null || listeners.size() == 0) {
            log.warn("Not setting trust managers there are no SSL listeners configured.");
        } else {
            if (log.isInfoEnabled())
                log.info("Set trust managers");
            for (Iterator i = listeners.iterator(); i.hasNext();) {
                SocketListener l = (SocketListener) i.next();
                if (l instanceof CustomJsseListener) {
                    ((CustomJsseListener) l).setNeedClientAuth(trustManager != null);
                    ((CustomJsseListener) l).setTrustManager(trustManager, require);
                }
            }
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.adito.boot.Context#addContextListener(com.adito.boot.ContextListener)
     */
    public void addContextListener(ContextListener contextListener) {
        contextListeners.add(contextListener);
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.adito.boot.Context#removeContextListener(com.adito.boot.ContextListener)
     */
    public void removeContextListener(ContextListener contextListener) {
        contextListeners.remove(contextListener);
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.adito.boot.Context#getPreferences()
     */
    public Preferences getPreferences() {
        return PREF;
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.adito.boot.Context#getContextLoaderClassPath()
     */
    public URL[] getContextLoaderClassPath() {
        List<URL> urlList = new ArrayList<URL>();
        ClassLoader webappContextClassLoader = webappContext.getClassLoader();
        while (webappContextClassLoader != null) {
            if (webappContextClassLoader != null && webappContextClassLoader instanceof URLClassLoader) {
                urlList.addAll(Arrays.asList(((URLClassLoader) webappContextClassLoader).getURLs()));
            }
            webappContextClassLoader = webappContextClassLoader.getParent();
        }
        URL[] urls = (URL[]) urlList.toArray(new URL[urlList.size()]);
        return urls;
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.adito.boot.Context#getContextLoader()
     */
    public ClassLoader getContextLoader() {
        return webappContext.getClassLoader();
    }

    public void removeResourceAlias(String uri) {
        webappContext.removeResourceAlias(uri);
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.adito.boot.Context#getResourceBases()
     */
    public Collection<URL> getResourceBases() {
        return resourceCaches.keySet();
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.adito.boot.Context#getBootProgressMonitor()
     */
    public BootProgressMonitor getBootProgressMonitor() {
        return bootProgressMonitor;
    }

    public void registerRequestHandler(RequestHandler requestHandler, HandlerProtocol protocol) {
        if (httpContext != null) {
            if (protocol == HandlerProtocol.HTTPS_PROTOCOL || protocol == HandlerProtocol.BOTH_PROTOCOLS)
                httpContext.registerRequestHandler(requestHandler);
        }

        if (protocol == HandlerProtocol.HTTP_PROTOCOL || protocol == HandlerProtocol.BOTH_PROTOCOLS) {
            HTTPRedirectHandler.registerHandler(requestHandler);
        }

    }

    public HttpServletRequest createServletRequest(RequestHandlerRequest request) {

        if (request instanceof com.adito.server.jetty.RequestAdapter) {
            ServletHttpRequest req = new ServletHttpRequest(servletHandler, request.getPath(),
                    ((com.adito.server.jetty.RequestAdapter) request).getHttpRequest());
            return req;
        } else
            throw new IllegalArgumentException("Request must be RequestAdapter");

    }

    public HttpServletResponse createServletResponse(RequestHandlerResponse response, HttpServletRequest request) {
        if (response instanceof com.adito.server.jetty.ResponseAdapter) {
            ServletHttpResponse res = new ServletHttpResponse((ServletHttpRequest) request,
                    ((com.adito.server.jetty.ResponseAdapter) response).getHttpResponse());
            ((ServletHttpRequest) request).getSession(true);
            return res;
        } else
            throw new IllegalArgumentException("Response must be ResponseAdapter");
    }

    private void doAddContextLoaderURL(URL u) {
        try {
            Class sysclass = URLClassLoader.class;
            Method method = sysclass.getDeclaredMethod("addURL", new Class[] { URL.class });
            method.setAccessible(true);
            method.invoke(webappContext.getClassLoader(), new Object[] { u });
            if (log.isInfoEnabled())
                log.info(u.toExternalForm() + " added to context classloader");
        } catch (Exception e) {
            log.error("Failed to add to classpath.", e);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.adito.boot.Context#access(javax.servlet.http.HttpSession)
     */
    public void access(HttpSession session) {
        ((SessionManager.Session) session).access();

    }

}