/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 * 
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, 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/CDDL+GPL.html
 * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code.  If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [year]
 * [name of copyright owner]"
 * 
 * Contributor(s):
 * 
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */
package com.sun.enterprise.appclient;

import com.sun.appserv.naming.S1ASCtxFactory;
import com.sun.enterprise.appclient.AppContainer;
import com.sun.enterprise.appclient.HttpAuthenticator;
import com.sun.enterprise.appclient.jws.TemplateCache;
import com.sun.enterprise.appclient.jws.Util;
import com.sun.enterprise.appclient.jws.boot.ClassPathManager;
import com.sun.enterprise.config.clientbeans.CertDb;
import com.sun.enterprise.config.clientbeans.ClientBeansFactory;
import com.sun.enterprise.config.clientbeans.ClientContainer;
import com.sun.enterprise.config.clientbeans.ClientCredential;
import com.sun.enterprise.config.clientbeans.ElementProperty;
import com.sun.enterprise.config.clientbeans.Security;
import com.sun.enterprise.config.clientbeans.Ssl;
import com.sun.enterprise.config.clientbeans.TargetServer;
import com.sun.enterprise.config.ConfigContext;
import com.sun.enterprise.config.ConfigException;
import com.sun.enterprise.config.ConfigFactory;
import com.sun.enterprise.connectors.ActiveResourceAdapter;
import com.sun.enterprise.connectors.ConnectorRegistry;
import com.sun.enterprise.connectors.ConnectorRuntime;
import com.sun.enterprise.deployment.*;
import com.sun.enterprise.deployment.archivist.AppClientArchivist;
import com.sun.enterprise.deployment.archivist.ApplicationArchivist;
import com.sun.enterprise.deployment.archivist.Archivist;
import com.sun.enterprise.deployment.archivist.ArchivistFactory;
import com.sun.enterprise.deployment.deploy.shared.AbstractArchive;
import com.sun.enterprise.deployment.deploy.shared.Archive;
import com.sun.enterprise.deployment.deploy.shared.ArchiveFactory;
import com.sun.enterprise.deployment.deploy.shared.FileArchive;
import com.sun.enterprise.deployment.deploy.shared.InputJarArchive;
import com.sun.enterprise.deployment.interfaces.SecurityRoleMapperFactory;
import com.sun.enterprise.deployment.interfaces.SecurityRoleMapperFactoryMgr;
import com.sun.enterprise.util.SystemPropertyConstants;
import com.sun.enterprise.webservice.ClientPipeCloser;
import com.sun.enterprise.InjectionManager;
import com.sun.enterprise.J2EESecurityManager;
import com.sun.enterprise.loader.EJBClassLoader;
import com.sun.enterprise.loader.InstrumentableClassLoader;
import com.sun.enterprise.naming.ProviderManager;
import com.sun.enterprise.security.GUIErrorDialog;
import com.sun.enterprise.security.SSLUtils;
import com.sun.enterprise.server.logging.ACCLogManager;
import com.sun.enterprise.Switch;
import com.sun.enterprise.util.FileUtil;
import com.sun.enterprise.util.i18n.StringManager;
import com.sun.enterprise.util.JarClassLoader;
import com.sun.enterprise.util.ORBManager;
import com.sun.enterprise.util.shared.ArchivistUtils;
import com.sun.enterprise.util.Utility;
import com.sun.logging.LogDomains;
import com.sun.web.server.HttpsURLStreamHandlerFactory;
import java.awt.EventQueue;
import java.awt.Toolkit;

import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.net.*;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.jar.JarFile;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import java.util.logging.LogManager;
import java.util.Properties;
import java.util.Vector;
import java.util.logging.MemoryHandler;
import java.util.logging.SimpleFormatter;
import javax.enterprise.deploy.shared.ModuleType;
import javax.naming.InitialContext;

import javax.security.auth.message.config.AuthConfigFactory;
import com.sun.enterprise.security.jmac.config.GFAuthConfigFactory;
import com.sun.enterprise.deployment.ServiceReferenceDescriptor;
import com.sun.enterprise.security.UsernamePasswordStore;
import javax.swing.SwingUtilities;

/**
 * This is the main that gets invoked first. It initializes the application
 * client container for an application client component
 * and other related items and then invokes the real main written by the
 * application developer.
 *
 * This version of the logic supports a new format of jar file as created
 * by the revised client jar creation logic on the back-end.  Under certain
 * conditions the jar file will be expanded into a temporary directory so
 * that, among other things, persistence unit handling can occur.
 */
public class MainWithModuleSupport {
    
    private static final String CLIENT = "-client";
    private static final String NAME = "-name";
    private static final String MAIN_CLASS = "-mainclass";
    private static final String TEXT_AUTH = "-textauth";
    private static final String XML_PATH = "-xml";
    private static final String ACC_CONFIG_XML = "-configxml";    
    private static final String DEFAULT_CLIENT_CONTAINER_XML = "sun-acc.xml";
    // duplicated in com.sun.enterprise.jauth.ConfigXMLParser
    private static final String SUNACC_XML_URL = "sun-acc.xml.url";
    private static final String NO_APP_INVOKE = "-noappinvoke";
    //Added for allow user to pass user name and password through command line.
    private static final String USER = "-user";
    private static final String PASSWORD = "-password";
    private static final String PASSWORD_FILE = "-passwordfile";
    private static final String LOGIN_NAME = "j2eelogin.name";
    private static final String LOGIN_PASSWORD = "j2eelogin.password";
    private static final String DASH = "-";

    private static final String lineSep = System.getProperty("line.separator");
    
    /**
     * Property names used on the server to send these values to a Java Web Start client
     * and by the ACC when running under Java Web Start to retrieve them
     */
    public static final String APPCLIENT_IIOP_DEFAULTHOST_PROPERTYNAME = "com.sun.aas.jws.iiop.defaultHost";
    public static final String APPCLIENT_IIOP_DEFAULTPORT_PROPERTYNAME = "com.sun.aas.jws.iiop.defaultPort";
    public static final String APPCLIENT_IIOP_FAILOVER_ENDPOINTS_PROPERTYNAME = "com.sun.aas.jws.iiop.failover.endpoints";
    public static final String APPCLIENT_PROBE_CLASSNAME_PROPERTYNAME = "com.sun.aas.jws.probeClassName";
    
    /** Prop name for keeping temporary files */
    public static final String APPCLIENT_RETAIN_TEMP_FILES_PROPERTYNAME = "com.sun.aas.jws.retainTempFiles";
    
    /** property name used to indicate that Java Web Start is active */
    public static final String APPCLIENT_ISJWS_PROPERTYNAME = "com.sun.aas.jws.isJWS";
    
    /** property name used to indicate the Java Web Start download host name */
    public static final String APPCLIENT_DOWNLOAD_HOST_PROPERTYNAME = "com.sun.aas.jws.download.host";
    
    /** Prop used when running under Java Web Start to point to a temporarily-created default file.  
     *This property appears in the template for the default sun-acc.xml content.  Logic below
     *assigns a value to it and then uses it to substitute in the template to create the
     *actual content.  (This is not a property set in the environment and then retrieved by Main.)
    */
    public static final String SUN_ACC_SECURITY_CONFIG_PROPERTY = "security.config.file";
    
    /** Used for constructing the name of the temp file that will hold the login conf. content */
    private static final String LOGIN_CONF_FILE_PREFIX = "login";
    private static final String LOGIN_CONF_FILE_SUFFIX = ".conf";
    
    /** The system property to be set that is later read by jaas */
    private static final String LOGIN_CONF_PROPERTY_NAME = "java.security.auth.login.config";
    
    /** Names of templates for default config for Java Web Start */
    private static final String DEFAULT_TEMPLATE_PREFIX = "jws/templates/";
    private static final String SUN_ACC_DEFAULT_TEMPLATE = DEFAULT_TEMPLATE_PREFIX + "default-sun-accTemplate.xml";
    private static final String WSS_CLIENT_CONFIG_TEMPLATE = DEFAULT_TEMPLATE_PREFIX + "default-wss-client-configTemplate.xml";
    private static final String LOGIN_CONF_TEMPLATE = DEFAULT_TEMPLATE_PREFIX + "appclientlogin.conf"; 
    
    /** Naming for temporary files created under Java Web Start */
    private static final String WSS_CLIENT_CONFIG_PREFIX = "wsscc";
    private static final String WSS_CLIENT_CONFIG_SUFFIX = ".xml";
    private static final String SUN_ACC_PREFIX = "sunacc";
    private static final String SUN_ACC_SUFFIX = ".xml";
    
    /** Paths to persistence JARs added to the classpath for the class loader created by the ACC */
    private static final String [] LOCAL_LAUNCH_PERSISTENCE_JAR_PATHS = new String[] {
        System.getProperty(SystemPropertyConstants.DERBY_ROOT_PROPERTY) + File.separator + "lib" + File.separator + "derbyclient.jar",
        System.getProperty(SystemPropertyConstants.INSTALL_ROOT_PROPERTY) + File.separator + "lib" + File.separator + "toplink-essentials.jar",
        System.getProperty(SystemPropertyConstants.INSTALL_ROOT_PROPERTY) + File.separator + "lib" + File.separator + "dbschema.jar"};
    
    private static final String ORB_INITIAL_HOST_PROPERTYNAME = "org.omg.CORBA.ORBInitialHost";
    private static final String ORB_INITIAL_PORT_PROPERTYNAME = "org.omg.CORBA.ORBInitialPort";
    
    protected static Logger _logger;

    protected final boolean debug = false;
    protected StringManager localStrings = 
                            StringManager.getManager(MainWithModuleSupport.class);
    protected boolean guiAuth = false; // unless the user sets the auth.gui prop and does not use -textauth
    protected boolean runClient=true;

    protected String host;

    protected String port;

    /** Saved arguments so they are accessible from the AWT thread if needed */
    protected String [] args;

    /** Records whether ACC is currently running under Java Web Start */
    protected boolean isJWS;
    
    /** Records whether temp config files created while running under Java Web Start should be retained */
    protected boolean retainTempFiles = false;

    protected String clientJar = null;
    protected String displayName = null;
    protected String mainClass = null;
    protected String xmlPath = null;
    protected String accConfigXml = null;
    protected String jwsACCConfigXml = null;
    protected Vector<String> appArgs = new Vector<String>();
    protected String classFileFromCommandLine = null;
    protected boolean useTextAuth = false;

    private static boolean lb_enabled = false;
    
    /**
     * Indicates whether ORB load balancing is turned on.
     *<p>
     * Should not be invoked until after setTargetServerProperties has been
     * invoked.  This method is intended for use by ORB initializers, which in
     * turn are run during the instantiation of the InitialContext.  The 
     * InitialContext is created (in the constructor for this class) after 
     * setTargetServerProperties has been run, so this precondition should
     * be met.
     * @return true if load balancing was specified or implied, false otherwise
     */
    public static boolean isLoadBalancingEnabled() {
        return lb_enabled;
    }
    
    public MainWithModuleSupport(String[] args) {
        /*
         *The appclient script triggers this constructor, so find the persistence
         *JARs in the local directories.  During a Java Web Start launch the
         *other constructor is invoked with the already-located JAR URLs.
         */
        this(args, locateLocalPersistenceJARs());
    }
    
    public MainWithModuleSupport(String[] args, URL[] persistenceJarURLs) {
        /*
         *Temporarily use a cheap memory logger to hold any messages created
         *until we can configure the real logger using the configuration settings.
         */
        BufferedLogger tempLogger = prepareBufferedLogging();
        
        /*
         *Assign the temp logger to _logger so methods that need to log don't 
         *need to know if they are doing so before the real logger is set up.
         */
        _logger = tempLogger;
        
        prepareJWSSettings();
        
	try {
            /*
             *Handle any command line arguments intended for the ACC (as opposed to
             *the client itself) and save the returned args intended for the client.
             */
            appArgs = processCommandLine(args);

            /*
             *Find the class name to be run if the user has asked to run a .class
             *file as the client.
             */
            String classNameForClassFile = determineClassNameForClassFileLaunch(clientJar, isJWS, mainClass, classFileFromCommandLine);
        
            /*
             *Choose and validate the XML configuration file to use, which depends on the 
             *-xml and -configxml command line arguments as well as whether this
             *is a Java Web Start launch or not.
             */
            String xmlPathToUse = chooseConfigFile(xmlPath, accConfigXml, isJWS);
            validateXMLFile(xmlPathToUse);

            /*
             *The configuration file just chosen may assign logging settings.
             *Prepare logger using those settings and also flush any records sent to the 
             *temp logger to the new, real one.
             */
            _logger = prepareLogging(tempLogger, xmlPathToUse, isJWS);

            /*
             *Locate the app client: in a user-specified jar or directory,
             *an implied directory (i.e., user is running a .class file), or the
             *Java Web Start downloaded jar file.
             */
            File appClientJarOrDir = locateAppclientJarOrDir(clientJar, classNameForClassFile, isJWS);

            /*
             *Set up the default login config for a Java Web Start launch.
             */
            prepareJWSLoginConfig();

            Utility.checkJVMVersion();
            prepareSecurity();

            Throwable terminatingException = null;
            AppClientInfo appClientInfo = null;

            ClassLoader jcl = null;
        
	    Switch.getSwitch().setProviderManager(ProviderManager.getProviderManager());
            /*
             * Set the container type before initializing the ORB so that ORB 
             * initializers can correctly identify which container type this is.
             */
            Switch.getSwitch().setContainerType(Switch.APPCLIENT_CONTAINER);

	    // added for ClientContainer.xml initialization
            // The returned properties will be null if load balancing is on or
            // non-null and containing the initial host and port properties if
            // load balancing is off. 
            //
            // Note that setTargetServerProperties must be invoked before the
            // the initial context is instantiated.
	    Properties iiopProperties = setTargetServerProperties(xmlPathToUse);
	    
	    int exitCode = 0; // 0 for success
            prepareURLStreamHandling();
	    
            /*
             *The "info" object for the app client collects together common behavior
             *regardless of how the app client was specified (as an archive file,
             *as a directory, or as a .class file) and - if not a .class file - 
             *what type of module (an ear or an app client jar).
             *
             *Creating this info object also may extract the ear or jar into
             *a temporary directory, for example for use by persistence unit
             *handling, and may also prepare the persistence unit handling if
             *that is required.
             */
            // log the endpoint address(es) to be used, per Jagadesh's request
            _logger.log(Level.INFO, "acc.endpoints", 
                    lb_enabled ? System.getProperty(S1ASCtxFactory.IIOP_ENDPOINTS_PROPERTY) : 
                        iiopProperties.getProperty(ORB_INITIAL_HOST_PROPERTYNAME) + ":" + 
                        iiopProperties.getProperty(ORB_INITIAL_PORT_PROPERTYNAME));

            //ic creation for enabling the ORB intialization with the 
	    //right host:port values (bug 6397533).  The InitialContext must 
            //not be instantiated before setTargetServerProperties has run.
	    InitialContext ic = AppContainer.initializeNaming(iiopProperties);

            appClientInfo = AppClientInfoFactory.buildAppClientInfo(isJWS, _logger, appClientJarOrDir, mainClass, displayName, classFileFromCommandLine, persistenceJarURLs);
            
            ApplicationClientDescriptor appDesc = appClientInfo.getAppClient();
            
            final AppContainer container = createAppContainer(appDesc, guiAuth);
            Cleanup cleanup = prepareShutdownCleanup(container, appClientInfo);

            // Set the authenticator which is called back when a
            // protected web resource is requested and authentication data is
            // needed.
            Authenticator.setDefault(new HttpAuthenticator(container));

            /*
             *The container needs to use the new classloader to locate any
             *user-specified security callback handler.
             */
            jcl = appClientInfo.getClassLoader();
            String appMainClass = container.preInvoke(ic, jcl);
            
            /*
             *The new classloader must be set up as the current context class
             *loader for injection to work.  During a Java Web Start launch
             *the class loader in the event dispatcher thread must also be
             *changed.
             */
            Thread.currentThread().setContextClassLoader(jcl);
            if (isJWS) {
                setClassLoaderForEDT(jcl);
            }

            /*
             *Load the main class of the app client.
             */
            Class cl = loadMainClientClass(jcl, appMainClass);
            
            //This is required for us to enable interrupt jaxws service 
            //creation calls
            System.setProperty("javax.xml.ws.spi.Provider", 
                               "com.sun.enterprise.webservice.spi.ProviderImpl");
            
            // Inject the application client's injectable resources.  This
            // must be done after java:comp/env is initialized but before
            // the application client's main class is invoked.  Also make
            // sure the injection mgr will clean up during shutdown.
            InjectionManager injMgr = Switch.getSwitch().getInjectionManager();
            cleanup.setInjectionManager(injMgr, cl, appDesc);
            
            injMgr.injectClass(cl, appDesc);            
                            
            if(runClient) {
                String[] applicationArgs = appArgs.toArray(new String[appArgs.size()]);
                Utility.invokeApplicationMain(cl, applicationArgs);
                _logger.info("Application main() returned; GUI elements may be continuing to run");
            }


            // System.exit is not called if application main returned
            // without error.  Registered shutdown hook will perform
            // container cleanup
        } catch (java.lang.reflect.InvocationTargetException ite) {
            Throwable tt = ite.getTargetException();
            _logger.log(Level.WARNING, "acc.app_exception", tt);
            throw new RuntimeException(ite);
        } catch (UserError ue) {
            ue.displayAndExit();
        } catch (Throwable t) {
            if (t instanceof javax.security.auth.login.FailedLoginException){

               _logger.info("acc.login_error");
                boolean isGui =
                    Boolean.valueOf
                        (System.getProperty ("auth.gui","true")).booleanValue();
                String errorMessage =
                    localStrings.getString
                        ("main.exception.loginError",
                         "Incorrect login and/or password");

                if (isGui) {
                    GUIErrorDialog ged = new GUIErrorDialog (errorMessage);
                    ged.show ();
                }
            }

            _logger.log(Level.WARNING, "acc.app_exception", t);

            if (t instanceof javax.naming.NamingException) {
                _logger.log(Level.WARNING, "acc.naming_exception_received");
            }
            throw new RuntimeException(t);
        } finally {
	    shutDownSystemAdapters();
        }      
    }

    /**
     *Sets the class loader for the event dispatcher thread in case of a 
     *Java Web Start launch.  
     */
    private void setClassLoaderForEDT(final ClassLoader loader) 
            throws InterruptedException, InvocationTargetException {
        SwingUtilities.invokeAndWait(new Runnable() {
            private ClassLoader theCL;
            public void run() {
                Thread.currentThread().setContextClassLoader(loader);
                Toolkit tk = Toolkit.getDefaultToolkit();
                EventQueue eq = tk.getSystemEventQueue();
                eq.push(new EventQueue());
            }
          }
        );
    }
    
    private static URL[] locateLocalPersistenceJARs() {
        try {
            URL[] localPersistenceJARURLs = new URL[LOCAL_LAUNCH_PERSISTENCE_JAR_PATHS.length];
            int slot = 0;
            for (String jarPath : LOCAL_LAUNCH_PERSISTENCE_JAR_PATHS) {
                File jarFile = new File(jarPath);
                localPersistenceJARURLs[slot++] = jarFile.toURI().toURL();
            }
            return localPersistenceJARURLs;
        } catch (MalformedURLException mue) {
            /*
             *This is not a recoverable error, so to minimize changes convert it
             *to an unchecked runtime exception.
             */
            throw new RuntimeException(mue);
        }
    }
    
    /**
     *Chooses behavior of a Java Web Start launch based on property settings.
     *<p>
     *Sets the instance variables isJWS and retainTempFiles as side-effects
     */
    private void prepareJWSSettings() {
        isJWS = Boolean.getBoolean(APPCLIENT_ISJWS_PROPERTYNAME);
        retainTempFiles = Boolean.getBoolean(APPCLIENT_RETAIN_TEMP_FILES_PROPERTYNAME);
    }
    
    /**
     *Set up the security manager and keystores/truststores.
     */
    private void prepareSecurity() {
        /*
         *If -textauth was specified, no need to look for the auth.gui
         *setting since -textauth takes precedence.
         */
        if ( ! useTextAuth) {
            guiAuth = Boolean.valueOf
            (System.getProperty("auth.gui", "true")).booleanValue();
        }
        
        /* security init */
        SecurityManager secMgr = System.getSecurityManager();
        if (!isJWS && secMgr != null &&
                !(J2EESecurityManager.class.equals(secMgr.getClass()))) {
            J2EESecurityManager mgr = new J2EESecurityManager();
            System.setSecurityManager(mgr);
        }
        if (_logger.isLoggable(Level.INFO)) {
            if (secMgr != null) {
                _logger.info("acc.secmgron");
            } else {
                _logger.info("acc.secmgroff");
            }
        }

        try{
            /* setup keystores.
             * This is required, for appclients that want to use SSL, especially
             * HttpsURLConnection to perform Https.
             */
            SSLUtils.initStoresAtStartup();
        } catch (Exception e){
             /* This is not necessarily an error. This will arise
              * if the user has not specified keystore/truststore properties.
              * and does not want to use SSL.
              */
            if(_logger.isLoggable(Level.FINER)){
                // show the exact stack trace
                _logger.log(Level.FINER, "main.ssl_keystore_init_failed", e);
            } else{
                // just log it as a warning.
                _logger.log(Level.WARNING, "main.ssl_keystore_init_failed");
            }
        }

	try {
	    /* setup jsr 196 factory
	     * define default factory if it is not already defined
	     */
	    String defaultFactory = java.security.Security.getProperty
		(AuthConfigFactory.DEFAULT_FACTORY_SECURITY_PROPERTY);
	    if (defaultFactory == null) {
		java.security.Security.setProperty
		    (AuthConfigFactory.DEFAULT_FACTORY_SECURITY_PROPERTY,
		     GFAuthConfigFactory.class.getName());
	    }

	} catch (Exception e) {
	    //  XXX put string in catablog
	    _logger.log(Level.WARNING, "main.jmac_default_factory");
	}

    }

    /**
     *Loads the app client's main class given its name and the loader to use.
     *@param jcl the class loader to use for loading the client's main class
     *@param clientMainClassName the name of the client's main class
     *@return the Class object for the client's main class
     *@throws RuntimeException (wraps the ClassNotFoundException) if the main class cannot be loaded
     */
    private Class loadMainClientClass(ClassLoader jcl, String clientMainClassName) {
        Class result = null;
        try {
            result = jcl.loadClass(clientMainClassName);
        } catch (java.lang.ClassNotFoundException cnf) {
            String errorMessage = localStrings.getString
                ("appclient.mainclass.not.found", clientMainClassName);
            throw new RuntimeException(errorMessage, cnf);
        }
        _logger.log(Level.INFO, "acc.load_app_class", clientMainClassName);           
        return result;
    }
    
    /**
     *Creates the appropriate logger, using any settings in the config file.
     *<p>
     *If any logging information has been buffered into the temporary memory-based
     *logger, flush it to the newly-created log.
     *@param xmlPathToUse config file path
     *@param isJWS indicates if this is a Java Web Start launch
     */
    private Logger prepareLogging(BufferedLogger tempLogger, String xmlPathToUse, boolean isJWS) {
        // make sure the default logger for ACCLogManager is set
        Logger result = LogDomains.getLogger(LogDomains.ACC_LOGGER);

        LogManager logMgr = LogManager.getLogManager();
        if (logMgr instanceof ACCLogManager) {
            ((ACCLogManager) logMgr).init(xmlPathToUse);
        }
        
        /*
         *Transfer any records from the temporary logger to the new one.
         */
        tempLogger.pushTo(result);
        
        return result;
    }

    /**
     *Processes any command-line arguments, setting up static variables for use
     *later in the processing.
     *<p>
     *As side-effects, these variables may be assigned values:
     *<ul>
     *<le>clientJar
     *<le>displayName
     *<le>mainClass
     *<le>xmlPath
     *<le>accConfigXml
     *<le>guiAuth
     *<le>runClient
     *<le>classFileFromCommandLine
     *<le>System property j2eelogin.name
     *<le>System property j2eelogin.password
     *</ul>
     *@param args the command-line arguments passed to the ACC
     *@return arguments to be passed to the actual client (with ACC arguments removed)
     */
    private Vector<String> processCommandLine(String[] args) throws UserError {
        Vector<String> clientArgs = new Vector<String>();

        AtomicInteger i = new AtomicInteger();
        String arg = null;
        
        // Parse command line arguments.
        if(args.length < 1) {
            usage();
        } else {
            while(i.get() < args.length) {
                arg = args[i.getAndIncrement()];
                if(arg.equals(CLIENT)) {
                    clientJar = getRequiredCommandOptionValue(args, CLIENT, i, "appclient.clientWithoutValue");
                } else if (arg.equals(NAME)) {
                    displayName = getRequiredCommandOptionValue(args, NAME, i, "appclient.nameWithoutValue");
                    ensureAtMostOneOfNameAndMainClass();
                } else if(arg.equals(MAIN_CLASS)) {
                    mainClass = getRequiredCommandOptionValue(args, MAIN_CLASS, i, "appclient.mainClassWithoutValue");
                    ensureAtMostOneOfNameAndMainClass();
                } else if(arg.equals(XML_PATH) ) {
                    xmlPath = getRequiredUniqueCommandOptionValue(args, XML_PATH, xmlPath, i, "appclient.xmlWithoutValue");
                } else if(arg.equals(ACC_CONFIG_XML) ) {
                    accConfigXml = getRequiredUniqueCommandOptionValue(args, ACC_CONFIG_XML, accConfigXml, i, "appclient.accConfigXmlWithoutValue");
                } else if(arg.equals(TEXT_AUTH)) {
                    // Overrides legacy auth.gui setting.
                    useTextAuth = true;
                    logOption(TEXT_AUTH);
                } else if(arg.equals(NO_APP_INVOKE)) {
                    runClient = false;
                    logOption(NO_APP_INVOKE);
                } else if(arg.equals(USER)) {
                    String userNameValue = getRequiredCommandOptionValue(args, USER, i, "appclient.userWithoutValue");
                    System.setProperty(LOGIN_NAME, userNameValue);
                } else if(arg.equals(PASSWORD)) {
                    String passwordValue = getRequiredCommandOptionValue(args, PASSWORD, i, "appclient.passwordWithoutValue");
                    System.setProperty(LOGIN_PASSWORD, passwordValue);
                } else if (arg.equals(PASSWORD_FILE)) {
                    String passwordFileValue = getRequiredCommandOptionValue(args, PASSWORD_FILE, i, "appclient.passwordFileWithoutValue");
                    try {
                        System.setProperty(LOGIN_PASSWORD,
                            loadPasswordFromFile(passwordFileValue));
                    } catch(IOException ex) {
                        throw new UserError(localStrings.getString("appclient.errorReadingFromPasswordFile", passwordFileValue), ex);
                    }
                } else {
                    clientArgs.add(arg);
                    logArgument(arg);
                }
            }
            

            String uname = System.getProperty(LOGIN_NAME);
            String upass = System.getProperty(LOGIN_PASSWORD);
            if( uname != null || upass != null ) {
                UsernamePasswordStore.set(uname, upass);
            }



            /*If this is not a Java Web Start launch, the user may have asked 
             *to execute a .class file by omitting the -client argument.  In this
             *case the user either specifies the name only of the class to run
             *using -mainclass or omits -mainclass and specifies the path to 
             *the .class file as the first command-line argument that would 
             *otherwise be passed to the actual client.  In this second
             *case, the first argument is removed from the list passed to the client.
             */
            if ((mainClass == null) && (clientJar == null) && ! isJWS) {
                /*
                 *Make sure there is at least one argument ready to be passed
                 *to the client before trying
                 *to use the first one as the class file spec.
                 */
                if (clientArgs.size() > 0) {
                    classFileFromCommandLine = clientArgs.elementAt(0);
                    clientArgs.removeElementAt(0);
                    logClassFileArgument(classFileFromCommandLine);
                } else {
                    usage();
                }
            }
        }
        logClientArgs(clientArgs);
        return clientArgs;
    }

    /**
     *Returns the next unused argument as a String value, so long as there is
     *a next argument and it does not begin with a dash which would indicate 
     *the next argument.
     *@param position the mutable current position in the argument array
     *@param errorKey the message key for looking up the correct error if the
     *next argument cannot be used as a value
     */
    private String getRequiredCommandOptionValue(String [] args, String optionName, AtomicInteger position, String errorKey) throws UserError {
        String result = null;
        /*
         *Make sure there is at least one more argument and that it does not
         *start with a dash.  Either of those cases means the user omitted
         *the required value.
         */
        if(position.get() < args.length && !args[position.get()].startsWith(DASH)) {
            result = args[position.getAndIncrement()];
        } else {
            throw new UserError(localStrings.getString(errorKey));
        }
        if (_logger.isLoggable(Level.FINE)) {
            _logger.fine(localStrings.getString("appclient.optionValueIs", optionName, result));
        }
        return result;
    }
    
    /**
     *Returns the next unused argument (if present and not prefixed with a dash)
     *as a string value as long as the current value of the argument expected
     *is not already set.
     *@param optionName the name of the option being processed
     *@param currentValue the current value of the argument
     *@param position the mutable current position in the argument array
     *@param errorKey the message key for looking up the correct error if the
     *next argument cannot be used as a value
     *@throws IllegalArgumentException 
     */
    private String getRequiredUniqueCommandOptionValue(String [] args, String optionName, String currentValue, AtomicInteger position, String errorKey) throws UserError {
        if (currentValue != null) {
            throw new UserError(localStrings.getString("appclient.duplicateValue", optionName, currentValue));
        }
        return getRequiredCommandOptionValue(args, optionName, position, errorKey);
    }
    /**
     *Makes sure that at most one of the -name and -mainclass arguments
     *appeared on the command line.
     *@throws IllegalArgumentException if both appeared
     */
    private void ensureAtMostOneOfNameAndMainClass() throws UserError {
        if (mainClass != null && displayName != null) {
            throw new UserError(localStrings.getString("appclient.mainclassOrNameNotBoth"));
        }
    }

    /**
     *Reports that the specified option name has been processed from the command line.
     *@param optionName the String name of the option
     */
    private void logOption(String optionName) {
        if (_logger.isLoggable(Level.FINE)) {
            _logger.fine(localStrings.getString("appclient.valuelessOptionFound", optionName));
        }
    }
    
    private void logArgument(String arg) {
        if (_logger.isLoggable(Level.FINE)) {
            _logger.fine(localStrings.getString("appclient.argumentValueFound", arg));
        }
    }
    
    private void logClassFileArgument(String classFile) {
        if (_logger.isLoggable(Level.FINE)) {
            _logger.fine(localStrings.getString("appclient.classFileUsed", classFile));
        }
    }
    
    private void logClientArgs(Vector<String> clientArgs) {
        if (_logger.isLoggable(Level.FINE)) {
            _logger.fine(localStrings.getString("appclient.clientArgs", clientArgs.toString()));
        }
    }
    
    /**
     *Decides what class name the user specified IF the user has asked to run 
     *a .class file as the client.
     *
     *@param clientJar path to the client jar from the command line processing
     *@param isJWS indicates if the current execution was launched via Java Web Start technology
     *@param mainClass main class name as possibly specified on the command line
     *@param classFileFromCommandLine class file spec from the command line (if present)
     *@return class name to be run if the user has chosen a .class file; null otherwise
     */
    private String determineClassNameForClassFileLaunch(String clientJar, boolean isJWS, String mainClass, String classFileFromCommandLine) throws UserError {
        String result = null;
        boolean isFine = _logger.isLoggable(Level.FINE);
        if(clientJar == null && ! isJWS) {
            // ok, if the first parameter was the appclient class, let's check 
            // for its existence.
            String value;
            
            /*
             *To run a .class file, the user omits the -client option and 
             *specifies either -mainclass <class-name> or provides
             *the class file path as an argument.  In the second case the
             *value will have been captured as classFileFromCommandLine during
             *command line processing.
             */
            if (classFileFromCommandLine != null) {
                value = classFileFromCommandLine;
                if (isFine) {
                    _logger.fine(localStrings.getString("appclient.classNameFromArg", classFileFromCommandLine));
                }
            } else {
                value = mainClass;
                if (isFine) {
                    _logger.fine(localStrings.getString("appclient.classNameFromMainClass", mainClass));
                }
            }
            if (value.endsWith(".class")) {
                result = value.substring(0, value.length()-".class".length());
            } else {
                result = value;                
            }
            
            String path = result.replace('.', File.separatorChar) + ".class";
            File file = new File(path);
            if (!file.isAbsolute()) {
                file = new File(System.getProperty("user.dir"),  path);
            }
            /*
             *We have tried to identify the class file to use based on either
             *the -mainclass value (with -client omitted) or the value of the
             *first command-line argument.  If we cannot find the class in the user's 
             *home directory (and we already know this is not a JWS launch because
             *we are inside the "if") then the user has
             *not entered a valid command or has specified a class we cannot find.
             */
            if (!file.exists()) {
                throw new UserError(localStrings.getString("appclient.cannotFindClassFile", result, file.getAbsolutePath()));
            } else {
                if (isFine) {
                    _logger.fine(localStrings.getString("appclient.usingClassFile", file.getAbsolutePath()));
                }
            }
        }
        return result;
   }
    
    /**
     *Selects which config file to use, based on which are specified and whether
     *this is a Java Web Start launch.
     *@param xmlPath config file path as specified on the command line
     *@param accConfigXml fall-back config file containing defaults
     *@param isJWS indicates if this is a Java Web Start launch
     *@return the appropriate config file path
     */
    private String chooseConfigFile(String xmlPath, String accConfigXml, boolean isJWS) {
        String pathToUse = null;
        boolean isFine = _logger.isLoggable(Level.FINE);
        
        if (xmlPath != null) {
            pathToUse = xmlPath;
            if (isFine) {
                _logger.fine(localStrings.getString("appclient.configFrom", XML_PATH, pathToUse));
            }
        } else if (accConfigXml != null ) {
            pathToUse = accConfigXml; //use AS_ACC_CONFIG
            if (isFine) {
                _logger.fine(localStrings.getString("appclient.configFrom", ACC_CONFIG_XML, pathToUse));
            }
        } else if (isJWS) {
            /*
             *Neither -xml nor -configxml were present and this is a Java 
             *Web Start invocation.  Use
             *the alternate mechanism to create the default config.
             */
            try {
                String jwsACCConfigXml = prepareJWSConfig();
                if (jwsACCConfigXml != null) {
                    pathToUse = jwsACCConfigXml;
                    if (isFine) {
                        _logger.fine(localStrings.getString("appclient.configFromJWSTemplate"));
                    }
                }
            } catch (Throwable thr) {
                throw new RuntimeException(localStrings.getString("appclient.errorPrepConfig"), thr);
            }
        }
        return pathToUse;
    }
    
    /**
     *Locates the jar or directory that contains the app client main class.
     *@param clientJar path to the client jar file (if specified)
     *@param className the main class name for the app client
     *@param isJWS indicates if this is a Java Web Start launch
     *
     *@return File object for the jar or directory containing the app client main class
     */
    private File locateAppclientJarOrDir(String clientJar, String className, boolean isJWS) throws ClassNotFoundException, URISyntaxException, UserError {
        File result = null;
        boolean isFine = _logger.isLoggable(Level.FINE);
        /*
         *For Java Web Start launches, locate the jar file implicitly.
         *Otherwise, if the user omitted the clientjar argument (and the
         *code has gotten this far) then the user must have used the
         *first argument as the name of the class in ${user.dir} to run.  If
         *the user actually specified the clientjar argument, then use that
         *value as the file spec for the client jar.
         */
        if (isJWS) {
            /*
             *Java Web Start case.
             */
            result = findAppClientFileForJWSLaunch();
            if (isFine) {
                _logger.fine(localStrings.getString("appclient.JWSAppClientFile", result.getAbsolutePath()));
            }
        } else if (clientJar==null) {
            /*
             *First-argument-as-class-name case
             */
            File userDir = new File(System.getProperty("user.dir"));
            File appClientClass = new File(userDir, className);
            result = appClientClass.getParentFile();
            if (isFine) {
                _logger.fine(localStrings.getString("appclient.classFileAppClientFile", result.getAbsolutePath()));
            }
        } else {
            /*
             *Normal case - clientjar argument specified.
             */
            result = new File(clientJar);
            if (isFine) {
                _logger.fine(localStrings.getString("appclient.clientJarAppClientFile", result.getAbsolutePath()));
            }
            /*
             *Make sure the user-specified file - client JAR or .class - exists.
             */
            if ( ! result.exists() ) {
                UserError ue = UserError.formatUserError(
                        localStrings.getString("appclient.cannotFindJarFile"), 
                        result.getAbsolutePath());
                throw ue;
            }
            
        }
        return result;
    }
    
    /**
     *Creates a class loader for the app client, using the jar or directory
     *which contains the main class.
     *@param jarOrDir the File object for the directory or jar file containing the main class
     *@return ClassLoader for use by the app client
     */
    private ClassLoader preparePreliminaryClassLoader(File jarOrDir) throws MalformedURLException {
        ClassLoader result = null;
        URL[] urls = new URL[1];
        urls[0] = jarOrDir.toURI().toURL();
        /*
         *Set the parent of the new class loader to the current loader.  
         *The Java Web Start-managed class path is implemented in the 
         *current loader, so it must remain on the loader stack.
         */
        ClassLoader currentCL = Thread.currentThread().getContextClassLoader();
        result = new URLClassLoader(urls, currentCL);
//        Thread.currentThread().setContextClassLoader(jcl);
        return result;
    }

    private AppContainer createAppContainer(ApplicationClientDescriptor appDesc, boolean guiAuth) {
        AppContainer result = new AppContainer(appDesc, guiAuth);
        if(result == null) {
            _logger.log(Level.WARNING, "acc.no_client_desc",
                        (displayName == null) ? mainClass : displayName);

            System.exit(1);
        }
        return result;
    }
            

    /**
     *Adds a shutdown hook to make sure clean-up work runs at JVM exit.
     */
    private static Cleanup prepareShutdownCleanup(
        AppContainer container, AppClientInfo appClientInfo) {
        // Ensure cleanup is performed, even if
        // application client calls System.exit().
        Cleanup cleanup = new Cleanup();
        Runtime runtime = Runtime.getRuntime();
        runtime.addShutdownHook(cleanup);
        cleanup.setAppContainer(container);
        cleanup.setAppClientInfo(appClientInfo);
        return cleanup;
    }	    

    /**
     *Assigns the URL stream handler factory.
     */
    private static void prepareURLStreamHandling() {
        // Set the HTTPS URL stream handler.
        java.security.AccessController.doPrivileged(new
                                       java.security.PrivilegedAction() {
                public Object run() {
                    URL.setURLStreamHandlerFactory(new
                                       HttpsURLStreamHandlerFactory());
                    return null;
                }
            });
    }

    private Properties setTargetServerProperties(String clientXmlLocation) 
	throws ConfigException {
        
        boolean isEndpointPropertySpecifiedByUser = false;
        String loadBalancingPolicy = null;
        
        Properties result = new Properties();
        
        StringBuilder completeEndpointList = new StringBuilder();
        
        //FIXME: may need to set the context in switch or generic context. but later
        try {
            if(clientXmlLocation == null || clientXmlLocation.equals("")) {
                clientXmlLocation = DEFAULT_CLIENT_CONTAINER_XML;
            }

	    // set for com.sun.enterprise.security.jauth.ConfigXMLParser
	    System.setProperty(SUNACC_XML_URL, clientXmlLocation);
             _logger.log(Level.INFO, "acc.using_xml_location", clientXmlLocation);
	       
            ConfigContext ctx = ConfigFactory.createConfigContext(
		clientXmlLocation, true,
		false, false,
		ClientContainer.class, 
		new ACCEntityResolver());

            ClientContainer cc = ClientBeansFactory.getClientBean(ctx);
        
	    host = cc.getTargetServer(0).getAddress();
	    port = cc.getTargetServer(0).getPort();

	    //check for targetServerEndpoints 
	    TargetServer[] tServer = cc.getTargetServer();
	    String targetServerEndpoints = null;
	    for (int i = 0; i < tServer.length; i++) {
	        if (targetServerEndpoints == null) {
		    targetServerEndpoints = tServer[i].getAddress() + 
		    ":" + tServer[i].getPort();
		} else {
		    targetServerEndpoints = targetServerEndpoints + "," + 
		      tServer[i].getAddress() + 
		      ":" + tServer[i].getPort();
                    lb_enabled = true;
		}
	    }
		
            setSSLData(cc);

            //FIXME: what do we do about realm
            ClientCredential cCrd = cc.getClientCredential();
            if(cCrd != null) {
                String uname = null;
                String upass = null;

                // if user entered user/password from command line,
                // it take percedence over the xml file. - y.l. 05/15/02
                if (System.getProperty(LOGIN_NAME) == null) {
                    _logger.config("using login name from client container xml...");
                    //System.setProperty(LOGIN_NAME, cCrd.getUserName());
                    uname = cCrd.getUserName();
                }
                if (System.getProperty(LOGIN_PASSWORD) == null) {
                    _logger.config("using password from client container xml...");
                    //System.setProperty(LOGIN_PASSWORD, cCrd.getPassword());
                    upass = cCrd.getPassword();
                }
                if( uname != null || upass != null ) {
                    UsernamePasswordStore.set(uname, upass);
                }
            }
		String endpoints_property = null;
	    // Check if client requires SSL to be used
	    ElementProperty[] props = cc.getElementProperty();
	    for ( int i=0; i<props.length; i++ ) {
		if ( props[i].getName().equals("ssl") ) {
		    if ( props[i].getValue().equals("required") ) {
			(ORBManager.getCSIv2Props()).put(ORBManager.ORB_SSL_CLIENT_REQUIRED,
				       "true");
		    }
		}
		if ( props[i].getName().equals(S1ASCtxFactory.LOAD_BALANCING_PROPERTY) ) {
                    loadBalancingPolicy = props[i].getValue();
		    lb_enabled = true;
		}	
		if ( props[i].getName().equals(S1ASCtxFactory.IIOP_ENDPOINTS_PROPERTY) ) {
                    isEndpointPropertySpecifiedByUser = true;
		    endpoints_property = props[i].getValue().trim();
		    lb_enabled = true;
		}
	    }    
            
            /*
             *If the endpoints property was not set in the XML file's property
             *settings, try to set it from the server's assignment in the JNLP document.
             */
	    String jwsEndpointsProperty = System.getProperty(Main.APPCLIENT_IIOP_FAILOVER_ENDPOINTS_PROPERTYNAME);
	    if (jwsEndpointsProperty != null) {
	        targetServerEndpoints = jwsEndpointsProperty;
                completeEndpointList.append(jwsEndpointsProperty);
		lb_enabled = true;
		_logger.fine("jwsEndpointsProperty = " + jwsEndpointsProperty);
	    } else {
	        /*
		 *Suppress the warning if the endpoints_property was set
		 *from the JNLP document, since that is in fact the preferred
		 *way to set the endpoints.
		 */
	        if (isEndpointPropertySpecifiedByUser){
                    _logger.warning("acc.targetserver.endpoints.warning");
                }
            
		_logger.fine("targetServerEndpoints = " + targetServerEndpoints + 
			     "endpoints_property = " + 
			     endpoints_property);	
		
                if (lb_enabled) {
                    completeEndpointList.append(targetServerEndpoints.trim());
                    if (endpoints_property != null) {
                        completeEndpointList.append(",").append(endpoints_property);
                    }
		}
	    }
            if (lb_enabled) {
                System.setProperty(S1ASCtxFactory.IIOP_ENDPOINTS_PROPERTY, completeEndpointList.toString());
                /*
                 * Honor any explicit setting of the load-balancing policy.
                 * Otherwise just defer to whatever default the ORB uses.
                 */
                if (loadBalancingPolicy != null) {
                    System.setProperty(S1ASCtxFactory.LOAD_BALANCING_PROPERTY, loadBalancingPolicy);
                }
                /*
                 * For load-balancing the Properties object is not used.  Rather,
                 * the ORB detects the system property settings.  So return a
                 * null for the LB case.
                 */
                result = null;
            } else {
                /*
                 * For the non-load-balancing case, the Properties object must
                 * contain the initial host and port settings for the ORB.
                 */
                result.setProperty(ORB_INITIAL_HOST_PROPERTYNAME, host);
                result.setProperty(ORB_INITIAL_PORT_PROPERTYNAME, port);
            }
            return result;
	} catch (ConfigException t) {
	    _logger.log(Level.WARNING,"acc.acc_xml_file_error" ,
			new Object[] {clientXmlLocation, t.getMessage()}); 
	    _logger.log(Level.FINE, "exception : " + t.toString(), t);
	    throw t;
	}
    }    

    private static void setSSLData(ClientContainer cc) {
        try {
            // Set the SSL related properties for ORB
            TargetServer tServer = cc.getTargetServer(0);
            // TargetServer is required.
	    //temp solution to target-server+ change in DTD
            // assuming that multiple servers can be specified but only 1st
	    // first one will be used.
	    Security security = tServer.getSecurity();
	    if (security == null) {
		_logger.fine("No Security input set in ClientContainer.xml");
		// do nothing
		return;
	    }
	    Ssl ssl = security.getSsl();
	    if (ssl == null) {
		_logger.fine("No SSL input set in ClientContainer.xml");
		// do nothing
		return;
		
	    }
	    //XXX do not use NSS in this release
	    //CertDb   certDB  = security.getCertDb();
	    SSLUtils.setAppclientSsl(ssl);	
	} catch (Exception ex) {

        }
    }

    
    private void validateXMLFile(String xmlFullName) throws UserError 
    {
        if(xmlFullName == null || 
           xmlFullName.startsWith("-")){ // If no file name is given after -xml argument
            usage();
        }
        try {
            File f = new File(xmlFullName);
            if((f != null) && f.exists() && f.isFile() && f.canRead()){
                return;
            }else{// If given file does not exists
                xmlMessage(xmlFullName);
            }
        } catch (Exception ex) {
            xmlMessage(xmlFullName);
        }
    }

    // Shut down system resource adapters. Currently it is
    // only JMS.
    private void shutDownSystemAdapters() {
       try {
	    com.sun.enterprise.PoolManager poolmgr = 
	        Switch.getSwitch().getPoolManager();
	    if ( poolmgr != null ) {	
	        Switch.getSwitch().getPoolManager().killFreeConnectionsInPools();
	    }	
	} catch( Exception e ) {
	    //ignore
	}
        
	try {
            ConnectorRegistry registry = ConnectorRegistry.getInstance();
            ActiveResourceAdapter activeRar = registry.getActiveResourceAdapter
                                         (ConnectorRuntime.DEFAULT_JMS_ADAPTER);
            if (activeRar != null) {
                activeRar.destroy();
            }
        } catch (Exception e) {
            // Some thing has gone wrong. No problem
            _logger.fine("Exception caught while shutting down system adapter:"+e.getMessage());
        }
    }

    private String getUsage() {
        return localStrings.getString(
            "main.usage",
            "appclient [ -client <appjar> | <classfile> ] [-mainclass <appClass-name>|-name <display-name>] [-xml <xml>] [-textauth] [-user <username>] [-password <password>|-passwordfile <password-file>] [app-args]");
    }
    
    private void usage() {
        System.out.println(getUsage());
	System.exit(1);
    }
    
    private void xmlMessage(String xmlFullName) throws UserError
    {
        UserError ue = new UserError(localStrings.getString("main.cannot_read_clientContainer_xml", xmlFullName,
             "Client Container xml: " + xmlFullName + " not found or unable to read.\nYou may want to use the -xml option to locate your configuration xml."));
        ue.setUsage(getUsage());
        throw ue;
       
    }

    private String loadPasswordFromFile(String fileName)
            throws IOException {
        InputStream inputStream = null;
        try {
            inputStream = new BufferedInputStream(new FileInputStream(fileName));
            Properties props = new Properties();
            props.load(inputStream);
            return props.getProperty("PASSWORD");
        } finally {
            if (inputStream != null) {
                inputStream.close();
            }
        }
    }

    private static class Cleanup extends Thread {
        private AppContainer appContainer = null;
        private AppClientInfo appClientInfo = null;
        private boolean cleanedUp = false;
        private InjectionManager injectionMgr = null;
        private ApplicationClientDescriptor appClient = null;
        private Class cls = null;

        public Cleanup() {
        }

        public void setAppContainer(AppContainer container) {
            appContainer = container;
        }

        public void setAppClientInfo(AppClientInfo info) {
            appClientInfo = info;
        }

        public void setInjectionManager(InjectionManager injMgr, Class cls, ApplicationClientDescriptor appDesc) {
            injectionMgr = injMgr;
            this.cls = cls;
            appClient = appDesc;
        }
        
        public void run() {
            _logger.info("Clean-up starting");
            cleanUp();
        }

        public void cleanUp() {
            if( !cleanedUp ) {
                try {
                    if( appContainer != null ) {
                        appContainer.postInvoke();
                    }
                    if ( appClientInfo != null ) {
                        appClientInfo.close();
                    }
                    if ( injectionMgr != null) {
                        // inject the pre-destroy methods before shutting down
                        injectionMgr.invokeClassPreDestroy(cls, appClient);
                        injectionMgr = null;
                    }
                    if(appClient != null && appClient.getServiceReferenceDescriptors() != null) {
                        // Cleanup client pipe line, if there were service references
                        for (Object desc: appClient.getServiceReferenceDescriptors()) {
                             ClientPipeCloser.getInstance()
                                .cleanupClientPipe((ServiceReferenceDescriptor)desc);  
                        }                                         
                    }
                }
                catch(Throwable t) {
                }
                finally {
                    cleanedUp = true;
                }
            } // End if -- cleanup required
        }
    }
    /**
     *Sets up the user-provided or default sun-acc.xml and 
     *wss-client-config.xml configurations.
     *@return the file name of the sun-acc.xml file
     */
    private String prepareJWSConfig() throws IOException, FileNotFoundException {
        return prepareJWSDefaultConfig();
    }
    
    /**
     *Creates temporary files for use as default sun-acc.xml and 
     *wss-client-config.xml configurations.
     *@return the file name of the temporary sun-acc.xml file
     */
    private String prepareJWSDefaultConfig() throws IOException, FileNotFoundException {
        String result = null;
        
        /*
         *Retrieve the sun-acc and wss-client-config templates.
         */
        String sunACCTemplate = Util.loadResource(this.getClass(), SUN_ACC_DEFAULT_TEMPLATE);
        String wssClientConfigTemplate = Util.loadResource(this.getClass(), WSS_CLIENT_CONFIG_TEMPLATE);
        
        /*
         *Prepare the property names and values for substitution in the templates.  Some
         *of the properties are specified in the environment already, so use those
         *as defaults and just add the extra ones.
         */
        Properties tokenValues = new Properties(System.getProperties());
        
        /**
         *Create the wss client config defaults, then write them to a temporary file.
         */
        String wssClientConfig = Util.replaceTokens(wssClientConfigTemplate, tokenValues);
        File wssClientConfigFile = Util.writeTextToTempFile(wssClientConfig, WSS_CLIENT_CONFIG_PREFIX, WSS_CLIENT_CONFIG_SUFFIX, retainTempFiles);
        _logger.fine("Temporary wss-client-config.xml file: " + wssClientConfigFile.getAbsolutePath() + lineSep);
//        pendingLogFine.append("Temporary wss-client-config.xml file: " + wssClientConfigFile.getAbsolutePath() + lineSep);
        
        /*
         *Now that the wss temp file is created, insert its name into the default
         *sun-acc text and write that to another temp file.
         *
         *On Windows, the backslashes in the path will be consumed by the replaceTokens method which will
         *interpret them as quoting the following character.  So replace each \ with \\ first.  All the slashes
         *have to do with quoting a slash to the Java compiler, then quoting it again to the regex
         *processor.
         */
        String quotedConfigFileSpec = wssClientConfigFile.getAbsolutePath().replaceAll("\\\\", "\\\\\\\\");
        tokenValues.setProperty(SUN_ACC_SECURITY_CONFIG_PROPERTY, quotedConfigFileSpec);
        
        String sunaccContent = Util.replaceTokens(sunACCTemplate, tokenValues);
        File sunaccFile = Util.writeTextToTempFile(sunaccContent, SUN_ACC_PREFIX, SUN_ACC_SUFFIX, retainTempFiles);
        _logger.fine("Temporary sun-acc.xml file: " + sunaccFile.getAbsolutePath());
//        pendingLogFine.append("Temporary sun-acc.xml file: " + sunaccFile.getAbsolutePath());
        
        return sunaccFile.getAbsolutePath();
    }

    /**
     *Prepares the JAAS login configuration for a Java Web Start invocation.
     *
     */
    private void prepareJWSLoginConfig() {
        /*
         *If this is a Java Web Start invocation, prepare the user-specified
         *or default login configuration.
         */
        if (isJWS) {
            try {
                prepareJWSDefaultLoginConfig();
            } catch (Throwable thr) {
                throw new RuntimeException(localStrings.getString("appclient.errorPrepJWSLogginConfig"), thr);
            }
        }        
    }
    
    /**
     *Extracts the default login.conf file into a temporary file and assigns the
     *java.security.auth.login.config property accordingly.
     */
    private void prepareJWSDefaultLoginConfig() throws IOException, FileNotFoundException {
        /*
         *For a Java Web Start launch, the default login configuration is in a
         *template bundled in the app server's jws jar file.  Putting it there
         *allows this method to locate it simply by loading it as a resource, whereas
         *the command-line appclient invocation needs to be able to find the
         *file on-disk somewhere.
         *
         *The contents of the template are loaded and then written to a temp file and
         *that temp file location is assigned to the system property that directs
         *JAAS to the login configuration.
         */
        String configContent = Util.loadResource(this.getClass(), LOGIN_CONF_TEMPLATE);
        File configFile = Util.writeTextToTempFile(configContent, LOGIN_CONF_FILE_PREFIX, LOGIN_CONF_FILE_SUFFIX, retainTempFiles);
        String configFilePath = configFile.getAbsolutePath();
        _logger.fine("Temporary appclientlogin.conf file: " + configFilePath);
        System.setProperty(LOGIN_CONF_PROPERTY_NAME, configFilePath);
    }
    
    /*
     *Returns the jar or directory that contains the specified resource.
     *@param target entry name to look for
     *@return URI object for the jar or directory containing the entry
     */
    private File findContainingJar(String target) throws IllegalArgumentException, URISyntaxException {
        File result = null;
        
        /*
         *Use the current class loader to find the resource.
         */
        URL resourceURL = getClass().getResource(target);
        if (resourceURL != null) {
            URI uri = resourceURL.toURI();
            String scheme = uri.getScheme();
            String ssp = uri.getSchemeSpecificPart();
            if (scheme.equals("jar")) {
                /*
                 *The scheme-specific part will look like "file:<file-spec>!/<path-to-class>.class"
                 *so we need to isolate the scheme and the <file-spec> part.  
                 *The subscheme (the scheme within the jar) precedes the colon
                 *and the file spec appears after it and before the exclamation point.
                 */
                int colon = ssp.indexOf(':');
                String subscheme = ssp.substring(0, colon);
                int excl = ssp.indexOf('!');
                String containingJarPath = ssp.substring(colon + 1, excl);
                result = new File(containingJarPath);
            } else if (scheme.equals("file")) {
                /*
                 *The URI is already a file, so the part we want is the part
                 *up to but not including the resource name we were looking for
                 in the first place..
                 */
                int resourceNamePosition = ssp.indexOf(target);
                String containingFilePath = ssp.substring(0, resourceNamePosition);
                result = new File(containingFilePath);
            } else {
                throw new IllegalArgumentException(resourceURL.toExternalForm());
            }
        }
        return result;
    }
    
    /**
     *Locate the app client jar file during a Java Web Start launch.
     *@return File object for the client jar file
     */
    private File findAppClientFileForJWSLaunch() throws URISyntaxException {
        /*
         *The JWSACCMain class has already located the downloaded app client
         *jar file and set a property pointing to it.  
         */
       File containingJar = new File(System.getProperty("com.sun.aas.downloaded.appclient.jar"));
        _logger.fine("Location of appclient jar file: " + containingJar.getAbsolutePath());
        return containingJar;
    }
    
    /**
     *Creates a memory-based logger for holding log messages that may occur before
     *the user's logging set-up is read.
     *@return a Logger that buffers its log records in memory.
     */
    private BufferedLogger prepareBufferedLogging() {
        /*
         *The buffered logger adds a handler automatically during instantiation.
         */
        BufferedLogger logger = new BufferedLogger();
        return logger;
    }

    /**
     *Logger implementation that records its log records in memory.  Normally
     *the logger will be flushed to another logger once the second logger has
     *been configured appropriately.
     */
    private class BufferedLogger extends Logger {
       
        /**
         *Creates a new instance of the buffered logger.
         */
        public BufferedLogger() {
            super(null, null);
            addHandler(new BufferedHandler());
        }
        
        /**
         *Flushes any accumulated log messages to the specified target logger.
         *@param target the Logger to receive any buffered messages
         */
        public void pushTo(Logger target) {
            for (Handler handler : getHandlers()) {
                if (handler instanceof BufferedHandler) {
                    ((BufferedHandler) handler).pushTo(target);
                }
            }
        }
    }
    
    /**
     *Log handler that accumulates each log record sent to it into memory and
     *flushes those records to another logger when asked.  Once this handler is
     *flushed it can no longer be used.
     */
    private class BufferedHandler extends Handler {
       
        /** holds log records until flushed to another logger */
        private Vector<LogRecord> buffer = new Vector<LogRecord>();
        
        /**
         *Creates a new instance of the buffered log handler.
         */
        public BufferedHandler() {
            setLevel(Level.ALL);
            setFilter(null);
            setFormatter(new SimpleFormatter());
        }
        
        /**
         *Publishes a log record to the handler.
         *<p>
         *In this handler, the log record is saved in the memory buffer.
         *@paran record the LogRecord to be written
         */
        public synchronized void publish(LogRecord record) {
            if (buffer == null) {
                throw new IllegalStateException("Handler asked to publish log record after pushTo invoked");
            }
            buffer.add(record);
        }
        
        /**
         *Flushes any accumulated log information to the specified target Logger.
         *@param target the Logger to receive buffered messages
         */
        public synchronized void pushTo(Logger target) {
            if (buffer == null) {
                throw new IllegalStateException("Handler asked to push to target more than once");
            }
            for (LogRecord record : buffer) {
                target.log(record);
            }
            close();
        }

        /**
         *Closes the handler to further use.
         */
        public void close() throws SecurityException {
            buffer.clear();
            buffer = null;
        }

        /**
         *Does nothing because there is no intermediate buffer between the
         *handler and its storage - the storage is itself a memory buffer.
         */
        public void flush() {
        }
    }

}
