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 [7/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/SlaveHarness.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/qa_refactor/trunk/qa/src/com/sun/jini/qa/harness/SlaveHarness.java?rev=1634322&r1=1634321&r2=1634322&view=diff
==============================================================================
--- river/jtsk/skunk/qa_refactor/trunk/qa/src/com/sun/jini/qa/harness/SlaveHarness.java (original)
+++ river/jtsk/skunk/qa_refactor/trunk/qa/src/com/sun/jini/qa/harness/SlaveHarness.java Sun Oct 26 13:17:28 2014
@@ -1,644 +1,646 @@
-/*
- * 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 java.io.File;
-import java.io.EOFException;
-import java.io.InputStream;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.io.OutputStream;
-import java.io.PrintStream;
-import java.net.ConnectException;
-import java.net.InetAddress;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.logging.Handler;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import java.util.Properties;
-import java.util.StringTokenizer;
-import java.util.jar.JarFile;
-
-/**
- * A 'harness' which accepts work requests from a master over
- * a socket. 
- * <p>
- * Generation of the MasterTest and SlaveTest command lines:
- * <p>
- * There are 4 VMs which participate in the distributed test  process:
- * <ul>
- *   <li>the master harness
- *   <li>the slave harness
- *   <li>the master test
- *   <li>the slave test request handler
- * <ul>
- * All 4 of these VMs maintain an instance of QAConfig. Because the
- * VMs are running on different systems, potentially with different
- * versions of the jdk/jsk kits, the values which define installation 
- * directories for the master/slave tests are defined as system properties
- * on the test VMs invoked by the master/slave harnesses.
- * <p>
- * When the MasterHarness and SlaveHarness classes are instantiated,
- * they construct an instance of QAConfig. The constructor of QAConfig
- * verifies the existance of the following system properties:
- * <ul>
- *    <li>com.sun.jini.jsk.home
- *    <li>com.sun.jini.qa.home
- * </ul>
- * which are typically provided by the user config file. These values serve as
- * the default values to apply to the master/slave test VMs.  When the
- * <code>MasterHarness</code> creates the master test VM, it obtains the command
- * line from the <code>TestDescription</code>.  When generating the command
- * line, the TD temporarily sets override properties generated from the QAConfig
- * getSystemProps("master") method.  Any master overrides supplied (indirectly)
- * through the environment file (provided by the -env command line option) will
- * be used to build the command line. Typically, one or more of the installation
- * system properties listed above will be replaced if the test is to use a
- * non-default kit. The master harness always provides property definitions for
- * all of these properties so that policy files and ConfigurationFile entries
- * have access to these definitions. The MasterHarness passes a serialized copy
- * of its QAConfig to the master test over its System.in stream; it is not
- * necessary for the master test to construct a QAConfig from scratch. Because
- * system property definitions override the user configuration file values when
- * QAConfig searches for a property, the values set on the command line
- * by the harness will take precedence.
- * <p>
- * When the <code>MasterHarness</code> submits a request to the slave
- * harness to create a <code>SlaveTest</code>, it also passes
- * a serialized copy of its QAConfig. The <code>SlaveHarness</code>
- * generates a command line, this time using overrides resulting from
- * calling the QAConfig getSystemProps("slave") method. The information
- * required to generate the slave overrides are carried in the serialized
- * QAConfig object, so the <code>MasterHarness</code> controls the
- * environment in which the <code>SlaveTest</code> runs.
- */
-class SlaveHarness {
-
-    /** The port used for piping System.out/System.err to the master */
-    final static int LOG_PORT=10001;
-
-    /** The port used to accept request messages */
-    final static int REQUEST_PORT=10002;
-
-    /** the logger */
-    private static Logger logger =
-	Logger.getLogger("com.sun.jini.qa.harness");
-
-    /** 
-     * The config object used directly by this class. It's primary
-     * purpose is to establish default values for installation
-     * properties such as com.sun.jini.jsk.home. The config used
-     * by the request handler vm is provided by the master harness
-     * and may provide overrides for the installation properties.
-     */
-    QAConfig config;
-
-    /** The list of slaves participating in this test run */
-    private static ArrayList slaveList = new ArrayList();
-
-    /** Data structure holding slave info */
-    private static class SlaveData {
-	String name;
-	InetAddress addr;
-	Socket logSocket;
-	Pipe pipe;
-    };
-    
-    /**
-     * Called by the master harness to connect to all slave harnesses
-     * to be used by the test. If the test property
-     * com.sun.jini.qa.harness.testhosts specifies any slave harnesses,
-     * create the pipes to capture any output from the slave harnesses
-     * into the test log. Failure to connect to any slave within 5
-     * minutes (XXX parameterize) results in a TestException being
-     * thrown, aborting the test run.
-     * 
-     * @throws TestException on connection timeout
-     */
-    static void connect() throws TestException {
-	ArrayList hostList = QAConfig.getConfig().getHostList();
-	if (hostList.size() < 2) {
-	    return;
-	}
-	// connect to all slaves and build slaveList
-	for (int i = 1; i < hostList.size(); i++) {
-	    String host = (String) hostList.get(i);
-	    SlaveData slave = new SlaveData();
-	    slave.name = host;
-	    connectToSlave(slave); // throws exception on timeout
-	    slaveList.add(slave);
-	}
-    }
-
-    /**
-     * Called by the master harness to set the log stream for
-     * all slaves. Used to allow log output to be discarded
-     * for passing tests.
-     */
-    static void setLogStreams(PrintStream stream) {
-	for (int i = 0; i < slaveList.size(); i++) {
-	    SlaveData slave = (SlaveData) slaveList.get(i);
-	    slave.pipe.setStream(stream);
-	}
-    }
-
-    /**
-     * Connect to the given slave harness. Repeatedly attempt to open a socket
-     * to the slave log port. When successful, examine the value of the
-     * test property com.sun.jini.qa.harness.slavepipe. If defined and false,
-     * write a 0 to the socket. Otherwise, write a 1 to the socket. The slave
-     * reads this value to determine whether to pipe output through this socket.
-     * In either case, establish a pipe to pass input from the socket to the 
-     * logger. 
-     *
-     * @param slave the <code>SlaveData</code> corresponding to the slave
-     * @throws TestException if slave host is unknown or on timeout
-     */
-    private static void connectToSlave(SlaveData slave) throws TestException {
-	try {
-	    slave.addr = InetAddress.getByName(slave.name);
-	} catch (UnknownHostException e) {
-	    throw new TestException("Unexpected exception", e);
-	}
-	for (int i = 0; i < 120; i++) { // try for 20 min at 10 sec intervals
-	    try {
-		slave.logSocket = new Socket(slave.addr, LOG_PORT);
-	        OutputStream os = slave.logSocket.getOutputStream();
-	        boolean doPipe = 
-		    QAConfig.getConfig().getBooleanConfigVal(
-					  "com.sun.jini.qa.harness.slavepipe", 
-					  true);
-		os.write((doPipe? 1 : 0));
-	 	os.flush();
-		slave.pipe = new Pipe("slave-" + slave.name,
-				      slave.logSocket.getInputStream(),
-				      System.out,
-				      null,
-				      null);
-                slave.pipe.start();
-		return;
-	    } catch (ConnectException ignore) {
-	    } catch (IOException e) {
-		throw new TestException("IOException connecting to " 
-					+ slave.name,
-					e);
-	    }
-	    try {
-		Thread.sleep(10000);
-	    } catch (InterruptedException ignore) {
-	    }
-	}
-	throw new TestException("Timeout connecting to slave " + slave.name);
-    }
-
-    /**
-     * Sends a <code>HarnessRequest</code> object to a slave harness.
-     * A socket to the given slave's request port is opened and
-     * the request object is written to it. Then a read is hung
-     * on the socket. The object read from the socket is returned
-     * by this method; null is returned if the read throws an
-     * EOFException. In either case the socket is closed.
-     *
-     * @param slave the <code>SlaveData</code> describing the slave
-     * @param request the request to send
-     * @returns the reply object send by the slave, or null if the
-     *          slave sent no reply
-     * @throws TestException on a connection or communication failure
-     */
-    private static Object sendHarnessRequest(SlaveData slave, 
-					     HarnessRequest request)
-	throws TestException
-    {
-	logger.log(Level.FINER,
-		   "Sending request to " + slave.name + ": " + request);
-	Socket s = null;
-	try {
-	    s = new Socket(slave.addr, REQUEST_PORT);
-	    ObjectOutputStream oos =
-		new ObjectOutputStream(s.getOutputStream());
-	    oos.writeObject(request);
-	    oos.flush();
-	} catch (Exception e) {
-	    // fatal, so don't worry about closing sockets/streams
-	    throw new TestException("Unexpected exception sending " 
-				    + "request to slave " 
-				    + slave.name,
-				    e);
-	}
-	ObjectInputStream ois = null;
-	try {
-	    ois = new ObjectInputStream(s.getInputStream());
-	    Object o = ois.readObject();
-	    return o;
-	} catch (EOFException e) {
-	    return null;
-	} catch (Exception e) {
-	    throw new TestException("Unexpected exception receiving " 
-				    + "response from slave "
-				    + slave.name, e);
-	} finally {
-	    try {
-		ois.close();
-		s.close(); // redundant, I think
-	    } catch (Exception ignore) {
-	    }
-	}
-    }
-
-    /**
-     * Broadcast the given request to all participating slaves.
-     *
-     * @param request the request to broadcast
-     * @throws TestException on a connection or communication failure
-     */
-    static void broadcastRequest(HarnessRequest request) 
-	throws TestException 
-    {
-	for (int i = 0; i < slaveList.size(); i++) {
-	    SlaveData slave = (SlaveData) slaveList.get(i);
-	    sendHarnessRequest(slave, request);
-	}
-    }
-
-    /**
-     * Construct the slave harness and its associated default config object.
-     * Exits the vm if the arg list is empty or an exception occurs while
-     * constructing the config object.
-     *
-     * @param args the command line args
-     */
-    SlaveHarness(String[] args) {
-	if (args.length < 1) {
-	    logger.log(Level.SEVERE, "Missing arguments");
-	    System.exit(1);
-	}
-	try {
-	    config = new QAConfig(args);
-	} catch (Exception e) {
-	    e.printStackTrace();
-	    logger.log(Level.SEVERE, 
-		       "Unexpected exception constructing config", 
-		       e);
-	    System.exit(1);
-	}
-	// set these system properties so they will override any install
-	// properties included in a config sent by the master
-	System.setProperty("com.sun.jini.qa.home", config.getKitHomeDir());
-	System.setProperty("com.sun.jini.jsk.home", config.getJSKHomeDir());
-	System.setProperty("com.sun.jini.jsk.port", 
-			   config.getStringConfigVal("com.sun.jini.jsk.port", 
-						     null));
-	System.setProperty("com.sun.jini.qa.port", 
-			   config.getStringConfigVal("com.sun.jini.qa.port",
-						     null));
-	System.setProperty(
-		"com.sun.jini.qa.harness.runjiniserver", 
-		config.getStringConfigVal("com.sun.jini.qa.harness.runjiniserver",
-					  null));
-	System.setProperty(
-		"com.sun.jini.qa.harness.runkitserver", 
-		config.getStringConfigVal("com.sun.jini.qa.harness.runkitserver", 
-					  null));
-	boolean genHtml = config.getBooleanConfigVal(
-	    "com.sun.jini.qa.harness.generateHtml", false);
-	if (genHtml) {
-	    try {
-		HtmlReport htmlReport = new HtmlReport(config, null);
-		htmlReport.generate();
-	    } catch (Exception e) {
-		logger.log(Level.SEVERE, "Exception trying to generate index.html", e);
-	    }
-	}
-    }
-
-    /**
-     * Start the request handler thread and wait for a connection to the
-     * logging port. When the log connection is made, read a byte from the
-     * socket. If the value of the byte is non-zero, redirect System.out
-     * and System.err and logger output to the sockets output stream. 
-     * Start the keep-alive thread which will detect the death of the master.
-     * A System.exit is done if any exceptions occur.
-     */
-    void handleRequests() {
-	new Thread(new RequestHandler(), "RequestThread").start();
-	try {
-	    ServerSocket socket = new ServerSocket(LOG_PORT);
-	    Timeout.TimeoutHandler handler = 
-		new Timeout.ServerSocketTimeoutHandler(socket);
-	    Timeout timeout = new Timeout(handler, 20 * 60 * 1000); // 20 min
-	    timeout.start();
-	    Socket logSocket = socket.accept();
-	    timeout.cancel();
-	    InputStream is = logSocket.getInputStream();
-	    int pipeFlag = is.read();
-	    PrintStream ps = new PrintStream(logSocket.getOutputStream());
-	    if (pipeFlag !=0) {
-		System.setOut(ps);
-		System.setErr(ps);
-		reconfigureLogger();
-	    }
-	    InetAddress masterAddress = logSocket.getInetAddress();
-	    new Thread(new Keepalive(masterAddress), "KeepAlive").start();
-	} catch (Exception e) {
-	    logger.log(Level.SEVERE, "Unexpected exception", e);
-	    System.exit(1);
-	}
-    }
-    
-    /**
-     * Replace the <code>ReportHander</code> bound to the logger with a new
-     * instance of <code>ReportHandler</code>. This is necessary when output
-     * is redirected because the handler caches the value of System.err/out.
-     */
-    private void reconfigureLogger() {
-	Handler[] handlers = logger.getHandlers();
-	for (int i = 0; i < handlers.length; i++) {
-	    if (handlers[i] instanceof ReportHandler) {
-		logger.removeHandler(handlers[i]);
-	    }
-	}
-	logger.addHandler(new ReportHandler());
-    }
-
-    /**
-     * A thread which opens a socket to the master harness keep-alive
-     * port and hangs a read on the socket. If an EOF occurs, or any
-     * IOException is thrown, a System.exit is done.
-     */
-    private class Keepalive implements Runnable {
-
-	InetAddress addr;
-
-	public Keepalive(InetAddress addr) {
-	    this.addr = addr;
-	}
-
-	public void run() {
-	    try {
-		Socket s = new Socket(addr, MasterHarness.KEEPALIVE_PORT);
-		InputStream is = s.getInputStream();
-		while (true) {
-		    if (is.read() == -1) {
-			//best effort teardown in case master died. Do this
-			//because the System.exit doesn't seem to cleanup
-			try {
-			    callLocalSlaveHandler(new TeardownRequest());
-			} catch (Exception ignore) {
-			}
-			System.exit(1);
-		    }
-		}
-	    } catch (IOException e) {
-		e.printStackTrace();
-		System.exit(1);
-	    }
-	}
-    }
-
-    /**
-     * A thread which processes slave <code>HarnessRequest</code> objects.
-     * A socket is accepted, the request object read from the socket
-     * input stream, and the requests <code>doHarnessRequest</code> method
-     * is called. On return, the request socket is closed; no return object
-     * is written to the socket. Any exception thrown will result in a
-     * System.exit.
-     */
-    private class RequestHandler implements Runnable {
-
-	ServerSocket socket;
-	Socket requestSocket;
-	Class policyClass = null;
-
-	public void run() {
-	    if (policyClass == null) {
-		try {
-		    policyClass = 
-			Class.forName("com.sun.jini.qa.harness.MergedPolicyProvider");
-		    if (policyClass.getClassLoader().getParent() != null) {
-			logger.log(Level.SEVERE, 
-				   "MergedPolicyProvider must be "
-				 + "installed in an extensions directory");
-			System.exit(1);
-		    }
-		} catch (Exception e) {
-		    logger.log(Level.SEVERE, 
-			       "failed to find MergedPolicyProvider");
-		    System.exit(1);
-		}
-	    }
-	    try {
-		socket = new ServerSocket(REQUEST_PORT);
-		while (true) {
-		    requestSocket = socket.accept();
-		    ObjectInputStream ois = 
-			new ObjectInputStream(requestSocket.getInputStream());
-		    HarnessRequest request = (HarnessRequest) ois.readObject();
-		    logger.log(Level.FINER, 
-			       "Received harness request: " + request);
-		    request.doHarnessRequest(SlaveHarness.this);
-		    ois.close();
-		    requestSocket.close();
-		}
-	    } catch (Exception e) {
-		e.printStackTrace();
-		System.exit(1);
-	    }
-	}
-    }
-
-    /**
-     * Start the slave request handler VM. This is a callback to be used
-     * by a request handler. The given config object is used to construct
-     * the command line and is passed to the handler vm as a serialized
-     * object written to the processes System.in stream. Note that the
-     * given config is NOT the config associated with the slave harness,
-     * but is the serialized copy sent by the master harness in the
-     * request message. This method does not return until the request
-     * handlers request port is ready to accept requests.
-     *
-     * @param config the config object associated with the test
-     */
-    void startSlaveTest(QAConfig masterConfig) {
-	if (getHarnessJar() != null) {
-	    masterConfig.setDynamicParameter("harnessJar", getHarnessJar());
-	} 
-	if (config.getStringConfigVal("testJar", null) != null) {
-	    masterConfig.setDynamicParameter("testJar", config.getStringConfigVal("testJar", null));
-	} 
-	masterConfig.buildSearchList(config.getStringConfigVal("searchPath", ""));
-	// configuration used to be reread by QAConfig.readObject, but jar references
-	// need to be fixed up before loading the configuration, and the local
-	// testJar param is not available to readObject
-	try {
-	    masterConfig.loadTestConfiguration();
-	} catch (TestException e) {
-	    e.printStackTrace();
-	}
-        try {
-	    /*
-	     * the following should theoretically be done with overrides set,
-	     * since the qa kit may not be default and the TD for the test
-	     * could be different. Ignoring this for now, due to an expectation
-	     * that ultimately all qa kits must be the same on participants
-	     * to avoid QAConfig versioning problems
-	    */
-	    TestDescription td = masterConfig.getTestDescription();
-	    /* 
-	     * The getSystemProps method returns a collection of properties
-	     * which override the default values. These properties will
-	     * typically be com.sun.jini.jsk.home, etc. In many cases
-	     * this collection will be empty.
-	     */
-	    Properties overrideProperties = new Properties();
-	    String overrideProp = 
-		config.getStringConfigVal("com.sun.jini.qa.harness.slaveOverrides",
-					  null);
-	    if (overrideProp != null) {
-		String[] overrides = config.parseString(overrideProp, ", "); // null OK
-		for (int i = 0; i < overrides.length; i++) {
-		    String key = overrides[i];
-		    String value = config.getStringConfigVal(key, null);
-		    if (value != null) {
-			masterConfig.setDynamicParameter(key, value);
-			overrideProperties.setProperty(key, value);
-		    }
-		}
-	    }
-	    masterConfig.setHostNameToken();
-//XXXXXXX NEED TO SET NEW VALUES FOR testJar AND harnessJar !!!!!!
-	    String[] cmdArray = 
-//  		td.getCommandLine(masterConfig.getSystemProps("slave"));
-		td.getCommandLine(overrideProperties);
-	    if (logger.isLoggable(Level.FINE)) {
-		StringBuffer sb = new StringBuffer();
-		for (int i = 0; i < cmdArray.length; i++) {
-		    if (cmdArray[i].indexOf(' ') >= 0) {
-			sb.append("'").append(cmdArray[i]).append("' ");
-		    } else {
-			sb.append(cmdArray[i]).append(" ");
-		    }
-		}
-		logger.log(Level.FINE, 
-			   "Starting slave request handler "
-			 + "in separate process with command:");
-		logger.log(Level.FINE, sb.toString());
-	    }
-	    File workingDir = td.getWorkingDir();
-            Process proc = 
-		Runtime.getRuntime().exec(cmdArray, null, workingDir);
-	    ObjectOutputStream os = 
-		new ObjectOutputStream(proc.getOutputStream());
-	    os.writeObject(masterConfig);
-	    os.flush();
-            bindOutput(proc);
-	    if (! waitForSlaveTest()) {
-		//XXX should I throw an exception here?
-		logger.log(Level.SEVERE, 
-			   "No response from slave request handler");
-	    }
-	    // an exception here seems pretty fatal, so log it and exit
-	    // Delay to allow output to drain???
-        } catch (Throwable e) {
-	    logger.log(Level.INFO, "Unexpected exception", e);
-	    System.exit(1);
-	} 
-    }
-
-    // return null if not found - support running from classes directory
-    private String getHarnessJar() {
-	String classpath = System.getProperty("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;
-    }
-
-    /**
-     * Repeatedly attempt to send ping requests to the test request handler
-     * running on this host. This method returns when a ping is sent
-     * successfully, or after a ten-second timeout expires.
-     * 
-     * @return true if a ping was went successfully, false if all pings failed
-     */
-    private boolean waitForSlaveTest() {
-	for (int i = 0; i < 20; i++) {
-	    try {
-		callLocalSlaveHandler(new PingRequest());
-		return true;
-	    } catch (Exception ignore) {
-	    }
-	    try {
-		Thread.sleep(500);
-	    } catch (InterruptedException ignore) {
-	    }
-	}
-	return false;
-    }
-
-    /**
-     * Call the test request handler on the local system. Any exception
-     * thrown is propogated to the caller
-     *
-     * @param request the message to send
-     * @throws Exception if the local host name cannot be resolved, or if
-     *         the call to the request handler throws an exception.
-     */
-    private void callLocalSlaveHandler(SlaveRequest request) throws Exception {
-	String name = InetAddress.getLocalHost().getHostName();
-	SlaveTest.call(name, request);
-    }
-
-    /**
-     * Bind the given process System.out/System.err streams to this 
-     * objects logger.
-     *
-     * @param proc the process to bind
-     * @throws IOException if there is a problem getting the I/O streams
-     */
-    private void bindOutput(Process proc) throws IOException {
-        new Pipe("slaveharness-out", 
-		 proc.getInputStream(),
-		 System.out, 
-		 null,
-		 null).start();
-	new Pipe("slaveharness-err", 
-		 proc.getErrorStream(),
-		 System.out,
-		 null,
-		 null).start();
-    }
-}
+/*
+ * 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 java.io.File;
+import java.io.EOFException;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.net.ConnectException;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.logging.Handler;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.Properties;
+import java.util.StringTokenizer;
+import java.util.jar.JarFile;
+
+/**
+ * A 'harness' which accepts work requests from a master over
+ * a socket. 
+ * <p>
+ * Generation of the MasterTest and SlaveTest command lines:
+ * <p>
+ * There are 4 VMs which participate in the distributed test  process:
+ * <ul>
+ *   <li>the master harness
+ *   <li>the slave harness
+ *   <li>the master test
+ *   <li>the slave test request handler
+ * <ul>
+ * All 4 of these VMs maintain an instance of QAConfig. Because the
+ * VMs are running on different systems, potentially with different
+ * versions of the jdk/jsk kits, the values which define installation 
+ * directories for the master/slave tests are defined as system properties
+ * on the test VMs invoked by the master/slave harnesses.
+ * <p>
+ * When the MasterHarness and SlaveHarness classes are instantiated,
+ * they construct an instance of QAConfig. The constructor of QAConfig
+ * verifies the existance of the following system properties:
+ * <ul>
+ *    <li>com.sun.jini.jsk.home
+ *    <li>com.sun.jini.qa.home
+ * </ul>
+ * which are typically provided by the user config file. These values serve as
+ * the default values to apply to the master/slave test VMs.  When the
+ * <code>MasterHarness</code> creates the master test VM, it obtains the command
+ * line from the <code>TestDescription</code>.  When generating the command
+ * line, the TD temporarily sets override properties generated from the QAConfig
+ * getSystemProps("master") method.  Any master overrides supplied (indirectly)
+ * through the environment file (provided by the -env command line option) will
+ * be used to build the command line. Typically, one or more of the installation
+ * system properties listed above will be replaced if the test is to use a
+ * non-default kit. The master harness always provides property definitions for
+ * all of these properties so that policy files and ConfigurationFile entries
+ * have access to these definitions. The MasterHarness passes a serialized copy
+ * of its QAConfig to the master test over its System.in stream; it is not
+ * necessary for the master test to construct a QAConfig from scratch. Because
+ * system property definitions override the user configuration file values when
+ * QAConfig searches for a property, the values set on the command line
+ * by the harness will take precedence.
+ * <p>
+ * When the <code>MasterHarness</code> submits a request to the slave
+ * harness to create a <code>SlaveTest</code>, it also passes
+ * a serialized copy of its QAConfig. The <code>SlaveHarness</code>
+ * generates a command line, this time using overrides resulting from
+ * calling the QAConfig getSystemProps("slave") method. The information
+ * required to generate the slave overrides are carried in the serialized
+ * QAConfig object, so the <code>MasterHarness</code> controls the
+ * environment in which the <code>SlaveTest</code> runs.
+ */
+class SlaveHarness {
+
+    /** The port used for piping System.out/System.err to the master */
+    final static int LOG_PORT=10001;
+
+    /** The port used to accept request messages */
+    final static int REQUEST_PORT=10002;
+
+    /** the logger */
+    private static Logger logger =
+	Logger.getLogger("com.sun.jini.qa.harness");
+
+    /** 
+     * The config object used directly by this class. It's primary
+     * purpose is to establish default values for installation
+     * properties such as com.sun.jini.jsk.home. The config used
+     * by the request handler vm is provided by the master harness
+     * and may provide overrides for the installation properties.
+     */
+    QAConfig config;
+
+    /** The list of slaves participating in this test run */
+    private static ArrayList slaveList = new ArrayList();
+
+    /** Data structure holding slave info */
+    private static class SlaveData {
+	String name;
+	InetAddress addr;
+	Socket logSocket;
+	Pipe pipe;
+    };
+    
+    /**
+     * Called by the master harness to connect to all slave harnesses
+     * to be used by the test. If the test property
+     * com.sun.jini.qa.harness.testhosts specifies any slave harnesses,
+     * create the pipes to capture any output from the slave harnesses
+     * into the test log. Failure to connect to any slave within 5
+     * minutes (XXX parameterize) results in a TestException being
+     * thrown, aborting the test run.
+     * 
+     * @throws TestException on connection timeout
+     */
+    static void connect() throws TestException {
+	ArrayList hostList = QAConfig.getConfig().getHostList();
+	if (hostList.size() < 2) {
+	    return;
+	}
+	// connect to all slaves and build slaveList
+	for (int i = 1; i < hostList.size(); i++) {
+	    String host = (String) hostList.get(i);
+	    SlaveData slave = new SlaveData();
+	    slave.name = host;
+	    connectToSlave(slave); // throws exception on timeout
+	    slaveList.add(slave);
+	}
+    }
+
+    /**
+     * Called by the master harness to set the log stream for
+     * all slaves. Used to allow log output to be discarded
+     * for passing tests.
+     */
+    static void setLogStreams(PrintStream stream) {
+	for (int i = 0; i < slaveList.size(); i++) {
+	    SlaveData slave = (SlaveData) slaveList.get(i);
+	    slave.pipe.setStream(stream);
+	}
+    }
+
+    /**
+     * Connect to the given slave harness. Repeatedly attempt to open a socket
+     * to the slave log port. When successful, examine the value of the
+     * test property com.sun.jini.qa.harness.slavepipe. If defined and false,
+     * write a 0 to the socket. Otherwise, write a 1 to the socket. The slave
+     * reads this value to determine whether to pipe output through this socket.
+     * In either case, establish a pipe to pass input from the socket to the 
+     * logger. 
+     *
+     * @param slave the <code>SlaveData</code> corresponding to the slave
+     * @throws TestException if slave host is unknown or on timeout
+     */
+    private static void connectToSlave(SlaveData slave) throws TestException {
+	try {
+	    slave.addr = InetAddress.getByName(slave.name);
+	} catch (UnknownHostException e) {
+	    throw new TestException("Unexpected exception", e);
+	}
+	for (int i = 0; i < 120; i++) { // try for 20 min at 10 sec intervals
+	    try {
+		slave.logSocket = new Socket(slave.addr, LOG_PORT);
+	        OutputStream os = slave.logSocket.getOutputStream();
+	        boolean doPipe = 
+		    QAConfig.getConfig().getBooleanConfigVal(
+					  "com.sun.jini.qa.harness.slavepipe", 
+					  true);
+		os.write((doPipe? 1 : 0));
+	 	os.flush();
+		slave.pipe = new Pipe("slave-" + slave.name,
+				      slave.logSocket.getInputStream(),
+				      System.out,
+				      null,
+				      null);
+                slave.pipe.start();
+		return;
+	    } catch (ConnectException ignore) {
+	    } catch (IOException e) {
+		throw new TestException("IOException connecting to " 
+					+ slave.name,
+					e);
+	    }
+	    try {
+		Thread.sleep(10000);
+	    } catch (InterruptedException ignore) {
+                Thread.currentThread().interrupt();
+	    }
+	}
+	throw new TestException("Timeout connecting to slave " + slave.name);
+    }
+
+    /**
+     * Sends a <code>HarnessRequest</code> object to a slave harness.
+     * A socket to the given slave's request port is opened and
+     * the request object is written to it. Then a read is hung
+     * on the socket. The object read from the socket is returned
+     * by this method; null is returned if the read throws an
+     * EOFException. In either case the socket is closed.
+     *
+     * @param slave the <code>SlaveData</code> describing the slave
+     * @param request the request to send
+     * @returns the reply object send by the slave, or null if the
+     *          slave sent no reply
+     * @throws TestException on a connection or communication failure
+     */
+    private static Object sendHarnessRequest(SlaveData slave, 
+					     HarnessRequest request)
+	throws TestException
+    {
+	logger.log(Level.FINER,
+		   "Sending request to " + slave.name + ": " + request);
+	Socket s = null;
+	try {
+	    s = new Socket(slave.addr, REQUEST_PORT);
+	    ObjectOutputStream oos =
+		new ObjectOutputStream(s.getOutputStream());
+	    oos.writeObject(request);
+	    oos.flush();
+	} catch (Exception e) {
+	    // fatal, so don't worry about closing sockets/streams
+	    throw new TestException("Unexpected exception sending " 
+				    + "request to slave " 
+				    + slave.name,
+				    e);
+	}
+	ObjectInputStream ois = null;
+	try {
+	    ois = new ObjectInputStream(s.getInputStream());
+	    Object o = ois.readObject();
+	    return o;
+	} catch (EOFException e) {
+	    return null;
+	} catch (Exception e) {
+	    throw new TestException("Unexpected exception receiving " 
+				    + "response from slave "
+				    + slave.name, e);
+	} finally {
+	    try {
+		ois.close();
+		s.close(); // redundant, I think
+	    } catch (Exception ignore) {
+	    }
+	}
+    }
+
+    /**
+     * Broadcast the given request to all participating slaves.
+     *
+     * @param request the request to broadcast
+     * @throws TestException on a connection or communication failure
+     */
+    static void broadcastRequest(HarnessRequest request) 
+	throws TestException 
+    {
+	for (int i = 0; i < slaveList.size(); i++) {
+	    SlaveData slave = (SlaveData) slaveList.get(i);
+	    sendHarnessRequest(slave, request);
+	}
+    }
+
+    /**
+     * Construct the slave harness and its associated default config object.
+     * Exits the vm if the arg list is empty or an exception occurs while
+     * constructing the config object.
+     *
+     * @param args the command line args
+     */
+    SlaveHarness(String[] args) {
+	if (args.length < 1) {
+	    logger.log(Level.SEVERE, "Missing arguments");
+	    System.exit(1);
+	}
+	try {
+	    config = new QAConfig(args);
+	} catch (Exception e) {
+	    e.printStackTrace();
+	    logger.log(Level.SEVERE, 
+		       "Unexpected exception constructing config", 
+		       e);
+	    System.exit(1);
+	}
+	// set these system properties so they will override any install
+	// properties included in a config sent by the master
+	System.setProperty("com.sun.jini.qa.home", config.getKitHomeDir());
+	System.setProperty("com.sun.jini.jsk.home", config.getJSKHomeDir());
+	System.setProperty("com.sun.jini.jsk.port", 
+			   config.getStringConfigVal("com.sun.jini.jsk.port", 
+						     null));
+	System.setProperty("com.sun.jini.qa.port", 
+			   config.getStringConfigVal("com.sun.jini.qa.port",
+						     null));
+	System.setProperty(
+		"com.sun.jini.qa.harness.runjiniserver", 
+		config.getStringConfigVal("com.sun.jini.qa.harness.runjiniserver",
+					  null));
+	System.setProperty(
+		"com.sun.jini.qa.harness.runkitserver", 
+		config.getStringConfigVal("com.sun.jini.qa.harness.runkitserver", 
+					  null));
+	boolean genHtml = config.getBooleanConfigVal(
+	    "com.sun.jini.qa.harness.generateHtml", false);
+	if (genHtml) {
+	    try {
+		HtmlReport htmlReport = new HtmlReport(config, null);
+		htmlReport.generate();
+	    } catch (Exception e) {
+		logger.log(Level.SEVERE, "Exception trying to generate index.html", e);
+	    }
+	}
+    }
+
+    /**
+     * Start the request handler thread and wait for a connection to the
+     * logging port. When the log connection is made, read a byte from the
+     * socket. If the value of the byte is non-zero, redirect System.out
+     * and System.err and logger output to the sockets output stream. 
+     * Start the keep-alive thread which will detect the death of the master.
+     * A System.exit is done if any exceptions occur.
+     */
+    void handleRequests() {
+	new Thread(new RequestHandler(), "RequestThread").start();
+	try {
+	    ServerSocket socket = new ServerSocket(LOG_PORT);
+	    Timeout.TimeoutHandler handler = 
+		new Timeout.ServerSocketTimeoutHandler(socket);
+	    Timeout timeout = new Timeout(handler, 20 * 60 * 1000); // 20 min
+	    timeout.start();
+	    Socket logSocket = socket.accept();
+	    timeout.cancel();
+	    InputStream is = logSocket.getInputStream();
+	    int pipeFlag = is.read();
+	    PrintStream ps = new PrintStream(logSocket.getOutputStream());
+	    if (pipeFlag !=0) {
+		System.setOut(ps);
+		System.setErr(ps);
+		reconfigureLogger();
+	    }
+	    InetAddress masterAddress = logSocket.getInetAddress();
+	    new Thread(new Keepalive(masterAddress), "KeepAlive").start();
+	} catch (Exception e) {
+	    logger.log(Level.SEVERE, "Unexpected exception", e);
+	    System.exit(1);
+	}
+    }
+    
+    /**
+     * Replace the <code>ReportHander</code> bound to the logger with a new
+     * instance of <code>ReportHandler</code>. This is necessary when output
+     * is redirected because the handler caches the value of System.err/out.
+     */
+    private void reconfigureLogger() {
+	Handler[] handlers = logger.getHandlers();
+	for (int i = 0; i < handlers.length; i++) {
+	    if (handlers[i] instanceof ReportHandler) {
+		logger.removeHandler(handlers[i]);
+	    }
+	}
+	logger.addHandler(new ReportHandler());
+    }
+
+    /**
+     * A thread which opens a socket to the master harness keep-alive
+     * port and hangs a read on the socket. If an EOF occurs, or any
+     * IOException is thrown, a System.exit is done.
+     */
+    private class Keepalive implements Runnable {
+
+	InetAddress addr;
+
+	public Keepalive(InetAddress addr) {
+	    this.addr = addr;
+	}
+
+	public void run() {
+	    try {
+		Socket s = new Socket(addr, MasterHarness.KEEPALIVE_PORT);
+		InputStream is = s.getInputStream();
+		while (true) {
+		    if (is.read() == -1) {
+			//best effort teardown in case master died. Do this
+			//because the System.exit doesn't seem to cleanup
+			try {
+			    callLocalSlaveHandler(new TeardownRequest());
+			} catch (Exception ignore) {
+			}
+			System.exit(1);
+		    }
+		}
+	    } catch (IOException e) {
+		e.printStackTrace();
+		System.exit(1);
+	    }
+	}
+    }
+
+    /**
+     * A thread which processes slave <code>HarnessRequest</code> objects.
+     * A socket is accepted, the request object read from the socket
+     * input stream, and the requests <code>doHarnessRequest</code> method
+     * is called. On return, the request socket is closed; no return object
+     * is written to the socket. Any exception thrown will result in a
+     * System.exit.
+     */
+    private class RequestHandler implements Runnable {
+
+	ServerSocket socket;
+	Socket requestSocket;
+	Class policyClass = null;
+
+	public void run() {
+	    if (policyClass == null) {
+		try {
+		    policyClass = 
+			Class.forName("com.sun.jini.qa.harness.MergedPolicyProvider");
+		    if (policyClass.getClassLoader().getParent() != null) {
+			logger.log(Level.SEVERE, 
+				   "MergedPolicyProvider must be "
+				 + "installed in an extensions directory");
+			System.exit(1);
+		    }
+		} catch (Exception e) {
+		    logger.log(Level.SEVERE, 
+			       "failed to find MergedPolicyProvider");
+		    System.exit(1);
+		}
+	    }
+	    try {
+		socket = new ServerSocket(REQUEST_PORT);
+		while (true) {
+		    requestSocket = socket.accept();
+		    ObjectInputStream ois = 
+			new ObjectInputStream(requestSocket.getInputStream());
+		    HarnessRequest request = (HarnessRequest) ois.readObject();
+		    logger.log(Level.FINER, 
+			       "Received harness request: " + request);
+		    request.doHarnessRequest(SlaveHarness.this);
+		    ois.close();
+		    requestSocket.close();
+		}
+	    } catch (Exception e) {
+		e.printStackTrace();
+		System.exit(1);
+	    }
+	}
+    }
+
+    /**
+     * Start the slave request handler VM. This is a callback to be used
+     * by a request handler. The given config object is used to construct
+     * the command line and is passed to the handler vm as a serialized
+     * object written to the processes System.in stream. Note that the
+     * given config is NOT the config associated with the slave harness,
+     * but is the serialized copy sent by the master harness in the
+     * request message. This method does not return until the request
+     * handlers request port is ready to accept requests.
+     *
+     * @param config the config object associated with the test
+     */
+    void startSlaveTest(QAConfig masterConfig) {
+	if (getHarnessJar() != null) {
+	    masterConfig.setDynamicParameter("harnessJar", getHarnessJar());
+	} 
+	if (config.getStringConfigVal("testJar", null) != null) {
+	    masterConfig.setDynamicParameter("testJar", config.getStringConfigVal("testJar", null));
+	} 
+	masterConfig.buildSearchList(config.getStringConfigVal("searchPath", ""));
+	// configuration used to be reread by QAConfig.readObject, but jar references
+	// need to be fixed up before loading the configuration, and the local
+	// testJar param is not available to readObject
+	try {
+	    masterConfig.loadTestConfiguration();
+	} catch (TestException e) {
+	    e.printStackTrace();
+	}
+        try {
+	    /*
+	     * the following should theoretically be done with overrides set,
+	     * since the qa kit may not be default and the TD for the test
+	     * could be different. Ignoring this for now, due to an expectation
+	     * that ultimately all qa kits must be the same on participants
+	     * to avoid QAConfig versioning problems
+	    */
+	    TestDescription td = masterConfig.getTestDescription();
+	    /* 
+	     * The getSystemProps method returns a collection of properties
+	     * which override the default values. These properties will
+	     * typically be com.sun.jini.jsk.home, etc. In many cases
+	     * this collection will be empty.
+	     */
+	    Properties overrideProperties = new Properties();
+	    String overrideProp = 
+		config.getStringConfigVal("com.sun.jini.qa.harness.slaveOverrides",
+					  null);
+	    if (overrideProp != null) {
+		String[] overrides = config.parseString(overrideProp, ", "); // null OK
+		for (int i = 0; i < overrides.length; i++) {
+		    String key = overrides[i];
+		    String value = config.getStringConfigVal(key, null);
+		    if (value != null) {
+			masterConfig.setDynamicParameter(key, value);
+			overrideProperties.setProperty(key, value);
+		    }
+		}
+	    }
+	    masterConfig.setHostNameToken();
+//XXXXXXX NEED TO SET NEW VALUES FOR testJar AND harnessJar !!!!!!
+	    String[] cmdArray = 
+//  		td.getCommandLine(masterConfig.getSystemProps("slave"));
+		td.getCommandLine(overrideProperties);
+	    if (logger.isLoggable(Level.FINE)) {
+		StringBuffer sb = new StringBuffer();
+		for (int i = 0; i < cmdArray.length; i++) {
+		    if (cmdArray[i].indexOf(' ') >= 0) {
+			sb.append("'").append(cmdArray[i]).append("' ");
+		    } else {
+			sb.append(cmdArray[i]).append(" ");
+		    }
+		}
+		logger.log(Level.FINE, 
+			   "Starting slave request handler "
+			 + "in separate process with command:");
+		logger.log(Level.FINE, sb.toString());
+	    }
+	    File workingDir = td.getWorkingDir();
+            Process proc = 
+		Runtime.getRuntime().exec(cmdArray, null, workingDir);
+	    ObjectOutputStream os = 
+		new ObjectOutputStream(proc.getOutputStream());
+	    os.writeObject(masterConfig);
+	    os.flush();
+            bindOutput(proc);
+	    if (! waitForSlaveTest()) {
+		//XXX should I throw an exception here?
+		logger.log(Level.SEVERE, 
+			   "No response from slave request handler");
+	    }
+	    // an exception here seems pretty fatal, so log it and exit
+	    // Delay to allow output to drain???
+        } catch (Throwable e) {
+	    logger.log(Level.INFO, "Unexpected exception", e);
+	    System.exit(1);
+	} 
+    }
+
+    // return null if not found - support running from classes directory
+    private String getHarnessJar() {
+	String classpath = System.getProperty("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;
+    }
+
+    /**
+     * Repeatedly attempt to send ping requests to the test request handler
+     * running on this host. This method returns when a ping is sent
+     * successfully, or after a ten-second timeout expires.
+     * 
+     * @return true if a ping was went successfully, false if all pings failed
+     */
+    private boolean waitForSlaveTest() {
+	for (int i = 0; i < 20; i++) {
+	    try {
+		callLocalSlaveHandler(new PingRequest());
+		return true;
+	    } catch (Exception ignore) {
+	    }
+	    try {
+		Thread.sleep(500);
+	    } catch (InterruptedException ignore) {
+                Thread.currentThread().interrupt();
+	    }
+	}
+	return false;
+    }
+
+    /**
+     * Call the test request handler on the local system. Any exception
+     * thrown is propogated to the caller
+     *
+     * @param request the message to send
+     * @throws Exception if the local host name cannot be resolved, or if
+     *         the call to the request handler throws an exception.
+     */
+    private void callLocalSlaveHandler(SlaveRequest request) throws Exception {
+	String name = InetAddress.getLocalHost().getHostName();
+	SlaveTest.call(name, request);
+    }
+
+    /**
+     * Bind the given process System.out/System.err streams to this 
+     * objects logger.
+     *
+     * @param proc the process to bind
+     * @throws IOException if there is a problem getting the I/O streams
+     */
+    private void bindOutput(Process proc) throws IOException {
+        new Pipe("slaveharness-out", 
+		 proc.getInputStream(),
+		 System.out, 
+		 null,
+		 null).start();
+	new Pipe("slaveharness-err", 
+		 proc.getErrorStream(),
+		 System.out,
+		 null,
+		 null).start();
+    }
+}

Modified: river/jtsk/skunk/qa_refactor/trunk/qa/src/com/sun/jini/qa/harness/SlaveTest.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/qa_refactor/trunk/qa/src/com/sun/jini/qa/harness/SlaveTest.java?rev=1634322&r1=1634321&r2=1634322&view=diff
==============================================================================
--- river/jtsk/skunk/qa_refactor/trunk/qa/src/com/sun/jini/qa/harness/SlaveTest.java (original)
+++ river/jtsk/skunk/qa_refactor/trunk/qa/src/com/sun/jini/qa/harness/SlaveTest.java Sun Oct 26 13:17:28 2014
@@ -1,381 +1,391 @@
-/*
- * 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 java.io.EOFException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.io.PrintStream;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.security.PrivilegedAction;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.logging.Logger;
-import java.util.logging.Level;
-
-import javax.security.auth.login.LoginContext;
-import javax.security.auth.login.LoginException;
-import javax.security.auth.Subject;
-
-import net.jini.config.Configuration;
-
-/**
- * The slave side of a distributed test. This class provides the main
- * method for in its own VM, provides static utility method for
- * sending messages to a <code>SlaveTest</code> instance, and implement
- * a message handler to receive and execute <code>SlaveRequest</code>
- * messages.
- */
-public class SlaveTest {
-
-    /** the port for receiving <code>SlaveRequest</code> messages */
-    private final static int REQUEST_PORT=10003;
-
-    /** the logger */
-    private static Logger logger = 
-	Logger.getLogger("com.sun.jini.qa.harness");
-
-    /** the original <code>System.err</code> stream for this VM */
-    private static PrintStream origErr;
-
-    /** the test properties, unmarshalled from <code>System.in</code> */
-    static QAConfig config; // MasterTest access hack
-
-    /** a flag indicating that the VM should <code>System.exit</code> */
-    private static boolean doExit = false;
-
-    /** the admin manager */
-    static AdminManager manager; // MasterTest access hack
-
-    /** persistent storage across calls */
-    private static HashMap storageMap = new HashMap();
-
-    /**
-     * Utility method to send a <code>SlaveRequest</code> message
-     * to a SlaveTest instance.
-     *
-     * @param name the name of the slave host
-     * @param request the request to send
-     * @return the object by the <code>SlaveRequest.doSlaveRequest</code> method
-     * @throws TestException if the call fails
-     */
-    public static Object call(String name, SlaveRequest request) 
-	throws TestException 
-    {
-	Socket s = null;
-	logger.log(Level.FINE, 
-		   "Sending request to slave test on " + name + ": " + request);
-	try {
-	    s = new Socket(name, REQUEST_PORT);
-	    ObjectOutputStream oos =
-		new ObjectOutputStream(s.getOutputStream());
-	    oos.writeObject(request);
-	    oos.flush();
-	    //	    oos.close();
-	} catch (Exception e) {
-	    // fatal, so don't worry about closing sockets/streams
-	    throw new TestException("Unexpected exception sending " 
-				    + "request to slave " 
-				    + name, e);
-	}
-	ObjectInputStream ois = null;
-	try {
-	    ois = new ObjectInputStream(s.getInputStream());
-	    Object o = ois.readObject();
-	    return o;
-	} catch (EOFException e) {
-	    return null;
-	} catch (Exception e) {
-	    throw new TestException("Unexpected exception receiving " 
-				    + "response from slave "
-				    + name);
-	} finally {
-	    try {
-		ois.close();
-		s.close(); // redundant, I think
-	    } catch (Exception ignore) {
-	    }
-	}
-    }
-
-    /**
-     * Utility method to 'broadcast' a <code>SlaveRequest</code> message
-     * to all participating <code>SlaveTest</code>s. The request
-     * is sent to each participant in sequence. Failures are logged,
-     * but otherwise ignored.
-     *
-     * @param request the <code>SlaveRequest</code> to send
-     */
-    public static void broadcast(SlaveRequest request) {
-	ArrayList hostList = QAConfig.getConfig().getHostList();
-	if (hostList.size() < 2) {
-	    return;
-	}
-	for (int i = 1; i < hostList.size(); i++) {
-	    String name = (String) hostList.get(i);
-	    try {
-		call(name, request);
-	    } catch (Exception e) {
-		logger.log(Level.INFO, "Call to slave threw exception", e);
-	    }
-	}
-    }
-
-    /**
-     * Wait for all slaves to die. All participating slave are
-     * pinged once per second until <code>timeout</code> seconds
-     * elapse. When all pings throw <code>Exception</code>,
-     * the slaves are assumed to be dead and this method returns.
-     *
-     * @param timeout maximum time in seconds to way for slave death. If
-     *        zero or negative, this method returns immediately
-     */ 
-    public static void waitForSlaveDeath(int timeout) {
-	ArrayList hostList = QAConfig.getConfig().getHostList();
-	if (hostList.size() < 2) {
-	    return;
-	}
-	ArrayList slaveList = new ArrayList();
-	for (int i = 1; i < hostList.size(); i++) {
-	    slaveList.add(hostList.get(i));
-	}
-	PingRequest ping = new PingRequest();
-	for (int i = 0; i < timeout; i++) {
-	    for (int j = slaveList.size() - 1; j >= 0; j--) {
-		String slaveName = (String) slaveList.get(j);
-		try {
-		    call(slaveName, ping);
-		} catch (Exception e) {
-		    slaveList.remove(j);
-		}
-	    }
-	    if (slaveList.size() == 0) {
-		return;
-	    }
-	    try {
-		Thread.sleep(1000);
-	    } catch (InterruptedException e) {
-	    }
-	}
-    }
-	
-
-    /**
-     * The main method for the slave test VM. The <code>QAConfig</code>
-     * object is read from <code>System.in</code>, and <code>AdminManager</code>
-     * is instantiated, class servers are started, and the request
-     * handling loop is entered (optionally in the context of a user login).
-     * The <code>args</code> passed to the main method are not used; the
-     * args supplied to the master test are available in <code>QAConfig</code>.
-     *
-     * @param args the command line arguments (unused)
-     */
-    public static void main(String[] args) {
-	origErr = System.err;
-	System.setErr(System.out);
-	if (System.getSecurityManager() == null) {
-	    System.setSecurityManager(new java.rmi.RMISecurityManager());
-	}
-	try {
-	    ObjectInputStream ois = new ObjectInputStream(System.in);
-	    config = (QAConfig) ois.readObject();
-	} catch (Exception e) {
-	    e.printStackTrace();
-	    System.exit(1);
-	}
-	// used to be handled by config.readObject, but this broke SlaveHarness
-	try {
-	    config.loadTestConfiguration();
-	} catch (TestException e) {
-	    e.printStackTrace();
-	}
-	manager = new AdminManager(config);
-	Configuration c = config.getConfiguration(); // the davis config
-	LoginContext context = null;
-	try {
-	    context = (LoginContext) c.getEntry("test", 
-						"loginContext",
-						LoginContext.class, 
-						null);
-	    if (context != null) {
-		logger.log(Level.FINEST, "got a login context");
-	    }
-	} catch (Throwable e) {
-	    e.printStackTrace();
-	    System.exit(1);
-	}	
-	Thread autotRequestThread =
-	    new Thread(new AutotRequestHandler());
-	// this property is supplied by the generator
-	String callAutot = 
-	    config.getStringConfigVal("com.sun.jini.qa.harness.callAutoT", 
-				      null);
-	if (callAutot != null) {
-	    autotRequestThread.setDaemon(true);
-	    autotRequestThread.start();
-	    config.enableTestHostCalls(true);
-	}
-	config.callTestHost(new InboundCallsEnabledRequest(true));
-	config.callTestHost(new TestStatusRequest("Handling slave requests"));
-	if (context != null) {
-	    handleRequestsWithLogin(context); //must call exit
-	} else {
-	    handleRequests(); // must call exit
-	}
-    }
-
-    /**
-     * Run the request loop in the context of the given 
-     * <code>LoginContext</code>.
-     *
-     * @param context the <code>LoginContext</code> to use
-     */
-    private static void handleRequestsWithLogin(LoginContext context) {
-	try {
-	    context.login();
-	} catch (Throwable e) {
-	    e.printStackTrace();
-	    System.exit(1);
-	}
-	// doTest should always call exit, so this call never returns
-	Subject.doAsPrivileged(context.getSubject(),
-			       new PrivilegedAction() {
-				       public Object run() {
-					   handleRequests();
-					   return null;
-				       }
-				   },
-			       null);
-    }
-
-    /**
-     * The loop for accepting and processing <code>SlaveRequest</code>
-     * messages. Each message is sent over a separate socket connection
-     * in serialized form. After unmarshalling the request, the
-     * <code>doSlaveRequeset</code> method is called passing an instance
-     * of this class; the object returned by the method is written
-     * back to the caller and the connection closed. If the method
-     * throws an exception, the exception is written back instead. 
-     * The loop (and VM) exits when a <code>SlaveRequest</code> is
-     * received which call <code>SlaveTest.exit()</code>.
-     */
-    private static void handleRequests() {
-	try {
-	    ServerSocket socket = new ServerSocket(REQUEST_PORT);
-	    while (!doExit) {
-		Socket requestSocket = socket.accept();
-	 	logger.log(Level.FINER, "Got a test slave request");
-		ObjectInputStream ois = 
-		    new ObjectInputStream(requestSocket.getInputStream());
-		SlaveRequest request = (SlaveRequest) ois.readObject();
-	        logger.log(Level.FINER, "Request is: " + request);
-		Object o = null;
-		try {
-		    o = request.doSlaveRequest(new SlaveTest());
-		} catch (Throwable e) {
-		    logger.log(Level.SEVERE, "Unexpected Exception", e);
-		    o = e;
-		}
-		ObjectOutputStream oos = 
-		    new ObjectOutputStream(requestSocket.getOutputStream());
-		oos.writeObject(o);
-		oos.flush();
-		oos.close();
-		requestSocket.close();//redundant??
-	    }
-	    socket.close();
-	    config.callTestHost(new TestStatusRequest("Advancing to next test"));
-	    config.callTestHost(new InboundCallsEnabledRequest(false));
-	    System.exit(0);
-	} catch (Throwable e) {
-	    logger.log(Level.SEVERE, "Unexpected exception", e);
-	    System.exit(1); //???
-	}
-    }
-
-    /**
-     * Called by a <code>SlaveRequest</code> to cause the <code>SlaveTest</code>
-     * to exit.
-     */
-    void exit() {
-	doExit = true;
-    }
-
-    /**
-     * Accessor for <code>SlaveRequest</code> to obtain the config object.
-     *
-     * @return the <code>QAConfig</code> object
-     */
-    public QAConfig getConfig() {
-	return config;
-    }
-
-    /**
-     * Accessor for <code>SlaveRequest</code> to obtain the admin manager.
-     *
-     * @return the <code>AdminManager</code>
-     */
-    public AdminManager getAdminManager() {
-	return manager;
-    }
-
-    /**
-     * Accessor for the storage map which allow the test to
-     * maintain state across slave calls.
-     * 
-     * @return the storage map
-     */
-    public HashMap getStorageMap() {
-	return storageMap;
-    }
-
-    private static class AutotRequestHandler implements Runnable {
-
-	public void run() {
-	    try {
-		ServerSocket socket = 
-		    new ServerSocket(InboundAutotRequest.PORT);
-		while (true) {
-		    Socket requestSocket = socket.accept();
-		    logger.log(Level.FINER, "Got an external request");
-		    ObjectInputStream ois = 
-			new ObjectInputStream(requestSocket.getInputStream());
-		    InboundAutotRequest request = 
-			(InboundAutotRequest) ois.readObject();
-		    logger.log(Level.FINER, "Request is: " + request);
-		    Object o = null;
-		    try {
-			o = request.doRequest(config, manager);
-		    } catch (Throwable e) {
-			logger.log(Level.SEVERE, "Unexpected Exception", e);
-			o = e;
-		    }
-		    ObjectOutputStream oos = 
-			new ObjectOutputStream(requestSocket.getOutputStream());
-		    oos.writeObject(o);
-		    oos.flush();
-		    oos.close();
-		    requestSocket.close();//redundant??
-		}
-	    } catch (Throwable e) {
-		logger.log(Level.SEVERE, "Unexpected exception", e);
-	    }
-	}
-    }
-}
+/*
+ * 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 java.io.EOFException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.PrintStream;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.logging.Logger;
+import java.util.logging.Level;
+
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+import javax.security.auth.Subject;
+
+import net.jini.config.Configuration;
+import org.apache.river.api.security.CombinerSecurityManager;
+
+/**
+ * The slave side of a distributed test. This class provides the main
+ * method for in its own VM, provides static utility method for
+ * sending messages to a <code>SlaveTest</code> instance, and implement
+ * a message handler to receive and execute <code>SlaveRequest</code>
+ * messages.
+ */
+public class SlaveTest {
+
+    /** the port for receiving <code>SlaveRequest</code> messages */
+    private final static int REQUEST_PORT=10003;
+
+    /** the logger */
+    private static Logger logger = 
+	Logger.getLogger("com.sun.jini.qa.harness");
+
+    /** the original <code>System.err</code> stream for this VM */
+    private static PrintStream origErr;
+
+    /** the test properties, unmarshalled from <code>System.in</code> */
+    static QAConfig config; // MasterTest access hack
+
+    /** a flag indicating that the VM should <code>System.exit</code> */
+    private static boolean doExit = false;
+
+    /** the admin manager */
+    static AdminManager manager; // MasterTest access hack
+
+    /** persistent storage across calls */
+    private static HashMap storageMap = new HashMap();
+
+    /**
+     * Utility method to send a <code>SlaveRequest</code> message
+     * to a SlaveTest instance.
+     *
+     * @param name the name of the slave host
+     * @param request the request to send
+     * @return the object by the <code>SlaveRequest.doSlaveRequest</code> method
+     * @throws TestException if the call fails
+     */
+    public static Object call(String name, SlaveRequest request) 
+	throws TestException 
+    {
+	Socket s = null;
+	logger.log(Level.FINE, 
+		   "Sending request to slave test on " + name + ": " + request);
+	try {
+	    s = new Socket(name, REQUEST_PORT);
+	    ObjectOutputStream oos =
+		new ObjectOutputStream(s.getOutputStream());
+	    oos.writeObject(request);
+	    oos.flush();
+	    //	    oos.close();
+	} catch (Exception e) {
+	    // fatal, so don't worry about closing sockets/streams
+	    throw new TestException("Unexpected exception sending " 
+				    + "request to slave " 
+				    + name, e);
+	}
+	ObjectInputStream ois = null;
+	try {
+	    ois = new ObjectInputStream(s.getInputStream());
+	    Object o = ois.readObject();
+	    return o;
+	} catch (EOFException e) {
+	    return null;
+	} catch (Exception e) {
+	    throw new TestException("Unexpected exception receiving " 
+				    + "response from slave "
+				    + name);
+	} finally {
+	    try {
+		ois.close();
+		s.close(); // redundant, I think
+	    } catch (Exception ignore) {
+	    }
+	}
+    }
+
+    /**
+     * Utility method to 'broadcast' a <code>SlaveRequest</code> message
+     * to all participating <code>SlaveTest</code>s. The request
+     * is sent to each participant in sequence. Failures are logged,
+     * but otherwise ignored.
+     *
+     * @param request the <code>SlaveRequest</code> to send
+     */
+    public static void broadcast(SlaveRequest request) {
+	ArrayList hostList = QAConfig.getConfig().getHostList();
+	if (hostList.size() < 2) {
+	    return;
+	}
+	for (int i = 1; i < hostList.size(); i++) {
+	    String name = (String) hostList.get(i);
+	    try {
+		call(name, request);
+	    } catch (Exception e) {
+		logger.log(Level.INFO, "Call to slave threw exception", e);
+	    }
+	}
+    }
+
+    /**
+     * Wait for all slaves to die. All participating slave are
+     * pinged once per second until <code>timeout</code> seconds
+     * elapse. When all pings throw <code>Exception</code>,
+     * the slaves are assumed to be dead and this method returns.
+     *
+     * @param timeout maximum time in seconds to way for slave death. If
+     *        zero or negative, this method returns immediately
+     */ 
+    public static void waitForSlaveDeath(int timeout) {
+	ArrayList hostList = QAConfig.getConfig().getHostList();
+	if (hostList.size() < 2) {
+	    return;
+	}
+	ArrayList slaveList = new ArrayList();
+	for (int i = 1; i < hostList.size(); i++) {
+	    slaveList.add(hostList.get(i));
+	}
+	PingRequest ping = new PingRequest();
+	for (int i = 0; i < timeout; i++) {
+	    for (int j = slaveList.size() - 1; j >= 0; j--) {
+		String slaveName = (String) slaveList.get(j);
+		try {
+		    call(slaveName, ping);
+		} catch (Exception e) {
+		    slaveList.remove(j);
+		}
+	    }
+	    if (slaveList.size() == 0) {
+		return;
+	    }
+	    try {
+		Thread.sleep(1000);
+	    } catch (InterruptedException e) {
+                Thread.currentThread().interrupt(); // restore
+	    }
+	}
+    }
+	
+
+    /**
+     * The main method for the slave test VM. The <code>QAConfig</code>
+     * object is read from <code>System.in</code>, and <code>AdminManager</code>
+     * is instantiated, class servers are started, and the request
+     * handling loop is entered (optionally in the context of a user login).
+     * The <code>args</code> passed to the main method are not used; the
+     * args supplied to the master test are available in <code>QAConfig</code>.
+     *
+     * @param args the command line arguments (unused)
+     */
+    public static void main(String[] args) {
+	origErr = System.err;
+	System.setErr(System.out);
+	if (System.getSecurityManager() == null) {
+	    System.setSecurityManager(new CombinerSecurityManager());
+	}
+	try {
+	    ObjectInputStream ois = new ObjectInputStream(System.in);
+	    config = (QAConfig) ois.readObject();
+	} catch (Exception e) {
+            e.fillInStackTrace();
+	    logger.log(Level.SEVERE, "Unexpected exception ", e);
+	    System.exit(1);
+	}
+	// used to be handled by config.readObject, but this broke SlaveHarness
+	try {
+	    config.loadTestConfiguration();
+	} catch (TestException e) {
+            e.fillInStackTrace();
+	    logger.log(Level.SEVERE, "Unexpected exception ", e);
+	}
+	manager = new AdminManager(config);
+	Configuration c = config.getConfiguration(); // the davis config
+	LoginContext context = null;
+	try {
+	    context = (LoginContext) c.getEntry("test", 
+						"loginContext",
+						LoginContext.class, 
+						null);
+	    if (context != null) {
+		logger.log(Level.FINEST, "got a login context");
+	    }
+	} catch (Throwable e) {
+            e.fillInStackTrace();
+	    logger.log(Level.SEVERE, "Unexpected exception ", e);
+	    System.exit(1);
+	}	
+	Thread autotRequestThread =
+	    new Thread(new AutotRequestHandler());
+	// this property is supplied by the generator
+	String callAutot = 
+	    config.getStringConfigVal("com.sun.jini.qa.harness.callAutoT", 
+				      null);
+	if (callAutot != null) {
+	    autotRequestThread.setDaemon(true);
+	    autotRequestThread.start();
+	    config.enableTestHostCalls(true);
+	}
+	config.callTestHost(new InboundCallsEnabledRequest(true));
+	config.callTestHost(new TestStatusRequest("Handling slave requests"));
+	if (context != null) {
+	    handleRequestsWithLogin(context); //must call exit
+	} else {
+	    handleRequests(); // must call exit
+	}
+    }
+
+    /**
+     * Run the request loop in the context of the given 
+     * <code>LoginContext</code>.
+     *
+     * @param context the <code>LoginContext</code> to use
+     */
+    private static void handleRequestsWithLogin(LoginContext context) {
+	try {
+	    context.login();
+	} catch (Throwable e) {
+            e.fillInStackTrace();
+	    logger.log(Level.SEVERE, "Unexpected exception ", e);
+	    System.exit(1);
+	}
+	// doTest should always call exit, so this call never returns
+	Subject.doAsPrivileged(context.getSubject(),
+			       new PrivilegedAction() {
+				       public Object run() {
+					   handleRequests();
+					   return null;
+				       }
+				   },
+			       null);
+    }
+
+    /**
+     * The loop for accepting and processing <code>SlaveRequest</code>
+     * messages. Each message is sent over a separate socket connection
+     * in serialized form. After unmarshalling the request, the
+     * <code>doSlaveRequeset</code> method is called passing an instance
+     * of this class; the object returned by the method is written
+     * back to the caller and the connection closed. If the method
+     * throws an exception, the exception is written back instead. 
+     * The loop (and VM) exits when a <code>SlaveRequest</code> is
+     * received which call <code>SlaveTest.exit()</code>.
+     */
+    private static void handleRequests() {
+	try {
+	    ServerSocket socket = new ServerSocket(REQUEST_PORT);
+	    while (!doExit) {
+		Socket requestSocket = socket.accept();
+	 	logger.log(Level.FINER, "Got a test slave request");
+		ObjectInputStream ois = 
+		    new ObjectInputStream(requestSocket.getInputStream());
+		SlaveRequest request = (SlaveRequest) ois.readObject();
+	        logger.log(Level.FINER, "Request is: " + request);
+		Object o = null;
+		try {
+		    o = request.doSlaveRequest(new SlaveTest());
+		} catch (Throwable e) {
+                    e.fillInStackTrace();
+		    logger.log(Level.SEVERE, "Unexpected Exception ", e);
+		    o = e;
+		}
+		ObjectOutputStream oos = 
+		    new ObjectOutputStream(requestSocket.getOutputStream());
+		oos.writeObject(o);
+		oos.flush();
+		oos.close();
+		requestSocket.close();//redundant??
+	    }
+	    socket.close();
+	    config.callTestHost(new TestStatusRequest("Advancing to next test"));
+	    config.callTestHost(new InboundCallsEnabledRequest(false));
+	    System.exit(0);
+	} catch (Throwable e) {
+            e.fillInStackTrace();
+	    logger.log(Level.SEVERE, "Unexpected exception", e);
+	    System.exit(1); //???
+	}
+    }
+
+    /**
+     * Called by a <code>SlaveRequest</code> to cause the <code>SlaveTest</code>
+     * to exit.
+     */
+    void exit() {
+	doExit = true;
+    }
+
+    /**
+     * Accessor for <code>SlaveRequest</code> to obtain the config object.
+     *
+     * @return the <code>QAConfig</code> object
+     */
+    public QAConfig getConfig() {
+	return config;
+    }
+
+    /**
+     * Accessor for <code>SlaveRequest</code> to obtain the admin manager.
+     *
+     * @return the <code>AdminManager</code>
+     */
+    public AdminManager getAdminManager() {
+	return manager;
+    }
+
+    /**
+     * Accessor for the storage map which allow the test to
+     * maintain state across slave calls.
+     * 
+     * @return the storage map
+     */
+    public HashMap getStorageMap() {
+	return storageMap;
+    }
+
+    private static class AutotRequestHandler implements Runnable {
+
+	public void run() {
+	    try {
+		ServerSocket socket = 
+		    new ServerSocket(InboundAutotRequest.PORT);
+		while (true) {
+		    Socket requestSocket = socket.accept();
+		    logger.log(Level.FINER, "Got an external request");
+		    ObjectInputStream ois = 
+			new ObjectInputStream(requestSocket.getInputStream());
+		    InboundAutotRequest request = 
+			(InboundAutotRequest) ois.readObject();
+		    logger.log(Level.FINER, "Request is: " + request);
+		    Object o = null;
+		    try {
+			o = request.doRequest(config, manager);
+		    } catch (Throwable e) {
+                        e.fillInStackTrace();
+			logger.log(Level.SEVERE, "Unexpected Exception ", e);
+			o = e;
+		    }
+		    ObjectOutputStream oos = 
+			new ObjectOutputStream(requestSocket.getOutputStream());
+		    oos.writeObject(o);
+		    oos.flush();
+		    oos.close();
+		    requestSocket.close();//redundant??
+		}
+	    } catch (Throwable e) {
+                e.fillInStackTrace();
+		logger.log(Level.SEVERE, "Unexpected exception ", e);
+	    }
+	}
+    }
+}

Modified: river/jtsk/skunk/qa_refactor/trunk/qa/src/com/sun/jini/qa/harness/Timeout.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/qa_refactor/trunk/qa/src/com/sun/jini/qa/harness/Timeout.java?rev=1634322&r1=1634321&r2=1634322&view=diff
==============================================================================
--- river/jtsk/skunk/qa_refactor/trunk/qa/src/com/sun/jini/qa/harness/Timeout.java (original)
+++ river/jtsk/skunk/qa_refactor/trunk/qa/src/com/sun/jini/qa/harness/Timeout.java Sun Oct 26 13:17:28 2014
@@ -1,190 +1,191 @@
-/*
- * 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 java.net.Socket;
-import java.net.ServerSocket;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Date;
-
-/**
- * Detects timeouts and calls a timeout handler.
- */
-class Timeout implements Runnable {
-
-    /** An interface defining the timeout behavior */
-    static interface TimeoutHandler {
-
-	/**
-	 * Perform an action required for a timeout.
-	 */
-	public void handleTimeout();
-    }
-
-    /** the timeout handler to call after the timer expires */
-    private TimeoutHandler timeoutHandler;
-
-    /** the thread which implements the timeout interval */
-    private Thread sleepThread;
-
-    /** flag set when a timeout is detected */
-    private boolean timedOut;
-
-    /** the timeout interval */
-    private long interval;
-
-    /**
-     * Construct a timeout object to wait the given <code>interval</code>
-     * and interrupts the given <code>targetThread</code> if the
-     * interval passes before the <code>cancel</code> method is called.
-     *
-     * @param timeoutHandler the handler to call if a timeout is detected
-     * @param interval the timeout interval in milliseconds
-     */
-    Timeout(TimeoutHandler timeoutHandler, int interval) {
-	this.timeoutHandler = timeoutHandler;
-	this.interval = interval;
-    }
-
-    /**
-     * Starts the timeout clock
-     */
-    void start() {
-	sleepThread = new Thread(this);
-	sleepThread.start();
-    }
-
-    /**
-     * Sleep for the timeout interval unless interrupted. If the
-     * sleep completes without interruption, the <code>timedOut</code>
-     * flag is set and the <code>TimeoutHandler.handleTimeout</code> method
-     * is called. Early returns from <code>Thread.sleep</code> will
-     * not cause premature indications of timeout.
-     */
-    public void run() {
-	timedOut = false;
-	long startTime = new Date().getTime();
-	long delta = 0;
-	while (delta < interval) {
-	    try {
-		Thread.sleep(interval - delta);
-	    } catch (InterruptedException e) {
-		return;
-	    }
-	    delta = new Date().getTime() - startTime;
-	}
-	timedOut = true;
-	timeoutHandler.handleTimeout();
-	return;
-    }
-
-    /**
-     * Cancel this timeout. An interrupt for this thread is fired.
-     */
-    void cancel() {
-	sleepThread.interrupt();
-    }
-
-    /**
-     * Return the state of the <code>timedOut</code> flag.
-     *
-     * @return <code>true</code> if the most recent call to <code>run</code>
-     *         resulted in a timeout.
-     */
-    boolean timedOut() {
-	return timedOut;
-    }
-
-    /**
-     * Handler for thread timeouts. The handled thread is interrupted.
-     */
-    static class ThreadTimeoutHandler implements TimeoutHandler {
-
-	/** the thread to interrupt */
-	Thread thread;
-
-	/**
-	 * Construct the <code>TimeoutHandler</code>. The given 
-	 * <code>Thread</code> is interrupted.
-	 */
-	public ThreadTimeoutHandler(Thread thread) {
-	    this.thread = thread;
-	}
-
-	/**
-	 * Interrupt the thread.
-	 */
-	public void handleTimeout() {
-	    thread.interrupt();
-	}
-    }
-
-    /**
-     * Handler for socket timeouts. The handled socket is closed.
-     */
-    static class ServerSocketTimeoutHandler implements TimeoutHandler {
-
-	/** the socket to close */
-	ServerSocket socket;
-
-	/**
-	 * Construct the <code>TimeoutHandler</code>.
-	 */
-	public ServerSocketTimeoutHandler(ServerSocket socket) {
-	    this.socket = socket;
-	}
-
-	/**
-	 * close the socket.
-	 */
-	public void handleTimeout() {
-	    try {
-	        socket.close();
-	    } catch (IOException e) {
-	    }
-	}
-    }
-
-    /**
-     * Handler for InputStream timeouts. The handled stream is closed.
-     */
-    static class InputStreamTimeoutHandler implements TimeoutHandler {
-
-	/** the socket to close */
-	InputStream stream;
-
-	/**
-	 * Construct the <code>TimeoutHandler</code>.
-	 */
-	public InputStreamTimeoutHandler(InputStream stream) {
-	    this.stream = stream;
-	}
-
-	/**
-	 * close the stream.
-	 */
-	public void handleTimeout() {
-	    try {
-	        stream.close();
-	    } catch (IOException e) {
-	    }
-	}
-    } 
-}
+/*
+ * 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 java.net.Socket;
+import java.net.ServerSocket;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Date;
+
+/**
+ * Detects timeouts and calls a timeout handler.
+ */
+class Timeout implements Runnable {
+
+    /** An interface defining the timeout behavior */
+    static interface TimeoutHandler {
+
+	/**
+	 * Perform an action required for a timeout.
+	 */
+	public void handleTimeout();
+    }
+
+    /** the timeout handler to call after the timer expires */
+    private TimeoutHandler timeoutHandler;
+
+    /** the thread which implements the timeout interval */
+    private Thread sleepThread;
+
+    /** flag set when a timeout is detected */
+    private boolean timedOut;
+
+    /** the timeout interval */
+    private long interval;
+
+    /**
+     * Construct a timeout object to wait the given <code>interval</code>
+     * and interrupts the given <code>targetThread</code> if the
+     * interval passes before the <code>cancel</code> method is called.
+     *
+     * @param timeoutHandler the handler to call if a timeout is detected
+     * @param interval the timeout interval in milliseconds
+     */
+    Timeout(TimeoutHandler timeoutHandler, int interval) {
+	this.timeoutHandler = timeoutHandler;
+	this.interval = interval;
+    }
+
+    /**
+     * Starts the timeout clock
+     */
+    void start() {
+	sleepThread = new Thread(this);
+	sleepThread.start();
+    }
+
+    /**
+     * Sleep for the timeout interval unless interrupted. If the
+     * sleep completes without interruption, the <code>timedOut</code>
+     * flag is set and the <code>TimeoutHandler.handleTimeout</code> method
+     * is called. Early returns from <code>Thread.sleep</code> will
+     * not cause premature indications of timeout.
+     */
+    public void run() {
+	timedOut = false;
+	long startTime = new Date().getTime();
+	long delta = 0;
+	while (delta < interval) {
+	    try {
+		Thread.sleep(interval - delta);
+	    } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+		return;
+	    }
+	    delta = new Date().getTime() - startTime;
+	}
+	timedOut = true;
+	timeoutHandler.handleTimeout();
+	return;
+    }
+
+    /**
+     * Cancel this timeout. An interrupt for this thread is fired.
+     */
+    void cancel() {
+	sleepThread.interrupt();
+    }
+
+    /**
+     * Return the state of the <code>timedOut</code> flag.
+     *
+     * @return <code>true</code> if the most recent call to <code>run</code>
+     *         resulted in a timeout.
+     */
+    boolean timedOut() {
+	return timedOut;
+    }
+
+    /**
+     * Handler for thread timeouts. The handled thread is interrupted.
+     */
+    static class ThreadTimeoutHandler implements TimeoutHandler {
+
+	/** the thread to interrupt */
+	Thread thread;
+
+	/**
+	 * Construct the <code>TimeoutHandler</code>. The given 
+	 * <code>Thread</code> is interrupted.
+	 */
+	public ThreadTimeoutHandler(Thread thread) {
+	    this.thread = thread;
+	}
+
+	/**
+	 * Interrupt the thread.
+	 */
+	public void handleTimeout() {
+	    thread.interrupt();
+	}
+    }
+
+    /**
+     * Handler for socket timeouts. The handled socket is closed.
+     */
+    static class ServerSocketTimeoutHandler implements TimeoutHandler {
+
+	/** the socket to close */
+	ServerSocket socket;
+
+	/**
+	 * Construct the <code>TimeoutHandler</code>.
+	 */
+	public ServerSocketTimeoutHandler(ServerSocket socket) {
+	    this.socket = socket;
+	}
+
+	/**
+	 * close the socket.
+	 */
+	public void handleTimeout() {
+	    try {
+	        socket.close();
+	    } catch (IOException e) {
+	    }
+	}
+    }
+
+    /**
+     * Handler for InputStream timeouts. The handled stream is closed.
+     */
+    static class InputStreamTimeoutHandler implements TimeoutHandler {
+
+	/** the socket to close */
+	InputStream stream;
+
+	/**
+	 * Construct the <code>TimeoutHandler</code>.
+	 */
+	public InputStreamTimeoutHandler(InputStream stream) {
+	    this.stream = stream;
+	}
+
+	/**
+	 * close the stream.
+	 */
+	public void handleTimeout() {
+	    try {
+	        stream.close();
+	    } catch (IOException e) {
+	    }
+	}
+    } 
+}



Mime
View raw message