river-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From peter_firmst...@apache.org
Subject svn commit: r1634322 [5/41] - in /river/jtsk/skunk/qa_refactor/trunk: qa/src/com/sun/jini/qa/harness/ qa/src/com/sun/jini/test/impl/end2end/e2etest/ qa/src/com/sun/jini/test/impl/joinmanager/ qa/src/com/sun/jini/test/impl/mahalo/ qa/src/com/sun/jini/te...
Date Sun, 26 Oct 2014 13:17:31 GMT
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 ...]


Mime
View raw message