/*
 * The contents of this file are subject to the terms 
 * of the Common Development and Distribution License 
 * (the License).  You may not use this file except in
 * compliance with the License.
 * 
 * You can obtain a copy of the license at 
 * https://glassfish.dev.java.net/public/CDDLv1.0.html or
 * glassfish/bootstrap/legal/CDDLv1.0.txt.
 * See the License for the specific language governing 
 * permissions and limitations under the License.
 * 
 * When distributing Covered Code, include this CDDL 
 * Header Notice in each file and include the License file 
 * at glassfish/bootstrap/legal/CDDLv1.0.txt.  
 * If applicable, add the following below the CDDL Header, 
 * with the fields enclosed by brackets [] replaced by
 * you own identifying information: 
 * "Portions Copyrighted [year] [name of copyright owner]"
 * 
 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
 */


package com.sun.enterprise.admin.servermgmt.launch;

import com.sun.enterprise.util.io.FileUtils;
import java.io.FileWriter;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.ByteArrayOutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.PrintStream;
import java.io.FileOutputStream;
import java.io.FileNotFoundException;
import java.io.BufferedWriter;
import java.io.PushbackInputStream;
import java.lang.InterruptedException;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.StringTokenizer;
import java.util.ArrayList;
import java.util.Properties;
import java.util.regex.Pattern;
import java.util.logging.Logger;
import java.util.logging.Level;
import java.util.logging.FileHandler;
import java.util.logging.Handler;
import java.util.logging.SimpleFormatter;
import java.util.List;
import java.util.ArrayList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import javax.xml.parsers.ParserConfigurationException;

import com.sun.logging.LogDomains;
import com.sun.enterprise.util.OS;
import com.sun.enterprise.util.RelativePathResolver;
import com.sun.enterprise.util.ASenvPropertyReader;
import com.sun.enterprise.util.SystemPropertyConstants;
import com.sun.enterprise.util.ProcessExecutor;
import com.sun.enterprise.util.i18n.StringManager;
import com.sun.enterprise.config.ConfigContext;
import com.sun.enterprise.config.ConfigException;
import com.sun.enterprise.config.ConfigFactory;
import com.sun.enterprise.config.ConfigBean;
import com.sun.enterprise.config.serverbeans.Config;
import com.sun.enterprise.config.serverbeans.Domain;
import com.sun.enterprise.config.serverbeans.Server;
import com.sun.enterprise.config.serverbeans.NodeAgent;
import com.sun.enterprise.config.serverbeans.Cluster;
import com.sun.enterprise.config.serverbeans.JavaConfig;
import com.sun.enterprise.config.serverbeans.LogService;
import com.sun.enterprise.config.serverbeans.Profiler;
import com.sun.enterprise.config.serverbeans.ElementProperty;
import com.sun.enterprise.config.serverbeans.SystemProperty;
import com.sun.enterprise.config.serverbeans.ConfigAPIHelper;
import com.sun.enterprise.config.serverbeans.ServerBeansFactory;
import com.sun.enterprise.config.serverbeans.ServerHelper;
import com.sun.enterprise.config.serverbeans.NodeAgentHelper;
import com.sun.enterprise.config.serverbeans.ClusterHelper;
import com.sun.enterprise.util.natives.NativeUtils;
import com.sun.enterprise.security.store.IdentityManager;
import com.sun.enterprise.admin.util.JvmOptionsHelper;

/**
 * This is the main class for the new ProcessLauncher which is responcible for creating and
 * executing a java command.  There are two general types of commands, internal and external.
 * An internal command is basically one that is mostly defined by domain.xml.  The command's
 * java-config (classpath, jvm option, system properties, debug info, ...) is extracted from
 * domain.xml using the config-api who's information is coupled with more general information
 * from the processLauncher.xml file.  How the final command is executed is based on the
 * LaunchConstants.LAUNCHER_RETURN_PROP system property:
 *
 * LaunchConstants.LAUNCHER_RETURN_RETURN_VALUE - denotes that the command is executed via the Runtime .exec and immediately returns.
 * If the "verbose" option is present as an argurment then the processes' stdout and stderr is
 * attached to and sent to the stderr of the calling process.
 *
 * "hold" - denotes that the command is written to a temporary script in the calling scripts
 * directory, then once the ProcessLauncher's java command returns the script is executed as the
 * next command in the script and doesn't return until the temportary script finishes execution.
 */

public class ASLauncher {
    
    protected static final String RELATIVE_LOCATION_DOMAIN_XML="/config/domain.xml";
    protected static final String CLASSPATH_ENV_NAME="CLASSPATH";
    protected static final String LAUNCHER_SCRIPT_LOCATION="bin";
    protected static final String LAUNCHER_START_ACTION="start";
    protected static final String LAUNCHER_STOP_ACTION="stop";
    protected static final String INTERNAL_SERVER_PROFILE="s1as8-server";
    protected static final String AS9_INTERNAL_SERVER_PROFILE="as9-server";
    protected static final String INTERNAL_NODE_AGENT_PROFILE="s1as8-nodeagent";
    protected static final int SLEEP_TIME_FOR_PROCESS_START=2000;
    protected static final String COMMAND_LINE_ARG_VERBOSE="verbose";
    protected static final String COMMAND_LINE_ARG_DEBUG="debug";
    protected static final String COMMAND_LINE_ARG_DISPLAY="display";
    protected static final String COMMAND_LINE_ARG_NATIVE="native";
    protected static final char COMMAND_DELIMITER_LIST[] = {'|','+','^','@','!','?','*','%','$','#','(',')','~','`','{','}','[',']','<','>', 127}; 
    public static final String DEBUG_OPTIONS = "com.sun.aas.jdwpOptions";
    public static final String VERBOSE_SYSTEM_PROPERTY = "com.sun.aas.verboseMode";
    public static final String LOGFILE_SYSTEM_PROPERTY = "com.sun.aas.defaultLogFile";
    public static final String PROPMPT_FOR_IDENTITY_SYSTEM_PROPERTY = "com.sun.aas.promptForIdentity";
    public static final String SPARC = "sparc";
    public static final String SPARCV9 = "sparcv9";
    public static final String X86 = "x86";
    public static final String AMD64 = "amd64";
    public static final String JVM_OPTION_FOR_64BIT = "-d64";
    
    private static final String CLASSPATH_PREFIX_PROPERTY = "com.sun.aas.ClassPathPrefix";
    private static final String CLASSPATH_SUFFIX_PROPERTY = "com.sun.aas.ClassPathSuffix";
    private static final String SERVER_CLASSPATH_PROPERTY = "com.sun.aas.ServerClassPath";
    private static final String UNDEFINED_ACTION = "undefined_action";
    private static final String ERROR_FLUSHER    = "Error Stream Flusher";
    private static final String OUT_FLUSHER    = "Output Stream Flusher";
    private static final String DOMAIN_NAME_XML_PROPERTY = "administrative.domain.name";
    private static final String DOMAIN_NAME_SYSTEM_PROPERTY = "domain.name";
    private static final String SCRIPT_PATH_SYSTEM_PROPERTY = "com.sun.aas.scriptpath";
    
    static       int    returnValue = 1;
    String[]    securityInfo;   // username & passwords if neccessary...
    
    // WBN the level is here as a constant so that it can be changed to INFO or better
    // when actively debugging in order to see the messages easily.  Always return to FINE
    // when finished.  
    private static final Level  FINE_LEVEL = Level.FINE;
    private Logger _logger = null;
    private String[] _args=null;
    private static boolean bDebug=false;
    
    
    public static void main(String[] args) {
        try {
            ASLauncher pl=new ASLauncher();
            pl.preProcess(args);    // figure out relocatable stuff
            pl.getSecurityFromUser(args);
            Process process = pl.process(args, pl.securityInfo);
        }
        catch(Exception e) {
            e.printStackTrace();
        }
        System.exit(returnValue);
    }
    
    
    /**
     * This method is meant to be called from the PLBootstrap.class to finish the setup
     * of the ProcessLauncher to execute normally
     *
     * arg[0] - should be the name of the profile to use from processLauncher.xml
     * arg[1] - should be the action to run (default is "start")
     */
    public static void bootstrap(String[] args) {
        ASLauncher pl=new ASLauncher();
        
        // set to nodeagent, by default
        String profile="s1as-deploytool";
        if (args != null && args.length >= 1) {
            // set to argument profile
            profile=args[0];
            
            // shift args
            String[] newArgs=new String[args.length - 1];
            for (int ii=0; ii < newArgs.length; ii++) {
                newArgs[ii]=args[ii + 1];
            }
            
            // set back to args
            args=newArgs;
        }
        
        // put profile in properties for later use
        System.setProperty(LaunchConstants.PROCESS_NAME_PROP, profile);
        
        if (bDebug) System.out.println("bootstrapping profile - " + profile);
        
        // start processing
        pl.process(args);
    }
    /**
     * WBN Oct 2006 -- The startserv script has been made relocatable.  But we have to be backward
     * compatible.  So look for the new args and preprocess them...
     * Adding this allows us to remove lots of unchanging stuff from the script.
     */
    public void preProcess(String[] args, Properties extraProps) throws ASLauncherException
    {
        boolean nativeLauncher = false;
        boolean verbose = false;

        readASEnv();
        
        for(String arg : args)
        {
            if("display".equalsIgnoreCase(arg))
                nativeLauncher = true;

            if("verbose".equalsIgnoreCase(arg))
                verbose = true;
        }
        
        // Set system props needed by launcher
        // first add all the ones that were passed in from a caller...
        
        Enumeration keys = extraProps.propertyNames();
        
        while(keys.hasMoreElements())
        {
            String key = (String)keys.nextElement();
            String value = extraProps.getProperty(key);
            System.setProperty(key, value);
        }
        

        // set props but only if NOT in extraProps already!
        setInstanceRoot(extraProps);

        if(extraProps.getProperty(LaunchConstants.JAVA_HOME_PROP) == null)
                System.setProperty(LaunchConstants.JAVA_HOME_PROP, System.getProperty(SystemPropertyConstants.JAVA_ROOT_PROPERTY));
        if(extraProps.getProperty(LaunchConstants.LOG_MGR_PROP) == null)
            System.setProperty(LaunchConstants.LOG_MGR_PROP, LaunchConstants.LOG_MGR_VALUE);
        if(extraProps.getProperty(LaunchConstants.ENV_FACTORY_PROP) == null)
            System.setProperty(LaunchConstants.ENV_FACTORY_PROP, LaunchConstants.ENV_FACTORY_VALUE);
        if(extraProps.getProperty(LaunchConstants.PROCESS_NAME_PROP) == null)
            System.setProperty(LaunchConstants.PROCESS_NAME_PROP, AS9_INTERNAL_SERVER_PROFILE);
        if( extraProps.getProperty(LaunchConstants.LAUNCHER_RETURN_PROP) == null)
        {
            if(verbose && nativeLauncher)
                System.setProperty(LaunchConstants.LAUNCHER_RETURN_PROP, LaunchConstants.LAUNCHER_RETURN_HOLD_VALUE);
            else
                System.setProperty(LaunchConstants.LAUNCHER_RETURN_PROP, LaunchConstants.LAUNCHER_RETURN_RETURN_VALUE);
        }
        
    }

    private void preProcess(String[] args) throws ASLauncherException
    {
        // We are being called from a script or native code
        // i.e. we are in our own JVM and we need to look at System Properties
        // and repackage them for the public preprocess method
        // These are the ONLY properties needed to start any kind of server.  
        // this allows us to change the script to pass these in as args instead
        // of System Properties later -- by changing this code and the scripts at
        // the same time.

        // these Strings ugly-up the code so let's make nice neat abbreviations here!
        String irpTag       = SystemPropertyConstants.INSTANCE_ROOT_PROPERTY; 
        String procNameTag  = LaunchConstants.PROCESS_NAME_PROP;
        String inTag        = SystemPropertyConstants.SERVER_NAME;   
        String rootTag      = SystemPropertyConstants.INSTALL_ROOT_PROPERTY;
        String retTag       = LaunchConstants.LAUNCHER_RETURN_PROP;
        
        Properties props = new Properties();
        
        copySystemProp(props, irpTag);
        copySystemProp(props, procNameTag);
        copySystemProp(props, inTag);
        copySystemProp(props, rootTag);
        copySystemProp(props, retTag);

        preProcess(args, props);
    }

    private void copySystemProp(Properties to, String name)
    {
        String val = System.getProperty(name);
        
        if(val != null)
            to.setProperty(name, val);
    }
    
    /**
     * process - This is the method that performs the work of creating the java command line to execute.
     * This is where the BootStrap launcher and general launcher execute the same track
     * WBN Oct 2006 -- I'd like to change the signature to return int -- but it is public so I'll
     * klunk up a instance variable instead.
     */
    public void process(String[] args) {
        process(args, null);
    }

    public Process process(String[] args, String[] SecurityInfo) {
        Process process = null;
        try {
            returnValue=1;
            securityInfo = SecurityInfo;

            if(securityInfo != null)
                System.setProperty(PROPMPT_FOR_IDENTITY_SYSTEM_PROPERTY, "true");
            // store args for later use (e.g. debug, verbose)
            setArgs(args);

            // look for Debug system property
            if (System.getProperty("Debug") != null) {
                // turn on debug, this option was added to help developers
                // debug the their code what adding/modifying tasks that are executed via
                // the ProcessLauncher
                bDebug=true;
            }
            
            // Set verboseMode early so logger will also show verbose output.
            // Each command type will take care of adding it again, but that is okay.
            // Only set it not being displayed for native launcher
            if (isVerboseEnabled() && !isDisplayEnabled()) {
                // add to System.properties for config conditional adds
                System.setProperty(VERBOSE_SYSTEM_PROPERTY, "true");
            }
            
            String passArg=null;
            if (args != null && args.length >= 1) {
                passArg=args[0];
            } else {
                passArg=LAUNCHER_START_ACTION;
            }
            
            // Build command and pass in action
            if (bDebug) System.out.println("ASLauncher Building command ..");
            Command command=buildCommand(passArg);
            
            // execute command that was build and pass in action
            if (bDebug) System.out.println("ASLauncher Executing command ..");

            // if no exception, should have executed properly or logged errors
            returnValue=0;
            
            process = executeCommand(command, passArg);
            
        } catch (ConfigException ce) {
            // try to log but the log main not be set up properly if domain.xml had a problem
            getLogger().log(Level.SEVERE, "launcher.config_exception", ce);
            ce.printStackTrace();
        } catch (Exception e) {
            // show in server log
            getLogger().log(Level.SEVERE, "enterprise.launcher_exception_startup",e);
            e.printStackTrace();
        }
        return process;
    }
    
    
    /**
     * executeCommand
     * @param cmd - command to execute
     * @param action - action to take, should be start or stop.  defaults to "start"
     */
    public Process executeCommand(Command command, String action) throws IOException {
        
        String[] cmd=null;
        
        // For native launcher and service, must send command to stdout in know format for
        // parsing by native code.  The purpose is to keep as much of the launcher functionality in
        // java as possible, keeping maintanance low and staying within a skillset that we have in abundance
        // It also is a easy way to show the command that is generated
        if (isDisplayEnabled()) {
            
            
            // First remove display argument that is used to trigger this action
            // This is the only arg that should be removed, all the rest should be passed through
            command.removeArg(COMMAND_LINE_ARG_DISPLAY);
            command.removeArg(COMMAND_LINE_ARG_NATIVE);
    
            // add launcher type for display to native exe, so the exe can desice whether to hold the child's
            // process or not.
            String launcherRet=System.getProperty(LaunchConstants.LAUNCHER_RETURN_PROP);
            if (launcherRet != null ) {
                if (bDebug) System.out.println("-D" + LaunchConstants.LAUNCHER_RETURN_PROP + "=" + launcherRet);
                command.addSystemVariable("-D" + LaunchConstants.LAUNCHER_RETURN_PROP + "=" + launcherRet);
            }
            
            // display java command to stdout in the following format.
            // NOTE: if this becomes externally available, use xml, for now its an internal structure.
            // Class Name (path is seperated by "/" |
            // Java Args |
            // everything else (should start with a "-") |
            // classpath should be prepended with "-Djava.class.path="
            cmd=command.getCommandInJNIFormatAsArray();
            
            // Find delimiter that doesn't exist in command, just to be safe
            boolean found=false;
            String sxDelim="|";
            for(int ii=0; ii < COMMAND_DELIMITER_LIST.length; ii++) {
                // reset found to false for new delimiter
                found=false;
                // first character should delimiter to used to parse command
                for(int jj=0; jj < cmd.length; jj++) {
                    if (bDebug) System.out.println(COMMAND_DELIMITER_LIST[ii] + " - " + cmd[jj]);
                    if(cmd[jj].indexOf(COMMAND_DELIMITER_LIST[ii]) >= 0) {
                        // delimiter is found in string
                        found=true;
                        break;
                    }
                }
                if (bDebug) System.out.println("\n***Delimiter = " + found + "\n");

                // see if char not found in any string
                if (!found) {
                    // didn't find delimiter, so set and exit
                    sxDelim=String.valueOf(COMMAND_DELIMITER_LIST[ii]);
                    break;
                }
            }
            
            if(found) {
                // serious delimiter error, since all the delimits in the 
                // COMMAND_DELIMITER_LIST array are in the java command
                // to startup the appserver then the native launcher can't 
                // parse the command correctly and a jvm.dll error will arise.
                getLogger().log(Level.SEVERE, "launcher.native_launcher_delimiter_error");
            }

            // INCREDIBLY IMPORTANT for native processing.
            // The only data the native process expects is the commandline
            // any other data sent to stdout will throw off the command parsing
            // Needed to make the native side have a synch string, because anything could go wrong
            // with either the script that initiates this class or the preBuildProcessing.
            System.out.print("STARTOFCOMMAND" + sxDelim);
            for(int ii=0; ii < cmd.length; ii++) {
                System.out.print(cmd[ii] + sxDelim);
            }
            System.out.print("ENDOFCOMMAND" + sxDelim);
            // we can't return a Process because we are being CALLED by the external process.
            // only ASNativeLauncher will have a handle to the native process that called us
            return null;
        } else { // all other execution paths
            return executeBackgroundCommand(command, isVerboseEnabled(), action);
        }
        
    }
    
    
    protected void preBuildProcessing() {
        // this is a place holder so ee can put in its
        // sychronization code
    }
    
    
    public Process executeBackgroundCommand(Command command, boolean verbose, String action) throws IOException {
        
        // execute the command as a runtime.exec and immediately return,
        // return will not pertain to the the executed process
        getLogger().log(FINE_LEVEL, "ASLauncher: executing Runtime execute...");
        
        // run command
        
        String[] cmd=command.getCommandAsArray();
        //getLogger().log(Level.INFO, "\n" + Arrays.toString(cmd));
        ProcessBuilder pb = new ProcessBuilder(cmd);

        File configDir = new File(System.getProperty(SystemPropertyConstants.INSTANCE_ROOT_PROPERTY) + "/config");
        
        if(FileUtils.safeIsDirectory(configDir))
            pb.directory(configDir);
        
        Process process = pb.start();
        
        // See is there is input that needs to be sent to the process
        // WBN Sept 2006 -- it used to check for the EXISTENCE of the System Property -
        // now it checks on whether it exists AND is set to true.
        // note we do NOT do this if the password/user name info is sent in as a parameter to process()!!  In that
        // case we already have the info and just need to write it to the AS process...
        // this is really overly-complex because of supporting native startup (sigh)
        
        if (Boolean.getBoolean(PROPMPT_FOR_IDENTITY_SYSTEM_PROPERTY) && action.equals(LAUNCHER_START_ACTION) ) {
            if(securityInfo == null)
                sendInputToProcessInput(System.in, process);    // ancient native launcher uses startserv script
            else
                writeSecurityInfoToProcess(process);
        }
        // start stream flusher to push output to parent streams and log if they exist
        StreamFlusher sfErr=new StreamFlusher(process.getErrorStream(), System.err, command.getLogFile());
        Thread sfErrThread = new Thread(sfErr, ERROR_FLUSHER);
        sfErrThread.setDaemon(true);
        sfErrThread.start();
        
        if (verbose || isWaitEnabled()) {
            // need to keep client around for start
            // this should only be invoked for start-domain command
            
            // set flusher on stdout also
            StreamFlusher sfOut=new StreamFlusher(process.getInputStream(), System.out, command.getLogFile());
            Thread sfOutThread = new Thread(sfOut, OUT_FLUSHER);
            sfOutThread.setDaemon(true);
            sfOutThread.start();
            
            try {
                process.waitFor();
                sfOutThread.join();
                sfErrThread.join();
            } catch (InterruptedException ie) {
                // just let fall through, but log at finest level
                System.out.println("While waiting in verbose mode, an InterruptedException was thrown ");
            }
        } else {
            
            // set flusher on stdout also, if not could stop with too much output
            StreamFlusher sfOut=new StreamFlusher(process.getInputStream(), System.out);
            Thread sfOutThread = new Thread(sfOut, OUT_FLUSHER);
            sfOutThread.setDaemon(true);
            sfOutThread.start();

            // if executing in the background and a log exists, print log location
            String logFile=command.getLogFile();
            
            // TODO extra redirect message?
            //./admin-cli/commands/src/java/com/sun/enterprise/cli/commands/LocalStrings.properties:LogRedirectedTo=Log redirected to {0}.
            if (logFile != null) {
                System.out.println(StringManager.getManager(ASLauncher.class).getString("launcher.redirecting.output",
                logFile));
            }
            // must sleep for a couple of seconds, so if there is a jvm startup error, the parent process
            // is around to catch and report it when the process in executed in verbose mode.
            // WBN  ?????? It doesn't appear that we need this ????  Verbose mode was handled in the "if"
            // above...
            //try {
                //Thread.currentThread().sleep(SLEEP_TIME_FOR_PROCESS_START);
            //} catch (InterruptedException ie) {}
        }
        return process;
    }
    
    private void sendInputToProcessInput(InputStream in, Process subProcess) {
        // return if no input
        if (in == null || subProcess == null) return;
        
        PrintWriter out=null;
        BufferedReader br=null;
        try {
            // open the output stream on the process which excepts the input
            out = new PrintWriter(new BufferedWriter(
            new OutputStreamWriter(subProcess.getOutputStream())));
            
            // WBN October 2006
            // See if there is input available first.  If not use standard strings...


            // read in each line and resend it to sub process
            br=new BufferedReader(new InputStreamReader(System.in));
            
            if(br.ready()) {
                String sxLine=null;
                while ((sxLine=br.readLine()) != null) {
                    // get input lines from process if any
                    out.println(sxLine);
                    if (bDebug) System.out.println("Feeding in Line:" + sxLine);
                }
            }
            else {
                out.println("");
                out.println("");
                out.println("changeit");
            }
            out.flush();
        } catch (Exception e) {
            getLogger().log(Level.INFO,"WRITE TO INPUT ERROR", e);
        } finally {
            try {
                if (out != null) out.close();
            } catch (Throwable t) {}
        }
    }
    
    public Command buildCommand(String action) throws ConfigException {
        
        // execute any preprocessing,
        preBuildProcessing();
        String processName=System.getProperty(LaunchConstants.PROCESS_NAME_PROP, INTERNAL_SERVER_PROFILE);
                //Set system properties that correspond directly to asenv.conf/bat. This
        //keeps us from having to pass them all from -D on the command line.

        // check to see whether to build external or internal (domain.xml) command
        Command command=null;
        if (isServerProfile()) {
            command=buildInternalCommand(action);
        } else {
            command=buildExternalCommand(action);
        }
        
        // log final command
        String finalCommand=command.toStringWithLines();
        getLogger().log(Level.INFO, finalCommand);
        
        return command;
    }
    
    
    /**
     * buildInternalCommand - This Method build san internal server command from domain.xml, so this
     * method is specifically used for server instances
     */
    public Command buildInternalCommand(String action) throws ConfigException {
        StringManager _strMgr=StringManager.getManager(ASLauncher.class);
        
        // derive domain.xml location and create config to be used by config api
        String domainXMLLocation=System.getProperty(SystemPropertyConstants.INSTANCE_ROOT_PROPERTY)
        + RELATIVE_LOCATION_DOMAIN_XML;
        ConfigContext configCtxt=ConfigFactory.createConfigContext(domainXMLLocation);
        Domain domain=ConfigAPIHelper.getDomainConfigBean(configCtxt);
        setDomainName(domain);
        // get the server's config by name, need it as soon as possible for logging
        String serverName=System.getProperty(SystemPropertyConstants.SERVER_NAME);
        Server server=ServerHelper.getServerByName(configCtxt, serverName);
        String configRef=server.getConfigRef();
        
        // create the command to execute
        Command command=new Command();
        
        // set jvmargs for thread dump to go to child process log, workaround for bug #4957071
        //command.addJvmOption("-XX:+UnlockDiagnosticVMOptions");
        //command.addJvmOption("-XX:+LogVMOutput");
        //command.addJvmOption("-XX:LogFile=" + logFile);
        //command.addJvmOption("-XX:LogFile=/tmp/threadDump.txt");
        
        // get server's config
        Config config=ServerHelper.getConfigForServer(configCtxt, serverName);
        
        // configure log service and print redirect message
        String logFile=configureLogService(config);
        if (bDebug) System.out.println("LOGFILE = " + logFile);
        
        // make sure log is writable, if not a message will be logged to the screen if in verbose mode
        createFileStructure(logFile);
        command.setLogFile(logFile);
        
        // should NOT need to addLogFileToLogger(logFile), logManager should already do this for us
        // may need to enable if log is movable ???
        
        // TODO Logging Weirdnesses
        addLogFileToLogger(logFile);
        
        // add log to properties so PEMAIN will redirect is applicable
        command.addSystemVariable("-D" + LOGFILE_SYSTEM_PROPERTY + "=" + logFile);
        
        getLogger().log(FINE_LEVEL,"Retrieved domain.xml from " + domainXMLLocation);
        getLogger().log(FINE_LEVEL,"Start building the command the to execute.");
        
        //Set system properties that correspond directly to asenv.conf/bat. This
        //keeps us from having to pass them all from -D on the command line.
        //ASenvPropertyReader reader = new ASenvPropertyReader(System.getProperty(SystemPropertyConstants.CONFIG_ROOT_PROPERTY));
        //reader.setSystemProperties();
       
        // verbose set, flag used in ServerLogManager to send logs to stderr
        if (isVerboseEnabled()) {
            command.addSystemVariable("-D" + VERBOSE_SYSTEM_PROPERTY + "=true");
            // add to System.properties for config conditional adds (could be set about if not native launcher)
            System.setProperty(VERBOSE_SYSTEM_PROPERTY, "true");
        }
        
        // read in ASLauncherConfig
        String launcherConfigFile=System.getProperty(SystemPropertyConstants.INSTALL_ROOT_PROPERTY)
        + File.separator + "lib" + File.separator + "processLauncher.xml";
        
        String processName=System.getProperty(LaunchConstants.PROCESS_NAME_PROP, INTERNAL_SERVER_PROFILE);
        getLogger().log(FINE_LEVEL,"Loading ASLauncher config from: " + launcherConfigFile
        + " - for the process named: " + processName);
        
        ASLauncherConfig plConfig=new ASLauncherConfig(launcherConfigFile, processName);
        
        // take plConfig properties as the base for the system jvm args for the new process
        Properties systemProperties=plConfig.getSystemProperties();

        // add domain.xml property elements to the jvm args in reverse order of precedence.
        // First add the domain properties
        addSystemProperties(domain.getSystemProperty(), systemProperties);
        
        

        // set config name (which is retrieved from domain.xml) into System properties to be used for path resolution
        System.setProperty(SystemPropertyConstants.CONFIG_NAME_PROPERTY, configRef);
        systemProperties.put(SystemPropertyConstants.CONFIG_NAME_PROPERTY, configRef);
        
        // get javaconfig for server that is being started
        JavaConfig javaConfig=config.getJavaConfig();
        
        // derive and add java command to Command
        String jvmCmd=javaConfig.getJavaHome() + File.separator + "bin"
            + File.separator + "java";

        command.setJavaCommand(jvmCmd);
        

        // fix for bug# 6323645
        // Do not add options which are not applicable for stop action.
	// For ex. debug options and profiler options.
	// In other words add the following options ony when the action
	// is other than stop.

        Profiler profiler=javaConfig.getProfiler();
        String profilerClasspath=null;

        if (!action.equals(LAUNCHER_STOP_ACTION)) {

	    // The debug options including the debug port would be added 
	    // to the command  when the action is start.
	    // If the action is stop adding the same port to the java command
	    // would stack up the ports and block until the port assigned for
	    // start action is released. To avoid this we check for stop action.

	    // If the stop action needs to be debugged then one work around is to
	    // copy the java command from server.log, change the debug settings and 
	    // run the command.

	    // debug options
            if ((javaConfig.isDebugEnabled() || isDebugEnabled())) {
                // add debug statements
                addDebugOptions(command, javaConfig.getDebugOptions());
            }

            // add profiler properties & jvm args
            if (profiler != null && profiler.isEnabled()) {
                // add config properties
                addElementProperties(profiler.getElementProperty(), systemProperties);
                String [] jvmOptions=profiler.getJvmOptions();
                addJvmOptions(command, jvmOptions, action);
                profilerClasspath=profiler.getClasspath();
            }
        }
        
        // set the default locale specified in domain.xml config file
        String locale=domain.getLocale();
        if (locale == null || locale.equals("")) {
            // if not specified in domain try system
            locale=System.getProperty(SystemPropertyConstants.DEFAULT_LOCALE_PROPERTY);
        }
        // make sure locale is specified before setting it
        if (locale != null && !locale.equals("")) {
            command.addSystemVariable("-D" + SystemPropertyConstants.DEFAULT_LOCALE_PROPERTY + "=" + locale);
        }
        //
        // add jvm args, look for combined jvm options
        String[] jvmOptions=javaConfig.getJvmOptions();
        addJvmOptions(command, jvmOptions, action);      
        
        //
        // add config system properties
        addSystemProperties(config.getSystemProperty(), systemProperties);
        
        //
        // add cluster system properties if the server instance is clustered
        if (ServerHelper.isServerClustered(configCtxt, server))  {
            Cluster cluster = ClusterHelper.getClusterForInstance(configCtxt,
            server.getName());
            addSystemProperties(cluster.getSystemProperty(), systemProperties);
        }
        
        //
        // add server system properties
        addSystemProperties(server.getSystemProperty(), systemProperties);
        
        //
        // add classpath
        // check to see if jvmCmd starts with same as processLauncher jvm.
        // if so, use the system property java-version to determine jvm version
        if(OS.isWindows()) {
            // make sure all delimeters are the same
            jvmCmd=jvmCmd.replace('/', '\\');
        }
        
        if (jvmCmd.startsWith(System.getProperty(SystemPropertyConstants.JAVA_ROOT_PROPERTY))) {
            // jvm command are the same, so use processLauncher jvm version
            jvmCmd=null;
        }
        String classpath=deriveClasspath(plConfig, jvmCmd, javaConfig, profilerClasspath);
        getLogger().log(FINE_LEVEL, "Complete process classpath = " + classpath);
        command.setClasspath(classpath);
        
        //
        //add main class
        command.setMainClass(plConfig.getMainClass());
        
        //
        // native library path to java path and system properties
        deriveNativeClasspath(command, javaConfig, profiler, systemProperties);
        
        //
        // add all system properties to command as jvm args
        Iterator it=systemProperties.keySet().iterator();
        String key=null;
        String property=null, value=null;;
        while(it.hasNext()) {
            key=(String)it.next();
            value=systemProperties.getProperty(key);
            
            // take care of vm arg that are sent in via the processlauncher.xml file
            if (key.startsWith("-")) {
                property = key;
                if (value != null && !value.equals("")) {
                    property += "=" + value;
                }
                command.addJvmOption(property);
                getLogger().log(FINE_LEVEL, "JVM Option: " + property);
            } else {
                // regular system property
                property = "-D" + key + "=" + value;
                command.addSystemVariable(property);
                getLogger().log(FINE_LEVEL, "System Property: " + property);
            }
        }

        //Add prefix and suffix for AS9Profile
        if (getProcessLauncherProfile().equals(AS9_INTERNAL_SERVER_PROFILE)) {
            String classpathPrefix=javaConfig.getClasspathPrefix();
            String classpathSuffix=javaConfig.getClasspathSuffix();
            String serverClasspath=javaConfig.getServerClasspath();
            getLogger().log(FINE_LEVEL, " prefix :: " + classpathPrefix 
                                          + " suffix :: " + classpathSuffix);
            if (classpathPrefix == null) classpathPrefix = "";
            command.addSystemVariable("-D" + CLASSPATH_PREFIX_PROPERTY + "=" + classpathPrefix);
            if (classpathSuffix == null) classpathSuffix = "";
            command.addSystemVariable("-D" + CLASSPATH_SUFFIX_PROPERTY + "=" + classpathSuffix);
            if (serverClasspath == null) serverClasspath = "";
            command.addSystemVariable("-D" + SERVER_CLASSPATH_PROPERTY + "=" + serverClasspath);
        }

        setPromptForIdentity(command);
        
        //
        // add command args from script
        String[] args=getArgs();
        for(int ii=0; ii < args.length; ii++) {
            command.addArg(args[ii]);
        }
        return command;
    }
    
    
    /**
     * This Method builds an external java command to execute componets like the nodeagent
     * and the deploy tool
     */
    public Command buildExternalCommand(String action) throws ConfigException {
        
        // create the command to execute
        Command command=new Command();
        
        getLogger().log(FINE_LEVEL,"Start building the command the to execute.");
        
        // read in ASLauncherConfig
        String launcherConfigFile=System.getProperty(SystemPropertyConstants.INSTALL_ROOT_PROPERTY)
        + File.separator + "lib" + File.separator + "processLauncher.xml";
        
        String processName=System.getProperty(LaunchConstants.PROCESS_NAME_PROP, INTERNAL_NODE_AGENT_PROFILE);
        getLogger().log(FINE_LEVEL,"Loading ProcessLauncher config from: " + launcherConfigFile +
        " - for the process named: " + processName);
        
        // see if we have a config root set
        String configRoot=System.getProperty(SystemPropertyConstants.CONFIG_ROOT_PROPERTY);
        if (configRoot == null) {
            // no config root try and make one from install root
            configRoot=System.getProperty(SystemPropertyConstants.INSTALL_ROOT_PROPERTY) + File.separator + "config";
        }
        //Set system properties that correspond directly to asenv.conf/bat. This
        //keeps us from having to pass them all from -D on the command line.
        //ASenvPropertyReader reader = new ASenvPropertyReader(configRoot);
        //reader.setSystemProperties();
        
        // verbose set, flag used in ServerLogManager to send logs to stderr
        if (isVerboseEnabled()) {
            command.addSystemVariable("-D" + VERBOSE_SYSTEM_PROPERTY + "=true");
            // add to System.properties for config conditional adds (could be set about if not native launcher)
            System.setProperty(VERBOSE_SYSTEM_PROPERTY, "true");
        }
        
        // get configuration
        ASLauncherConfig plConfig=new ASLauncherConfig(launcherConfigFile, processName);
        
        // take plConfig properties as the base for the system jvm args for the new process
        Properties systemProperties=plConfig.getSystemProperties();
        
        // check for log file location
        String logFile=systemProperties.getProperty(LOGFILE_SYSTEM_PROPERTY);
        if(bDebug) System.out.println("Is external command nodeagent - " + isNodeAgentProfile());
        if (isNodeAgentProfile()) {
            // need to get log info from domain.xml
            getLogger().log(FINE_LEVEL,"BuildExternalCommand for NodeAgent");
            
            try {
                // derive domain.xml location and create config to be used by config api
                String domainXMLLocation=System.getProperty(SystemPropertyConstants.INSTANCE_ROOT_PROPERTY)
                + RELATIVE_LOCATION_DOMAIN_XML;
                ConfigContext configCtxt=ConfigFactory.createConfigContext(domainXMLLocation);
                Domain domain=ConfigAPIHelper.getDomainConfigBean(configCtxt);
                setDomainName(domain);

                // get the nodeagent by name, need it as soon as possible for logging
                String nodeAgentName=System.getProperty(SystemPropertyConstants.SERVER_NAME);
                NodeAgent nodeAgent=NodeAgentHelper.getNodeAgentByName(configCtxt, nodeAgentName);
                LogService logService=nodeAgent.getLogService();

                if(logService != null) {
                    getLogger().log(FINE_LEVEL, "LogService found for nodeagent");
                    // get logservice info from config beans
                    String logFileX=logService.getFile();

                    if (logFileX !=null) {
                        logFile=logFileX;
                        // add log to properties so NodeAgentMain will redirect is applicable
                        systemProperties.setProperty(LOGFILE_SYSTEM_PROPERTY, logFile);                
                    }

                    // set log level to the level that the nodeagent is set to
                    String logLevel=logService.getModuleLogLevels().getNodeAgent();
                    getLogger().setLevel(Level.parse(logLevel));
                }
            } catch (ConfigException ce) {
                // domain.xml should not be there on first start up, just log
                getLogger().log(FINE_LEVEL,"domain.xml does not exist yet for the nodeagent");
            }
        }

        // set log file for logger and native launcher
        if (logFile != null) {
            // make sure log is writable, if not a message will be logged to the screen if in verbose mode
            if(createFileStructure(logFile) && !isInternalLogger()) {
                // add this file to the logger so at least some of the launcher info gets propagated
                addLogFileToLogger(logFile);
            }
            command.setLogFile(logFile);
        }
        
        // derive and add java command to Command
        String javaHome=System.getProperty(LaunchConstants.JAVA_HOME_PROP);
        // use javaw for windows instead of java
        String javaCall="java", javaCmd=null;
        if(OS.isWindows()) {
            javaCall="javaw";
        }
        
        if (javaHome != null) {
            // use standard java home
            javaCmd=javaHome + File.separator + "bin" + File.separator + javaCall;
        } else {
            // use executed jmv location of java.home
            String javaInt=System.getProperty("java.home");
            javaCmd=javaInt + File.separator + "bin" + File.separator + javaCall;
        }
        
        command.setJavaCommand(javaCmd);
        
        //
        // add classpath
        String classpath=deriveClasspath(plConfig, null);
        command.setClasspath(classpath);
        
        //
        //add main class
        command.setMainClass(plConfig.getMainClass());
        
        //
        // add all system properties to command as system variables args
        Iterator it=systemProperties.keySet().iterator();
        String key=null;
        
        String property=null, value=null;;
        while(it.hasNext()) {
            key=(String)it.next();
            value=systemProperties.getProperty(key);
            
            // take care of vm arg that are sent in via the processlauncher.xml file
            if (key.startsWith("-")) {
                property = key;
                if (value != null && !value.equals("")) {
                    property += "=" + value;
                }
                if ( ( key.equals("-client")) || (key.equals("-server")) ) {
                    // If user sets server or client mode for VM
                    // then use that over default which is "client"
                    // As we want to keep this as first arg don't add
                    // to the jvmArgsList yet
                    command.setMode(key);
                } else {
                    command.addJvmOption(property);
                }            
            } else {
                // regular system property
                property = "-D" + key + "=" + value;
                command.addSystemVariable(property);
            }
        }

        //
        // derive native library path for native part of launcher
        command.addSystemVariable("-Djava.library.path=" + deriveNativeClasspath(command, null, null, systemProperties));
        
        //
        // add command args from script
        String[] args=getArgs();
        for(int ii=0; ii < args.length; ii++) {
            command.addArg(args[ii]);
        }
        return command;
    }
    
    
    /**
     * configureLogService - get log information out of domain.xml and set the processLaunchers log level
     * to that of the instance log level
     *
     * @param config This represents the config element in domain.xml for the server
     * @return logFile Log file for server
     */
    protected String configureLogService(Config config) {
        String logFile="";
        LogService logService=config.getLogService();
        if(logService != null) {
            // get logservice info from config beans
            logFile=logService.getFile();
            String logLevel=logService.getModuleLogLevels().getAdmin();
            
            // set log level to the level that the instance is set to (for internal instances only
            getLogger().setLevel(Level.parse(logLevel));
        }
        return logFile;
    }
    
    
    /**
     * This method derives the classpath by using the information from the processLauncher.xml file which gives criteria for the classpath
     * to be derived from the library directory.
     */
    protected String deriveClasspath(ASLauncherConfig plConfig, String jvmCmd) {
        return deriveClasspath(plConfig, jvmCmd, null, null);
    }
    
    
    /**
     * This method derives the classpath by using the information from the processLauncher.xml file which gives criteria for the classpath
     * to be derived from the library directory.  It also will include the javaConfig classpath and profilerClasspath if they are passed in
     *
     * classpath construction hierarchy:
     * (javaConfig.getClasspathPrefix)
     * (processLauncher.Classpath.prefix)
     * (processLauncher.Classpath.j2se1_4_prefix or processLauncher.Classpath.j2se1_5_or_later_prefix)
     * (derivedClasspath based on processLauncher.Classpath excludes and includes)
     * (javaConfig.getSystemClasspath)
     * (javaConfig.getClasspathSuffix)
     * (javaConfig.profilerClasspath if enabled)
     * (Environment classpath if enabled)
     *
     */
    protected String deriveClasspath(ASLauncherConfig plConfig, String jvmCmd, JavaConfig javaConfig, String profilerClasspath) {
        // add classpath
        String libDir=RelativePathResolver.resolvePath(plConfig.getClasspathLibDir());
        String classpath=Classpath.getLibClasspath(libDir, plConfig.getClasspathIncludes(),
            plConfig.getClasspathExcludes());
        getLogger().log(FINE_LEVEL, "Derived Classpath from " + libDir + " - \n" + classpath);

        // handle processLauncher.xml j2se prefixes always go first
        // check to see what jdk we are using
        String javaVersion=System.getProperty("java.version"); // default to 1.5 or later
        if (jvmCmd != null) {
            try {
                // execute the java command with version option
                Process process=Runtime.getRuntime().exec(jvmCmd + " -version");
                // get streams and capture output
                ByteArrayOutputStream baosOut= new ByteArrayOutputStream();
                ByteArrayOutputStream baosErr= new ByteArrayOutputStream();
                StreamFlusher sfOut=new StreamFlusher(process.getInputStream(), baosOut);
                StreamFlusher sfErr=new StreamFlusher(process.getErrorStream(), baosErr);
                new Thread(sfOut, OUT_FLUSHER).start();
                new Thread(sfErr, ERROR_FLUSHER).start();
                // wait for process to end, should be fast
                process.waitFor();
                javaVersion=baosErr.toString();
            } catch (Exception e) {
                // log at fine incase of problem
                getLogger().log(FINE_LEVEL,"Java version retrieving error, will default to 1.5 or later!", e);
            }
        }
        
        // put in jdk prefix, if exits, goes before what currently has been built
        String prefix="";
        String jvmv="";
        if(javaVersion.indexOf("1.4") >= 0) {
            // j2se 1.4
            jvmv="j2se 1.4";
            prefix=plConfig.getClasspathJ2se14Prefix();
        } else {
            // j2se 1.5 or later
            jvmv="j2se 1.5 or later";
            prefix=plConfig.getClasspathJ2se15OrLaterPrefix();
        }
        // log java version
        getLogger().log(FINE_LEVEL,"Java version being used is: ->" + jvmv +
            "<- based on ->" + javaVersion + "<-");
        
        // set j2se prefix, this alway goes first so the components classpath doesn't interfer with
        // the j2se's requirements
        if(prefix != null && !prefix.equals("")) {

            // resolve any tokens in the prefix
            prefix=RelativePathResolver.resolvePath(prefix);

            // only add prefix to path if one exists.
            if(classpath.equals("")) {
                // no other classpath information so only use prefix, could be know classpath
                classpath=prefix;
            } else {
                // prepend prefix to path
                classpath=prefix + File.pathSeparator + classpath;
            }
        }

        
        // handle processLauncher.xml prefix, if exits, goes before what currently has been built
        prefix=plConfig.getClasspathPrefix();
        // set prefix, this also can be used as a method for entering a know classpath.
        if(prefix != null && !prefix.equals("")) {

            // resolve any tokens in the prefix
            prefix=RelativePathResolver.resolvePath(prefix);

            // only add prefix to path if one exists.
            if(classpath.equals("")) {
                // no other classpath information so only use prefix, could be know classpath
                classpath=prefix;
            } else {
                // prepend prefix to path
                classpath=prefix + File.pathSeparator + classpath;
            }
        }
        
        
        // add in the javaconfig paths
        if(javaConfig != null) {
            String classpathPrefix=javaConfig.getClasspathPrefix();
            String classpathSystem=javaConfig.getSystemClasspath();
            String classpathSuffix=javaConfig.getClasspathSuffix();
            
            if(classpathSystem != null) {
                classpath += File.pathSeparator + classpathSystem;
            }
            
            //Classpath prefix/suffix and server classpath gets now prefixed/suffixed to
            //the shared classloader at PELaunch.java. Instead of setting it here, set 
            //system properties, so that the system properties could be used to construct
            //the classpaths in PELaunch
            if (getProcessLauncherProfile().equals(INTERNAL_SERVER_PROFILE)){
                if(classpathPrefix != null) {
                    classpath=classpathPrefix + File.pathSeparator + classpath;
                }
                if(classpathSuffix != null) {
                    classpath=classpath + File.pathSeparator +  classpathSuffix;
                }
            }
            
            // add profiler information
            if(profilerClasspath != null) {
                classpath += File.pathSeparator + profilerClasspath;
            }
            // See if need to add user classpath
            // WBN changed to use JDK 1.5 built-in getEnv()
            if(!javaConfig.isEnvClasspathIgnored()) {
                // Add user classpath...
                String userCP = System.getenv(CLASSPATH_ENV_NAME);
                if(userCP != null && userCP.length() > 0)
                    classpath += File.pathSeparator + userCP;
            }
        }
        
        getLogger().log(FINE_LEVEL, "Final classpath - \n" + classpath);
        if (bDebug) System.out.println("Final classpath=" + classpath);
        return classpath;
    }
    
    protected String deriveNativeClasspath(Command command, JavaConfig javaConfig, Profiler profiler, Properties systemProperties) {
        // native path works in native launcher
        String javaLibPath = System.getProperty("java.library.path");
        if (bDebug) System.out.println("Current java.library.path=" + javaLibPath + "\n");
        if (javaLibPath == null) javaLibPath="";
        String libDirFor64Bit = "";
       
        if (javaConfig != null) {
            String nativePrefix=javaConfig.getNativeLibraryPathPrefix();
            String nativeSuffix=javaConfig.getNativeLibraryPathSuffix();

            String nativeProfiler=null;
            if(profiler != null && profiler.isEnabled()) {
                nativeProfiler=profiler.getNativeLibraryPath();
            }

            // put native path together in designated order
            if ( (nativePrefix != null ) && !nativePrefix.trim().equals("")) {
                javaLibPath=nativePrefix + (javaLibPath.equals("") ? "" : File.pathSeparator + javaLibPath);
            }
            if ( (nativeSuffix != null ) && !nativeSuffix.trim().equals("")) {
                javaLibPath=(javaLibPath.equals("") ? "" : javaLibPath + File.pathSeparator) + nativeSuffix ;
            }
            if (( nativeProfiler!= null ) && !nativeProfiler.trim().equals("")) {
                javaLibPath=(javaLibPath.equals("") ? "" : javaLibPath + File.pathSeparator) + nativeProfiler ;
            }

           
        String[] jvmOptions=javaConfig.getJvmOptions();
        for(String s:jvmOptions){
	    if(s.indexOf(JVM_OPTION_FOR_64BIT)!=-1){
                String osArch = System.getProperty("os.arch");
                if(osArch.equals(SPARC)) libDirFor64Bit = SPARCV9;
                else if(osArch.equals(X86))  libDirFor64Bit = AMD64;
	 
                String nssRoot=System.getProperty(SystemPropertyConstants.NSS_ROOT_PROPERTY);
                String installRoot=System.getProperty(SystemPropertyConstants.INSTALL_ROOT_PROPERTY);
        	String imqLib=System.getProperty(SystemPropertyConstants.IMQ_LIB_PROPERTY);
                String icuLib=System.getProperty(SystemPropertyConstants.ICU_LIB_PROPERTY);
                String java64BitLibPath = ""; 
                if (installRoot != null){
                    if(nssRoot != null){
                        java64BitLibPath= nssRoot + File.separator + libDirFor64Bit;
                    }
                    if(imqLib != null){
                        java64BitLibPath = imqLib + File.separator + libDirFor64Bit + File.pathSeparator + java64BitLibPath;
                    }
            
                    if(icuLib != null){
                        java64BitLibPath = icuLib + File.separator + libDirFor64Bit + File.pathSeparator + java64BitLibPath;
                    }
                    javaLibPath = java64BitLibPath + File.pathSeparator + javaLibPath; 
                }
           }
       }
        }
        // add nss and lib directories to from of java.library.path if windows to get around jdk 
        // addition of c:\windows\system32
        if (OS.isWindows()) {
            String nssRoot=System.getProperty(SystemPropertyConstants.NSS_ROOT_PROPERTY);
            String installRoot=System.getProperty(SystemPropertyConstants.INSTALL_ROOT_PROPERTY);

            if (installRoot != null && nssRoot != null) {
                javaLibPath= nssRoot + File.pathSeparator + 
                    installRoot + File.separator + "lib" + File.pathSeparator + javaLibPath;
            }
        }        
        
        if (isDisplayEnabled()) {
            // need to add the path the correct jvm library so the native portion will
            // switch jvm modes, it should be set to client by default, but set it just in case
                
	    // fix for bug# 6240672 and 6318497
	    // This fix enables the use of java_home available in java-config.
	    // If the java_home picked up from java-config is invalid then it falls back
	    // to default java_home of config/asenv.conf

	    String java_home = null;
	    if ((javaConfig != null) && (javaConfig.getJavaHome() != null)) {
		java_home = javaConfig.getJavaHome();
	    } else {
		java_home = SystemPropertyConstants.JAVA_ROOT_PROPERTY;
	    }
            String jvmLibPath = java_home + 
                System.getProperty(SystemPropertyConstants.NATIVE_LAUNCHER_LIB_PREFIX);

            // remove default jvm mode and add proper one
            if (command.getMode() != null) {
                jvmLibPath=jvmLibPath.substring(0, jvmLibPath.lastIndexOf(File.separator) + 1) + 
                    command.getMode().substring(1);
            }
                
            if (javaLibPath != null) {
                javaLibPath=jvmLibPath + File.pathSeparator + javaLibPath;
            } else {
                javaLibPath=jvmLibPath;
            }
        }

        
        // now check for spaces in path
        if(javaLibPath.indexOf(" ") >= 0) {
            // there are spaces in the path so send warning message
            // Almost every Windows user has spaces in their path.  
            // Bug 6342806 determined that this is "noise"
            // I changed the message to FINE and I added in the names of the items
            // in the path that have spaces...
            String items = getPathItemsWithSpaces(javaLibPath);
            getLogger().log(FINE_LEVEL,"launcher.spacesInPath", new Object[] { items });
            
            // remove all quotes, because either the java or native launchers will not consistenly 
            // accept the mix in the java.library.path
            // This is a problem between the JNI invocation api and the straight java command.
            javaLibPath=javaLibPath.replaceAll("\"", "");
        }

        systemProperties.put("java.library.path" , javaLibPath);
        

        
        System.setProperty("java.library.path", javaLibPath );
        command.setNativeClasspath(javaLibPath);
        
        
        if (bDebug) System.out.println("Final java.library.path=" + javaLibPath + "\n");
        return javaLibPath;
    }
    
    
    protected void addSystemProperties(SystemProperty sp[], Properties systemProperties) {
        if(sp != null) {
            for(int ii=0; ii < sp.length; ii++) {
                systemProperties.put(sp[ii].getName(), sp[ii].getValue());
            }
        }
    }
    
    protected void addElementProperties(ElementProperty ep[], Properties systemProperties) {
        if(ep != null) {
            for(int ii=0; ii < ep.length; ii++) {
                systemProperties.put(ep[ii].getName(), ep[ii].getValue());
            }
        }
    }
    
   
    
    
    protected Logger getLogger() {
        if (_logger == null) {
            // check log manager to see if it is internal or external
           // if (!isInternalLogger()) {
                // external log manager, add resource bundle for i18n
                // will associate a file to the logger if it is specified in the system args
                _logger = Logger.getLogger(LogDomains.PROCESS_LAUNCHER_LOGGER, "com.sun.logging.enterprise.system.tools.launcher.LogStrings");
                
                // check to see if in verbose mode, if not remove default console handler
                if (!isVerboseEnabled()) {
                    Handler[] h=_logger.getParent().getHandlers();
                    for (int ii=0; ii < h.length; ii++) {
                        if (h[ii].getClass().getName().equals("java.util.logging.ConsoleHandler")) {
                            _logger.getParent().removeHandler(h[ii]);
                        }
                    }
                }
                
            //} else {
                // use internal log manager which associates the resource bundle and
                // log file automatically
            //    _logger = Logger.getLogger(LogDomains.PROCESS_LAUNCHER_LOGGER, "com.sun.logging.enterprise.system.tools.launcher.LogStrings");
            //}
        }
        
        // set each time reguardless of what is preset in domain.xml (for internals)
        if (bDebug) _logger.setLevel(Level.FINEST);
        return _logger;
    }
    
    
    protected boolean isInternalLogger() {
        boolean bRet=false;
        // check log manager to see if it is internal or external
        String logManager=System.getProperty(LaunchConstants.LOG_MGR_PROP);
        if (logManager != null && logManager.equals(LaunchConstants.LOG_MGR_VALUE)) {
            bRet=true;
        }
        return bRet;
    }
    
    protected void addLogFileToLogger(String logFile) {
        if (logFile == null) return;
        
        // Send logger output to our FileHandler.
        getLogger().log(FINE_LEVEL, "*** Adding logFileHandler - " + logFile);
        // already created directory structure for log file, so just add log
        try {
            FileHandler fh = new FileHandler(logFile, true);
            fh.setFormatter(new SimpleFormatter());
            fh.setLevel(Level.ALL);
            getLogger().addHandler(fh);
        } catch(IOException e) {
            // should be seen in verbose mode for debugging
            e.printStackTrace();
        }
    }
    
    
    /**
     * This method handles the jvm options and was pulled from the LaunchFilter
     */
    protected void addJvmOptions(Command command, String[] args, String action) {
        String systemProperty = null;
        String property = null;
        String value = null;
        String[] jvmOptions = null;
        
        try {
          jvmOptions = (new JvmOptionsHelper(args)).getJvmOptions();
        } catch(Exception e) {
        }

        if(jvmOptions!=null)
        {
            for(int i=0; i<jvmOptions.length; i++) 
            {
                addJvmArg(command, jvmOptions[i], action);
            }
            return;
        }
        
        //here we are only in case if jvm-options helper had exception 
        // then - old style of parsing, to avoid exception
        if(args != null) {
            // loop through args
            for(int ii=0; ii < args.length; ii++) {
                // remove leading and trailing spaces
                systemProperty=args[ii].trim();
                
                if (bDebug) System.out.println("addJvmOptions: IN Property " + systemProperty);
                
                // ignore white space
                if(systemProperty.trim().equals("")) {
                    continue;
                }
                
                int iSpace=0, iQuote1=0, iQuote2=0;
                // loop through jvm-options line and see if multple entries on one line
                while(systemProperty.length() > 0) {
                            
                    // Find first space and quote
                    iSpace=systemProperty.indexOf(" -");
                    iQuote1=systemProperty.indexOf("\"");
                        
                    // see if it has a space that may specify 2 args
                    if (iSpace >= 0) {
                        // see if there are double quotes, which could mean the space is part of the value
                        if (iQuote1 >= 0) {
                            // see where quote is in relation to space, if a space exists
                            if (iQuote1 > iSpace && iSpace >= 0 ) {
                                // quote is before space so break up to space, should be full arg
                                addJvmArg(command, systemProperty.substring(0, iSpace), action);
                                // set remainder string, minus space delimiter
                                systemProperty=systemProperty.substring(iSpace + 1).trim();
                                if (bDebug) System.out.println("*** left 1:" + systemProperty);
                            } else {
                                // quote is first, could have a space in quotes, or just quoted string at end
                                
                                // loop to find next un-escaped quote
                                int iQuoteStartPos=iQuote1 + 1;
                                while (true) {
                                    iQuote2=systemProperty.indexOf("\"", iQuoteStartPos);
                                    if (iQuote2 < 0) {
                                        // error can't find last quote, so log and exit loop
                                        getLogger().log(Level.WARNING, "launcher.missMatchQuotesInArg", systemProperty);
                                        // set to "" to end multiple arg loop
                                        systemProperty="";
                                        // breakout of inner quote loop
                                        break;
                                    }

                                    // found second quote see if it is escaped,which means the 
                                    // value has internal quotes
                                    if (systemProperty.charAt(iQuote2 - 1) == '\\') {
                                        // quote escaped, look for next quote
                                        iQuoteStartPos=iQuote2 + 1;
                                        continue;
                                    } else {
                                        // found end quote that is not escaped
                                        
                                        // see if there are any more spaces after second quote
                                        // this happends when directories are added with spaces in them
                                        // like java.dirs.ext system property
                                        if (systemProperty.indexOf(" -", iQuote2) < 0) {
                                            // no more space, so space was enclosed in quotes
                                            // send total line as one
                                            addJvmArg(command, systemProperty, action);
                                            
                                            // should be at the end
                                            // set to "" to end multiple arg loop
                                            systemProperty="";
                                        } else {
                                            // another space was found in the line
                                            iQuote2++; // add on to include quote in arg
                                            addJvmArg(command, systemProperty.substring(0, iQuote2), action);
                                            // set remainder string minus space delimiter
                                            if (iQuote2 < systemProperty.length()) {
                                                systemProperty=systemProperty.substring(iQuote2 + 1).trim();
                                                if (bDebug) System.out.println("*** left 2:" + systemProperty);
                                            } else {
                                                // should be at the end
                                                // set to "" to end multiple arg loop
                                                systemProperty="";
                                            }
                                        }
                                        // breakout of inner quote loop
                                        break;
                                    }
                                }
                            }
                            
                        } else {
                            // no quotes, just break on " -" for multiple args
                            // space could be non-quoted like in java.ext.dirs
                            int iDel=systemProperty.indexOf(" -");
                            while(iDel >= 0) {
                                // found token
                                addJvmArg(command, systemProperty.substring(0, iDel), action);
                                systemProperty=systemProperty.substring(iDel + 1).trim();
                                iDel=systemProperty.indexOf(" -");
                            }
                            
                            // make sure you get the last one
                            if (!systemProperty.equals("")) {
                                addJvmArg(command, systemProperty, action);
                            }
                            
                            // break out of multiple arg loop
                            break;
                        }
                    } else {
                        // no spaces, should just be one value so add
                        addJvmArg(command, systemProperty, action);
                        // break out of multiple arg loop
                        break;
                    }
                }
            }
        }
    
    }
    

    protected void addJvmArg(Command command, String option, String action) {
 	    
        //
        // fix for bug# 6416997 so that the memory options
        // -Xmx and -Xms are not passed to jvm for stop command
        // 
        
        // WBN
        if(option == null)
            return;
        
        if (LAUNCHER_STOP_ACTION.equals(action)) {
            option = removeJVMStopOption(option);
           if (option.equals("")) {
            return;
            }
        }
      if ( option.startsWith("-D") ) {
                // set to systemvaiables for commandline ordering
                command.addSystemVariable(option);
        } else {
            if ( ( option.equals("-client")) || (option.equals("-server")) ) {
                //If user mentions server or client mode for VM
                // then use that over default which is "server"
                // As we want to keep this as first arg don't add
                // to the jvmArgsList yet
                command.setMode(option);
            } else {
                // just add the option to the jvm options
                command.addJvmOption(option);
            }
        }
        
        //getLogger().log(Level.INFO,"addJvmOptions: OUT Property " + option);
        if (bDebug) System.out.println("addJvmOptions: OUT Property " + option);
    }
    
    
    /**
     * This method handles the debug options and was pulled from the LaunchFilter
     */
    protected void addDebugOptions(Command command, String debug_options) {
        // only do for start action, not stop
        
        // If debug is enabled, then we need to pass on -Xdebug option
        command.addDebugOption("-Xdebug");
        
        // It seems that -Xdebug and other debug options shouldn't go
        // as one argument  So we will check if debug_options starts
        //  with that and give it as separate argument
        debug_options=debug_options.trim();
        if ( debug_options.startsWith("-Xdebug") ) {
            debug_options =debug_options.substring("-Xdebug".length()).trim();
        }
        
        // Get the JPDA transport and address (port) from the
        // debug_options. If address is not specified in debug_options
        // for transport=dt_socket, we find a free port
        // and add it to -Xrunjdwp.
        //
        // If address is specified in -Xrunjdwp, then the JVM
        // does not print any debug message, so we need to print it for
        // easy viewing by the user.
        // If address is not specified in debug_options,
        // then the JVM will print a message like:
        // Listening for transport dt_socket at address: 33305
        // This is only visible with "asadmin start-domain --verbose"
        //
        // The format of debug_options is:
        // -Xrunjdwp:<name1>[=<value1>],<name2>[=<value2>]
        
        String transport = getDebugProperty(debug_options, "transport");
        String addr = getDebugProperty(debug_options, "address");
        
        if ( transport == null || transport.equals("") ) {
            // XXX I18N this
            // throw exception
            System.out.println("Cannot start server in debug mode: no transport specified in debug-options in domain.xml.");
        }
        
        if ( transport.equals("dt_socket") ) {
            if ( addr != null && !addr.equals("") ) {
                // XXX Should we check if the port is free using
                // com.sun.enterprise.util.net.NetUtils.isPortFree(port)
            }
            else {
                // Get a free port
                int port =
                com.sun.enterprise.util.net.NetUtils.getFreePort();
                if ( port == 0 ) {
                    // XXX I18N this
                    // throw exception ???
                    System.out.println("Cannot start server in debug mode: unable to obtain a free port for transport dt_socket.");
                }
                addr = String.valueOf(port);
                
                debug_options = debug_options + ",address=" + addr;
            }
        }
        
        command.addDebugOption(debug_options);
        
        // Provide the actual JDWP options to the server using a
        // system property. This allow the server to make it available
        // to the debugger (e.g. S1 Studio) using an API.
        String jdwpOptions = debug_options.substring(
        debug_options.indexOf("-Xrunjdwp:") + "-Xrunjdwp:".length());
        
        command.addSystemVariable("-D" + DEBUG_OPTIONS + "=" + jdwpOptions);
        
    }
    
    protected String getDebugProperty(String debug_options, String name) {
        int nameIndex;
        if ( (nameIndex = debug_options.indexOf(name)) != -1 ) {
            // format is "name=value"
            String value = debug_options.substring(nameIndex
            + name.length() + 1);
            int commaIndex;
            if ( (commaIndex = value.indexOf(",")) != -1 ) {
                value = value.substring(0, commaIndex);
            }
            return value;
        }
        return null;
    }
    
    protected boolean isWaitEnabled() {
        boolean bRet=false;
        String launcherRet=System.getProperty(LaunchConstants.LAUNCHER_RETURN_PROP);
        if(launcherRet != null && launcherRet.equals(LaunchConstants.LAUNCHER_RETURN_HOLD_VALUE)) {
            bRet=true;
        }
        return bRet;
    }
    
    protected boolean isVerboseEnabled() {
        return argExists(COMMAND_LINE_ARG_VERBOSE);
    }
    
    
    protected boolean isDebugEnabled() {
        return argExists(COMMAND_LINE_ARG_DEBUG);
    }
    
    protected boolean isDisplayEnabled() {
        return argExists(COMMAND_LINE_ARG_DISPLAY);
    }
    
    protected boolean isServerProfile() {
        String processName=getProcessLauncherProfile();
        return processName.equals(INTERNAL_SERVER_PROFILE) || 
                                           processName.equals(AS9_INTERNAL_SERVER_PROFILE);
    }
    
    protected boolean isNodeAgentProfile() {
        String processName=getProcessLauncherProfile();
        return processName.equals(INTERNAL_NODE_AGENT_PROFILE);
    }
    
    
    protected String getFiletRelativeName(String action) {
        return System.getProperty(SystemPropertyConstants.INSTANCE_ROOT_PROPERTY) + File.separator
        + LAUNCHER_SCRIPT_LOCATION + File.separator + action;
    }

    
    protected String getProcessLauncherProfile() {
        return System.getProperty(LaunchConstants.PROCESS_NAME_PROP, INTERNAL_SERVER_PROFILE);
    }
    
    protected String getScriptRelativeName(String action) {
        String sxRet=getFiletRelativeName(action);
        if(OS.isWindows()) {
            sxRet+="_temp.bat";
        } else {
            sxRet+="_temp.sh";
        }
        return sxRet;
    }
    
    protected void setArgs(String[] args) {
        ArrayList<String> processedArgs = new ArrayList<String>(args.length);
        // Process the args
        for(String arg : args)
        {
            // allow "debug" to be used as an arg in startserv script...
            if(arg.equalsIgnoreCase("debug"))
                System.setProperty("Debug", "true");
            
            processedArgs.add(arg);
        }
        _args = processedArgs.toArray(new String[processedArgs.size()]);
    }
    
    protected String[] getArgs() {
        return _args;
    }
    
    protected boolean argExists(String arg) {
        boolean bRet=false;
        String[] args=getArgs();
        if(args != null) {
            for(int ii=0; ii < args.length; ii++) {
                if(args[ii].equals(arg)) {
                    bRet=true;
                    break;
                }
            }
        }
        return bRet;
    }
    
   
    /**
     * createFileStructure - This method validates that that the file can be written to.  It the
     * if the parent directory structure does not exist, it will be created
     *
     * @param logFile - fully qualified path of the logfile
     */
    protected boolean createFileStructure(String logFile) {
        boolean bRet=false;
        File outputFile=new File(logFile);
        
        try {
            // Verify that we can write to the output file
            File parentFile = new File(outputFile.getParent());
            // To take care of non-existent log directories
            if ( !parentFile.exists() ) {
                // Trying to create non-existent parent directories
                parentFile.mkdirs();
            }
            // create the file if it doesn't exist
            if (!outputFile.exists()) {
                outputFile.createNewFile();
            }
            if (outputFile.canWrite()) {
                // everything is okay to logfile
                bRet=true;
            }
        } catch (IOException e) {
            // will only see on verbose more, so okay
            e.printStackTrace();
        }

        if (!bRet) {
            // log can't be created or isn't writtable
            getLogger().log(Level.WARNING,"launcher.logWriteFailure", logFile);
        }

        return bRet;
    }
    
    private String getPathItemsWithSpaces(String path)
    {
        StringTokenizer st = new StringTokenizer(path, File.pathSeparator);
        String ret = "";
        boolean firstItem = true;
        
        while (st.hasMoreTokens()) 
        {
            String item = st.nextToken();
            
            if(item.indexOf(' ') >= 0)
            {
                if(!firstItem)
                    ret += File.pathSeparator;
                else
                    firstItem = false;
                
                ret += item;
            }
        }
        
        return ret;
    }


    private String removeJVMStopOption(String jvmOption) {
        if (jvmOption.startsWith("-X")) {
            jvmOption = "";
        }
        if (jvmOption.startsWith("-server")) {
            jvmOption = "-client";
        }
        return jvmOption;
    }
    private void setPromptForIdentity(Command command)
    {
        // WBN -- the current xml files wires in the sys prop: com.sun.aas.promptForIdentity=true
        // we may not want that so we remove it if it is false in the "real" System Properties
        // note that if it is set to ANYTHING it is taken as "true", even if it is set to "false"!
        // so it has to be gone for it to be "false"

        Object prompt = null;
        
        // may be null!
        String actual = System.getProperty(PROPMPT_FOR_IDENTITY_SYSTEM_PROPERTY);
        String startsWith = "-D" + PROPMPT_FOR_IDENTITY_SYSTEM_PROPERTY;
        // remove it in all cases and add it back only if actual is not null...
        
        for(Object o : command._systemVariables)
        {
                String s = (String)o;
            
                if(s.startsWith(startsWith))
                {
                    prompt = o;
                    break;
                }
        }
        // remove it always
        if(prompt != null)
            command._systemVariables.remove(prompt);

        // put it back in if it is set in System Properties
        if(actual != null)
            command.addSystemVariable(startsWith + "=" + actual);
    }
    
    private void readASEnv() throws ASLauncherException
    {
        // WBN October 2006
        // see if we have a config root set
        String configRoot=System.getProperty(SystemPropertyConstants.CONFIG_ROOT_PROPERTY);

        if (configRoot == null) 
        {
            // no config root try and make one from install root
            configRoot=System.getProperty(SystemPropertyConstants.INSTALL_ROOT_PROPERTY) + File.separator + "config";
            // let's go ahead and set the sys prop now...
            System.setProperty(SystemPropertyConstants.CONFIG_ROOT_PROPERTY, configRoot);
        }

        if(!FileUtils.safeIsDirectory(configRoot))
        {
            String message = "Can't find the AS configuration script.  This script normally lives in <install>/config and is named asenv";
            
            if(OS.isWindows())
                message += ".bat";
            message += "\nYou need to set these 2 System Properties: \n" + SystemPropertyConstants.INSTALL_ROOT_PROPERTY + "=your-install-directory\n" +
                SystemPropertyConstants.CONFIG_ROOT_PROPERTY + "=your-config-directory\n";
            throw new ASLauncherException(message);
        }
        ASenvPropertyReader reader = new ASenvPropertyReader(configRoot);
        reader.setSystemProperties();
    }

    
    void writeSecurityInfoToProcess(Process p)
    {
        if(securityInfo == null) // impossible!
            return;
        
        PrintWriter out = null;
        try {
            out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(p.getOutputStream())));

            for(String s : securityInfo) {
                if(bDebug) {
                    System.out.println("InputLine ->" + s + "<-");
                }
                out.println(s);
            }
            out.flush();
        }
        finally
        {
            try
            {
                out.close();
            }
            catch (Throwable t)
            {
            }
        }
    }
    
    private void setDomainName(Domain domain) throws ConfigException
    {
        try {
            ElementProperty ep = domain.getElementPropertyByName(DOMAIN_NAME_XML_PROPERTY);
            
            if(ep == null)
                throw new ConfigException("The property, " + DOMAIN_NAME_XML_PROPERTY + ", was null");
            
            String domainName = ep.getValue();
            
            if(domainName == null || domainName.length() < 1)
                throw new ConfigException("The value of the property, " + DOMAIN_NAME_XML_PROPERTY + ", was null or empty");
            
            System.setProperty(DOMAIN_NAME_SYSTEM_PROPERTY, domainName);
        }
        catch(ConfigException ce)
        {
            throw ce;
        }
        catch(Exception e)
        {
            throw new ConfigException("Error getting " + DOMAIN_NAME_XML_PROPERTY + " from domain.xml", e);
        }
    }
        
    
    private void getSecurityFromUser(String[] args) throws IOException 
    {
        // HACK-O-RAMA
        // yet another hack for backward compatibility!
        // CLI will gather up passwords and send them in as an arg...
        if(System.getProperty("caller", "not-cli").equals("cli"))
            return;
        
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        securityInfo = new String[3];
        NativeUtils nativeUtils = new NativeUtils(); 
        System.out.print("admin username:");
        securityInfo[0] = reader.readLine();
        securityInfo[1] = nativeUtils.getPassword("admin password: ");
        securityInfo[2] = nativeUtils.getPassword("master password: ");
        
        System.setProperty(PROPMPT_FOR_IDENTITY_SYSTEM_PROPERTY, "true");
    }

    private void setInstanceRoot(Properties callerProps)  throws ASLauncherException
    {
        String irpTag = SystemPropertyConstants.INSTANCE_ROOT_PROPERTY; // too ugly to type!
        String irp = callerProps.getProperty(irpTag);
        
        if(irp != null)
            System.setProperty(irpTag, irp);

        
        // if the user is running a script stand-alone the instance-root may be
        // something like "./startserv".  We need to convert it to a real path
        
        irp = System.getProperty(irpTag);

        if(irp == null || irp.length() <= 0 )
        {
            // if we are being called from stand-alone java or a script -- they need to
            // do a -DirpTag=...
            // if we are called from PEInstancesManager, it has been set.  If it isn't
            // set let's fail right now!
            throw new ASLauncherException("Missing System Property: " + irpTag);
        }
        
		File f = new File(irp);
		try
		{
			irp = f.getCanonicalPath();
		}
		catch(Exception e)
		{
			irp = f.getAbsolutePath();
		}

        System.setProperty(irpTag, irp);
    }

    // END END END END ENDEND -- this is here for grepping to the end of this gigantic class!!
    
    //**************************************************************************
    // ***********        protected inner Command class        *****************
    //**************************************************************************
    /**
     * This class is a structure class that holds the executable command as it is compiled.  Once the
     * command is completed, an instance method returns the command in the form of a
     * string array, with or without the classpath information present.  The without
     * classpath option is triggered by having the commands LaunchConstants.LAUNCHER_RETURN_PROP
     * is flagged as "hold",  setting the "com.sun.aas.limitedCommamdExecution" and being on
     * the Windows platform.  This was used as an intermediate solution to get around the
     * command execution length limitation on Windows.  The classpath information was
     * written out to be executed as a separate "set" command.
     */
    protected class Command {
        
        private ArrayList _jvmOptions=new ArrayList();
        private ArrayList _systemVariables=new ArrayList();
        private ArrayList _args=new ArrayList();
        private ArrayList _debugOptions=new ArrayList();
        private String _mainClass=null;
        private String _classpath=null;
        private String _javaCommand=null;
        private String _mode=null;
        private String _logFile=null;
        private String _nativeClasspath=null;
        
        
        protected void addJvmOption(String jvmOptions) {
            // must trim these or Runtime.exec will blow with leading spaces
            _jvmOptions.add(jvmOptions.trim());
        }
        
        protected void addSystemVariable(String systemVariable) {
            // must trim these or Runtime.exec will blow with leading spaces
            _systemVariables.add(systemVariable.trim());
        }
        
        protected void addArg(String arg) {
            _args.add(arg);
        }
        protected String[] getArgs() {
            return (String[])_args.toArray(new String[_args.size()]);
        }
        protected void removeArg(String arg) {
            _args.remove(arg);
        }
        
        protected void addDebugOption(String debugOption) {
            _debugOptions.add(debugOption);
        }
        protected String[] getDebugOptions() {
            return (String[])_debugOptions.toArray(new String[_debugOptions.size()]);
        }
        
        protected void setMode(String mode) {
            _mode=mode;
        }
        protected String getMode() {
            return _mode;
        }
        
        protected void setMainClass(String mainClass) {
            _mainClass=mainClass;
        }
        protected String getMainClass() {
            return _mainClass;
        }
        
        protected void setNativeClasspath(String classpath) {
            _nativeClasspath=classpath;
        }
        protected String getNativeClasspath() {
            return _nativeClasspath;
        }
        
        protected void setClasspath(String classpath) {
            _classpath=classpath;
        }
        protected String getClasspath() {
            return _classpath;
        }
        
        protected void setJavaCommand(String javaCommand) {
            _javaCommand=javaCommand;
        }
        protected String getJavaCommand() {
            return _javaCommand;
        }
        
        protected void setLogFile(String logFile) {
            if (bDebug) System.out.println("Logfile set to " + logFile);
            _logFile=logFile;
        }
        protected String getLogFile() {
            return _logFile;
        }
        
        /*
         * This method returns the command in standard java format
         * which can be used by Runtime.execute
         */
        protected String[] getCommandAsArray() {
            ArrayList cmd=new ArrayList();
            cmd.add(_javaCommand);
            if(_mode != null) {
                cmd.add(_mode);
            }
            cmd.addAll(_debugOptions);
            cmd.addAll(_jvmOptions);
            cmd.addAll(_systemVariables);
            // put cp on separate line of Runtimes execute
            // doesn't work
            cmd.add("-cp");
            cmd.add(_classpath);
            cmd.add(_mainClass);
            cmd.addAll(_args);
            return (String[])cmd.toArray(new String[cmd.size()]);
        }
        
        /**
         * This method return the executable command without the classpath attached
         * It is used when building scripts that
         * are executed in an environment with a limited command line length like
         * windows 2000 - 2071 character limit and XP - 8000 character limit
         * To get full command use getClasspath() also
         */
        protected String[] getLimitedCommandAsArray() {
            ArrayList cmd=new ArrayList();
            cmd.add(_javaCommand);
            if(_mode != null) {
                cmd.add(_mode);
            }
            cmd.addAll(_debugOptions);
            cmd.addAll(_jvmOptions);
            cmd.addAll(_systemVariables);
            cmd.add(_mainClass);
            cmd.addAll(_args);
            return (String[])cmd.toArray(new String[cmd.size()]);
        }
        
        
        /*
         * This method returns the command in a format that is JNI invocation api friendly and
         * can be easely digested in the native environment
         */
        protected String[] getCommandInJNIFormatAsArray() {
            // display java command to stdout in the following format.
            // NOTE: if this becomes externally available, use xml, for now its an internal structure.
            // Class Name (path is seperated by "/"
            // commandline Args
            // everything else (should start with a "-")
            // classpath should be prepended with "-Djava.class.path="
            
            // alter main class path for jni invocation api, do it here to keep as much out of native as possible
            String jniMainClassName=_mainClass;
            int iPos=0;
            while((iPos=jniMainClassName.indexOf(".")) >= 0) {
                jniMainClassName=jniMainClassName.substring(0, iPos) + "/" + jniMainClassName.substring(iPos + 1);
            }
            
            ArrayList cmd=new ArrayList();
            if(_mode != null) {
                cmd.add(_mode);
            }
            cmd.addAll(_debugOptions);
            cmd.addAll(_jvmOptions);
            cmd.addAll(_systemVariables);
            cmd.add("-Djava.class.path=" + _classpath);
            cmd.add(_mainClass);
            cmd.addAll(_args);
            return (String[])cmd.toArray(new String[cmd.size()]);
        }
        
        
        /**
         * This method returns only the system variables for the command to execute
         */
        protected String[] getSystemVariablesAsArray() {
            ArrayList cmd=new ArrayList();
            cmd.addAll(_systemVariables);
            return (String[])cmd.toArray(new String[cmd.size()]);
        }
        
        
        public String toString() {
            StringBuffer cmd= new StringBuffer();
            
            String[] ret=getCommandAsArray();
            for(int ii=0; ii < ret.length; ii++) {
                cmd.append(ret[ii]);
            }
            
            return cmd.toString();
        }
        
        public String toStringWithLines() {
            StringBuffer cmd= new StringBuffer();
            
            String[] ret=getCommandAsArray();
            for(int ii=0; ii < ret.length; ii++) {
                cmd.append("\n" + ret[ii]);
            }
            return cmd.toString();
        }
    }
    
    
    //**************************************************************************
    //***********        protected inner Classpath class       *****************
    //**************************************************************************
    /**
     * A class that encaspilates the functionality required to derive the classpath from criteria in
     * attributes of the classpath element that is present in the processLauncher.xml file.
     */
    protected static class Classpath {
        /**
         * getLibClasspath - This method returns a string classpath which represents the items in the
         * lib directory in accordance with the regular expressions that represents the
         * include and excludes attributes of the processLaunher.xml
         */
        protected static String getLibClasspath(String libDir, String includes, String excludes) {
            ArrayList arIncludes=new ArrayList();
            ArrayList arExcludes=new ArrayList();
            
            // construct include and excludes for comparison
            StringTokenizer st=new StringTokenizer(includes, ",");
            while(st.hasMoreTokens()) {
                arIncludes.add(st.nextToken().trim());
            }
            st=new StringTokenizer(excludes, ",");
            while(st.hasMoreTokens()) {
                arExcludes.add(st.nextToken().trim());
            }
            
            String path="";
            // if lib dir exists then see if items can be included or excluded.
            if (libDir != null && !libDir.equals("")) {
                // get file dir
                File dir=new File(libDir);
                
                // loop through items in directory
                String[] filenames=dir.list();
                for(int ii=0; ii < filenames.length; ii++) {
                    
                    // see if should be included
                    if(matchStringToList(filenames[ii], arIncludes) && !matchStringToList(filenames[ii], arExcludes)) {
                        // see if in excluded list
                        path += libDir + File.separator + filenames[ii] + File.pathSeparator;
                    }
                }
                
                // remove last pasthSeparator
                if(path.endsWith(File.pathSeparator)) {
                    path=path.substring(0, path.length()-1);
                }
            }

            return path;
        }
        
        
        /**
         * matchStringToList - This method performs a match to each item in the arraylist to the
         * filename.
         */
        protected static boolean matchStringToList(String filename, ArrayList list) {
            boolean bRet=false;
            
            String criteria=null, endMatch=null;
            Iterator it=list.iterator();
            while(it.hasNext()) {
                criteria=(String)it.next();
                
                if (criteria.startsWith("*")) {
                    // give a work around for people who don't know regular expressions
                    // legacy functionality
                    endMatch=criteria.substring(1);
                    // see if end matches rest of criteria string
                    if (filename.endsWith(endMatch)) {
                        // match, so set return and break out
                        bRet=true;
                        break;
                    }
                } else if (isRegularExpression(criteria)) {
                    // contains wildcard use regexpression
                    if(Pattern.matches(criteria, filename)) {
                        // equals, so set return and break out
                        bRet=true;
                        break;
                    }
                } else {
                    // perform straight equals
                    if(filename.equals(criteria)) {
                        // equals, so set return and break out
                        bRet=true;
                        break;
                    }
                }
            }
            
            return bRet;
        }
        
        /**
         * isRegularExpression - This method checks to see if the item in the arraylist could be a regular expression.
         * This method is not full proof and may need to be modified to suite the appservers needs
         */
        protected static boolean isRegularExpression(String criteria) {
            boolean bRet=false;
            if(criteria.indexOf("^") > -1 || criteria.indexOf("$")  > -1 || criteria.indexOf("[")  > -1
            || criteria.indexOf("]")  > -1 || criteria.indexOf("*")  > -1) {
                bRet=true;
            }
            return bRet;
        }
    }
    
    
    
    //**************************************************************************
    //***********        protected inner StreamFlusher class   *****************
    //**************************************************************************
    /**
     * A class that attaches to the output streams of the executed process and sends the data
     * to the calling processes output streams
     */
    protected class StreamFlusher implements Runnable{
        
        private InputStream _input=null;
        private OutputStream _output=null;
        private String _logFile=null;

        
        public StreamFlusher(InputStream input, OutputStream output) {
            this(input, output, null);
        }
        
        public StreamFlusher(InputStream input, OutputStream output, String logFile) {
            this._input=input;
            this._output=output;
            this._logFile=logFile;
        }
        
        public void run() {
            
            // check for null stream
            if (_input == null) return;
            
            PrintStream printStream=null;
            
            // If applicable, write to a log file
            if (_logFile != null) {
                try {
                    if(createFileStructure(_logFile)) {
                        // reset streams to logfile
                        printStream = new PrintStream(new FileOutputStream(_logFile, true), true);
                    } else {
                        // could not write to log for some reason
                        _logFile=null;
                    }
                } catch (IOException ie) {
                    ie.printStackTrace();
                    _logFile=null;
                }
            }
            
            // transfer bytes from input to output stream
            try {
                int byteCnt=0;
                byte[] buffer=new byte[4096];
                while ((byteCnt=_input.read(buffer)) != -1) {
                    if (_output != null && byteCnt > 0) {
                        _output.write(buffer, 0, byteCnt);
                        _output.flush();
                        
                        // also send to log, if it exists
                        if (_logFile != null) {
                            printStream.write(buffer, 0, byteCnt);
                            printStream.flush();
                        }
                    }
                    //yield();
                }
            } catch (IOException e) {
                // just log this as an finest exception, because it really should matter
                //getLogger().log(FINE_LEVELST,"Exception thrown while reading/writing verbose error stream", e);
            }
        }
        
    }
}
