Modified: 
river/jtsk/skunk/qa_refactor/trunk/qa/src/com/sun/jini/qa/harness/QAConfig.java
URL: 
http://svn.apache.org/viewvc/river/jtsk/skunk/qa_refactor/trunk/qa/src/com/sun/jini/qa/harness/QAConfig.java?rev=1634322&r1=1634321&r2=1634322&view=diff
==============================================================================
--- 
river/jtsk/skunk/qa_refactor/trunk/qa/src/com/sun/jini/qa/harness/QAConfig.java 
(original)
+++ 
river/jtsk/skunk/qa_refactor/trunk/qa/src/com/sun/jini/qa/harness/QAConfig.java 
Sun Oct 26 13:17:28 2014
@@ -1,3003 +1,3006 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- * 
- *      http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.sun.jini.qa.harness;
-
-import com.sun.jini.start.ClassLoaderUtil;
-import com.sun.jini.system.CommandLine.BadInvocationException;
-import com.sun.jini.system.MultiCommandLine;
-
-import java.io.BufferedInputStream;
-import java.io.BufferedReader;
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
-import java.io.FileWriter;
-import java.io.InputStream;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.Serializable;
-
-import java.net.InetAddress;
-import java.net.MalformedURLException;
-import java.net.UnknownHostException;
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.rmi.activation.ActivationGroup;
-import java.rmi.activation.ActivationException;
-import java.rmi.RemoteException;
-
-import java.security.AccessController;
-import java.security.PrivilegedAction;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.logging.Logger;
-import java.util.logging.Level;
-import java.util.MissingResourceException;
-import java.util.Properties;
-import java.util.Random;
-import java.util.ResourceBundle;
-import java.util.Set;
-import java.util.StringTokenizer;
-import java.util.jar.JarFile;
-import java.util.zip.ZipEntry;
-
-import net.jini.core.constraint.MethodConstraints;
-import net.jini.core.discovery.LookupLocator;
-import net.jini.config.Configuration;
-import net.jini.config.ConfigurationException;
-import net.jini.config.ConfigurationFile;
-import net.jini.discovery.ConstrainableLookupLocator;
-import net.jini.export.Exporter;
-import net.jini.jeri.BasicJeriExporter;
-import net.jini.jeri.BasicILFactory;
-import net.jini.jeri.tcp.TcpServerEndpoint;
-import net.jini.security.ProxyPreparer;
-import net.jini.url.httpmd.HttpmdUtil;
-import org.apache.river.api.net.RFC3986URLClassLoader;
-
-/**
- * This class represents the environment for tests running in
- * the QA test harness. 
- * <p>
- * This class implements the search policy for obtaining test
- * parameter values. The sources of test parameters, in order 
- * of precedence, are:
- * <ul>
- * <li>the overrides, explicitly set by the <code>setOverrides</code> method
- * <li>the dynamic properties
- * <li>the system properties
- * <li>the command line arguments
- * <li>the test Configuration
- * <li>the test description
- * <li>the Configuration set property file
- * <li>the user provided property file
- * <li>the default property file 
- * <li>the default specified by the test
- * </ul>
- * This set of data sources is collectively referred to as the
- * <code>test properties</code>. The overrides exist to 
- * allow the harness to temporarily redefine the values for installation
- * properties such as <code>com.sun.jini.jsk.home</code> or
- * <code>com.sun.jini.qa.home</code>. This is needed when generating the
- * command line for the SlaveTest VM for local parameters that
- * must take precedence over the serialized <code>QAConfig</code>
- * instance provided by the master.
- * <p>
- * The dynamic properties exist to allow a test to create or override
- * a test property on-the-fly. This would most likely be used if
- * a test needed to construct a parameter needed by an admin, for
- * instance to provide a generated codebase. Dynamic properties are
- * automatically reset for every test, and are rarely used. The
- * other sources are treated as immutable objects.
- * <p>
- * The test description file generally provides the parameters used
- * by the harness to locate and execute a test, as well as test 
- * specific property values.
- * <p>
- * The net.jini.config.Configuration object associated with this test
- * configuration. This object may contain values which have relavance only to
- * particular configurations in a test run (for instance, the value of
- * <code>com.sun.jini.qa.harness.integrityhash</code> is only meaningful for
- * secure configuations). The <code>ConfigurationFile</code> from which the
- * <code>Configuration</code> is derived is named by the
- * <code>testConfiguration</code> property in the test description.
- * <p>
- * The configuration set property file contains properties which should
- * be applied to all tests executing in a given configuration. This
- * property file is named <code>configSet.properties</code> and resides
- * in the root directory of the configuration set.
- * <p>
- * The command-line property file is the primary place for those who run
- * tests to provide installation values, or to override the defaults provided
- * by qaDefaults.
- * <p>
- * The default configuration file is named 
- * <code>com.sun.jini.qa.resources.qaDefaults</code>.
- * <p>
- * Before any parameter value is returned, '$' substitutions
- * are performed (recursively). Any occurances of the
- * token <code>&lt;gethost&gt;</code> are replaced with the name of
- * the host on which this code is running. Any occurances of the
- * token <code>&lt;harnessJar&gt;</code> are replaced with the fully
- * qualified path of the harness JAR file. Any occurances of the
- * token <code>&lt;testJar&gt;</code> are replaced with the fully
- * qualified path of the test JAR file. 
- * <p>
- * Any tokens of the form &lt;url:foo&gt; will result in the generation
- * of a URL for <code>foo</code>. A search is performed for the file relative 
to
- * all of the root directories defined by the <code>searchPath</code>
- * property. If the file is found, a file URL for the fully qualified
- * name of the file is generated and returned as the token value. Otherwise,
- * test and harness JAR files are searched for <code>foo</code>, looking
- * first relative to the directory in which the test description file is 
- * located, then relative to the root of the test jar file and finally
- * relative to the root of the harness jar file. In this case, an appropriate
- * JAR file URL is generated and returned as the token value. If the
- * file is not found, a <code>TestException</code> is thrown.
- * <p>
- * Any tokens of the form &lt;file:foo&gt; will result in the generation of a
- * fully qualified path for <code>foo</code>. A search is performed for the 
file
- * relative to all of the root directories defined by the
- * <code>searchPath</code> property. If the file is found, the fully qualified
- * name of the file is generated and returned as the token value. If the file 
is
- * not found, a <code>TestException</code> is thrown.
- * <p>
- * This class is serializable. It's constructor is called only in
- * the master/slave harness VMs. The instance is serialized and
- * passed from the master harness to the master test through its System.in
- * stream. The master harness also passes a serialized copy to the
- * slave harness in a SlaveTestRequest object, which in
- * turn passes the copy to the slave test VM though its System.in stream.
- * <p>
- * The logger named <code>com.sun.jini.qa.harness.config</code> is used
- * to log debug information
- *
- * <table border=1 cellpadding=5>
- *
- *<tr> <th> Level <th> Description
- *
- *<tr><td>SEVERE <td>if a parsing error when doing string substitution, on
- *                   failure to find the component-level resource file, or
- *                   on failure to start a shared group
- *
- *<tr> <td> FINE <td>any calls to <code>logDebugText</code>
- *</table> <p>
- */
-public class QAConfig implements Serializable {
-
-    /** static ref for access by utils which don't accept a config arg */
-    private static QAConfig harnessConfig;
-
-    /*
-     * This field is static for historical reasons. testLocatorConstraints is
-     * lazily instantiated in response to the first call to 
-     * <code>getConstrainedLocator</code> in this VM. Lazy instantiation
-     * eliminates a premature instantiation problem in the master/slave
-     * harnesses which do not have the installation properties
-     * defined which are referenced by the configuration.
-     */
-    private static MethodConstraints testLocatorConstraints;
-
-    /** A unique string which is used to generate unique group names. */
-    private String uniqueString;
-
-    /** the name of the configurator class */
-    private String configuratorClassName = null;
-
-    /** the configuration override providers */
-    private ArrayList overrideProviders = new ArrayList();
-
-    /** the failure analyzers */
-    private ArrayList failureAnalyzers = new ArrayList();
-
-    /** The number of configurations to cycle through */
-    private int configurationCount = -1;
-
-    /** Properties obtained from the user configuration file. */
-    private Properties configProps;
-
-    /** properties obtained from the configuration group file */
-    private Properties configSetProps = new Properties();
-
-    /** Properties obtained from qaDefaults.properties. */
-    private Properties defaultProps = new Properties();
-
-    /** The test <code>Configuration</code> object, not serializable*/
-    private transient Configuration configuration;
-
-    /** The TestDescription */
-    private TestDescription td;
-
-    /** Dynamic properties set on-the-fly by tests. */
-    private Properties dynamicProps = new Properties();
-
-    /** Override properties, for cmd lines with non-default install props */
-    private Properties propertyOverrides = null;
-
-    /** The original command line arguments. */
-    private String[] args;
-
-    /** The configuration group tags for the test run */
-    private String[] configTags;
-
-    /** The currently active configuration group tag */
-    private String currentTag;
-
-    /** The key name to track */
-    private String trackKey;
-
-    /** The set of hosts participating in this test run */
-    private ArrayList hostList = new ArrayList();
-
-    /** The host index memory for the uniformrandom selection policy */
-    private ArrayList selectedIndexes;
-
-    /** Flag controlling use of IP address or host names during resolution */
-    private boolean useAddress = false;
-
-    /** The resolver */
-    private Resolver resolver;
-
-    /** the jar file containing the tests */
-    private String testJar = null;
-
-    /** the harness jar file name */
-    private String harnessJar = null;
-
-    /** the test class loader */
-    private transient ClassLoader testLoader;
-
-    /** the search list, usually empty */
-    private String[] searchList = new String[0];
-
-    /** lock object for suspend/resume run */
-    private transient Object runLock = new Object();
-
-    /** suspended state (for UI) */
-    private boolean testSuspended = false;
-
-    /** 
-     * The host selection index for the selection policy.
-     * It's intiialized to 1 in case the roundrobin policy
-     * is being used, which begins with the first slave.
-     */
-    private int hostIndex = 1;
-
-    /** The rerun pass counter */
-    private int passCount;
-
-    private boolean callAutot = false;
-    
-    private int testTotal = 0;
-
-    private int testIndex = 0;
-
-    private String resumeMessage;
-
-    /**
-     * Static accessor for the config instance.
-     *
-     * @return the config object
-     * @throws IllegalStateException if the config hasn't been built yet
-     */
-    public static QAConfig getConfig() {
-        if (harnessConfig == null) {
-            throw new IllegalStateException("harnessConfig not yet defined");
-        }
-        return harnessConfig;
-    }
-
-    /**
-     * Return the path to the QA home (installation) directory
-     *
-     * @return the path to the QA kit
-     */
-    public String getKitHomeDir() {
-       return getStringConfigVal("com.sun.jini.qa.home", null);
-    }
-
-    /**
-     * Return the path to the JSK home (installation) directory
-     *
-     * @return the path to the JSK
-     */
-    public String getJSKHomeDir() {
-       return getStringConfigVal("com.sun.jini.jsk.home", null);
-    }
-
-    /**
-     * The logger, with an associated static initializer that creates a
-     * com.sun.jini.qa.harness level logger with a specialized handler which
-     * logs to <code>System.out</code> and formats more concisely than
-     * <code>SimpleFormatter</code>.
-     */
-    protected static Logger logger;
-    private static ReportHandler reportHandler;
-    static {
-        logger = Logger.getLogger("com.sun.jini.qa.harness");
-       reportHandler = new ReportHandler();
-        logger.addHandler(reportHandler);
-        logger.setUseParentHandlers(false);
-    }
-
-    /**
-     * Constructs a new instance of this class, and loads the configuration
-     * files which are global to every test. Specifically:
-     * <ul>
-     * <li>the command-line configuration file is loaded
-     * <li>the default configuration file is loaded
-     * <li>mandatory user supplied parameters are validated
-     * <li>the host list is built if running distributed
-     * </ul>
-     * The command-line configuration file must define the
-     * following properties:
-     * <ul>
-     * <li>com.sun.jini.jsk.home
-     * <li>com.sun.jini.qa.home
-     * </ul>
-     * and the values of these parameters must resolve to directories
-     * which exist.
-     * <p>
-     * When the master/slave harnesses exec VMs, the values of these
-     * parameters are supplied on the command line as system properties.
-     * This allows the harnesses to override the default values. The
-     * values of these properties must be provided as system properties
-     * so they can be accessed from policy/configuration files. Since
-     * the harness VMs do not run with these properties set, the
-     * master/slave harnesses are expected to run with policy.all, and
-     * cannot access configuration objects which have dependencies on
-     * these system properties.
-     * <p>
-     * Note that the manager field is not initialized in the constructor.
-     * The manager is not used by the master harness. The field
-     * is initialized in the <code>readObject</code> method when this class is
-     * deserialized in the slave harness and  master/slave test VMs.
-     *
-     * @param args  the command line arguments passed to the harness
-     * 
-     * @throws TestException if the configuration file identified
-     *                       by <code>args[0]</code> is empty or missing or
-     *                       if com.sun.jini.jsk.home is undefined or if
-     *                       the directory it names does not exist or
-     *                       if com.sun.jini.qa.home is undefined or if
-     *                       the directory it names does not exist
-     *
-     */
-    public QAConfig(String[] args) throws TestException {
-       this.args = args;
-       resolver = new Resolver(this);
-       configProps = loadProperties(args[0]);
-       if (configProps.size() == 0) {
-           throw new TestException("Config file" 
-                                 + " " + args[0] + " "
-                                 + "is empty or missing");
-       }
-       String jskDir = getStringConfigVal("com.sun.jini.jsk.home", null);
-       if (jskDir == null) {
-           throw new TestException("com.sun.jini.jsk.home is undefined");
-       }
-       File jskFile = new File(jskDir);
-       if (!jskFile.exists()) {
-           throw new TestException("The directory "
-                                  + jskFile.toString() + " identified by "
-                                  + "com.sun.jini.jsk.home does not exist");
-       }
-       String qaDir = getStringConfigVal("com.sun.jini.qa.home", null);
-       if (qaDir == null) {
-           throw new TestException("com.sun.jini.qa.home is undefined");
-       }
-       File qaFile = new File(qaDir);
-       if (!qaFile.exists()) {
-           throw new TestException("The directory "
-                                   + qaFile.toString() + " identified by "
-                                  + "com.sun.jini.qa.home does not exist");
-       }
-       harnessJar = getHarnessJar();
-       if (harnessJar != null) {
-           resolver.setToken("harnessJar", harnessJar);
-       }
-       testJar = getStringConfigVal("testJar", null);
-       if (testJar != null) {
-           resolver.setToken("testJar", testJar);
-       }
-       buildSearchList(getStringConfigVal("searchPath", ""));
-       /*
-        * The default file is loaded after the command-line
-        * file is loaded and installation properties validated
-        * since it's location is specified relative to
-        * the the harness root url.
-        */
-       defaultProps = 
-           loadProperties("com/sun/jini/qa/resources/qaDefaults.properties");
-       trackKey = getParameterString("track");
-       harnessConfig = this;
-       setHostNameToken();
-       // set the test class loader
-       if (testJar == null) {
-           logger.log(Level.INFO, "Warning: testjar is not defined");
-           testLoader = getClass().getClassLoader();
-       } else {
-           File testJarFile = new File(testJar);
-           if (! testJarFile.exists()) {
-               throw new TestException("test jar found not found: " + testJar);
-           }
-           try {
-               URL testJarURL = testJarFile.getCanonicalFile().toURI().toURL();
-               testLoader = new RFC3986URLClassLoader(new URL[]{testJarURL},
-                                               getClass().getClassLoader());
-           } catch (Exception e) {
-               throw new TestException("Failed to create test loader", e);
-           }
-       }
-       String hostNames = 
-           getStringConfigVal("com.sun.jini.qa.harness.testhosts",
-                              null);
-       if (hostNames != null) {
-           StringTokenizer tok = new StringTokenizer(hostNames, "|");
-           if (tok.countTokens() > 1) {
-               while (tok.hasMoreTokens()) {
-                   hostList.add(tok.nextToken());
-               }
-           }
-       }
-    }
-
-    /**
-     * Set the token &lt;gethost&gt; to have the value of the local host
-     * name. If the property <code>com.sun.jini.qa.harness.useAddress</code> is
-     * defined and has the value <code>true</code>, the token is set to the
-     * local host IP address rather than its name. The slave harness must
-     * have access to this method to fix up the local slave reference
-     * in the serialized config supplied by the master.
-     */
-    void setHostNameToken() {
-       boolean useAddress = 
-           getBooleanConfigVal("com.sun.jini.qa.harness.useAddress", false);
-       String hostName = (useAddress ? "127.0.0.1" : "localhost");
-       try {
-           InetAddress address = InetAddress.getLocalHost();
-           hostName = (useAddress ? address.getHostAddress() 
-                                  : address.getHostName());
-       } catch (UnknownHostException ex) { 
-           ex.printStackTrace();
-       }
-       resolver.setToken("gethost", hostName);
-    }
-
-    /**   
-     * Build the search path used to resolve the &lt;url:&gt; and &lt;file:&gt;
-     * tokens. The slave harness must have access to this method to fix up the
-     * local slave search path in the serialized config supplied by the master.
-     * 
-     * @param searchPath a string containing a comma separated list of
-     *                   paths
-     */
-    void buildSearchList(String searchPath) {
-       ArrayList list = new ArrayList();
-       StringTokenizer tok = new StringTokenizer(searchPath, ",");
-       while (tok.hasMoreTokens()) {
-           String path = tok.nextToken();
-           if (path.trim().length() == 0 || list.contains(path)) {
-               continue;
-           }
-           list.add(path);
-       }
-       String qaDir = getStringConfigVal("com.sun.jini.qa.home", null);
-       if (!list.contains(qaDir)) {
-           list.add(qaDir);
-        }
-       searchList = (String[]) list.toArray(new String[list.size()]);
-    }
-
-    /**
-     * Perform custom deserialization actions. Set static and transient
-     * fields. <code>OverrideProviders</code> referenced by
-     * <code>loadTestConfiguration</code> are assumed to have been
-     * installed before serialization was performed.
-     */
-    private void readObject(ObjectInputStream stream)
-       throws IOException, ClassNotFoundException
-    {
-       stream.defaultReadObject();
-       harnessConfig = this;
-       runLock = new Object();
-    }
-
-    /**
-     * This method will retrieve from the locally stored property list,
-     * the value of a property whose name corresponds to the string
-     * contained in the input argument "propertyName" and whose value
-     * is of type 'int'.
-     * <p>
-     * If the property list does not exist, or if the string value of the
-     * property can not be "cast" to the expected data type, then the
-     * default value will be returned.
-     */
-    public int getIntConfigVal(String propertyName, int defaultVal) {
-        int val = defaultVal;
-        String strVal = getParameterString(propertyName);
-       if (strVal != null) {
-           try {
-               val = Integer.parseInt(strVal);
-           } catch (NumberFormatException ignore) { }
-       }
-        return val;
-    }
-
-    /**
-     * This method will retrieve from the locally stored property list,
-     * the value of a property whose name corresponds to the string
-     * contained in the input argument "propertyName" and whose value
-     * is of type 'long'.
-     * <p>
-     * If the property list does not exist, or if the string value of the
-     * property can not be "cast" to the expected data type, then the
-     * default value will be returned.
-     */
-    public long getLongConfigVal(String propertyName, long defaultVal) {
-        long val = defaultVal;
-        String strVal = getParameterString(propertyName);
-       if (strVal != null) {
-           try {
-               val = Long.parseLong(strVal);
-           } catch (NumberFormatException ignore) { }
-       }
-        return val;
-    }
-
-    /**
-     * This method will retrieve from the locally stored property list,
-     * the value of a property whose name corresponds to the string
-     * contained in the input argument "propertyName" and whose value
-     * is of type 'float'.
-     * <p>
-     * If the property list does not exist, or if the string value of the
-     * property can not be "cast" to the expected data type, then the
-     * default value will be returned.
-     */
-    public float getFloatConfigVal(String propertyName, float defaultVal) {
-        float val = defaultVal;
-        String strVal = getParameterString(propertyName);
-       if (strVal != null) {
-           try {
-               float tmpval = (Float.valueOf(strVal)).floatValue();
-               if (!(Float.isInfinite(tmpval))&&!(Float.isNaN(tmpval)) ) {
-                   val = tmpval;
-               }
-           } catch (NumberFormatException ignore) { }
-       }
-        return val;
-    }
-
-    /**
-     * This method will retrieve from the locally stored property list,
-     * the value of a property whose name corresponds to the string
-     * contained in the input argument "propertyName" and whose value
-     * is of type 'double'.
-     * <p>
-     * If the property list does not exist, or if the string value of the
-     * property can not be "cast" to the expected data type, then the
-     * default value will be returned.
-     */
-    public double getDoubleConfigVal(String propertyName, double  defaultVal) {
-        double val = defaultVal;
-        String strVal = getParameterString(propertyName);
-       if (strVal != null) {
-           try {
-               double tmpval = (Double.valueOf(strVal)).doubleValue();
-               if (!(Double.isInfinite(tmpval))&&!(Double.isNaN(tmpval))){
-                   val = tmpval;
-               }
-           } catch (NumberFormatException ignore) { }
-       }
-        return val;
-    }
-
-    /**
-     * This method will retrieve from the locally stored property list,
-     * the value of a property whose name corresponds to the string
-     * contained in the input argument "propertyName" and whose value
-     * is of type 'String'.
-     * <p>
-     * If the property list does not exist, or if the string value of the
-     * property can not be "cast" to the expected data type, then the
-     * default value will be returned.
-     */
-    public String getStringConfigVal(String propertyName,
-                                     String defaultVal) {
-        String strVal = getParameterString(propertyName);
-       return (strVal != null ? strVal : defaultVal);
-    }
-
-    public String getString(String propertyName) {
-        String strVal = getParameterString(propertyName);
-       if (strVal == null) {
-           throw new IllegalArgumentException("test property undefined: " 
-                                              + propertyName);
-       }
-       return strVal;
-    }
-
-    /**
-     * This method will retrieve from the locally stored property list,
-     * the value of a property whose name corresponds to the string
-     * contained in the input argument "propertyName" and whose value
-     * is of type 'boolean'.
-     * <p>
-     * If the property list does not exist, or if the string value of the
-     * property can not be "cast" to the expected data type, then the
-     * default value will be returned.
-     */
-    public boolean getBooleanConfigVal(String propertyName,boolean defaultVal) 
{
-        boolean val    = defaultVal;
-        String  strVal = getParameterString(propertyName);
-       if (strVal != null) {
-           val = (Boolean.valueOf(strVal)).booleanValue();
-       }
-        return val;
-    }
-
-    /**
-     * Return the path to the harness JAR file. Each JAR file in the
-     * classpath is examined. The first one found that contains
-     * <code>QARunner.class</code> is assumed to be the harness JAR
-     * file. 
-     *
-     * @return the path for the harness JAR file
-     */
-    private String getHarnessJar() {
-       String classpath = System.getProperty("java.class.path");
-       if (classpath == null) {
-           throw new IllegalStateException("no value for java.class.path");
-       }
-       StringTokenizer tok = 
-           new StringTokenizer(classpath, File.pathSeparator);
-       while (tok.hasMoreTokens()) {
-           String path = tok.nextToken();
-           JarFile jarFile;
-           try {
-               jarFile = new JarFile(path);
-           } catch (IOException e) {
-               logger.log(Level.FINEST, "failed to open jar file " + path, e);
-               continue;
-           }
-           if (jarFile.getEntry("com/sun/jini/qa/harness/QARunner.class") 
-                                 != null) 
-           {
-               return path;
-           }
-       }
-       return null;
-    }
-
-    /**
-     * Return the url associated with <code>entryName</code>. If
-     * <code>entryName</code> is already a url, it is returned. If
-     * <code>entryName</code> is the name of an existing file, an appropriate
-     * file URL is returned. Otherwise search the harness and test jars for the
-     * given <code>entryName</code> and return the URL string for the first
-     * match. The search is performed relative to the test directory, then
-     * relative to the root of the test jar, and finally relative to the root 
of
-     * the harness jar.
-     *
-     * @param entryName the name of the entry to locate, expressed as a 
-     *        relative file name
-     */
-    public URL getComponentURL(String entryName, TestDescription td) throws 
TestException {
-       try {
-           return new URL(entryName);
-       } catch (MalformedURLException ignore) {
-       }
-       File entryFile = getComponentFile(entryName, td);
-       if (entryFile != null) { 
-           try {
-               return entryFile.getCanonicalFile().toURI().toURL();
-           } catch (Exception e) {
-               throw new TestException("problem converting file to url", e);
-           }
-       }
-       try {
-           if (td == null) {
-               td = getTestDescription(); // if undefined, get current
-           }
-           if (td != null) {
-               String tdDir = td.getName().replace('\\', '/');
-               tdDir = tdDir.substring(0, tdDir.lastIndexOf("/"));
-               
-               String fqEntry = canonicalize(tdDir + "/" + entryName);
-               logger.log(Level.FINEST, "checking test jar file for " + 
fqEntry);
-               if (getJarEntry(testJar, fqEntry) != null) {
-                   return new URL("jar:file:" + testJar.replace('\\', '/') + 
"!/" + fqEntry);
-               }
-           }
-           if (getJarEntry(testJar, entryName) != null) {
-               return new URL("jar:file:" + testJar.replace('\\', '/') + "!/" 
+ entryName);
-           }
-           if (getJarEntry(harnessJar, entryName) != null) {
-               return new URL("jar:file:" + harnessJar.replace('\\', '/') + 
"!/" + entryName);
-           }
-       } catch (MalformedURLException e) {
-           throw new TestException("failed to construct entry URL", e);
-       }
-       throw new TestException("no jar entry found for " + entryName);
-    }
-
-    /**
-     * Return the <code>File</code> associated with <code>entryName</code>. 
-     * The working directory and components of the <code>searchPath</code>
-     * are searched for a file having the name <code>enterName</code>.
-     *
-     * @param entryName the name of the entry to locate, expressed as a 
-     *        relative file name
-     * @return the associated <code>File</code>, or <code>null</code> if no
-     *        file is found
-     */
-    public File getComponentFile(String entryName, TestDescription td) {
-       File entryFile = new File(entryName);
-       try {
-           if (entryFile.exists()) {
-               return entryFile;
-           }
-       } catch (SecurityException e) {
-       }
-       logger.log(Level.FINEST, "failed existance check on " + entryFile);
-       for (int i = 0; i < searchList.length; i++) {
-           String base = searchList[i] + "/";
-           String fqName;
-           if (td == null) {
-               td = getTestDescription();
-           }
-           if (td != null) {
-               String tdDir = td.getName().replace('\\', '/');
-               tdDir = tdDir.substring(0, tdDir.lastIndexOf("/"));
-               
-               fqName = canonicalize(base + tdDir + "/" + entryName);
-               entryFile = new File(fqName);
-               try {
-                   if (entryFile.exists()) {
-                       return entryFile;
-                   }
-               } catch (SecurityException e) {
-                   logger.log(Level.FINEST, "Can't access " + entryFile, e);
-               }
-               logger.log(Level.FINEST, 
-                          "failed existance check on " + entryFile);
-           }
-           fqName = base + entryName;
-           entryFile = new File(fqName);
-           try {
-               if (entryFile.exists()) {
-                   return entryFile;
-               }
-           } catch (SecurityException e) {
-               logger.log(Level.FINEST, "Can't access " + entryFile, e);
-           }
-           logger.log(Level.FINEST, 
-                      "failed existance check on " + entryFile);
-       }
-       return null;
-    }
-
-
-    /**
-     * Canonicalize a path, eliminating relative references such
-     * as '.' and '..'
-     *
-     * @param path the path to canonicalize
-     * @return the new path
-     */
-    private String canonicalize(String path) {
-       ArrayList list = new ArrayList();
-       StringTokenizer tok = new StringTokenizer(path, "/\\");
-       while (tok.hasMoreTokens()) {
-           String component = tok.nextToken();
-           if (component.equals(".")) {
-               continue;
-           } else if (component.equals("..")) {
-               if (list.size() == 0) {
-                   return path; // bad path, just bail
-               }
-               list.remove(list.size() - 1);
-           } else {
-               list.add(component);
-           }
-       }
-       StringBuffer buf = new StringBuffer();
-       if (path.startsWith("/")) {
-           buf.append("/");
-       }
-       for (int i = 0; i < list.size(); i++) {
-           if (i > 0) {
-               buf.append("/");
-           }
-           buf.append((String) list.get(i));
-       }
-       return buf.toString();
-    }
-
-
-    /**
-     * Return a <code>ZipEntry</code> from a JAR file.
-     *
-     * @param jarName the name of the jar file
-     * @param entryName the name of the entry to retrieve
-     * @return the entry, or null if not found
-     * @throws TestException if an error occurs accessing the jar file
-     */
-    ZipEntry getJarEntry(String jarName, String entryName) 
-       throws TestException 
-    {
-       if (jarName == null) {
-           return null;
-       }
-       try {
-           logger.log(Level.FINEST, 
-                      "getting jar entry " + jarName + ":" + entryName);
-           JarFile jarFile = new JarFile(jarName);
-           return jarFile.getEntry(entryName);
-       } catch (IOException e) {
-           throw new TestException("cannot access jar file " + jarName, e);
-       }
-    }
-
-    /**
-     * Convert the given <code>path</code> to an absolute path resolved
-     * relative to the given <code>baseDir</code> if the path is not
-     * already absolute.
-     *
-     * @param path the path to convert to an absolute path
-     * @param baseDir the base directory if <code>path</code> is relative
-     * @return an absolute value for <code>path</code>
-     */
-     String relativeToAbsolutePath(String baseDir, String path) {
-        File pathFile = new File(path);
-        if (pathFile.isAbsolute()) {
-            return path;
-        }
-        return new File(baseDir, path).toString();
-     }
-
-    /**
-     * Create a properties object and load it from the contents of
-     * <code>propName</code>.  <code>propName</code> may be specified as a 
-     * URL or a path. If it is a path, then it is converted into a URL
-     * by calling getComponentURL. 
-     *
-     * @param propName the path of the resource to load
-     * @return a properites object initialized with the contents of the source
-     *         identified by <code>propName</code>, or an empty properties 
-     *         object if the source cannot be found or is null
-     */
-    public Properties loadProperties(String propName) throws TestException {
-       if (propName == null) {
-           return new Properties();
-       }
-       URL propURL= null;
-       try {
-           propURL = new URL(propName);
-       } catch (MalformedURLException e) {
-           propURL = getComponentURL(propName, null);
-       }
-       if (propURL == null) {
-           logger.log(Level.INFO, "could not locate properties: " + propName);
-           return new Properties();
-       }
-       return loadProperties(propURL);
-    }
-
-    /**
-     * Return a properties initialized by the properties file referenced by
-     * the given URL.
-     * 
-     * @param propURL to URL for the properties file
-     * @return the initialized properites object, or an empty  properties 
object
-     *         if propURL cannot be loaded
-     */
-    Properties loadProperties(URL propURL) {
-       Properties props = new Properties();
-       try {
-           InputStream s = propURL.openStream();
-           props.load(s);
-           logger.log(Level.FINEST, "loaded properties: " + propURL);
-       } catch (IOException e) {
-           logger.log(Level.INFO, "could not load properties: " + propURL, e);
-       }
-       return props;
-    }
-
-    private Properties getPropFromURL(String urlString, String relativeName) {
-        logger.log(Level.FINEST, "attempting to load " + relativeName  + " 
from url " + urlString);
-       Properties p = new Properties();
-       if (urlString == null) { 
-           return p;
-       }
-       urlString = urlString + "/" + relativeName;
-       try {
-           URL url = new URL(urlString);
-           InputStream s = url.openStream();
-           p.load(s);
-       } catch (Exception e) {
-           logger.log(Level.FINEST, 
-                      "failed to load properties from " + urlString);
-       }
-       return p;
-    }
-
-    /**
-     * Determine whether the current test run is distributed or not.
-     * A test run is distributed if the
-     * <code>com.sun.jini.qa.harness.testhosts</code> property
-     * has more than one element.
-     *
-     * @return true if this test run is distributed
-     */
-    public static boolean isDistributed() {
-        boolean distributed = false;
-        String hostList = AccessController.doPrivileged(
-           new PrivilegedAction<String>() {
-               public String run() {
-                   return 
-                       System.getProperty(
-                           "com.sun.jini.qa.harness.testhosts");
-               }
-            }
-        );
-//        String hostList =
-//            System.getProperty("com.sun.jini.qa.harness.testhosts");
-        if (hostList != null) {
-            StringTokenizer tok = new StringTokenizer(hostList, "|");
-            if (tok.countTokens() > 1) {
-                distributed = true;
-            }
-        }
-       return distributed;
-    }
-
-    /**
-     * Creates a directory that is guaranteed to be unique on
-     * the file system and return it as a <code>File</code> object.
-     * 
-     * @param prefix  the prefix string to be used in generating the directory
-     *                name; must be at least three characters long.
-     * @param suffix  the suffix string to be used in generating the directory
-     *                name; may be null, in which case the suffix ".tmp" 
-     *                will be used.
-     * @param path    the path under which the directory is to be
-     *                created, or null if the default temporary-file directory
-     *                is to be used.
-     * @return        a directory that is unique on the file system.
-     */
-    public File createUniqueDirectory(String prefix, String suffix, String 
path)
-       throws IOException, TestException
-    {
-       String directoryName = createUniqueFileName(prefix, suffix, path);
-       File file = new File(directoryName);
-       file.mkdirs();
-       return file;
-    }
-
-    /**
-     * Create an absolute filename that is guaranteed to be unique on the 
-     * file system.
-     * 
-     * @param prefix  the prefix string to be used in generating the file's
-     *                name; must be at least three characters long.
-     * @param suffix  the suffix string to be used in generating the file's
-     *                name; may be null, in which case the suffix ".tmp" will
-     *                be used.
-     * @param path    the directory in which the file is to be created, or
-     *                null if the default temporary-file directory is to be 
used
-     * @return        a file name that is unique on the file system.
-     * 
-     */
-    public String createUniqueFileName(String prefix, 
-                                      String suffix, 
-                                      String path)
-       throws TestException
-    {
-       for (int i = 0; i < 5; i++) {
-           try {
-               File temp = null;
-               if (path != null) {
-                   File directory = new File(path);
-                   temp = File.createTempFile(prefix, suffix, directory);
-               } else {
-                   temp = File.createTempFile(prefix, suffix);
-               }           
-               String filenamePath = temp.getAbsolutePath();
-               if (!temp.delete()) {
-                   throw new TestException("createUniqueFileName didn't "
-                                           + "remove temp file");
-               }
-               return filenamePath;
-           } catch (IOException e) {
-               String retrying = (i < 4) ? ", retrying" : "";
-               logger.log(Level.INFO,
-                          "createTempFile failed"
-                          +  ", prefix = " + prefix 
-                          +  ", suffix = " + suffix 
-                          +  ", path = " + path
-                          + retrying,
-                          e);
-           }
-       }
-       throw new TestException("Could not create temp file after 5 tries");
-    }
-
-    /**
-     * Obtain the value of a service property interpreted as an int. The
-     * property is identified by three tokens:
-     * <ul>
-     * <li> <code>serviceName</code>, the identifier for the service
-     * <li> <code>propertyName</code>, the property name for the service
-     * <li> <code>serviceIndx</code>, an instance count for the service
-     * </ul>
-     * A configuration property name is constructed by constructing
-     * the string:
-     * <pre>
-     *       serviceName + "." + propertyName + "." + serviceIndx
-     * </pre>
-     * If a value cannot be found, <code>serviceIndx</code> is decremented
-     * and another search is performed. This is repeated until
-     * the index reaches zero. If no match has been found, a search
-     * is done omitting the index.
-     *
-     * @param serviceName  the service prefix identifier
-     * @param propertyName the specific service property identifier
-     * @param serviceIndx  the service instance count
-     *
-     * @return the property value interpreted as an int. 
-     *         If the property value could not be found,
-     *         a value of <code>Integer.MIN_VALUE</code> is returned.
-     */
-    public int getServiceIntProperty(String     serviceName,
-                                     String     propertyName,
-                                     int        serviceIndx)
-    {
-       int retVal = Integer.MIN_VALUE;
-       String value = getServiceParameter(serviceName, 
-                                          propertyName,
-                                          serviceIndx);
-       if (value != null) {
-           try {
-               retVal = Integer.parseInt(value);
-           } catch (NumberFormatException ignore) {
-           }
-       }
-       return retVal;
-    }
-
-    /**
-     * Obtain the value of a service property interpreted as an int. The
-     * property is identified by three tokens:
-     * <ul>
-     * <li> <code>serviceName</code>, the identifier for the service
-     * <li> <code>propertyName</code>, the property name for the service
-     * <li> <code>serviceIndx</code>, an instance count for the service
-     * </ul>
-     * A configuration property name is constructed by constructing
-     * the string:
-     * <pre>
-     *       serviceName + "." + propertyName + "." + serviceIndx
-     * </pre>
-     * If a value cannot be found, <code>serviceIndx</code> is decremented
-     * and another search is performed. This is repeated until
-     * the index reaches zero. If no match has been found, a search
-     * is done omitting the index.
-     *
-     * @param serviceName  the service prefix identifier
-     * @param propertyName the specific service property identifier
-     * @param serviceIndx  the service instance count
-     * @param defaultVal   the value to return if the parameter is undefined
-     *
-     * @return the property value interpreted as an int. 
-     */
-    public int getServiceIntProperty(String serviceName,
-                                    String propertyName,
-                                    int serviceIndx,
-                                    int defaultVal) 
-    {
-       int val = getServiceIntProperty(serviceName, propertyName, serviceIndx);
-       if (val == Integer.MIN_VALUE) {
-           val = defaultVal;
-       }
-       return val;
-    }
-
-    /**
-     * Obtain the value of a service property interpreted as a boolean. The
-     * property is identified by three tokens:
-     * <ul>
-     * <li> <code>serviceName</code>, the identifier for the service
-     * <li> <code>propertyName</code>, the property name for the service
-     * <li> <code>serviceIndx</code>, an instance count for the service
-     * </ul>
-     * A configuration property name is constructed by constructing
-     * the string:
-     * <pre>
-     *       serviceName + "." + propertyName + "." + serviceIndx
-     * </pre>
-     * If a value cannot be found, <code>serviceIndx</code> is decremented
-     * and another search is performed. This is repeated until
-     * the index reaches zero. If no match has been found, a search
-     * is done omitting the index.
-     *
-     * @param serviceName  the service prefix identifier
-     * @param propertyName the specific service property identifier
-     * @param serviceIndx  the service instance count
-     * @param defaultVal   the default value if the parameter is not found
-     *
-     * @return the property value interpreted as a boolean, 
-     */
-    public boolean getServiceBooleanProperty(String     serviceName,
-                                            String     propertyName,
-                                            int        serviceIndx,
-                                            boolean    defaultVal)
-    {
-       String value = getServiceParameter(serviceName, propertyName, 
serviceIndx);
-       if (value == null) {
-           return defaultVal;
-       }
-       return Boolean.valueOf(value).booleanValue();
-    }
-
-    /**
-     * Obtain the value of a service property as a string. The
-     * property is identified by three tokens:
-     * <ul>
-     * <li> <code>serviceName</code>, the identifier for the service
-     * <li> <code>propertyName</code>, the property name for the service
-     * <li> <code>serviceIndx</code>, an instance count for the service
-     * </ul>
-     * A configuration property name is constructed by constructing
-     * the string:
-     * <pre>
-     *       serviceName + "." + propertyName + "." + serviceIndx
-     * </pre>
-     * If a value cannot be found, <code>serviceIndx</code> is decremented
-     * and another search is performed. This is repeated until
-     * the index reaches zero. If no match has been found, a search
-     * is done omitting the index.
-     *
-     * @param serviceName  the service prefix identifier
-     * @param propertyName the specific service property identifier
-     * @param serviceIndx  the service instance count
-     *
-     * @return the parameter string matching the service parameters.
-     *         Return <code>null</code> if no match is found.
-     */
-    public String getServiceStringProperty(String serviceName,
-                                           String propertyName,
-                                           int    serviceIndx)
-    {
-       return getServiceParameter(serviceName, propertyName, serviceIndx);
-    }
-
-    /**
-     * Get the <code>String</code> value of a service property. The search
-     * is first qualified by the type, unless the search is for type.
-     *
-     * @param serviceName  the service name
-     * @param propertyName the property name
-     * @param index        the service instance count
-     * @return             the value of the property, or <code>null</code> if
-     *                     the property is undefined
-     */
-    private String getServiceParameter(String prefix, 
-                                      String propertyName, 
-                                      int index)
-    {
-       String keyBase = prefix + ".type";
-       String type = null;
-       String serviceProp = null;
-       for (int i = index; i >= 0; i--) {
-           type = getStringConfigVal(keyBase + "." + i, null);
-           if (type != null) {
-               break;
-           }
-       } 
-       if (type == null) {
-           type = getStringConfigVal(keyBase, null);
-       }
-       if (propertyName.equals("type")) {
-           return type;
-       }
-       keyBase = prefix + "." + type + "." + propertyName;
-       for (int i = index; i >= 0; i--) {
-           serviceProp = getStringConfigVal(keyBase + "." + i, null);
-           if (serviceProp != null) {
-               return serviceProp;
-           }
-       }
-       serviceProp = getStringConfigVal(keyBase, null);
-       if (serviceProp == null) {
-           keyBase = prefix + "." + propertyName;
-           for (int i = index; i >= 0; i--) {
-               serviceProp = getStringConfigVal(keyBase + "." + i, null);
-               if (serviceProp != null) {
-                   return serviceProp;
-               }
-           }
-           serviceProp = getStringConfigVal(keyBase, null);
-       }
-       return serviceProp;
-    }
-
-
-    /**
-     * Obtain the value of a service property as a string. The
-     * property is identified by three tokens:
-     * <ul>
-     * <li> <code>serviceName</code>, the identifier for the service
-     * <li> <code>propertyName</code>, the property name for the service
-     * <li> <code>serviceIndx</code>, an instance count for the service
-     * </ul>
-     * A configuration property name is constructed by constructing
-     * the string:
-     * <pre>
-     *       serviceName + "." + propertyName + "." + serviceIndx
-     * </pre>
-     * If a value cannot be found, <code>serviceIndx</code> is decremented
-     * and another search is performed. This is repeated until
-     * the index reaches zero. If no match has been found, a search
-     * is done omitting the index.
-     *
-     * @param serviceName  the service name
-     * @param propertyName the service property identifier
-     * @param serviceIndx  the service instance count
-     * @param defaultVal   the value to return if the parameter is undefined
-     *
-     * @return the parameter string matching the service parameters.
-     */
-    public String getServiceStringProperty(String serviceName,
-                                          String propertyName,
-                                          int serviceIndx,
-                                          String defaultVal) 
-    {
-       String val = getServiceStringProperty(serviceName, 
-                                             propertyName, 
-                                             serviceIndx);
-       if (val == null) {
-           val = defaultVal;
-       }
-       return val;
-    }
-
-    /**
-     * Parses a string in which the tokens of the string are separated by the
-     * delimiter contained in the <code>delimiter</code> parameter. If the
-     * <code>delimiter</code> parameter is <code>null</code>, then the 
-     * default delimiter of white space (space, tab, newline, and return)
-     * will be used when parsing the input <code>String</code>. The tokens
-     * obtained from parsing the input <code>String</code> are returned
-     * in a <code>String</code> array.
-     *
-     * @param str       <code>String</code> to parse
-     * @param delimiter <code>String</code> containing the delimiters to
-     *                  use in the parsing process. If this parameter is 
-     *                  <code>null</code>, white space will be used as
-     *                  delimiter
-     *
-     * @return <code>String</code> array in which each element contains the
-     *         corresponding token from the input <code>String</code>, or
-     *         <code>null</code> if <code>str</code> is <code>null</code> or
-     *         has no tokens.
-     */
-    public String[] parseString(String str, String delimiter){
-        String[] strArray = null;
-       if (str != null) {
-           StringTokenizer st = null;
-           if(delimiter == null) {
-               st = new StringTokenizer(str);
-           } else {
-               st = new StringTokenizer(str, delimiter);
-           }
-           int n = st.countTokens();
-           if (n > 0) {
-               strArray = new String[n];
-               for (int i=0; i<n; i++) {
-                   strArray[i] = st.nextToken();
-               }
-           }
-       }
-       return strArray;
-    }
-
-    /**
-     * Parse the parameter string into an array of command-line argument
-     * strings. Arguments must be separated only with ',' characters so
-     * that file names with embedded white space are parsed property.
-     * A '+' character can be used to escape a ',' character which must
-     * be included in an argument. Leading ','s are discarded; multiple
-     * consecutive ','s are treated as a single ','.
-     *
-     * @param str the string to parse
-     * @return the array of command-line arguments
-     */
-    public String[] parseArgList(String str) {
-       str = (str == null) ? "" : str;
-       ArrayList list = new ArrayList();
-       while (str.trim().length() > 0) {
-           String buffer = "";
-           while (true) {
-               int comma = str.indexOf(",");
-               if (comma < 0) {
-                   buffer += str;
-                   str = "";
-                   break;
-               }
-               if (comma > 0 && str.charAt(comma - 1) == '+') {
-                   buffer += str.substring(0, comma - 1) + ",";
-                   if (comma + 1 < str.length()) {
-                       str = str.substring(comma + 1);
-                       continue;
-                   } else {
-                       str = "";
-                       break;
-                   }
-                   
-               }
-               buffer += str.substring(0, comma);
-               if (comma + 1 < str.length()) {
-                   str = str.substring(comma + 1);
-               } else {
-                   str = "";
-               }
-               break;
-           }
-           // ignore doubled or leading commas
-           if (buffer.length() > 0) {
-               list.add(buffer);
-           }
-       }
-       return (String[]) list.toArray(new String[list.size()]);
-    }
-
-    /**
-     * Parses a <code>String</code> using white space (space, tab, newline,
-     * and return) as the delimiter.
-     *
-     * @param str       <code>String</code> to parse
-     *
-     * @return <code>String</code> array in which each element contains the
-     *         corresponding token from the input <code>String</code>, or
-     *         <code>null</code> if <code>str</code> is <code>null</code> or
-     *         has no tokens.
-     */
-    public String[] parseString(String str){
-        return parseString(str,null);
-    }
-
-    /**
-     * This method parses the given string and determines if a particular 
string
-     * token is a group name or a locator. If a token is a group name, a
-     * sub-string is appended that results in a group name that has a high
-     * probability of being unique with respect to other tests being run
-     * simultaneously, and returns a new <code>String</code>, identical to the
-     * input <code>String</code> except that each group name is replaced with
-     * the new group name. This method produces the same results for the same
-     * value of <code>str</code> on repeated calls for all participants for a
-     * given test. This method may therefore be called by tests which generate
-     * group names before starting services.
-     *
-     * @param str the <code>String</code> to parse and modify.
-     * @return    a new <code>String</code>, identical to the input
-     *            <code>String</code> except that each occurrence a group name
-     *            is replaced with a new name, derived from the original name,
-     *            that has a high probability of being unique with respect to
-     *            other tests being run simultaneously.
-     */
-    public String makeGroupsUnique(String str){
-       String ret = str;
-       if (ret != null) {
-           StringTokenizer tok = new StringTokenizer(str, ", ", true);
-           StringBuffer buf = new StringBuffer();
-           while (tok.hasMoreTokens()) {
-               String fragment = tok.nextToken();
-               if (!(fragment.equals(",") || fragment.equals(" "))) {
-                   fragment = makeGroupUnique(fragment, 
-                                              getUniqueString());
-               }
-               buf.append(fragment);
-           }
-           ret = buf.toString();
-       }
-       return ret;
-    }
-
-     /**
-     * Determines if the input <code>String</code> represents a group name or
-     * a locator. If the input parameter represents a group name, this method
-     * appends a sub-string that results in a group name that has a high
-     * probability of being unique. If the input parameter represents a
-     * locator, the original <code>String</code> is returned.
-     *
-     * @param baseStr   <code>String</code> containing group to make unique.
-     * @param appendStr <code>String</code> to append to <code>baseStr</code>
-     *                  parameter. 
-     *
-     * @return <code>String</code> identical to the input parameter if the
-     *         input parameter represents a locator; otherwise, a new
-     *         <code>String</code>, derived from the input parameter, in which
-     *         a unique sub-string is appended.
-     */
-    private String makeGroupUnique(String baseStr, String appendStr) {
-        if(!(baseStr == null || isLocator(baseStr) || specialGroup(baseStr))) {
-           baseStr = baseStr + "_" + appendStr;
-       }
-       return baseStr;
-    }
-
-    /**
-     * Return a boolean indicating whether <code>group</code> is one of
-     * the strings "none", "all", or "public"
-     *
-     * @param group the string to check for a special group name
-     * @return <code>true</code> if <code>group</code> is a special name
-     */
-    private boolean specialGroup(String group) {
-       return    group.equals("none") 
-              || group.equals("all") 
-              || group.equals("public");
-    }
-
-    /** 
-     * Determines if <code>str</code> represents a <code>LookupLocator</code>.
-     *
-     * @param str <code>String</code> to analyze.
-     *
-     * @return <code>true</code> if the input parameter represents a 
-     *         <code>LookupLocator</code>; <code>false</code> otherwise.
-     */
-    public boolean isLocator(String str) {
-        if(str != null) {
-           try {
-               new LookupLocator(str);
-               return true;
-           } catch(MalformedURLException e) {
-           }
-       }
-       return false;
-    }
-
-    /** 
-     * Generates a <code>String</code> that has a high probability of being
-     * unique with respect to any other invocations of this method by other
-     * tests on any machine.
-     *
-     * @return <code>String</code> that has a high probability of being
-     *         unique with respect to any other invocations of this method
-     *         by other tests.
-     */
-    private String getUniqueString() {
-       if (uniqueString == null) {
-           initUniqueString();
-       }
-       return uniqueString;
-    }
-
-    /**
-     * Initialize the value of the unique string.
-     */
-    private void initUniqueString() {
-       uniqueString = getLocalHostName() + "_" + System.currentTimeMillis();
-    }
-
-    /**
-     * Tests for the existence of the ActivationSystem. This method
-     * will always make at least one attempt to verify the existence of a
-     * a running ActivationSystem.
-     *
-     * @param n <code>int</code> value representing the number of additional
-     *          attempts (beyond the initial attempt) to wait for the
-     *          activation system to come up. This values translates
-     *          directly to the number of seconds to wait for the
-     *          activation system.
-     *
-     * @return <code>true</code> if the activation system is up and running;
-     *         <code>false</code> otherwise
-     */
-     boolean activationUp(int n) {
-        /* First attempt */
-       Exception lastException = null;
-        try {
-            ActivationGroup.getSystem();
-            return true;
-        } catch (ActivationException e) { 
-           lastException = e;
-       }
-        /* Make a new attempt every second for n seconds */
-        for(int i=0; i<n; i++) {
-            try {
-                ActivationGroup.getSystem();
-                return true;
-            } catch (ActivationException e) {    
-               lastException = e;
-           }
-            try {
-                Thread.sleep(1000); // wait 1 second
-            } catch (InterruptedException e) { }
-        }
-       if (lastException != null) {
-           logger.log(Level.SEVERE, 
-                      "Act System wouldn't start", 
-                      lastException);
-       }
-        return false;
-    }
-
-    /**
-     * Register a file for deletion upon completion of test execution.
-     * A line containing the absolute path name of the given <code>File</code>
-     * is appended to the file named <code>JinitestDeletionList.txt</code>
-     * in the default temp directory.
-     *
-     * @param file the <code>File</code> to register for deletion
-     */
-    void registerDeletion(File file) {
-       ArrayList list = new ArrayList();
-       String listFileName = System.getProperty("java.io.tmpdir")
-                           + File.separator 
-                           + "JinitestDeletionList.txt";
-       File listFile = new File(listFileName);
-       BufferedReader r = null;
-       if (listFile.exists()) {
-           try {
-               r = new BufferedReader(new FileReader(listFileName));
-               String s = null;
-               while ((s = r.readLine()) != null) {
-                   list.add(s);
-               }
-           } catch (IOException e) {
-               logger.log(Level.SEVERE, "deletion file load failed", e);
-               return;
-           } finally {
-               try {
-                   r.close();
-               } catch (Exception ignore) {
-               }
-           }
-       }
-       list.add(file.getAbsolutePath());
-       BufferedWriter w = null;
-       try {
-           w = new BufferedWriter(new FileWriter(listFileName));
-           for (int i = 0; i < list.size(); i++) {
-               w.write((String) list.get(i));
-               w.newLine();
-           }
-           
-       } catch (IOException e) {
-           logger.log(Level.SEVERE, "deletion file update failed", e);
-       } finally {
-           try {
-               w.close();
-           } catch (Exception ignore) {
-           }
-       }
-    }
-
-    /**
-     * Reads the contents of the file <code>JinitestDeletionList.txt</code>
-     * located in the default temp directory, and attempts to delete every
-     * file or directory named therein. Failures are silently ignored.
-     */
-     void deleteRegisteredFiles() {
-       ArrayList files = new ArrayList();
-       String listFileName = System.getProperty("java.io.tmpdir")
-                           + File.separator 
-                           + "JinitestDeletionList.txt";
-       File listFile = new File(listFileName);
-       BufferedReader r = null;
-       if (listFile.exists()) {
-           try {
-               r = new BufferedReader(new FileReader(listFileName));
-               String s = null;
-               while ((s = r.readLine()) != null) {
-                   files.add(new File(s));
-               }
-           } catch (IOException e) {
-               logger.log(Level.SEVERE, "deletion file load failed", e);
-               return;
-           } finally {
-               try {
-                   r.close();
-               } catch (Exception ignore) {
-               }
-           }
-       }
-       for (int i = 0; i < files.size(); i++) {
-           File f = (File) files.get(i);
-           deleteTree(f);
-       }
-       listFile.delete();
-    }
-
-    /**
-     * Delete the given <code>File</code>. If the file is a directory, 
-     * first call this method for each of the entries in that
-     * directory.
-     *
-     * @param f the file or directory to delete
-     */
-    private void deleteTree(File f) {
-       if (f.isDirectory()) {
-           File[] fileList = f.listFiles();
-           for (int i = 0; i < fileList.length; i++) {
-               deleteTree(fileList[i]);
-           }
-       }
-       logger.log(Level.FINEST, "deleting " + f);
-       f.delete();
-    }
-
-    /**
-     * Return any options or properties which are required for all VMs.
-     * The value of <code>com.sun.jini.qa.harness.globalvmargs</code>
-     * is returned as a string array, with components separated by
-     * ',' characters. There must be no cosmetic whitespace in this
-     * string since filenames may include whitespace. It is expected
-     * that this property would be set in the configuration set property file.
-     *
-     * @return the array of global vm options/properties, or null if none
-     *         are defined
-     */
-     String[] getGlobalVMArgs() {
-       String vmArgs = 
-           getStringConfigVal("com.sun.jini.qa.harness.globalvmargs", null);
-       return parseArgList(vmArgs);
-     }
-     
-     /**
-     * Return an array of VM options extracted from the given array
-     * of combined options and properties. These are structured
-     * to be input to service starter descriptions, i.e. one
-     * fully specified option per array element, including the
-     * leading '-' character.
-     *
-     * @param vmArgs an array of vm options and properties
-     * @return the subset of vm options. A zero length array is
-     *         returned if there are none, or if <code>vmArgs</code>
-     *         is <code>null</code>.
-     */
-    String[] extractOptions(String[] vmArgs) {
-       if (vmArgs == null) {
-           return new String[0];
-       }
-       ArrayList list = new ArrayList();
-       for (int i = 0; i < vmArgs.length; i++) {
-           if (! (vmArgs[i].startsWith("-D") || vmArgs[i].startsWith("-OD"))) {
-               list.add(vmArgs[i]);
-           }
-       }
-       return (String[]) list.toArray(new String[list.size()]);
-    }
-       
-    /**
-     * Return an array of VM properties extracted from the given array
-     * of combined options and properties. These are structured
-     * to be input to service starter descriptions, i.e. alternating
-     * property names and values, with the '-D' stripped from
-     * the property names. The returned array should therefore always
-     * contain an even number of elements. Optional properties
-     * begin with '-OD'. If an optional property is found and it
-     * has no value, then it will not be included in the list.
-     *
-     * @param vmArgs an array of vm options and properties
-     * @return the subset of vm properties. A zero length array is
-     *         returned if there are none, or if <code>vmArgs</code>
-     *         is <code>null</code>.
-     * @throws TestException if a parsing error occurs
-     */
-    String[] extractProperties(String[] vmArgs) throws TestException {
-       if (vmArgs == null) {
-           return new String[0];
-       }
-       ArrayList list = new ArrayList();
-       for (int i = 0; i < vmArgs.length; i++) {
-           if (vmArgs[i].startsWith("-D") || vmArgs[i].startsWith("-OD")) {
-               String[] prop = parseProp(vmArgs[i]);
-               if (prop != null) {
-                   list.add(prop[0]);
-                   list.add(prop[1]);
-               }
-           }
-       }
-       return (String[]) list.toArray(new String[list.size()]);
-    }
-
-    /**
-     * Parse the given string of the form "-Dname=value" or "-ODname=value to
-     * return a two element string array containing "name" and "value". If
-     * "value" is omitted and the '-D' form was specified, it's return value is
-     * a zero length string. If "value" is omitted and the '-OD' form was
-     * specified, this method returns <code>null</code>.
-     *
-     * @param p the string to parse
-     * @return a two element string array containing name and value, or
-     *         <code>null</code> if there is not value and the '-OD'
-     *         form was specified.
-     * @throws TestException if <code>p</code> does not start with "-D",
-     *                       or "-OD", or does not contain an "=" character, or
-     *                       does not define a value for "name"
-     */
-    private String[] parseProp(String p) 
-       throws TestException 
-    {
-       if (! (p.startsWith("-D") || p.startsWith("-OD"))) {
-           throw new TestException("String " 
-                                   + p 
-                                   + " is not a valid property definition");
-       }
-       int startIndex = ((p.startsWith("-D") ? 2 : 3));
-       String pnew = p.substring(startIndex);
-       int eq = pnew.indexOf("=");
-       if (eq <= 0) {
-           throw new TestException("String " 
-                                   + p 
-                                   + " is not a valid property definition");
-       }
-       String[] ret = new String[2];
-       ret[0] = pnew.substring(0, eq);
-       ret[1] = "";
-       if (pnew.length() > (eq + 1)) {
-           ret[1] = pnew.substring(eq + 1);
-       }
-       if (ret[1].length() == 0 && p.startsWith("-OD")) {
-           ret = null;
-       }
-       return ret;
-    }
-       
-    /**
-     * Convert an http codebase to an httpmd codebase.  If
-     * <code>hash</code> is <code>null</code>, then a search is
-     * performed for the key "com.sun.jini.qa.harness.integrityhash" and
-     * the returned value assigned to <code>hash</code>.  if
-     * <code>hash</code> is not <code>null</code> and not
-     * <code>"none"</code> then codebase integrity is assumed to be
-     * required.  hash may contain multiple comma-separated hash function 
names;
-     * one of the names will be randomly selected.
-     * <code>codebase</code> may contain multiple URL's
-     * separated by white space. If integrity is required, then each
-     * URL which is an http URL will be converted to a fully specified
-     * httpmd URL using the randomly selected hashing function name.
-     *
-     * @param codebase the codebase string to convert, which may be 
-     *                 <code>null</code> and may contain multiple
-     *                 URL's separated by whitespace
-     * @param hash a comma separated list of hash function names, or null
-     * @return the possibly modified codebase string
-     * @throws TestException if codebase integrity is required and
-     *         <code>codebase</code> contains a malformed URL 
-     */
-    public String genIntegrityCodebase(String codebase, String hash) 
-       throws TestException
-    {
-       if (codebase == null) {
-               return null;
-       }
-       if (hash == null) {
-           hash = getStringConfigVal("com.sun.jini.qa.harness.integrityhash", 
-                                     null);
-       }
-       if (hash == null || hash.equals("none")) {
-           return codebase;
-       }
-       String[] hashNames = parseString(hash, ", \t");
-       if (hashNames.length == 0) {
-           return codebase; // must have been just sep characters
-       }
-       if (hashNames.length > 1) {
-           hash = hashNames[new Random().nextInt(hashNames.length)];
-       }
-       URL[] urls = null;
-       try {
-           urls = ClassLoaderUtil.getCodebaseURLs(codebase);
-       } catch (MalformedURLException e) {
-           throw new TestException("Bad URL in codebase", e);
-       }
-       StringBuffer sb = new StringBuffer();
-       for (int i = 0; i < urls.length; i++) {
-           if (i > 0) {
-               sb.append(" ");
-           }
-           if (! urls[i].getProtocol().equals("http")) {
-               sb.append(urls[i].toString());
-           } else {
-               int port = urls[i].getPort();
-               if (port == -1) {
-                   port = 80;
-               }
-               String key = "com.sun.jini.qa.harness.dldir." + port;
-               String dir = getStringConfigVal(key, null);
-               if (dir == null) {
-                   throw new TestException("missing download directory"
-                                           + " identified by key "
-                                           + key);
-               }
-               try {
-                   sb.append(HttpmdUtil.computeDigestCodebase(
-                                         dir,
-                                         reformat(urls[i], hash)));
-               } catch (FileNotFoundException e) {
-                   logger.log(Level.WARNING, 
-                              "WARNING: file not found for codebase " + urls[i]
-                              + " in directory " + dir
-                              + ", DISCARDING");
-               } catch (IOException e) {
-                   throw new TestException("Failed reformatting codebase" ,e);
-               }
-           }
-       }
-       return sb.toString();
-    }
- 
-    /**
-     * Converts an http codebase to an httpmd codebase with a hash
-     * value of zero. If the protocol
-     * of the given codebase is anything other than <code>http</code>,
-     * the original codebase is returned. The formatting is such that
-     * the string can be used as input to the
-     * <code>HttpmdUtil.computeDigestCodebase</code> method.
-     *
-     * @param url the <code>URL</code> to convert
-     * @param hashType the name of the hashing function
-     *
-     * @return the converted codebase
-     * @throws TestException if the input codebase is not a properly
-     *         formatted URL
-     */
-    private String reformat(URL url, String hashType) 
-       throws TestException 
-    {
-       if (! url.getProtocol().equals("http")) {
-           return url.toString();
-       }
-       StringBuffer sb = new StringBuffer();
-       sb.append("httpmd://");
-       sb.append(url.getHost());
-       if (url.getPort() != -1) {
-           sb.append(":");
-           sb.append(Integer.toString(url.getPort()));
-       }
-       sb.append(url.getFile());
-       sb.append(";");
-       sb.append(hashType);
-       sb.append("=0");
-       return sb.toString();
-    }
-
-    /**
-     * Merge two string arrays into a single array. Either
-     * argument may be null, in which case the other argument
-     * is used as the returned array. <code>null</code> is
-     * returned if both arguments are <code>null</code>.
-     *
-     * @param a1 an array to merge
-     * @param a2 an array to merge
-     * @return an array containing the union of <code>a1</code> 
-     *         and <code>a2</code>
-     */
-    String[] mergeArrays(String[] a1, String[] a2) {
-       if (a1 == null) {
-           return a2;
-       }
-       if (a2 == null) {
-           return a1;
-       }
-       String[] ret = new String[a1.length + a2.length];
-       System.arraycopy(a1, 0, ret, 0, a1.length);
-       System.arraycopy(a2, 0, ret, a1.length, a2.length);
-       return ret;
-    }
-
-    /**
-     * Merge two sets of option strings. The options present
-     * in the given string arrays are combined into a
-     * single array, discarding duplicates. The original
-     * ordering is not retained.
-     *
-     * @param o1 an array of option strings
-     * @param o2 an array of option strings
-     * @return the combined strings, discarding duplicates
-     */
-    String[] mergeOptions(String[] o1, String[] o2) {
-       if (o1 == null && o2 == null) {
-           return new String[0];
-       }
-       if (o1 == null || o2 == null) {
-           return (o1 == null ? o2 : o1);
-       }
-       HashSet options = new HashSet();
-       for (int i = 0; i < o1.length; i++) {
-           options.add(o1[i]);
-       }
-       for (int i = 0; i < o2.length; i++) {
-           options.add(o2[i]);
-       }
-       return (String[]) options.toArray(new String[options.size()]);
-    }
-
-    /**
-     * Merge two sets of property definitions. The property key/value
-     * pairs present in the given string arrays are combined into a
-     * single array. If a property key is defined in both arrays, the
-     * value in <code>p2</code> overrides the value in
-     * <code>p1</code>. The original ordering is not retained.
-     *
-     * @param p1 an array of property key/value pairs
-     * @param p2 an array of property key/value pairs
-     * @return an array containing the combined propeties key/value pairs
-     */
-    String[] mergeProperties(String[] p1, String[] p2) {
-       if (p1 == null && p2 == null) {
-           return new String[0];
-       }
-       if (p1 == null || p2 == null) {
-           return (p1 == null ? p2 : p1);
-       }
-       HashMap map = new HashMap();
-       for (int i = 0; i < p1.length; i += 2) {
-           map.put(p1[i], p1[i + 1]);
-       }
-       for (int i = 0; i < p2.length; i += 2) {
-           map.put(p2[i], p2[i + 1]);
-       }
-       Set keys = map.keySet();
-       Iterator it = keys.iterator();
-       String[] ret = new String[keys.size() * 2];
-       int i = 0;
-       while (it.hasNext()) {
-           String key = (String) it.next();
-           String value = (String) map.get(key);
-           ret[i++] = key;
-           ret[i++] = value;
-       }
-       return ret;
-    }
-
-    /**
-     * Add to the set of configuration override providers. This method must be
-     * called prior to starting any service for which test supplied overrides
-     * are required. 
-     *
-     * @param provider the override provider
-     */
-    public void addOverrideProvider(OverrideProvider provider) {
-       overrideProviders.add(provider);
-       if (isMaster()) { // probably unnecessary, but can't hurt
-           SlaveRequest request = new AddOverrideProviderRequest(provider);
-           SlaveTest.broadcast(request);
-       }
-    }
-
-    /**
-     * Get the configuration override providers. This method is called by 
admins
-     * during service argument list generation to augment the set of 
-     * configuration options provided to the service. If no providers have
-     * been registered, an empty array is returned.
-     *
-     * @return the override providers
-     */
-    OverrideProvider[] getOverrideProviders() {
-       OverrideProvider[] oa = new OverrideProvider[overrideProviders.size()];
-       return  (OverrideProvider[]) overrideProviders.toArray(oa);
-    }
-
-    /**
-     * Add a analyzer to the set of failure analyzers called if a 
-     * test throws an exception.
-     *
-     * @param analyzer the <code>FailureAnalyzer</code> to add
-     */
-    public void addFailureAnalyzer(FailureAnalyzer analyzer) {
-       failureAnalyzers.add(analyzer);
-    }
-
-    /**
-     * Analyze a failure exception. All registered analyzers are 
-     * called with the given exception in the order registered. If an
-     * analyzer returns a value other than LegacyTest.UNKNOWN then that value
-     * is returned by this method. If all analyzers return LegacyTest.UNKNOWN,
-     * or if no analyzers are registered, then this method returns the
-     * given default value.
-     *
-     * @param e the exception to analyze
-     * @param defaultType the default failure type to return if all analyzers
-     *                    return <code>LegacyTest.UNKNOWN</code>, or if there 
are no
-     *                    registered analyzers
-     * @return the failure type
-     */
-    int analyzeFailure(Throwable e, int defaultType) {
-       for (int i = 0; i < failureAnalyzers.size(); i++) {
-           FailureAnalyzer fa = (FailureAnalyzer) failureAnalyzers.get(i);
-           int type = fa.analyzeFailure(e);
-           if (type != Test.UNKNOWN) {
-               return type;
-           }
-       }
-       return defaultType;
-    }
-
-    /**
-     * Split a dot-separated identifier by removing the last token and
-     * discarding the separator. Return the resulting pair in a
-     * two-element string array. Thus, if <code>key</code> has
-     * the value "a.b.c", then element 0 will have the value "a.b"
-     * and element 1 will have the value "c".
-     *
-     * @param key the identifier to split
-     * @return the split pair, or null if <code>key</code> was not
-     *         a properly formed identifier (such as the value "none")
-     */
-    String[] splitKey(String key) {
-       if (key == null) {
-           return null;
-       }
-       int index = key.lastIndexOf('.');
-       if (index <= 0 || index == (key.length() - 1)) {
-           return null;
-       }
-       String[] frags = new String[2];
-       frags[0] = key.substring(0, index);
-       frags[1] = key.substring(index + 1);
-       return frags;
-    }
-
-    /**
-     * Return an indication of whether this host is the master host. This
-     * value is always computed rather than cached to avoid any consistancy
-     * glitches when an instance of this class is deserialized in another VM.
-     *
-     * @return true if this host is the master host
-     */
-    boolean isMaster() {
-       boolean isMaster = true;
-       if (hostList.size() > 0) {
-           try {
-               InetAddress thisAddr = InetAddress.getLocalHost(); //XXX 
multinic??
-               String masterName = (String) hostList.get(0);
-               InetAddress masterAddr = InetAddress.getByName(masterName);
-               isMaster = masterAddr.equals(thisAddr);
-           } catch (UnknownHostException e) {
-               logger.log(Level.SEVERE, "Unexpected exception", e);
-           }
-       }
-       return isMaster;
-    }
-
-
-    /**
-     * Return an indication of whether the <code>hostName</code> is this host. 
-     *
-     * @return true if this <code>hostName</code> is this host
-     */
-    boolean isThisHost(String hostName) {
-       try {
-           InetAddress thisAddr = InetAddress.getLocalHost(); //XXX multinic??
-           InetAddress hostAddr = InetAddress.getByName(hostName);
-           return hostAddr.equals(thisAddr);
-       } catch (UnknownHostException e) {
-           logger.log(Level.SEVERE, "Unexpected exception", e);
-       }
-       return true;
-    }
-
-    /**
-     * Return the name of the local host. If the name cannot be determined,
-     * return "localhost".
-     *
-     * @return the host name
-     */
-    public String getLocalHostName() {
-       String host = "localhost";
-       try {
-           host = InetAddress.getLocalHost().getHostName();
-       } catch (UnknownHostException ignore) {
-       }
-       return host;
-    }
-
-    /**
-     * Internal utility method to intialize the locator constraints from
-     * the <code>test.locatorConstraints</code> entry of the test
-     * configuration file.
-     */
-    private static void initLocatorConstraints() {
-       Configuration c = harnessConfig.configuration;
-       try {
-           testLocatorConstraints = 
-               (MethodConstraints) c.getEntry("test", 
-                                              "locatorConstraints",
-                                              MethodConstraints.class);
-       } catch (ConfigurationException e) {
-           if (c instanceof QAConfiguration) {
-               logger.log(Level.INFO, 
-                          "Warning: couldn't init locator constraints",
-                          e);
-           }
-       }
-    }
-
-    /**
-     * Get a <code>ConstrainableLookupLocator</code> for the given host and 
port
-     * using constraints provided by the <code>test.locatorConstraints</code>
-     * entry from the test configuration file. If the <code>none</code>
-     * configuration is being run, the constraints will be null.
-     *
-     * @param host the host name
-     * @param port the host port
-     * @return the locator
-     */
-    public static LookupLocator getConstrainedLocator(String host, int port) 
-    {
-       if (testLocatorConstraints == null) {
-           initLocatorConstraints();
-       }
-       return new ConstrainableLookupLocator(getFQHostName(host), 
-                                             port, 
-                                             testLocatorConstraints);
-    }
-
-    /**
-     * Get a <code>ConstrainableLookupLocator</code> for the host and port
-     * provided by the given locator, using constraints provided by the
-     * <code>test.locatorConstraints</code> entry from the test configuration
-     * file.
-     *
-     * @param loc the <code>LookupLocator</code> of the target
-     * @return the locator
-     */
-    public static LookupLocator getConstrainedLocator(LookupLocator loc) 
-    {
-       if (loc == null) {
-           return null;
-       }
-       return getConstrainedLocator(getFQHostName(loc.getHost()), 
-                                    loc.getPort());
-    }
-
-    /**
-     * Get a <code>ConstrainableLookupLocator</code> for the given
-     * <code>URL</code> , using constraints provided by the
-     * <code>test.locatorConstraints</code> entry from the test configuration
-     * file.
-     *
-     * @param url the <code>URL</code> for the locator
-     * @return the locator
-     * @throws MalformedURLException if <code>url</code> is not 
-     *                               properly formatted
-     */
-    public static LookupLocator getConstrainedLocator(String url) 
-       throws MalformedURLException
-    {
-       LookupLocator loc = new LookupLocator(url);
-       return getConstrainedLocator(loc);
-    }
-
-    private static final CharSequence NXDOMAIN = "NXDOMAIN";
-    private static final CharSequence nxdomain = "nxdomain";
-    
-    /**
-     * Convert a host name to canonical form. If the name cannot
-     * be converted for any reason, the original name is returned.
-     *
-     * @param hostName the name to convert
-     * @return the converted name
-     */
-    private static String getFQHostName(String hostName) {
-       try {
-           InetAddress hostAddr = InetAddress.getByName(hostName);
-           String fqHostName = hostAddr.getCanonicalHostName();
-            if (fqHostName.contains(NXDOMAIN)) return hostName;
-            if (fqHostName.contains(nxdomain)) return hostName;
-            return fqHostName;
-       } catch (UnknownHostException ignore) {
-            logger.info("InetAddress threw unknown host exception: " + 
hostName);
-       }
-       return hostName;
-    }
-
-    /**
-     * Implement the configuration value search policy for the harness.
-     * The various sources of test parameters are searched in the following 
-     * order:
-     * <ul>
-     * <li>the default property file
-     * <li>the Configuration set property file
-     * <li>the user provided configuration file
-     * <li>the component level property file
-     * <li>the named test property file (see below)
-     * <li>the co-located test property file (see below)
-     * <li>the test description
-     * <li>the test Configuration
-     * <li>the System properties
-     * <li>the dynamic properties
-     * <li>the overrides
-     * </ul>
-     * The last source which returns a non-null value is used, so
-     * the search is done from lowest to highest precedence. Configration
-     * definitions may be self-referential, causing a substitution
-     * to be performed from lower precedence definitions. For example
-     * assume the default property file had the following definition:
-     * <pre>
-     *    com.sun.foo=foo
-     * </pre>
-     * and assume that the test description contained the following:
-     * <pre>
-     *    com.sun.foo=${com.sun.foo} bar
-     * </pre>
-     * then the value retrieved by searching for <code>com.sun.foo</code>
-     * would be <code>foo bar</code>.
-     * <p>
-     * Before the parameter value is returned, any '$' substitutions
-     * are performed (recursively), and any occurances of the
-     * token <code><gethost></code> are replaced with the name of
-     * the host on which this code is running. If a parsing error
-     * is encountered during symbol resolution, an error message
-     * is written to the log, and the unresolved parameter value
-     * is returned.
-     * <p>
-     * Any tokens of the form &lt;url:foo&gt; will result in the generation
-     * of a URL for <code>foo</code>. A search is performed for the file 
relative to
-     * all of the root directories defined by the <code>searchPath</code>
-     * property. If the file is found, a file URL for the fully qualified
-     * name of the file is generated and returned as the token value. 
Otherwise,
-     * test and harness JAR files are searched for <code>foo</code>, looking
-     * first relative to the directory in which the test description file is 
-     * located, then relative to the root of the test jar file and finally
-     * relative to the root of the harness jar file. In this case, an 
appropriate
-     * JAR file URL is generated and returned as the token value. If the
-     * file is not found, a <code>TestException</code> is thrown.
-     * <p>
-     * Any tokens of the form &lt;file:foo&gt; will result in the generation 
of a
-     * fully qualified path for <code>foo</code>. A search is performed for 
the file
-     * relative to all of the root directories defined by the
-     * <code>searchPath</code> property. If the file is found, the fully 
qualified
-     * name of the file is generated and returned as the token value. If the 
file is
-     * not found, a <code>TestException</code> is thrown.
-     * <p>
-     * If an error is detected during the search/resolution process, it is
-     * considered fatal. If the error were simply logged, an incorrect value
-     * would be used and the error message could easily be missed in the
-     * other test output. A <code>RuntimeException</code> is thrown rather
-     * than a <code>TestException</code> to avoid the need to wrap the many
-     * calls to this method in try/catch blocks.
-     * <p>
-     * When searching the test configuration, they key is split into
-     * an entry type/entry name pair. If the key is a single token, such
-     * as <code>testjvmargs</code>, then the entry type will be
-     * "test" and the entry name will be the key (<code>testjvmargs</code>).
-     * 
-     * @param key the property name to search for
-     *
-     * @return the value of the property, or <code>null</code> if the
-     *         property cannot be found.
-     * @throws RuntimeException if a fatal error occurs while locating and
-     *                          resolving the value.
-     */
-    String getParameterString(String key) {
-       String previousVal = "";
-       String val = defaultProps.getProperty(key);
-       String source = null;
-       try {
-           if (val != null) {
-               source = "qaDefault.properties";
-               previousVal = resolver.resolveReference(val, key, previousVal);
-           }
-           val = configSetProps.getProperty(key);
-           if (val != null) {
-               source = "configurationSet property file";
-               previousVal = resolver.resolveReference(val, key, previousVal);
-           }
-           val = configProps.getProperty(key);
-           if (val != null) {
-               source = "user configuration file";
-               previousVal = resolver.resolveReference(val, key, previousVal);
-           }
-           if (td != null) {
-               val = td.getProperty(key);
-               if (val != null) {
-                   source = "test description properties";
-                   previousVal = 
-                       resolver.resolveReference(val, key, previousVal);
-               }
-           }
-           if (configuration  != null) {
-               String[] frags = splitKey(key);
-               if (frags == null) {
-                   frags = new String[]{"test", key};
-               }
-               try {
-                   val = (String) configuration.getEntry(frags[0],
-                                                         frags[1], 
-                                                         String.class, 
-                                                         null);
-                   if (val != null) {
-                       source = "test configuration file";
-                       previousVal = 
-                           resolver.resolveReference(val, 
-                                                     key,
-                                                     previousVal);
-                   }
-               } catch (Exception ignore) {
-               }
-           }
-           MultiCommandLine mcl = new MultiCommandLine(args);
-           try {
-               val = mcl.getString(key, null);
-               if (val != null) {
-                   source = "command line";
-                   previousVal = resolver.resolveReference(val, 
-                                                           key,
-                                                           previousVal);
-               }
-           } catch (BadInvocationException e) {
-           }
-           val = System.getProperty(key);
-           if (val != null) {
-               source = "System property";
-               previousVal = resolver.resolveReference(val, key, previousVal);
-           }
-           val = dynamicProps.getProperty(key);
-           if (val != null) {
-               source = "dynamic property";
-               previousVal = resolver.resolveReference(val, key, previousVal);
-           }
-           if (propertyOverrides != null) {
-               val = propertyOverrides.getProperty(key);
-               if (val != null) {
-                   source = "propertyOverrides";
-                   previousVal = resolver.resolveReference(val, key, 
previousVal);
-               }
-           }
-           if (previousVal.equals("")) {
-               val = null;
-           } else {
-               val = resolver.resolve(previousVal);
-           }
-           if (key.equals(trackKey)) {
-               logger.log(Level.INFO, 
-                          "Value for " + key 
-                          + " obtained from " + source
-                          + " is " + val);
-           }
-       } catch (TestException e) {
-           throw new RuntimeException("Error resolving key " + key + " 
obtained from source " + source, e);
-       }
-       if (val != null) {
-           val = val.trim();
-       }
-       return val;
-    }
-
-    /**
-     * Sets a dynamic test parameter. These parameters are intended for use
-     * by tests which must perform special manipulations of property values,
-     * such as setting a codebase string on-the-fly. In a distributed run, 
-     * a <code>SlaveRequest</code> will be broadcast to all slaves to
-     * set their dynamic test parameters as well, to retain consistancy. 
-     * As a result, this method should not be called until all slave tests
-     * are running.
-     *
-     * @param key    a <code>String</code> identifying the parameter
-     * @param value  the parameter value to set
-     */
-    public void setDynamicParameter(String key, String value) {
-        dynamicProps.setProperty(key, value);
-       //XXX the harnessJar/testJar special cases feels like a hack
-       if (key.equals("harnessJar")) {
-           resolver.setToken(key, value);
-           harnessJar = value;
-       }
-       if (key.equals("testJar")) {
-           testJar = value;
-           resolver.setToken(key, value);
-       }
-       if (isMaster()) {
-           SlaveRequest request = new SetDynamicParameterRequest(key, value);
-           SlaveTest.broadcast(request);
-       }
-    }
-
-    /**
-     * Returns the name to use for the <code>ConfigurationFile</code>. If the
-     * name is not provided by the <code>testConfiguration</code> parameter in
-     * the test config, or if the current configuration tag is 'none', then it
-     * is assumed that no test specific configuration file exists for this 
test;
-     * in this case, this method returns "-".
-     *
-     * @return the name of the configuration file to
-     *         be used by this test expressed as a jar file URL
-     */
-    private String getConfigurationName() throws TestException {
-       String name = "-";
-       if (!currentTag.equals("none")) {
-           name = getStringConfigVal("testConfiguration", "-");
-        }
-       if (! name.equals("-")) {
-           name = getComponentURL(name, null).toString();;
-       }
-       return name;
-    }
-
-    /**
-     * Install <code>OverrideProviders</code> defined by the test
-     * description property <code>testOverrideProviders</code>. The value
-     * of this propery may be a list of providers, separated by commas
-     * or white space. Any previously registered providers are discarded.
-     *
-     * @throws TestException if a specified provider does not exist

[... 3560 lines stripped ...]

Reply via email to