Package winstone

Source Code of winstone.Launcher

/*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license (CDDL), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.ObjectInputStream;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;

/**
* Implements the main launcher daemon thread. This is the class that gets
* launched by the command line, and owns the server socket, etc.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: Launcher.java,v 1.29 2007/04/23 02:55:35 rickknowles Exp $
*/
public class Launcher implements Runnable {
   
    static final String HTTP_LISTENER_CLASS = "winstone.HttpListener";
    static final String HTTPS_LISTENER_CLASS = "winstone.ssl.HttpsListener";
    static final String AJP_LISTENER_CLASS = "winstone.ajp13.Ajp13Listener";
    static final String CLUSTER_CLASS = "winstone.cluster.SimpleCluster";
    static final String DEFAULT_JNDI_MGR_CLASS = "winstone.jndi.ContainerJNDIManager";

    public static final byte SHUTDOWN_TYPE = (byte) '0';
    public static final byte RELOAD_TYPE = (byte) '4';
   
    private int CONTROL_TIMEOUT = 2000; // wait 2s for control connection
    private int DEFAULT_CONTROL_PORT = -1;
   
    private Thread controlThread;
    public final static WinstoneResourceBundle RESOURCES = new WinstoneResourceBundle("winstone.LocalStrings");
    private int controlPort;
    private HostGroup hostGroup;
    private ObjectPool objectPool;
    private List listeners;
    private Map args;
    private Cluster cluster;
    private JNDIManager globalJndiManager;
   
    /**
     * Constructor - initialises the web app, object pools, control port and the
     * available protocol listeners.
     */
    public Launcher(Map args) throws IOException {
       
        boolean useJNDI = WebAppConfiguration.booleanArg(args, "useJNDI", false);
       
        // Set jndi resource handler if not set (workaround for JamVM bug)
        if (useJNDI) try {
            Class ctxFactoryClass = Class.forName("winstone.jndi.java.javaURLContextFactory");
            if (System.getProperty("java.naming.factory.initial") == null) {
                System.setProperty("java.naming.factory.initial", ctxFactoryClass.getName());
            }
            if (System.getProperty("java.naming.factory.url.pkgs") == null) {
                System.setProperty("java.naming.factory.url.pkgs", "winstone.jndi");
            }
        } catch (ClassNotFoundException err) {}
       
        Logger.log(Logger.MAX, RESOURCES, "Launcher.StartupArgs", args + "");
       
        this.args = args;
        this.controlPort = (args.get("controlPort") == null ? DEFAULT_CONTROL_PORT
                : Integer.parseInt((String) args.get("controlPort")));

        // Check for java home
        List jars = new ArrayList();
        List commonLibCLPaths = new ArrayList();
        String defaultJavaHome = System.getProperty("java.home");
        String javaHome = WebAppConfiguration.stringArg(args, "javaHome", defaultJavaHome);
        Logger.log(Logger.DEBUG, RESOURCES, "Launcher.UsingJavaHome", javaHome);
        String toolsJarLocation = WebAppConfiguration.stringArg(args, "toolsJar", null);
        File toolsJar = null;
        if (toolsJarLocation == null) {
            toolsJar = new File(javaHome, "lib/tools.jar");

            // first try - if it doesn't exist, try up one dir since we might have
            // the JRE home by mistake
            if (!toolsJar.exists()) {
                File javaHome2 = new File(javaHome).getParentFile();
                File toolsJar2 = new File(javaHome2, "lib/tools.jar");
                if (toolsJar2.exists()) {
                    javaHome = javaHome2.getCanonicalPath();
                    toolsJar = toolsJar2;
                }
            }
        } else {
            toolsJar = new File(toolsJarLocation);
        }

        // Add tools jar to classloader path
        if (toolsJar.exists()) {
            jars.add(toolsJar.toURL());
            commonLibCLPaths.add(toolsJar);
            Logger.log(Logger.DEBUG, RESOURCES, "Launcher.AddedCommonLibJar",
                    toolsJar.getName());
        } else if (WebAppConfiguration.booleanArg(args, "useJasper", false))
            Logger.log(Logger.WARNING, RESOURCES, "Launcher.ToolsJarNotFound");

        // Set up common lib class loader
        String commonLibCLFolder = WebAppConfiguration.stringArg(args,
                "commonLibFolder", "lib");
        File libFolder = new File(commonLibCLFolder);
        if (libFolder.exists() && libFolder.isDirectory()) {
            Logger.log(Logger.DEBUG, RESOURCES, "Launcher.UsingCommonLib",
                    libFolder.getCanonicalPath());
            File children[] = libFolder.listFiles();
            for (int n = 0; n < children.length; n++)
                if (children[n].getName().endsWith(".jar")
                        || children[n].getName().endsWith(".zip")) {
                    jars.add(children[n].toURL());
                    commonLibCLPaths.add(children[n]);
                    Logger.log(Logger.DEBUG, RESOURCES, "Launcher.AddedCommonLibJar",
                            children[n].getName());
                }
        } else {
            Logger.log(Logger.DEBUG, RESOURCES, "Launcher.NoCommonLib");
        }
        ClassLoader commonLibCL = new URLClassLoader((URL[]) jars.toArray(new URL[jars.size()]),
                getClass().getClassLoader());
       
        Logger.log(Logger.MAX, RESOURCES, "Launcher.CLClassLoader",
                commonLibCL.toString());
        Logger.log(Logger.MAX, RESOURCES, "Launcher.CLClassLoader",
                commonLibCLPaths.toString());
                                       
        this.objectPool = new ObjectPool(args);

        // Optionally set up clustering if enabled and libraries are available
        String useCluster = (String) args.get("useCluster");
        boolean switchOnCluster = (useCluster != null)
                && (useCluster.equalsIgnoreCase("true") || useCluster
                        .equalsIgnoreCase("yes"));
        if (switchOnCluster) {
            if (this.controlPort < 0) {
                Logger.log(Logger.INFO, RESOURCES,
                        "Launcher.ClusterOffNoControlPort");
            } else {
                String clusterClassName = WebAppConfiguration.stringArg(args, "clusterClassName",
                        CLUSTER_CLASS).trim();
                try {
                    Class clusterClass = Class.forName(clusterClassName);
                    Constructor clusterConstructor = clusterClass
                            .getConstructor(new Class[] { Map.class, Integer.class });
                    this.cluster = (Cluster) clusterConstructor
                            .newInstance(new Object[] { args, new Integer(this.controlPort) });
                } catch (ClassNotFoundException err) {
                    Logger.log(Logger.DEBUG, RESOURCES, "Launcher.ClusterNotFound");
                } catch (Throwable err) {
                    Logger.log(Logger.WARNING, RESOURCES, "Launcher.ClusterStartupError", err);
                }
            }
        }
       
        // If jndi is enabled, run the container wide jndi populator
        if (useJNDI) {
            String jndiMgrClassName = WebAppConfiguration.stringArg(args, "containerJndiClassName",
                    DEFAULT_JNDI_MGR_CLASS).trim();
            try {
                // Build the realm
                Class jndiMgrClass = Class.forName(jndiMgrClassName, true, commonLibCL);
                Constructor jndiMgrConstr = jndiMgrClass.getConstructor(new Class[] {
                        Map.class, List.class, ClassLoader.class });
                this.globalJndiManager = (JNDIManager) jndiMgrConstr.newInstance(new Object[] {
                        args, null, commonLibCL });
                this.globalJndiManager.setup();
            } catch (ClassNotFoundException err) {
                Logger.log(Logger.DEBUG, RESOURCES,
                        "Launcher.JNDIDisabled");
            } catch (Throwable err) {
                Logger.log(Logger.ERROR, RESOURCES,
                        "Launcher.JNDIError", jndiMgrClassName, err);
            }
        }
       
        // Open the web apps
        this.hostGroup = new HostGroup(this.cluster, this.objectPool, commonLibCL,
                (File []) commonLibCLPaths.toArray(new File[0]), args);

        // Create connectors (http, https and ajp)
        this.listeners = new ArrayList();
        spawnListener(HTTP_LISTENER_CLASS);
        spawnListener(AJP_LISTENER_CLASS);
        try {
            Class.forName("javax.net.ServerSocketFactory");
            spawnListener(HTTPS_LISTENER_CLASS);
        } catch (ClassNotFoundException err) {
            Logger.log(Logger.DEBUG, RESOURCES,
                    "Launcher.NeedsJDK14", HTTPS_LISTENER_CLASS);
        }

        this.controlThread = new Thread(this, RESOURCES.getString(
                "Launcher.ThreadName", "" + this.controlPort));
        this.controlThread.setDaemon(false);
        this.controlThread.start();

        Runtime.getRuntime().addShutdownHook(new ShutdownHook(this));

    }

    /**
     * Instantiates listeners. Note that an exception thrown in the
     * constructor is interpreted as the listener being disabled, so
     * don't do anything too adventurous in the constructor, or if you do,
     * catch and log any errors locally before rethrowing.
     */
    protected void spawnListener(String listenerClassName) {
        try {
            Class listenerClass = Class.forName(listenerClassName);
            Constructor listenerConstructor = listenerClass
                    .getConstructor(new Class[] { Map.class,
                            ObjectPool.class, HostGroup.class});
            Listener listener = (Listener) listenerConstructor
                    .newInstance(new Object[] { args, this.objectPool,
                            this.hostGroup });
            if (listener.start()) {
                this.listeners.add(listener);
            }
        } catch (ClassNotFoundException err) {
            Logger.log(Logger.INFO, RESOURCES,
                    "Launcher.ListenerNotFound", listenerClassName);
        } catch (Throwable err) {
            Logger.log(Logger.ERROR, RESOURCES,
                    "Launcher.ListenerStartupError", listenerClassName, err);
        }
    }

    /**
     * The main run method. This handles the normal thread processing.
     */
    public void run() {
        boolean interrupted = false;
        try {
            ServerSocket controlSocket = null;

            if (this.controlPort > 0) {
                controlSocket = new ServerSocket(this.controlPort);
                controlSocket.setSoTimeout(CONTROL_TIMEOUT);
            }

            Logger.log(Logger.INFO, RESOURCES, "Launcher.StartupOK",
                    new String[] {RESOURCES.getString("ServerVersion"),
                                    (this.controlPort > 0 ? "" + this.controlPort
                                            : RESOURCES.getString("Launcher.ControlDisabled"))});

            // Enter the main loop
            while (!interrupted) {
//                this.objectPool.removeUnusedRequestHandlers();
//                this.hostGroup.invalidateExpiredSessions();

                // Check for control request
                Socket accepted = null;
                try {
                    if (controlSocket != null) {
                        accepted = controlSocket.accept();
                        if (accepted != null) {
                            handleControlRequest(accepted);
                        }
                    } else {
                        Thread.sleep(CONTROL_TIMEOUT);
                    }
                } catch (InterruptedIOException err) {
                } catch (InterruptedException err) {
                    interrupted = true;
                } catch (Throwable err) {
                    Logger.log(Logger.ERROR, RESOURCES,
                            "Launcher.ShutdownError", err);
                } finally {
                    if (accepted != null) {
                        try {accepted.close();} catch (IOException err) {}
                    }
                    if (Thread.interrupted()) {
                        interrupted = true;
                    }
                }
            }

            // Close server socket
            if (controlSocket != null) {
                controlSocket.close();
            }
        } catch (Throwable err) {
            Logger.log(Logger.ERROR, RESOURCES, "Launcher.ShutdownError", err);
        }
        Logger.log(Logger.INFO, RESOURCES, "Launcher.ControlThreadShutdownOK");
    }

    protected void handleControlRequest(Socket csAccepted) throws IOException {
        InputStream inSocket = null;
        OutputStream outSocket = null;
        ObjectInputStream inControl = null;
        try {
            inSocket = csAccepted.getInputStream();
            int reqType = inSocket.read();
            if ((byte) reqType == SHUTDOWN_TYPE) {
                Logger.log(Logger.INFO, RESOURCES,
                        "Launcher.ShutdownRequestReceived");
                shutdown();
            } else if ((byte) reqType == RELOAD_TYPE) {
                inControl = new ObjectInputStream(inSocket);
                String host = inControl.readUTF();
                String prefix = inControl.readUTF();
                Logger.log(Logger.INFO, RESOURCES, "Launcher.ReloadRequestReceived", host + prefix);
                HostConfiguration hostConfig = this.hostGroup.getHostByName(host);
                hostConfig.reloadWebApp(prefix);
            } else if (this.cluster != null) {
                outSocket = csAccepted.getOutputStream();
                this.cluster.clusterRequest((byte) reqType,
                        inSocket, outSocket, csAccepted,
                        this.hostGroup);
            }
        } finally {
            if (inControl != null) {
                try {inControl.close();} catch (IOException err) {}
            }
            if (inSocket != null) {
                try {inSocket.close();} catch (IOException err) {}
            }
            if (outSocket != null) {
                try {outSocket.close();} catch (IOException err) {}
            }
        }
    }
   
    public void shutdown() {
        // Release all listeners/pools/webapps
        for (Iterator i = this.listeners.iterator(); i.hasNext();)
            ((Listener) i.next()).destroy();
        this.objectPool.destroy();
        if (this.cluster != null)
            this.cluster.destroy();
        this.hostGroup.destroy();
        if (this.globalJndiManager != null) {
            this.globalJndiManager.tearDown();
        }

        if (this.controlThread != null) {
            this.controlThread.interrupt();
        }
        Thread.yield();

        Logger.log(Logger.INFO, RESOURCES, "Launcher.ShutdownOK");
    }

    public boolean isRunning() {
        return (this.controlThread != null) && this.controlThread.isAlive();
    }
   
    /**
     * Main method. This basically just accepts a few args, then initialises the
     * listener thread. For now, just shut it down with a control-C.
     */
    public static void main(String argv[]) throws IOException {
        Map args = getArgsFromCommandLine(argv);
       
        if (args.containsKey("usage") || args.containsKey("help")) {
            printUsage();
            return;
        }

        // Check for embedded war
        deployEmbeddedWarfile(args);
       
        // Check for embedded warfile
        if (!args.containsKey("webroot") && !args.containsKey("warfile")
                && !args.containsKey("webappsDir")&& !args.containsKey("hostsDir")) {
            printUsage();
            return;
        }
        // Launch
        try {
            new Launcher(args);
        } catch (Throwable err) {
            Logger.log(Logger.ERROR, RESOURCES, "Launcher.ContainerStartupError", err);
        }
    }
   
    public static Map getArgsFromCommandLine(String argv[]) throws IOException {
        Map args = loadArgsFromCommandLineAndConfig(argv, "nonSwitch");
       
        // Small hack to allow re-use of the command line parsing inside the control tool
        String firstNonSwitchArgument = (String) args.get("nonSwitch");
        args.remove("nonSwitch");
       
        // Check if the non-switch arg is a file or folder, and overwrite the config
        if (firstNonSwitchArgument != null) {
            File webapp = new File(firstNonSwitchArgument);
            if (webapp.exists()) {
                if (webapp.isDirectory()) {
                    args.put("webroot", firstNonSwitchArgument);
                } else if (webapp.isFile()) {
                    args.put("warfile", firstNonSwitchArgument);
                }
            }
        }
        return args;
    }

    public static Map loadArgsFromCommandLineAndConfig(String argv[], String nonSwitchArgName)
            throws IOException {
        Map args = new HashMap();
       
        // Load embedded properties file
        String embeddedPropertiesFilename = RESOURCES.getString(
                "Launcher.EmbeddedPropertiesFile");
       
        InputStream embeddedPropsStream = Launcher.class.getResourceAsStream(
                embeddedPropertiesFilename);
        if (embeddedPropsStream != null) {
            loadPropsFromStream(embeddedPropsStream, args);
            embeddedPropsStream.close();
        }
       
        // Get command line args
        String configFilename = RESOURCES.getString("Launcher.DefaultPropertyFile");
        for (int n = 0; n < argv.length; n++) {
            String option = argv[n];
            if (option.startsWith("--")) {
                int equalPos = option.indexOf('=');
                String paramName = option.substring(2,
                        equalPos == -1 ? option.length() : equalPos);
                if (equalPos != -1) {
                    args.put(paramName, option.substring(equalPos + 1));
                } else {
                    args.put(paramName, "true");
                }
                if (paramName.equals("config")) {
                    configFilename = (String) args.get(paramName);
                }
            } else {
                args.put(nonSwitchArgName, option);
            }
        }

        // Load default props if available
        File configFile = new File(configFilename);
        if (configFile.exists() && configFile.isFile()) {
            InputStream inConfig = new FileInputStream(configFile);
            loadPropsFromStream(inConfig, args);
            inConfig.close();
            initLogger(args);
            Logger.log(Logger.DEBUG, RESOURCES, "Launcher.UsingPropertyFile",
                    configFilename);
        } else {
            initLogger(args);
        }
        return args;
    }
   
    protected static void deployEmbeddedWarfile(Map args) throws IOException {
        String embeddedWarfileName = RESOURCES.getString("Launcher.EmbeddedWarFile");
        InputStream embeddedWarfile = Launcher.class.getResourceAsStream(
                embeddedWarfileName);
        if (embeddedWarfile != null) {
            File tempWarfile = File.createTempFile("embedded", ".war").getAbsoluteFile();
            tempWarfile.getParentFile().mkdirs();
            tempWarfile.deleteOnExit();

            String embeddedWebroot = RESOURCES.getString("Launcher.EmbeddedWebroot");
            File tempWebroot = new File(tempWarfile.getParentFile(), embeddedWebroot);
            tempWebroot.mkdirs();
           
            Logger.log(Logger.DEBUG, RESOURCES, "Launcher.CopyingEmbeddedWarfile",
                    tempWarfile.getAbsolutePath());
            OutputStream out = new FileOutputStream(tempWarfile, true);
            int read = 0;
            byte buffer[] = new byte[2048];
            while ((read = embeddedWarfile.read(buffer)) != -1) {
                out.write(buffer, 0, read);
            }
            out.close();
            embeddedWarfile.close();
           
            args.put("warfile", tempWarfile.getAbsolutePath());
            args.put("webroot", tempWebroot.getAbsolutePath());
            args.remove("webappsDir");
            args.remove("hostsDir");
        }
    }
   
    protected static void loadPropsFromStream(InputStream inConfig, Map args) throws IOException {
        Properties props = new Properties();
        props.load(inConfig);
        for (Iterator i = props.keySet().iterator(); i.hasNext(); ) {
            String key = (String) i.next();
            if (!args.containsKey(key.trim())) {
                args.put(key.trim(), props.getProperty(key).trim());
            }
        }
        props.clear();
    }
   
    public static void initLogger(Map args) throws IOException {
        // Reset the log level
        int logLevel = WebAppConfiguration.intArg(args, "debug", Logger.INFO);
//        boolean showThrowingLineNo = WebAppConfiguration.booleanArg(args, "logThrowingLineNo", false);
        boolean showThrowingThread = WebAppConfiguration.booleanArg(args, "logThrowingThread", false);
        OutputStream logStream = null;
        if (args.get("logfile") != null) {
            logStream = new FileOutputStream((String) args.get("logfile"));
        } else if (WebAppConfiguration.booleanArg(args, "logToStdErr", false)) {
            logStream = System.err;
        } else {
            logStream = System.out;
        }
//        Logger.init(logLevel, logStream, showThrowingLineNo, showThrowingThread);
        Logger.init(logLevel, logStream, showThrowingThread);
    }

    protected static void printUsage() {
        System.out.println(RESOURCES.getString("Launcher.UsageInstructions",
                RESOURCES.getString("ServerVersion")));
    }
}
TOP

Related Classes of winstone.Launcher

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.