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 [19/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/t...
Date Sun, 26 Oct 2014 13:17:31 GMT
Modified: river/jtsk/skunk/qa_refactor/trunk/qa/src/com/sun/jini/test/share/DiscoveryProtocolSimulator.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/qa_refactor/trunk/qa/src/com/sun/jini/test/share/DiscoveryProtocolSimulator.java?rev=1634322&r1=1634321&r2=1634322&view=diff
==============================================================================
--- river/jtsk/skunk/qa_refactor/trunk/qa/src/com/sun/jini/test/share/DiscoveryProtocolSimulator.java (original)
+++ river/jtsk/skunk/qa_refactor/trunk/qa/src/com/sun/jini/test/share/DiscoveryProtocolSimulator.java Sun Oct 26 13:17:28 2014
@@ -1,1177 +1,1182 @@
-/*
- * 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.test.share;
-
-import com.sun.jini.qa.harness.QAConfig;
-import com.sun.jini.qa.harness.AdminManager;
-import com.sun.jini.qa.harness.TestException;
-
-import com.sun.jini.start.HTTPDStatus;
-import com.sun.jini.start.ServiceStarter;
-
-import com.sun.jini.thread.TaskManager;
-
-import com.sun.jini.discovery.ClientSubjectChecker;
-import com.sun.jini.discovery.Discovery;
-import com.sun.jini.discovery.DiscoveryConstraints;
-import com.sun.jini.discovery.DiscoveryProtocolException;
-import com.sun.jini.discovery.EncodeIterator;
-import com.sun.jini.discovery.MulticastAnnouncement;
-import com.sun.jini.discovery.MulticastRequest;
-import com.sun.jini.discovery.UnicastResponse;
-
-import net.jini.discovery.Constants;
-import net.jini.discovery.IncomingMulticastRequest;
-import net.jini.discovery.IncomingUnicastRequest;
-import net.jini.discovery.OutgoingMulticastAnnouncement;
-import net.jini.discovery.OutgoingUnicastResponse;
-
-import net.jini.core.constraint.MethodConstraints;
-import net.jini.core.discovery.LookupLocator;
-import net.jini.core.event.EventRegistration;
-import net.jini.core.event.RemoteEventListener;
-import net.jini.core.lookup.ServiceID;
-import net.jini.core.lookup.ServiceItem;
-import net.jini.core.lookup.ServiceMatches;
-import net.jini.core.lookup.ServiceRegistrar;
-import net.jini.core.lookup.ServiceRegistration;
-import net.jini.core.lookup.ServiceTemplate;
-
-import com.sun.jini.test.services.lookupsimulator.LookupSimulatorProxyInterface;
-
-import net.jini.constraint.BasicMethodConstraints;
-import net.jini.io.UnsupportedConstraintException;
-import net.jini.config.Configuration;
-import net.jini.config.NoSuchEntryException;
-import net.jini.config.ConfigurationException;
-import net.jini.core.constraint.InvocationConstraint;
-import net.jini.core.constraint.InvocationConstraints;
-import com.sun.jini.config.Config;
-import net.jini.security.Security;
-import net.jini.security.AuthenticationPermission;
-
-import java.lang.reflect.Array;
-
-import java.net.DatagramPacket;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.MulticastSocket;
-import java.net.NetworkInterface;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.net.SocketException;
-import java.net.UnknownHostException;
-
-import java.io.DataInputStream;
-import java.io.IOException;
-import java.io.InterruptedIOException;
-
-import java.nio.ByteBuffer;
-import java.nio.BufferUnderflowException;
-import java.nio.channels.ServerSocketChannel;
-import java.nio.channels.SocketChannel;
-
-import java.rmi.activation.ActivationGroupDesc;
-import java.rmi.activation.ActivationGroupDesc.CommandEnvironment;
-import java.rmi.activation.ActivationGroup;
-import java.rmi.activation.ActivationGroupID;
-import java.rmi.activation.ActivationDesc;
-import java.rmi.activation.Activatable;
-import java.rmi.activation.ActivationException;
-import java.rmi.MarshalledObject;
-import java.rmi.RemoteException;
-
-import java.security.Permission;
-import java.security.Principal;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Properties;
-import java.util.Random;
-import java.util.StringTokenizer;
-
-import java.util.logging.Logger;
-import java.util.logging.Level;
-
-import javax.security.auth.login.LoginContext;
-import javax.security.auth.Subject;
-import javax.security.auth.login.LoginException;
-import java.security.PrivilegedExceptionAction;
-import java.security.PrivilegedActionException;
-
-
-/**
- * Instances of this class provide general-purpose functions related to the
- * discovery protocol. This utility class is intended to be useful to all
- * categories of tests that wish to simulate the multicast and unicast
- * message exchange between a client or service and a lookup service -- 
- * from the point of view of the lookup service. (For provide the
- * analogous functionality from the point of view of the client,
- * use the <code>net.jini.discovery.LookupDiscovery</code> and the
- * <code>net.jini.discovery.LookupLocatorDiscovery</code> utilities.
- *
- * @see net.jini.core.discovery.LookupLocator
- *
- * @see net.jini.discovery.IncomingMulticastAnnouncement
- * @see net.jini.discovery.IncomingMulticastRequest
- * @see net.jini.discovery.IncomingUnicastRequest
- * @see net.jini.discovery.IncomingUnicastResponse
- *
- * @see net.jini.discovery.OutgoingMulticastAnnouncement
- * @see net.jini.discovery.OutgoingMulticastRequest
- * @see net.jini.discovery.OutgoingUnicastRequest
- * @see net.jini.discovery.OutgoingUnicastResponse
- */
-public class DiscoveryProtocolSimulator {
-
-    /** Maximum minMax lease duration for both services and events */
-    private static final long MAX_LEASE = 1000L * 60 * 60 * 24 * 365 * 1000;
-    /** Maximum minimum renewal interval */
-    private static final long MAX_RENEW = 1000L * 60 * 60 * 24 * 365;
-    /** Default maximum size of multicast packets to send and receive */
-    private static final int DEFAULT_MAX_PACKET_SIZE = 512;
-    /** Default time to live value to use for sending multicast packets */
-    private static int DEFAULT_MULTICAST_TTL;
-    /** Default timeout to set on sockets used for unicast discovery */
-    private static final int DEFAULT_SOCKET_TIMEOUT = 1*60*1000;
-
-    private static Logger logger = Logger.getLogger("com.sun.jini.qa.harness");
-
-    /** Socket timeout for multicast request receive() */
-    private static final int SOCKET_TIMEOUT = 5*60*1000;
-
-    /** Only allow connections from this address */
-    private InetAddress thisInetAddress = null;
-    /** Current number of multicast announcements sent by this class */
-    private int nAnnouncements = 0;
-    /** Internet Protocol (IP) addresses of the network interfaces (NICs)
-     *  through which multicast packets will be sent.
-     */
-    private InetAddress[] nicAddresses;
-
-    /** Port for unicast discovery */
-    private int unicastPort = 0;
-    /** The locator to send */
-    private volatile LookupLocator lookupLocator = null;
-    /** The member groups to send */
-    private String[] memberGroups = {};
-
-    /** Thread to receive/process multicast requests from client or service */
-    volatile MulticastThread multicastRequestThread;
-    /** Thread to receive/process unicast requests from client or service */
-    volatile UnicastThread unicastRequestThread;
-    /** Thread to send multicast announcements to from client or service */
-    volatile AnnounceThread multicastAnnouncementThread;
-
-    /** Task manager for sending events and discovery responses */
-    private final TaskManager taskMgr = new TaskManager(10, 1000 * 15, 1.0f);
-
-    /** Proxy for the "fake" lookup service that is sent */
-    private volatile LookupSimulatorProxyInterface lookupProxy;
-    /** The service ID to assign to the lookup service that is sent */
-    private volatile ServiceID lookupServiceID = null;
-
-    /** Socket timeout for unicast discovery request processing */
-    private int unicastTimeout =
-	Integer.getInteger("com.sun.jini.reggie.unicastTimeout",
-			   1000 * 60).intValue();
-    /* For synchronization, instead of ReadersWriter locks used by reggie */
-    private final Object lockNAnnouncements = new Object();
-    private final Object lockLookupLocator  = new Object();
-    private final Object lockMemberGroups   = new Object();
-
-    /* new fields taken from the davis reggie */
-
-    /** Network interfaces to use for multicast discovery */
-    private volatile NetworkInterface[] multicastInterfaces;
-    /** Flag indicating whether network interfaces were explicitly specified */
-    private volatile boolean multicastInterfacesSpecified;
-    private volatile Discovery protocol2;
-    /** Constraints specified for incoming multicast requests */
-    private volatile DiscoveryConstraints multicastRequestConstraints;
-    /** Constraints specified for outgoing multicast announcements */
-    private volatile DiscoveryConstraints multicastAnnouncementConstraints;
-    /** Constraints specified for handling unicast discovery */
-    private volatile DiscoveryConstraints unicastDiscoveryConstraints;
-    /** Client subject checker to apply to incoming multicast requests */
-    private volatile ClientSubjectChecker multicastRequestSubjectChecker;
-    /** Client subject checker to apply to unicast discovery attempts */
-    private volatile ClientSubjectChecker unicastDiscoverySubjectChecker;
-    /** Interval to wait in between sending multicast announcements */
-    private volatile long multicastAnnouncementInterval = 1000 * 60 * 2;
-
-    
-    public DiscoveryProtocolSimulator(QAConfig config, String[] memberGroups, int unicastPort, LookupSimulatorProxyInterface proxy)
-                                       throws ActivationException, IOException, TestException
-    {
-        this.memberGroups = memberGroups;
-        this.unicastPort  = unicastPort;
-        // start LUS before switching identity to reggie
-        lookupProxy = proxy;
-	LoginContext context = null;
-	Configuration c = config.getConfiguration();
-	try {
-	    context = (LoginContext) c.getEntry("test",
-						"reggieLoginContext", 
-						LoginContext.class,
-						null);
-	} catch (ConfigurationException e) {
-	    throw new RuntimeException("Bad configuration", e);
-	}
-	if (context == null) {
-		init(config);
-        } else {
-	    try {
-		Principal reggie = 
-		    (Principal) c.getEntry("principal", "reggie", Principal.class);
-		// allow the simulator, running as reggie, to authenticate as reggie
-		// XXX Should the permission be obtained from the configuration???
-		Security.grant(lookupProxy.getClass(), 
-			       new Principal[]{reggie}, 
-			       new Permission[] {
-				   new AuthenticationPermission(
-						 Collections.singleton(reggie),
-						 Collections.singleton(reggie),
-						 "connect")});
-		context.login();
-	    } catch (LoginException e) {
-		throw new RuntimeException("LoginFailed", e);
-	    } catch (ConfigurationException e) {
-		throw new RuntimeException("Configuration error", e);
-	    }
-	    try {
-		final QAConfig finalConfig = config;
-		Subject.doAsPrivileged(context.getSubject(),
-			     	new PrivilegedExceptionAction() {
-				    public Object run() throws ActivationException, IOException {
-					init(finalConfig);
-				 	return null;
-				    }
-				},
-				null);
-	    } catch (PrivilegedActionException e) {
-		Throwable t = e.getException();
-		if (t instanceof ActivationException) {
-		    throw (ActivationException) t;
-		}
-		if (t instanceof IOException) {
-		    throw (IOException) t;
-		}
-	    }
-	}
-    }//end constructor
-
-    public void stopAnnouncements() {
-        stopAnnouncements(null);
-    }//end stopAnnouncements
-    public void stopAnnouncements(String testname) {
-        logger.log(Level.FINE, "   stopAnnouncements entered");
-        /* terminate all daemons */
-        unicastRequestThread.interrupt();
-	multicastRequestThread.interrupt();
-	multicastAnnouncementThread.interrupt();
-        logger.log(Level.FINE, "     interrupted all threads");
-	taskMgr.terminate();
-        logger.log(Level.FINE, "     terminated task manager");
-	try {
-            logger.log(Level.FINE, "     unicastRequestThread.join()");
-            unicastRequestThread.join();
-            logger.log(Level.FINE, "     multicastRequestThread.join()");
-            multicastRequestThread.join();
-            logger.log(Level.FINE, 
-                              "     multicastAnnouncementThread.join()");
-            multicastAnnouncementThread.join();
-        } catch (InterruptedException e) { }
-        logger.log(Level.FINE, "     close all request sockets");
-        closeRequestSockets(taskMgr.getPending());
-        logger.log(Level.FINE, "   stopAnnouncements exited");
-    }//end stopAnnouncements
-
-//    public void destroyLookup() throws RemoteException {
-//        lookupProxy.destroy();
-//    }//end destroyLookup
-
-    public int getNAnnouncementsSent() {
-        synchronized(lockNAnnouncements) {
-            return nAnnouncements;
-        }//end sync
-    }//end getNAnnouncementsSent
-
-    public ServiceRegistrar getLookupProxy() {
-        return lookupProxy;
-    }//end getLookupProxy
-
-    public LookupLocator getLookupLocator() {
-        synchronized(lockLookupLocator) {
-            return lookupLocator;
-        }//end sync
-    }//end getLookupLocator
-
-    public String[] getMemberGroups() {
-        synchronized(lockMemberGroups) {
-            return memberGroups; // don't clone, never modified once created
-        }//end sync
-    }//end getMemberGroups
-
-    public void addMemberGroups(String[] groups) throws RemoteException {
-        String[] tmpArray = null;
-        /* Change the member groups locally, then replace them remotely */
-        synchronized(lockMemberGroups) {
-            for (int i=0;i<groups.length;i++) {
-                if (indexOf(memberGroups, groups[i]) < 0)
-                    memberGroups = (String[])arrayAdd(memberGroups,groups[i]);
-            }
-            tmpArray = new String[memberGroups.length];
-            System.arraycopy(memberGroups,0,tmpArray,0,memberGroups.length);
-        }//end sync
-        /* Replace the groups remotely - no remote calls in sync block*/
-        lookupProxy.setMemberGroups(tmpArray);
-        synchronized (multicastAnnouncementThread) {
-            multicastAnnouncementThread.notify();
-        }//end sync
-    }//end addMemberGroups
-
-    public void removeMemberGroups(String[] groups) throws RemoteException {
-        String[] tmpArray = null;
-        /* Change the member groups locally, then replace them remotely */
-        synchronized(lockMemberGroups) {
-            for (int i=0;i<groups.length;i++) {
-                int j = indexOf(memberGroups, groups[i]);
-                if (j >= 0) memberGroups = (String[])arrayDel(memberGroups,j);
-            }
-            tmpArray = new String[memberGroups.length];
-            System.arraycopy(memberGroups,0,tmpArray,0,memberGroups.length);
-        }//end sync
-        /* Replace the groups remotely - no remote calls in sync block*/
-        lookupProxy.setMemberGroups(tmpArray);
-        synchronized (multicastAnnouncementThread) {
-            multicastAnnouncementThread.notify();
-        }//end sync
-    }//end removeMemberGroups
-
-    public void setMemberGroups(String[] groups) throws RemoteException {
-        String[] tmpArray = null;
-        /* Change the member groups locally, then replace them remotely */
-        synchronized(lockMemberGroups) {
-            memberGroups = (String[])removeDups(groups);
-            tmpArray = new String[memberGroups.length];
-            System.arraycopy(memberGroups,0,tmpArray,0,memberGroups.length);
-        }//end sync
-        /* Replace the groups remotely - no remote calls in sync block*/
-        lookupProxy.setMemberGroups(memberGroups);
-        synchronized (multicastAnnouncementThread) {
-            multicastAnnouncementThread.notify();
-        }//end sync
-    }//end setMemberGroups
-
-    public int getUnicastPort() {
-        synchronized(lockLookupLocator) {
-            return unicastPort;
-        }//end sync
-    }//end getUnicastPort
-
-    public void setUnicastPort(int port) throws IOException {
-	if (port == unicastPort) return;
-        LookupLocator tmpLocator = null;
-        synchronized(lockLookupLocator) {
-            if(    (    (port == 0)
-                     && (unicastRequestThread.port == Constants.discoveryPort))
-                || (port == unicastRequestThread.port) )
-            {
-                unicastPort = port;
-                return;
-            }
-            /* create a UnicastThread that listens on the new port */
-            UnicastThread newUnicastRequestThread = new UnicastThread(port);
-            /* terminate the current UnicastThread listening on the old port */
-            unicastRequestThread.interrupt();
-            try {
-                unicastRequestThread.join();
-            } catch (InterruptedException e) { }
-            /* start the UnicastThread listening on the new port */
-            unicastRequestThread = newUnicastRequestThread;
-            unicastRequestThread.start();
-            unicastPort = port;
-            lookupLocator = QAConfig.getConstrainedLocator(lookupLocator.getHost(),
-                                                           unicastRequestThread.port);
-            /* Equality (same port&host ==> lookupLocator.equals(tmpLocator) */
-            tmpLocator = QAConfig.getConstrainedLocator(lookupLocator.getHost(),
-                                                        unicastRequestThread.port);
-        }//end sync
-        /* Set unicast port remotely - no remote calls in sync block*/
-        lookupProxy.setLocator(tmpLocator); // also sets unicastPort
-        synchronized (multicastAnnouncementThread) {
-            multicastAnnouncementThread.notify();
-        }//end sync
-    }//end setUnicastPort
-
-    /** Multicast discovery request thread code. */
-    private class MulticastThread extends Thread {
-	/** Multicast socket to receive packets */
-	private final MulticastSocket socket;
-
-	/**
-	 * Create a high priority daemon thread.  Set up the socket now
-	 * rather than in run, so that we get any exception up front.
-	 */
-	public MulticastThread() throws IOException {
-	    super("multicast request");
-	    setDaemon(true);
-	    if (multicastInterfaces != null && multicastInterfaces.length == 0)
-	    {
-		socket = null;
-		return;
-	    }
-	    InetAddress requestAddr = Constants.getRequestAddress();
-	    socket = new MulticastSocket(Constants.discoveryPort);
-	    socket.setTimeToLive(
-		multicastAnnouncementConstraints.getMulticastTimeToLive(
-		    DEFAULT_MULTICAST_TTL));
-	    if (multicastInterfaces != null) {
-		Level failureLogLevel =
-		    multicastInterfacesSpecified ? Level.WARNING : Level.FINE;
-		for (int i = 0; i < multicastInterfaces.length; i++) {
-		    try {
-			socket.setNetworkInterface(multicastInterfaces[i]);
-			socket.joinGroup(requestAddr);
-		    } catch (IOException e) {
-			logger.log(
-			    failureLogLevel,
-			    "exception configuring multicast interface", e);
-		    }
-		}
-	    } else {
-		// REMIND: tolerate failure here?
-		socket.joinGroup(requestAddr);
-	    }
-	}
-
-	/** True if thread has been interrupted */
-	private volatile boolean interrupted = false;
-
-	/* This is a workaround for Thread.interrupt not working on
-	 * MulticastSocket.receive on all platforms.
-	 */
-	public void interrupt() {
-	    interrupted = true;
-	    socket.close();
-	}
-
-	public boolean isInterrupted() {
-	    return interrupted;
-	}
-
-	public void run() {
-	    if (multicastInterfaces != null && multicastInterfaces.length == 0)
-	    {
-		return;
-	    }
-	    byte[] buf = new byte[
-		multicastRequestConstraints.getMulticastMaxPacketSize(
-		    DEFAULT_MAX_PACKET_SIZE)];
-	    DatagramPacket dgram = new DatagramPacket(buf, buf.length);
-	    while (!isInterrupted()) {
-		try {
-		    dgram.setLength(buf.length);
-		    try {
-			socket.receive(dgram);
-		    } catch (NullPointerException e) {
-			break; // workaround for bug 4190513
-		    }
-		    int pv;
-		    try {
-			pv = ByteBuffer.wrap(dgram.getData(),
-					     dgram.getOffset(),
-					     dgram.getLength()).getInt();
-		    } catch (BufferUnderflowException e) {
-			throw new DiscoveryProtocolException(null, e);
-		    }
-		    multicastRequestConstraints.checkProtocolVersion(pv);
-
-		    MulticastRequest req = 
-			getDiscovery(pv).decodeMulticastRequest(
-			    dgram,
-			    multicastRequestConstraints.
-				getUnfulfilledConstraints(),
-			    multicastRequestSubjectChecker);
-                    synchronized (lockMemberGroups){
-                        if ((req.getGroups().length != 0 &&
-                             !overlap(memberGroups, req.getGroups())) ||
-                            indexOf(req.getServiceIDs(), lookupServiceID) >= 0)
-                            continue;
-                    }
-		    logger.log(Level.FINE, "Received valid multicast for " + lookupLocator);
-		    taskMgr.addIfNew(
-			new AddressTask(InetAddress.getByName(req.getHost()),
-					req.getPort()));
-		} catch (InterruptedIOException ignore) {
-		    break;
-		} catch (DiscoveryProtocolException ignore) {
-		    break;
-		} catch (Exception e) {
-		    if (isInterrupted()) {
-			break;
-		    }
-		    logger.log(Level.FINE,
-			       "exception receiving multicast request", e);
-		}
-	    }
-	    socket.close();
-	}
-    }
-
-    /** Unicast discovery request thread code. */
-    private class UnicastThread extends Thread {
-	/** Server socket to accepts connections on. */
-	private final ServerSocket listen;
-	/** Listen port */
-	public final int port;
-
-	/**
-	 * Create a daemon thread.  Set up the socket now rather than in run,
-	 * so that we get any exception up front.
-	 */
-	public UnicastThread(int port) throws IOException {
-	    super("unicast request");
-	    setDaemon(true);
-            ServerSocket listen = null;
-	    if (port == 0) {
-		try {
-		    listen = new ServerSocket(Constants.discoveryPort);
-		} catch (IOException e) {
-		    logger.log(Level.FINE,
-			       "failed to bind to default port", e);
-		}
-	    }
-	    if (listen == null) {
-		listen = new ServerSocket(port);
-	    }
-	    this.port = listen.getLocalPort();
-            this.listen = listen;
-	}
-
-	/** True if thread has been interrupted */
-	private volatile boolean interrupted = false;
-
-	/* This is a workaround for Thread.interrupt not working on
-	 * ServerSocket.accept on all platforms.  ServerSocket.close
-	 * can't be used as a workaround, because it also doesn't work
-	 * on all platforms.
-	 */
-	public void interrupt() {
-	    interrupted = true;
-	    try {
-		(new Socket(InetAddress.getLocalHost(), port)).close();
-	    } catch (IOException e) {
-	    }
-	}
-
-	public boolean isInterrupted() {
-	    return interrupted;
-	}
-
-	public void run() {
-	    while (!isInterrupted()) {
-		try {
-		    Socket socket = listen.accept();
-		    if (isInterrupted()) {
-			try {
-			    socket.close();
-			} catch (IOException e) {
-			    logger.log(Level.FINE,
-				       "exception closing socket", e);
-			}
-			break;
-		    }
-		    logger.log(Level.FINE, "Adding socket task for " + lookupLocator);
-		    taskMgr.add(new SocketTask(socket));
-		} catch (InterruptedIOException e) {
-		    break;
-		} catch (Exception e) {
-		    logger.log(Level.FINE, "exception listening on socket", e);
-		}
-		/* if we fail in any way, just forget about it */
-	    }
-	    try {
-		listen.close();
-	    } catch (IOException e) {
-	    }
-	}
-    }
-
-    /** Multicast discovery announcement thread code. */
-    private class AnnounceThread extends Thread {
-	/** Multicast socket to send packets on */
-	private final MulticastSocket socket;
-
-	private volatile boolean interrupted = false;
-
-	/* This is a workaround for Thread.interrupt not working due
-	 * to the logging system sometimes throwing away InterruptedIOException
-	 */
-	public void interrupt() {
-	    interrupted = true;
-	    super.interrupt();
-	}
-
-	public boolean isInterrupted() {
-	    return interrupted;
-	}
-
-	/**
-	 * Create a daemon thread.  Set up the socket now rather than in run,
-	 * so that we get any exception up front.
-	 */
-	public AnnounceThread() throws IOException {
-	    super("discovery announcement");
-	    setDaemon(true);
-	    if (multicastInterfaces == null || multicastInterfaces.length > 0)
-	    {
-		socket = new MulticastSocket();
-		socket.setTimeToLive(
-		    multicastAnnouncementConstraints.getMulticastTimeToLive(
-			DEFAULT_MULTICAST_TTL));
-	    } else {
-		socket = null;
-	    }
-	}
-
-	public void run() {
-	    if (multicastInterfaces != null && multicastInterfaces.length == 0)
-	    {
-		return;
-	    }
-	    try {
-		while (!isInterrupted()) {
-                    synchronized (lockMemberGroups){
-                        if (!announce(memberGroups)) break;
-                    }
-                    synchronized (this){
-                        wait(multicastAnnouncementInterval);
-                    }
-		}
-	    } catch (InterruptedException e) {
-	    }
-// disable this to allow simulation of disappearance of multicast announcements
-//	    if (memberGroups.length > 0) 
-//		announce(new String[0]);//send NO_GROUPS just before shutdown
-	    socket.close();
-	}
-
-	/**
-	 * Announce membership in the specified groups, and return false if
-	 * interrupted, otherwise return true.
-	 */
-	private boolean announce(String[] groups) {
-	    // REMIND: cache latest announcement to skip re-encoding
-	    List packets = new ArrayList();
-	    Discovery disco;
-	    try {
-		disco = getDiscovery(
-		    multicastAnnouncementConstraints.chooseProtocolVersion());
-	    } catch (DiscoveryProtocolException e) {
-		throw new AssertionError(e);
-	    }
-	    EncodeIterator ei = disco.encodeMulticastAnnouncement(
-		new MulticastAnnouncement(System.currentTimeMillis(),
-					  lookupLocator.getHost(),
-					  lookupLocator.getPort(),
-					  groups,
-					  lookupServiceID),
-		multicastAnnouncementConstraints.getMulticastMaxPacketSize(
-					  DEFAULT_MAX_PACKET_SIZE),
-		multicastAnnouncementConstraints.getUnfulfilledConstraints());
-	    while (ei.hasNext()) {
-		try {
-		    packets.addAll(Arrays.asList(ei.next()));
-		} catch (Exception e) {
-		// UnsupportedConstraintException is expected and normal
-                    if (! (e instanceof UnsupportedConstraintException)) {
-		        logger.log(Level.INFO,
-			           "exception encoding multicast announcement", e);
-                    }
-		}
-	    }
-	    try {
-	        logger.log(Level.FINE, "Sending packets from " + lookupLocator);
-		send((DatagramPacket[])
-		    packets.toArray(new DatagramPacket[packets.size()]));
-		    synchronized(lockNAnnouncements) {
-                        nAnnouncements++;
-                    }
-	    } catch (InterruptedIOException e) {
-		return false;
-	    } catch (IOException e) {
-		logger.log(Level.INFO,
-			   "exception sending multicast announcement", e);
-	    }
-	    return true;
-	}
-
-	/**
-	 * Attempts to multicast the given packets on each of the configured
-	 * network interfaces.
-	 */
-	private void send(DatagramPacket[] packets)
-	    throws InterruptedIOException
-	{
-	    if (multicastInterfaces != null) {
-		Level failureLogLevel =
-		    multicastInterfacesSpecified ? Level.WARNING : Level.FINE;
-		for (int i = 0; i < multicastInterfaces.length; i++) {
-		    try {
-			socket.setNetworkInterface(multicastInterfaces[i]);
-			send(packets, failureLogLevel);
-		    } catch (SocketException e) {
-			logger.log(failureLogLevel,
-				   "exception setting interface", e);
-		    }
-		}
-	    } else {
-		send(packets, Level.WARNING);
-	    }
-	}
-
-	/**
-	 * Attempts to multicast the given packets on the currently set network
-	 * interface, logging failures at the specified logging level.
-	 */
-	private void send(DatagramPacket[] packets, Level failureLogLevel)
-	    throws InterruptedIOException
-	{
-	    for (int i = 0; i < packets.length; i++) {
-		try {
-		    socket.send(packets[i]);
-		} catch (InterruptedIOException e) {
-		    throw e;
-		} catch (IOException e) {
-		    logger.log(failureLogLevel, "exception sending packet", e);
-		}
-	    }
-	}
-    }
-
-    /** Returns Discovery instance implementing the given protocol version */
-    private Discovery getDiscovery(int version)
-	throws DiscoveryProtocolException
-    {
-	switch (version) {
-	    case Discovery.PROTOCOL_VERSION_1:
-		return Discovery.getProtocol1();
-	    case Discovery.PROTOCOL_VERSION_2:
-		return protocol2;
-	    default:
-		throw new DiscoveryProtocolException(
-		    "unsupported protocol version: " + version);
-	}
-    }
-
-    private final class AddressTask implements TaskManager.Task {
-
-	/** The address */
-	public final InetAddress addr;
-	/** The port */
-	public final int port;
-
-	/** Simple constructor */
-	public AddressTask(InetAddress addr, int port) {
-	    this.addr = addr;
-	    this.port = port;
-	}
-
-	public int hashCode() {
-	    return addr.hashCode();
-	}
-
-	/** Two tasks are equal if they have the same address and port */
-	public boolean equals(Object obj) {
-	    if (!(obj instanceof AddressTask))
-		return false;
-	    AddressTask ua = (AddressTask)obj;
-	    return addr.equals(ua.addr) && port == ua.port;
-	}
-
-	/** Connect and then process a unicast discovery request */
-	public void run() {
-	    try {
-                logger.log(Level.FINE, "Responding to multicast with unicast from " + lookupLocator);
-		respond(new Socket(addr, port));
-	    } catch (IOException e) {
-	    } catch (SecurityException e) {
-	    }
-	}
-
-	/** No ordering */
-	public boolean runAfter(List tasks, int size) {
-	    return false;
-	}
-    }//end class AddressTask
-
-    /** Socket for unicast discovery response. */
-    private final class SocketTask implements TaskManager.Task {
-
-	/** The socket */
-	public final Socket socket;
-
-	/** Simple constructor */
-	public SocketTask(Socket socket) {
-	    this.socket = socket;
-	}
-
-	/** Process a unicast discovery request */
-	public void run() {
-	    respond(socket);
-	}
-
-	/** No ordering */
-	public boolean runAfter(List tasks, int size) {
-	    return false;
-	}
-    }//end class SocketTask
-
-    /** Process a unicast discovery request, and respond. */
-    private void respond(Socket socket) {
-	try {
-	    socket.setSoTimeout(
-		unicastDiscoveryConstraints.getUnicastSocketTimeout(
-		    DEFAULT_SOCKET_TIMEOUT));
-
-	    int pv = new DataInputStream(socket.getInputStream()).readInt();
-	    unicastDiscoveryConstraints.checkProtocolVersion(pv);
-	    getDiscovery(pv).handleUnicastDiscovery(
-		new UnicastResponse(lookupLocator.getHost(),
-				    lookupLocator.getPort(),
-				    memberGroups,
-				    lookupProxy),
-		socket,
-		unicastDiscoveryConstraints.getUnfulfilledConstraints(),
-		unicastDiscoverySubjectChecker,
-		Collections.EMPTY_LIST);
-	    logger.log(Level.FINE, "Responded to unicast request for " + lookupLocator);
-
-	} catch (Exception e) {
-	    try {
-		if (InetAddress.getLocalHost().equals(socket.getInetAddress())) {
-		    logger.log(Level.FINE, 
-			       "exception handling unicast discovery from "
-			       + socket.getInetAddress() + ":" 
-			       + socket.getPort(),
-			       e);
-		} else {
-		    logger.log(Level.FINE, 
-			       "Ignoring spurious request packet from " 
-			       + socket.getInetAddress());
-		}
-	    } catch (UnknownHostException ue) {
-		logger.log(Level.SEVERE, "Unknown host!", ue);
-	    }
-	} finally {
-	    try {
-		socket.close();
-	    } catch (IOException e) {
-		logger.log(Level.FINE, "exception closing socket", e);
-	    }
-	}
-    }
-
-    /** Close any sockets that were sitting in the task queue. */
-    private void closeRequestSockets(ArrayList tasks) {
-	for (int i = tasks.size(); --i >= 0; ) {
-	    Object obj = tasks.get(i);
-	    if (obj instanceof SocketTask) {
-		try {
-		    ((SocketTask)obj).socket.close();
-		} catch (IOException e) {
-		}
-	    }
-	}
-    }//end closeRequestSockets
-
-    /** Return a new array containing the elements of the given array
-     *  plus the given element added to the end.
-     */
-    private static Object[] arrayAdd(Object[] array, Object elt) {
-	int len = array.length;
-	Object[] narray =
-	    (Object[])Array.newInstance(array.getClass().getComponentType(),
-					len + 1);
-	System.arraycopy(array, 0, narray, 0, len);
-	narray[len] = elt;
-	return narray;
-    }//end arrayAdd
-
-    /** Return a new array containing all the elements of the given array
-     *  except the one at the specified index.
-     */
-    private static Object[] arrayDel(Object[] array, int i) {
-	int len = array.length - 1;
-	Object[] narray =
-	    (Object[])Array.newInstance(array.getClass().getComponentType(),
-					len);
-	System.arraycopy(array, 0, narray, 0, i);
-	System.arraycopy(array, i + 1, narray, i, len - i);
-	return narray;
-    }//end ArrayDel
-
-    /** Returns the first index of elt in the array, else -1. */
-    private static int indexOf(Object[] array, Object elt) {
-	return indexOf(array, array.length, elt);
-    }//end indexOf
-
-    /** Returns the first index of elt in the array if < len, else -1. */
-    private static int indexOf(Object[] array, int len, Object elt) {
-	for (int i = 0; i < len; i++) {
-	    if (elt.equals(array[i]))
-		return i;
-	}
-	return -1;
-    }//end indexOf
-
-    /** Return true if some object is an element of both arrays */
-    private static boolean overlap(Object[] arr1, Object[] arr2) {
-	for (int i = arr1.length; --i >= 0; ) {
-	    if (indexOf(arr2, arr1[i]) >= 0)
-		return true;
-	}
-	return false;
-    }//end overlap
-
-    /** Weed out duplicates. */
-    private static Object[] removeDups(Object[] arr) {
-	for (int i = arr.length; --i >= 0; ) {
-	    if (indexOf(arr, i, arr[i]) >= 0)
-		arr = arrayDel(arr, i);
-	}
-	return arr;
-    }//end removeDups
-
-    /**
-     * Sends the given packet data on the given <code>MulticastSocket</code>
-     * through each of the network interfaces corresponding to elements of
-     * the given array of IP addresses. If the given array of IP addresses
-     * is <code>null</code> or empty, then the data will be sent through only
-     * the default network interface.
-     *
-     * @param mcSocket   the <code>MulticastSocket</code> on which the data
-     *                   will be sent
-     * @param packet     <code>DatagramPacket</code> array whose elements are
-     *                   the data to send 
-     * @param addresses  <code>InetAddress</code> array whose elements
-     *                   represent the Internet Protocol (IP) addresses
-     *                   corresponding to the network interfaces (NICs)
-     *                   through which the multicast data should be sent
-     *
-     * @throws java.io.InterruptedIOException
-     */
-    private static void sendPacketByNIC(MulticastSocket mcSocket,
-                                        DatagramPacket[] packet,
-                                        InetAddress[] addresses)
-                                                 throws InterruptedIOException
-    {
-        if( (addresses != null) && (addresses.length > 0) ) {
-            for(int i=0;i<addresses.length;i++) {
-                try {
-                    mcSocket.setInterface(addresses[i]);
-                } catch(SocketException e) {
-                    continue;//skip to next interface address
-                }
-                sendPacket(mcSocket,packet);
-            }//end loop
-        } else {//use default interface
-            sendPacket(mcSocket,packet);
-        }//endif
-    }//end sendPacketByNIC
-
-    /**
-     * Sends the given packet data on the given <code>MulticastSocket</code>
-     * through the network interface that is currently set.
-     *
-     * @param mcSocket the <code>MulticastSocket</code> on which the data
-     *                 will be sent
-     * @param packet   <code>DatagramPacket</code> array whose elements are 
-     *                 the data to send 
-     *
-     * @throws java.io.InterruptedIOException
-     */
-    private static void sendPacket(MulticastSocket mcSocket,
-                                   DatagramPacket[] packet)
-                                                throws InterruptedIOException
-    {
-        for(int i=0;i<packet.length;i++) {
-            try {
-                mcSocket.send(packet[i]);
-            } catch(InterruptedIOException e) {
-                throw e;
-            } catch(IOException e) {
-            }
-        }//end loop
-    }//end sendPacket
-
-    /**
-     * Retrieves and parses the <code>-Dnet.jini.discovery.interface</code>
-     * system property, converting each parsed value to an instance of
-     * <code>InetAddress</code>, and returning the results of each conversion
-     * in an array.
-     *
-     * @return <code>InetAddress</code> array in which each element represents
-     *         the Internet Protocol (IP) address corresponding to a network
-     *         interface.
-     *
-     * @throws java.net.UnknownHostException
-     */
-    private static InetAddress[] getNICAddresses() throws UnknownHostException
-    {
-        String str = null;
-	try {
-	    str = System.getProperty("net.jini.discovery.interface");
-	} catch (SecurityException e) { /* ignore */ }
-        if (str == null) return null;
-        InetAddress[] addrs = null;
-        String delimiter = ",";
-        StringTokenizer st = null;
-        st = new StringTokenizer(str,delimiter);
-        int n = st.countTokens();
-        if (n > 0) {
-            addrs = new InetAddress[n];
-            for(int i=0;((st.hasMoreTokens())&&(i<n));i++) {
-                addrs[i] = InetAddress.getByName(st.nextToken());
-            }
-            return addrs;
-        } else {
-            return addrs;
-        }
-    }//end getNICAddresses
-
-    /* Note that the QAConfig is named qaConfig here to avoid cut/paste
-     * screwups pulling davis configuration entries into the code, which
-     * use the name 'config' for the Configuration object.
-     */
-    private void init(QAConfig qaConfig)
-                                       throws ActivationException, IOException
-    {
-        String host = System.getProperty("java.rmi.server.hostname");
-        if (host == null) {
-            host = InetAddress.getLocalHost().getHostName();
-        }
-        thisInetAddress = InetAddress.getByName(host);
-        unicastRequestThread = new UnicastThread(unicastPort);
-        synchronized (lockLookupLocator){
-            lookupLocator = QAConfig.getConstrainedLocator(host, unicastRequestThread.port);
-        }
-        /* start an activatable lookup service simulation */
-        if (lookupServiceID == null) {
-            lookupServiceID = lookupProxy.getServiceID();
-        }
-        if( (lookupProxy == null) || (lookupServiceID == null) ) {
-            throw new ActivationException("failure creating lookup service");
-        }
-	// the code block was a noop for unicastPort > 0, because 
-	// setUnicastPort does nothing if the argument is unicastPort
-//          if(unicastPort > 0) {
-//              /* Change the locator port for this lookup service. */
-//              setUnicastPort(unicastPort);
-//          } else {
-//              /* Port is already set (randomly). need to set only the locator. */
-//              lookupProxy.setLocator(lookupLocator);
-//          }//endif
-	// change to set unconditionally
-        lookupProxy.setLocator(lookupLocator);
-
-	/* add new configration entries from the davis reggie implementation */
-	Configuration config = qaConfig.getConfiguration();
-	MethodConstraints discoveryConstraints = null;
-	try {
-	    try {
-	        multicastInterfaces = (NetworkInterface[]) config.getEntry(
-	    	    "test", "multicastInterfaces", NetworkInterface[].class);
-	        multicastInterfacesSpecified = true;
-	    } catch (NoSuchEntryException e) {
-	        List l = Collections.list(NetworkInterface.getNetworkInterfaces());
-	        multicastInterfaces = (NetworkInterface[])
-		    l.toArray(new NetworkInterface[l.size()]);
-	        multicastInterfacesSpecified = false;
-	    }
-//	    multicastAnnouncementInterval = Config.getLongEntry(
-//	        config, "test", "multicastAnnouncementInterval",
-//	        multicastAnnouncementInterval, 1, Long.MAX_VALUE);
-	    multicastAnnouncementInterval = 
-	        qaConfig.getLongConfigVal("net.jini.discovery.announce", 120000);
-	    discoveryConstraints = 
-	        (MethodConstraints) config.getEntry(
-		    "test", "discoveryConstraints",
-		    MethodConstraints.class, null);
-	    if (discoveryConstraints == null) {
-	        discoveryConstraints = 
-		    new BasicMethodConstraints(InvocationConstraints.EMPTY);
-	    }
-	    try {
-	        multicastRequestSubjectChecker =
-		    (ClientSubjectChecker) Config.getNonNullEntry(
-		        config, "test", "multicastRequestSubjectChecker",
-		        ClientSubjectChecker.class);
-	    } catch (NoSuchEntryException e) {
-	        // leave null
-	    }
-	    try {
-	        unicastDiscoverySubjectChecker =
-		    (ClientSubjectChecker) Config.getNonNullEntry(
-		        config, "test", "unicastDiscoverySubjectChecker",
-		        ClientSubjectChecker.class);
-	    } catch (NoSuchEntryException e) {
-	        // leave null
-	    }
-	} catch (ConfigurationException ce) {
-	    throw new RuntimeException("Configuration error", ce);
-	}
-	protocol2 = Discovery.getProtocol2(null);
-	multicastRequestConstraints = DiscoveryConstraints.process(
-	    discoveryConstraints.getConstraints(
-		DiscoveryConstraints.multicastRequestMethod));
-	multicastAnnouncementConstraints = DiscoveryConstraints.process(
-	    discoveryConstraints.getConstraints(
-		DiscoveryConstraints.multicastAnnouncementMethod));
-	unicastDiscoveryConstraints = DiscoveryConstraints.process(
-	    discoveryConstraints.getConstraints(
-		DiscoveryConstraints.unicastDiscoveryMethod));
-
-	try {
-            DEFAULT_MULTICAST_TTL = Config.getIntEntry(
-	        config, "multicast", "ttl", 1, 0, 15);
-	} catch (ConfigurationException ce) {
-            DEFAULT_MULTICAST_TTL = 1;
-	}
-
-        /* create the discovery-related threads */
-        multicastRequestThread = new MulticastThread();
-        multicastAnnouncementThread = new AnnounceThread();
-        
-    }//end init
-    
-    public void start(){
-        /* start the threads */
-        unicastRequestThread.start();
-        multicastRequestThread.start();
-        multicastAnnouncementThread.start();
-    }
-
-}//end class DiscoveryProtocolSimulator
-
-
+/*
+ * 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.test.share;
+
+import com.sun.jini.qa.harness.QAConfig;
+import com.sun.jini.qa.harness.AdminManager;
+import com.sun.jini.qa.harness.TestException;
+
+import com.sun.jini.start.HTTPDStatus;
+import com.sun.jini.start.ServiceStarter;
+
+import com.sun.jini.thread.TaskManager;
+
+import com.sun.jini.discovery.ClientSubjectChecker;
+import com.sun.jini.discovery.Discovery;
+import com.sun.jini.discovery.DiscoveryConstraints;
+import com.sun.jini.discovery.DiscoveryProtocolException;
+import com.sun.jini.discovery.EncodeIterator;
+import com.sun.jini.discovery.MulticastAnnouncement;
+import com.sun.jini.discovery.MulticastRequest;
+import com.sun.jini.discovery.UnicastResponse;
+
+import net.jini.discovery.Constants;
+import net.jini.discovery.IncomingMulticastRequest;
+import net.jini.discovery.IncomingUnicastRequest;
+import net.jini.discovery.OutgoingMulticastAnnouncement;
+import net.jini.discovery.OutgoingUnicastResponse;
+
+import net.jini.core.constraint.MethodConstraints;
+import net.jini.core.discovery.LookupLocator;
+import net.jini.core.event.EventRegistration;
+import net.jini.core.event.RemoteEventListener;
+import net.jini.core.lookup.ServiceID;
+import net.jini.core.lookup.ServiceItem;
+import net.jini.core.lookup.ServiceMatches;
+import net.jini.core.lookup.ServiceRegistrar;
+import net.jini.core.lookup.ServiceRegistration;
+import net.jini.core.lookup.ServiceTemplate;
+
+import com.sun.jini.test.services.lookupsimulator.LookupSimulatorProxyInterface;
+
+import net.jini.constraint.BasicMethodConstraints;
+import net.jini.io.UnsupportedConstraintException;
+import net.jini.config.Configuration;
+import net.jini.config.NoSuchEntryException;
+import net.jini.config.ConfigurationException;
+import net.jini.core.constraint.InvocationConstraint;
+import net.jini.core.constraint.InvocationConstraints;
+import com.sun.jini.config.Config;
+import net.jini.security.Security;
+import net.jini.security.AuthenticationPermission;
+
+import java.lang.reflect.Array;
+
+import java.net.DatagramPacket;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.MulticastSocket;
+import java.net.NetworkInterface;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+
+import java.nio.ByteBuffer;
+import java.nio.BufferUnderflowException;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
+
+import java.rmi.activation.ActivationGroupDesc;
+import java.rmi.activation.ActivationGroupDesc.CommandEnvironment;
+import java.rmi.activation.ActivationGroup;
+import java.rmi.activation.ActivationGroupID;
+import java.rmi.activation.ActivationDesc;
+import java.rmi.activation.Activatable;
+import java.rmi.activation.ActivationException;
+import java.rmi.MarshalledObject;
+import java.rmi.RemoteException;
+
+import java.security.Permission;
+import java.security.Principal;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Properties;
+import java.util.Random;
+import java.util.StringTokenizer;
+
+import java.util.logging.Logger;
+import java.util.logging.Level;
+
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.Subject;
+import javax.security.auth.login.LoginException;
+import java.security.PrivilegedExceptionAction;
+import java.security.PrivilegedActionException;
+
+
+/**
+ * Instances of this class provide general-purpose functions related to the
+ * discovery protocol. This utility class is intended to be useful to all
+ * categories of tests that wish to simulate the multicast and unicast
+ * message exchange between a client or service and a lookup service -- 
+ * from the point of view of the lookup service. (For provide the
+ * analogous functionality from the point of view of the client,
+ * use the <code>net.jini.discovery.LookupDiscovery</code> and the
+ * <code>net.jini.discovery.LookupLocatorDiscovery</code> utilities.
+ *
+ * @see net.jini.core.discovery.LookupLocator
+ *
+ * @see net.jini.discovery.IncomingMulticastAnnouncement
+ * @see net.jini.discovery.IncomingMulticastRequest
+ * @see net.jini.discovery.IncomingUnicastRequest
+ * @see net.jini.discovery.IncomingUnicastResponse
+ *
+ * @see net.jini.discovery.OutgoingMulticastAnnouncement
+ * @see net.jini.discovery.OutgoingMulticastRequest
+ * @see net.jini.discovery.OutgoingUnicastRequest
+ * @see net.jini.discovery.OutgoingUnicastResponse
+ */
+public class DiscoveryProtocolSimulator {
+
+    /** Maximum minMax lease duration for both services and events */
+    private static final long MAX_LEASE = 1000L * 60 * 60 * 24 * 365 * 1000;
+    /** Maximum minimum renewal interval */
+    private static final long MAX_RENEW = 1000L * 60 * 60 * 24 * 365;
+    /** Default maximum size of multicast packets to send and receive */
+    private static final int DEFAULT_MAX_PACKET_SIZE = 512;
+    /** Default time to live value to use for sending multicast packets */
+    private static int DEFAULT_MULTICAST_TTL;
+    /** Default timeout to set on sockets used for unicast discovery */
+    private static final int DEFAULT_SOCKET_TIMEOUT = 1*60*1000;
+
+    private static Logger logger = Logger.getLogger("com.sun.jini.qa.harness");
+
+    /** Socket timeout for multicast request receive() */
+    private static final int SOCKET_TIMEOUT = 5*60*1000;
+
+    /** Only allow connections from this address */
+    private InetAddress thisInetAddress = null;
+    /** Current number of multicast announcements sent by this class */
+    private int nAnnouncements = 0;
+    /** Internet Protocol (IP) addresses of the network interfaces (NICs)
+     *  through which multicast packets will be sent.
+     */
+    private InetAddress[] nicAddresses;
+
+    /** Port for unicast discovery */
+    private int unicastPort = 0;
+    /** The locator to send */
+    private volatile LookupLocator lookupLocator = null;
+    /** The member groups to send */
+    private String[] memberGroups = {};
+
+    /** Thread to receive/process multicast requests from client or service */
+    volatile MulticastThread multicastRequestThread;
+    /** Thread to receive/process unicast requests from client or service */
+    volatile UnicastThread unicastRequestThread;
+    /** Thread to send multicast announcements to from client or service */
+    volatile AnnounceThread multicastAnnouncementThread;
+
+    /** Task manager for sending events and discovery responses */
+    private final TaskManager taskMgr = new TaskManager(10, 1000 * 15, 1.0f);
+
+    /** Proxy for the "fake" lookup service that is sent */
+    private volatile LookupSimulatorProxyInterface lookupProxy;
+    /** The service ID to assign to the lookup service that is sent */
+    private volatile ServiceID lookupServiceID = null;
+
+    /** Socket timeout for unicast discovery request processing */
+    private int unicastTimeout =
+	Integer.getInteger("com.sun.jini.reggie.unicastTimeout",
+			   1000 * 60).intValue();
+    /* For synchronization, instead of ReadersWriter locks used by reggie */
+    private final Object lockNAnnouncements = new Object();
+    private final Object lockLookupLocator  = new Object();
+    private final Object lockMemberGroups   = new Object();
+
+    /* new fields taken from the davis reggie */
+
+    /** Network interfaces to use for multicast discovery */
+    private volatile NetworkInterface[] multicastInterfaces;
+    /** Flag indicating whether network interfaces were explicitly specified */
+    private volatile boolean multicastInterfacesSpecified;
+    private volatile Discovery protocol2;
+    /** Constraints specified for incoming multicast requests */
+    private volatile DiscoveryConstraints multicastRequestConstraints;
+    /** Constraints specified for outgoing multicast announcements */
+    private volatile DiscoveryConstraints multicastAnnouncementConstraints;
+    /** Constraints specified for handling unicast discovery */
+    private volatile DiscoveryConstraints unicastDiscoveryConstraints;
+    /** Client subject checker to apply to incoming multicast requests */
+    private volatile ClientSubjectChecker multicastRequestSubjectChecker;
+    /** Client subject checker to apply to unicast discovery attempts */
+    private volatile ClientSubjectChecker unicastDiscoverySubjectChecker;
+    /** Interval to wait in between sending multicast announcements */
+    private volatile long multicastAnnouncementInterval = 1000 * 60 * 2;
+
+    
+    public DiscoveryProtocolSimulator(QAConfig config, String[] memberGroups, int unicastPort, LookupSimulatorProxyInterface proxy)
+                                       throws ActivationException, IOException, TestException
+    {
+        this.memberGroups = memberGroups;
+        this.unicastPort  = unicastPort;
+        // start LUS before switching identity to reggie
+        lookupProxy = proxy;
+	LoginContext context = null;
+	Configuration c = config.getConfiguration();
+	try {
+	    context = (LoginContext) c.getEntry("test",
+						"reggieLoginContext", 
+						LoginContext.class,
+						null);
+	} catch (ConfigurationException e) {
+	    throw new RuntimeException("Bad configuration", e);
+	}
+	if (context == null) {
+		init(config);
+        } else {
+	    try {
+		Principal reggie = 
+		    (Principal) c.getEntry("principal", "reggie", Principal.class);
+		// allow the simulator, running as reggie, to authenticate as reggie
+		// XXX Should the permission be obtained from the configuration???
+		Security.grant(lookupProxy.getClass(), 
+			       new Principal[]{reggie}, 
+			       new Permission[] {
+				   new AuthenticationPermission(
+						 Collections.singleton(reggie),
+						 Collections.singleton(reggie),
+						 "connect")});
+		context.login();
+	    } catch (LoginException e) {
+		throw new RuntimeException("LoginFailed", e);
+	    } catch (ConfigurationException e) {
+		throw new RuntimeException("Configuration error", e);
+	    }
+	    try {
+		final QAConfig finalConfig = config;
+		Subject.doAsPrivileged(context.getSubject(),
+			     	new PrivilegedExceptionAction() {
+				    public Object run() throws ActivationException, IOException {
+					init(finalConfig);
+				 	return null;
+				    }
+				},
+				null);
+	    } catch (PrivilegedActionException e) {
+		Throwable t = e.getException();
+		if (t instanceof ActivationException) {
+		    throw (ActivationException) t;
+		}
+		if (t instanceof IOException) {
+		    throw (IOException) t;
+		}
+	    }
+	}
+    }//end constructor
+
+    public void stopAnnouncements() {
+        stopAnnouncements(null);
+    }//end stopAnnouncements
+    public void stopAnnouncements(String testname) {
+        logger.log(Level.FINE, "   stopAnnouncements entered");
+        /* terminate all daemons */
+        unicastRequestThread.interrupt();
+	multicastRequestThread.interrupt();
+	multicastAnnouncementThread.interrupt();
+        logger.log(Level.FINE, "     interrupted all threads");
+	taskMgr.terminate();
+        logger.log(Level.FINE, "     terminated task manager");
+	try {
+            logger.log(Level.FINE, "     unicastRequestThread.join()");
+            unicastRequestThread.join();
+            logger.log(Level.FINE, "     multicastRequestThread.join()");
+            multicastRequestThread.join();
+            logger.log(Level.FINE, 
+                              "     multicastAnnouncementThread.join()");
+            multicastAnnouncementThread.join();
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+        }
+        logger.log(Level.FINE, "     close all request sockets");
+        closeRequestSockets(taskMgr.getPending());
+        logger.log(Level.FINE, "   stopAnnouncements exited");
+    }//end stopAnnouncements
+
+//    public void destroyLookup() throws RemoteException {
+//        lookupProxy.destroy();
+//    }//end destroyLookup
+
+    public int getNAnnouncementsSent() {
+        synchronized(lockNAnnouncements) {
+            return nAnnouncements;
+        }//end sync
+    }//end getNAnnouncementsSent
+
+    public ServiceRegistrar getLookupProxy() {
+        return lookupProxy;
+    }//end getLookupProxy
+
+    public LookupLocator getLookupLocator() {
+        synchronized(lockLookupLocator) {
+            return lookupLocator;
+        }//end sync
+    }//end getLookupLocator
+
+    public String[] getMemberGroups() {
+        synchronized(lockMemberGroups) {
+            return memberGroups; // don't clone, never modified once created
+        }//end sync
+    }//end getMemberGroups
+
+    public void addMemberGroups(String[] groups) throws RemoteException {
+        String[] tmpArray = null;
+        /* Change the member groups locally, then replace them remotely */
+        synchronized(lockMemberGroups) {
+            for (int i=0;i<groups.length;i++) {
+                if (indexOf(memberGroups, groups[i]) < 0)
+                    memberGroups = (String[])arrayAdd(memberGroups,groups[i]);
+            }
+            tmpArray = new String[memberGroups.length];
+            System.arraycopy(memberGroups,0,tmpArray,0,memberGroups.length);
+        }//end sync
+        /* Replace the groups remotely - no remote calls in sync block*/
+        lookupProxy.setMemberGroups(tmpArray);
+        synchronized (multicastAnnouncementThread) {
+            multicastAnnouncementThread.notify();
+        }//end sync
+    }//end addMemberGroups
+
+    public void removeMemberGroups(String[] groups) throws RemoteException {
+        String[] tmpArray = null;
+        /* Change the member groups locally, then replace them remotely */
+        synchronized(lockMemberGroups) {
+            for (int i=0;i<groups.length;i++) {
+                int j = indexOf(memberGroups, groups[i]);
+                if (j >= 0) memberGroups = (String[])arrayDel(memberGroups,j);
+            }
+            tmpArray = new String[memberGroups.length];
+            System.arraycopy(memberGroups,0,tmpArray,0,memberGroups.length);
+        }//end sync
+        /* Replace the groups remotely - no remote calls in sync block*/
+        lookupProxy.setMemberGroups(tmpArray);
+        synchronized (multicastAnnouncementThread) {
+            multicastAnnouncementThread.notify();
+        }//end sync
+    }//end removeMemberGroups
+
+    public void setMemberGroups(String[] groups) throws RemoteException {
+        String[] tmpArray = null;
+        /* Change the member groups locally, then replace them remotely */
+        synchronized(lockMemberGroups) {
+            memberGroups = (String[])removeDups(groups);
+            tmpArray = new String[memberGroups.length];
+            System.arraycopy(memberGroups,0,tmpArray,0,memberGroups.length);
+        }//end sync
+        /* Replace the groups remotely - no remote calls in sync block*/
+        lookupProxy.setMemberGroups(memberGroups);
+        synchronized (multicastAnnouncementThread) {
+            multicastAnnouncementThread.notify();
+        }//end sync
+    }//end setMemberGroups
+
+    public int getUnicastPort() {
+        synchronized(lockLookupLocator) {
+            return unicastPort;
+        }//end sync
+    }//end getUnicastPort
+
+    public void setUnicastPort(int port) throws IOException {
+	if (port == unicastPort) return;
+        LookupLocator tmpLocator = null;
+        synchronized(lockLookupLocator) {
+            if(    (    (port == 0)
+                     && (unicastRequestThread.port == Constants.discoveryPort))
+                || (port == unicastRequestThread.port) )
+            {
+                unicastPort = port;
+                return;
+            }
+            /* create a UnicastThread that listens on the new port */
+            UnicastThread newUnicastRequestThread = new UnicastThread(port);
+            /* terminate the current UnicastThread listening on the old port */
+            unicastRequestThread.interrupt();
+            try {
+                unicastRequestThread.join();
+            } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+            }
+            /* start the UnicastThread listening on the new port */
+            unicastRequestThread = newUnicastRequestThread;
+            unicastRequestThread.start();
+            unicastPort = port;
+            lookupLocator = QAConfig.getConstrainedLocator(lookupLocator.getHost(),
+                                                           unicastRequestThread.port);
+            /* Equality (same port&host ==> lookupLocator.equals(tmpLocator) */
+            tmpLocator = QAConfig.getConstrainedLocator(lookupLocator.getHost(),
+                                                        unicastRequestThread.port);
+        }//end sync
+        /* Set unicast port remotely - no remote calls in sync block*/
+        lookupProxy.setLocator(tmpLocator); // also sets unicastPort
+        synchronized (multicastAnnouncementThread) {
+            multicastAnnouncementThread.notify();
+        }//end sync
+    }//end setUnicastPort
+
+    /** Multicast discovery request thread code. */
+    private class MulticastThread extends Thread {
+	/** Multicast socket to receive packets */
+	private final MulticastSocket socket;
+
+	/**
+	 * Create a high priority daemon thread.  Set up the socket now
+	 * rather than in run, so that we get any exception up front.
+	 */
+	public MulticastThread() throws IOException {
+	    super("multicast request");
+	    setDaemon(true);
+	    if (multicastInterfaces != null && multicastInterfaces.length == 0)
+	    {
+		socket = null;
+		return;
+	    }
+	    InetAddress requestAddr = Constants.getRequestAddress();
+	    socket = new MulticastSocket(Constants.discoveryPort);
+	    socket.setTimeToLive(
+		multicastAnnouncementConstraints.getMulticastTimeToLive(
+		    DEFAULT_MULTICAST_TTL));
+	    if (multicastInterfaces != null) {
+		Level failureLogLevel =
+		    multicastInterfacesSpecified ? Level.WARNING : Level.FINE;
+		for (int i = 0; i < multicastInterfaces.length; i++) {
+		    try {
+			socket.setNetworkInterface(multicastInterfaces[i]);
+			socket.joinGroup(requestAddr);
+		    } catch (IOException e) {
+			logger.log(
+			    failureLogLevel,
+			    "exception configuring multicast interface", e);
+		    }
+		}
+	    } else {
+		// REMIND: tolerate failure here?
+		socket.joinGroup(requestAddr);
+	    }
+	}
+
+	/** True if thread has been interrupted */
+	private volatile boolean interrupted = false;
+
+	/* This is a workaround for Thread.interrupt not working on
+	 * MulticastSocket.receive on all platforms.
+	 */
+	public void interrupt() {
+	    interrupted = true;
+	    socket.close();
+	}
+
+	public boolean isInterrupted() {
+	    return interrupted;
+	}
+
+	public void run() {
+	    if (multicastInterfaces != null && multicastInterfaces.length == 0)
+	    {
+		return;
+	    }
+	    byte[] buf = new byte[
+		multicastRequestConstraints.getMulticastMaxPacketSize(
+		    DEFAULT_MAX_PACKET_SIZE)];
+	    DatagramPacket dgram = new DatagramPacket(buf, buf.length);
+	    while (!isInterrupted()) {
+		try {
+		    dgram.setLength(buf.length);
+		    try {
+			socket.receive(dgram);
+		    } catch (NullPointerException e) {
+			break; // workaround for bug 4190513
+		    }
+		    int pv;
+		    try {
+			pv = ByteBuffer.wrap(dgram.getData(),
+					     dgram.getOffset(),
+					     dgram.getLength()).getInt();
+		    } catch (BufferUnderflowException e) {
+			throw new DiscoveryProtocolException(null, e);
+		    }
+		    multicastRequestConstraints.checkProtocolVersion(pv);
+
+		    MulticastRequest req = 
+			getDiscovery(pv).decodeMulticastRequest(
+			    dgram,
+			    multicastRequestConstraints.
+				getUnfulfilledConstraints(),
+			    multicastRequestSubjectChecker);
+                    synchronized (lockMemberGroups){
+                        if ((req.getGroups().length != 0 &&
+                             !overlap(memberGroups, req.getGroups())) ||
+                            indexOf(req.getServiceIDs(), lookupServiceID) >= 0)
+                            continue;
+                    }
+		    logger.log(Level.FINE, "Received valid multicast for " + lookupLocator);
+		    taskMgr.addIfNew(
+			new AddressTask(InetAddress.getByName(req.getHost()),
+					req.getPort()));
+		} catch (InterruptedIOException ignore) {
+		    break;
+		} catch (DiscoveryProtocolException ignore) {
+		    break;
+		} catch (Exception e) {
+		    if (isInterrupted()) {
+			break;
+		    }
+		    logger.log(Level.FINE,
+			       "exception receiving multicast request", e);
+		}
+	    }
+	    socket.close();
+	}
+    }
+
+    /** Unicast discovery request thread code. */
+    private class UnicastThread extends Thread {
+	/** Server socket to accepts connections on. */
+	private final ServerSocket listen;
+	/** Listen port */
+	public final int port;
+
+	/**
+	 * Create a daemon thread.  Set up the socket now rather than in run,
+	 * so that we get any exception up front.
+	 */
+	public UnicastThread(int port) throws IOException {
+	    super("unicast request");
+	    setDaemon(true);
+            ServerSocket listen = null;
+	    if (port == 0) {
+		try {
+		    listen = new ServerSocket(Constants.discoveryPort);
+		} catch (IOException e) {
+		    logger.log(Level.FINE,
+			       "failed to bind to default port", e);
+		}
+	    }
+	    if (listen == null) {
+		listen = new ServerSocket(port);
+	    }
+	    this.port = listen.getLocalPort();
+            this.listen = listen;
+	}
+
+	/** True if thread has been interrupted */
+	private volatile boolean interrupted = false;
+
+	/* This is a workaround for Thread.interrupt not working on
+	 * ServerSocket.accept on all platforms.  ServerSocket.close
+	 * can't be used as a workaround, because it also doesn't work
+	 * on all platforms.
+	 */
+	public void interrupt() {
+	    interrupted = true;
+	    try {
+		(new Socket(InetAddress.getLocalHost(), port)).close();
+	    } catch (IOException e) {
+	    }
+	}
+
+	public boolean isInterrupted() {
+	    return interrupted;
+	}
+
+	public void run() {
+	    while (!isInterrupted()) {
+		try {
+		    Socket socket = listen.accept();
+		    if (isInterrupted()) {
+			try {
+			    socket.close();
+			} catch (IOException e) {
+			    logger.log(Level.FINE,
+				       "exception closing socket", e);
+			}
+			break;
+		    }
+		    logger.log(Level.FINE, "Adding socket task for " + lookupLocator);
+		    taskMgr.add(new SocketTask(socket));
+		} catch (InterruptedIOException e) {
+		    break;
+		} catch (Exception e) {
+		    logger.log(Level.FINE, "exception listening on socket", e);
+		}
+		/* if we fail in any way, just forget about it */
+	    }
+	    try {
+		listen.close();
+	    } catch (IOException e) {
+	    }
+	}
+    }
+
+    /** Multicast discovery announcement thread code. */
+    private class AnnounceThread extends Thread {
+	/** Multicast socket to send packets on */
+	private final MulticastSocket socket;
+
+	private volatile boolean interrupted = false;
+
+	/* This is a workaround for Thread.interrupt not working due
+	 * to the logging system sometimes throwing away InterruptedIOException
+	 */
+	public void interrupt() {
+	    interrupted = true;
+	    super.interrupt();
+	}
+
+	public boolean isInterrupted() {
+	    return interrupted;
+	}
+
+	/**
+	 * Create a daemon thread.  Set up the socket now rather than in run,
+	 * so that we get any exception up front.
+	 */
+	public AnnounceThread() throws IOException {
+	    super("discovery announcement");
+	    setDaemon(true);
+	    if (multicastInterfaces == null || multicastInterfaces.length > 0)
+	    {
+		socket = new MulticastSocket();
+		socket.setTimeToLive(
+		    multicastAnnouncementConstraints.getMulticastTimeToLive(
+			DEFAULT_MULTICAST_TTL));
+	    } else {
+		socket = null;
+	    }
+	}
+
+	public void run() {
+	    if (multicastInterfaces != null && multicastInterfaces.length == 0)
+	    {
+		return;
+	    }
+	    try {
+		while (!isInterrupted()) {
+                    synchronized (lockMemberGroups){
+                        if (!announce(memberGroups)) break;
+                    }
+                    synchronized (this){
+                        wait(multicastAnnouncementInterval);
+                    }
+		}
+	    } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+	    }
+// disable this to allow simulation of disappearance of multicast announcements
+//	    if (memberGroups.length > 0) 
+//		announce(new String[0]);//send NO_GROUPS just before shutdown
+	    socket.close();
+	}
+
+	/**
+	 * Announce membership in the specified groups, and return false if
+	 * interrupted, otherwise return true.
+	 */
+	private boolean announce(String[] groups) {
+	    // REMIND: cache latest announcement to skip re-encoding
+	    List packets = new ArrayList();
+	    Discovery disco;
+	    try {
+		disco = getDiscovery(
+		    multicastAnnouncementConstraints.chooseProtocolVersion());
+	    } catch (DiscoveryProtocolException e) {
+		throw new AssertionError(e);
+	    }
+	    EncodeIterator ei = disco.encodeMulticastAnnouncement(
+		new MulticastAnnouncement(System.currentTimeMillis(),
+					  lookupLocator.getHost(),
+					  lookupLocator.getPort(),
+					  groups,
+					  lookupServiceID),
+		multicastAnnouncementConstraints.getMulticastMaxPacketSize(
+					  DEFAULT_MAX_PACKET_SIZE),
+		multicastAnnouncementConstraints.getUnfulfilledConstraints());
+	    while (ei.hasNext()) {
+		try {
+		    packets.addAll(Arrays.asList(ei.next()));
+		} catch (Exception e) {
+		// UnsupportedConstraintException is expected and normal
+                    if (! (e instanceof UnsupportedConstraintException)) {
+		        logger.log(Level.INFO,
+			           "exception encoding multicast announcement", e);
+                    }
+		}
+	    }
+	    try {
+	        logger.log(Level.FINE, "Sending packets from " + lookupLocator);
+		send((DatagramPacket[])
+		    packets.toArray(new DatagramPacket[packets.size()]));
+		    synchronized(lockNAnnouncements) {
+                        nAnnouncements++;
+                    }
+	    } catch (InterruptedIOException e) {
+		return false;
+	    } catch (IOException e) {
+		logger.log(Level.INFO,
+			   "exception sending multicast announcement", e);
+	    }
+	    return true;
+	}
+
+	/**
+	 * Attempts to multicast the given packets on each of the configured
+	 * network interfaces.
+	 */
+	private void send(DatagramPacket[] packets)
+	    throws InterruptedIOException
+	{
+	    if (multicastInterfaces != null) {
+		Level failureLogLevel =
+		    multicastInterfacesSpecified ? Level.WARNING : Level.FINE;
+		for (int i = 0; i < multicastInterfaces.length; i++) {
+		    try {
+			socket.setNetworkInterface(multicastInterfaces[i]);
+			send(packets, failureLogLevel);
+		    } catch (SocketException e) {
+			logger.log(failureLogLevel,
+				   "exception setting interface", e);
+		    }
+		}
+	    } else {
+		send(packets, Level.WARNING);
+	    }
+	}
+
+	/**
+	 * Attempts to multicast the given packets on the currently set network
+	 * interface, logging failures at the specified logging level.
+	 */
+	private void send(DatagramPacket[] packets, Level failureLogLevel)
+	    throws InterruptedIOException
+	{
+	    for (int i = 0; i < packets.length; i++) {
+		try {
+		    socket.send(packets[i]);
+		} catch (InterruptedIOException e) {
+		    throw e;
+		} catch (IOException e) {
+		    logger.log(failureLogLevel, "exception sending packet", e);
+		}
+	    }
+	}
+    }
+
+    /** Returns Discovery instance implementing the given protocol version */
+    private Discovery getDiscovery(int version)
+	throws DiscoveryProtocolException
+    {
+	switch (version) {
+	    case Discovery.PROTOCOL_VERSION_1:
+		return Discovery.getProtocol1();
+	    case Discovery.PROTOCOL_VERSION_2:
+		return protocol2;
+	    default:
+		throw new DiscoveryProtocolException(
+		    "unsupported protocol version: " + version);
+	}
+    }
+
+    private final class AddressTask implements TaskManager.Task {
+
+	/** The address */
+	public final InetAddress addr;
+	/** The port */
+	public final int port;
+
+	/** Simple constructor */
+	public AddressTask(InetAddress addr, int port) {
+	    this.addr = addr;
+	    this.port = port;
+	}
+
+	public int hashCode() {
+	    return addr.hashCode();
+	}
+
+	/** Two tasks are equal if they have the same address and port */
+	public boolean equals(Object obj) {
+	    if (!(obj instanceof AddressTask))
+		return false;
+	    AddressTask ua = (AddressTask)obj;
+	    return addr.equals(ua.addr) && port == ua.port;
+	}
+
+	/** Connect and then process a unicast discovery request */
+	public void run() {
+	    try {
+                logger.log(Level.FINE, "Responding to multicast with unicast from " + lookupLocator);
+		respond(new Socket(addr, port));
+	    } catch (IOException e) {
+	    } catch (SecurityException e) {
+	    }
+	}
+
+	/** No ordering */
+	public boolean runAfter(List tasks, int size) {
+	    return false;
+	}
+    }//end class AddressTask
+
+    /** Socket for unicast discovery response. */
+    private final class SocketTask implements TaskManager.Task {
+
+	/** The socket */
+	public final Socket socket;
+
+	/** Simple constructor */
+	public SocketTask(Socket socket) {
+	    this.socket = socket;
+	}
+
+	/** Process a unicast discovery request */
+	public void run() {
+	    respond(socket);
+	}
+
+	/** No ordering */
+	public boolean runAfter(List tasks, int size) {
+	    return false;
+	}
+    }//end class SocketTask
+
+    /** Process a unicast discovery request, and respond. */
+    private void respond(Socket socket) {
+	try {
+	    socket.setSoTimeout(
+		unicastDiscoveryConstraints.getUnicastSocketTimeout(
+		    DEFAULT_SOCKET_TIMEOUT));
+
+	    int pv = new DataInputStream(socket.getInputStream()).readInt();
+	    unicastDiscoveryConstraints.checkProtocolVersion(pv);
+	    getDiscovery(pv).handleUnicastDiscovery(
+		new UnicastResponse(lookupLocator.getHost(),
+				    lookupLocator.getPort(),
+				    memberGroups,
+				    lookupProxy),
+		socket,
+		unicastDiscoveryConstraints.getUnfulfilledConstraints(),
+		unicastDiscoverySubjectChecker,
+		Collections.EMPTY_LIST);
+	    logger.log(Level.FINE, "Responded to unicast request for " + lookupLocator);
+
+	} catch (Exception e) {
+	    try {
+		if (InetAddress.getLocalHost().equals(socket.getInetAddress())) {
+		    logger.log(Level.FINE, 
+			       "exception handling unicast discovery from "
+			       + socket.getInetAddress() + ":" 
+			       + socket.getPort(),
+			       e);
+		} else {
+		    logger.log(Level.FINE, 
+			       "Ignoring spurious request packet from " 
+			       + socket.getInetAddress());
+		}
+	    } catch (UnknownHostException ue) {
+		logger.log(Level.SEVERE, "Unknown host!", ue);
+	    }
+	} finally {
+	    try {
+		socket.close();
+	    } catch (IOException e) {
+		logger.log(Level.FINE, "exception closing socket", e);
+	    }
+	}
+    }
+
+    /** Close any sockets that were sitting in the task queue. */
+    private void closeRequestSockets(ArrayList tasks) {
+	for (int i = tasks.size(); --i >= 0; ) {
+	    Object obj = tasks.get(i);
+	    if (obj instanceof SocketTask) {
+		try {
+		    ((SocketTask)obj).socket.close();
+		} catch (IOException e) {
+		}
+	    }
+	}
+    }//end closeRequestSockets
+
+    /** Return a new array containing the elements of the given array
+     *  plus the given element added to the end.
+     */
+    private static Object[] arrayAdd(Object[] array, Object elt) {
+	int len = array.length;
+	Object[] narray =
+	    (Object[])Array.newInstance(array.getClass().getComponentType(),
+					len + 1);
+	System.arraycopy(array, 0, narray, 0, len);
+	narray[len] = elt;
+	return narray;
+    }//end arrayAdd
+
+    /** Return a new array containing all the elements of the given array
+     *  except the one at the specified index.
+     */
+    private static Object[] arrayDel(Object[] array, int i) {
+	int len = array.length - 1;
+	Object[] narray =
+	    (Object[])Array.newInstance(array.getClass().getComponentType(),
+					len);
+	System.arraycopy(array, 0, narray, 0, i);
+	System.arraycopy(array, i + 1, narray, i, len - i);
+	return narray;
+    }//end ArrayDel
+
+    /** Returns the first index of elt in the array, else -1. */
+    private static int indexOf(Object[] array, Object elt) {
+	return indexOf(array, array.length, elt);
+    }//end indexOf
+
+    /** Returns the first index of elt in the array if < len, else -1. */
+    private static int indexOf(Object[] array, int len, Object elt) {
+	for (int i = 0; i < len; i++) {
+	    if (elt.equals(array[i]))
+		return i;
+	}
+	return -1;
+    }//end indexOf
+
+    /** Return true if some object is an element of both arrays */
+    private static boolean overlap(Object[] arr1, Object[] arr2) {
+	for (int i = arr1.length; --i >= 0; ) {
+	    if (indexOf(arr2, arr1[i]) >= 0)
+		return true;
+	}
+	return false;
+    }//end overlap
+
+    /** Weed out duplicates. */
+    private static Object[] removeDups(Object[] arr) {
+	for (int i = arr.length; --i >= 0; ) {
+	    if (indexOf(arr, i, arr[i]) >= 0)
+		arr = arrayDel(arr, i);
+	}
+	return arr;
+    }//end removeDups
+
+    /**
+     * Sends the given packet data on the given <code>MulticastSocket</code>
+     * through each of the network interfaces corresponding to elements of
+     * the given array of IP addresses. If the given array of IP addresses
+     * is <code>null</code> or empty, then the data will be sent through only
+     * the default network interface.
+     *
+     * @param mcSocket   the <code>MulticastSocket</code> on which the data
+     *                   will be sent
+     * @param packet     <code>DatagramPacket</code> array whose elements are
+     *                   the data to send 
+     * @param addresses  <code>InetAddress</code> array whose elements
+     *                   represent the Internet Protocol (IP) addresses
+     *                   corresponding to the network interfaces (NICs)
+     *                   through which the multicast data should be sent
+     *
+     * @throws java.io.InterruptedIOException
+     */
+    private static void sendPacketByNIC(MulticastSocket mcSocket,
+                                        DatagramPacket[] packet,
+                                        InetAddress[] addresses)
+                                                 throws InterruptedIOException
+    {
+        if( (addresses != null) && (addresses.length > 0) ) {
+            for(int i=0;i<addresses.length;i++) {
+                try {
+                    mcSocket.setInterface(addresses[i]);
+                } catch(SocketException e) {
+                    continue;//skip to next interface address
+                }
+                sendPacket(mcSocket,packet);
+            }//end loop
+        } else {//use default interface
+            sendPacket(mcSocket,packet);
+        }//endif
+    }//end sendPacketByNIC
+
+    /**
+     * Sends the given packet data on the given <code>MulticastSocket</code>
+     * through the network interface that is currently set.
+     *
+     * @param mcSocket the <code>MulticastSocket</code> on which the data
+     *                 will be sent
+     * @param packet   <code>DatagramPacket</code> array whose elements are 
+     *                 the data to send 
+     *
+     * @throws java.io.InterruptedIOException
+     */
+    private static void sendPacket(MulticastSocket mcSocket,
+                                   DatagramPacket[] packet)
+                                                throws InterruptedIOException
+    {
+        for(int i=0;i<packet.length;i++) {
+            try {
+                mcSocket.send(packet[i]);
+            } catch(InterruptedIOException e) {
+                throw e;
+            } catch(IOException e) {
+            }
+        }//end loop
+    }//end sendPacket
+
+    /**
+     * Retrieves and parses the <code>-Dnet.jini.discovery.interface</code>
+     * system property, converting each parsed value to an instance of
+     * <code>InetAddress</code>, and returning the results of each conversion
+     * in an array.
+     *
+     * @return <code>InetAddress</code> array in which each element represents
+     *         the Internet Protocol (IP) address corresponding to a network
+     *         interface.
+     *
+     * @throws java.net.UnknownHostException
+     */
+    private static InetAddress[] getNICAddresses() throws UnknownHostException
+    {
+        String str = null;
+	try {
+	    str = System.getProperty("net.jini.discovery.interface");
+	} catch (SecurityException e) { /* ignore */ }
+        if (str == null) return null;
+        InetAddress[] addrs = null;
+        String delimiter = ",";
+        StringTokenizer st = null;
+        st = new StringTokenizer(str,delimiter);
+        int n = st.countTokens();
+        if (n > 0) {
+            addrs = new InetAddress[n];
+            for(int i=0;((st.hasMoreTokens())&&(i<n));i++) {
+                addrs[i] = InetAddress.getByName(st.nextToken());
+            }
+            return addrs;
+        } else {
+            return addrs;
+        }
+    }//end getNICAddresses
+
+    /* Note that the QAConfig is named qaConfig here to avoid cut/paste
+     * screwups pulling davis configuration entries into the code, which
+     * use the name 'config' for the Configuration object.
+     */
+    private void init(QAConfig qaConfig)
+                                       throws ActivationException, IOException
+    {
+        String host = System.getProperty("java.rmi.server.hostname");
+        if (host == null) {
+            host = InetAddress.getLocalHost().getHostName();
+        }
+        thisInetAddress = InetAddress.getByName(host);
+        unicastRequestThread = new UnicastThread(unicastPort);
+        synchronized (lockLookupLocator){
+            lookupLocator = QAConfig.getConstrainedLocator(host, unicastRequestThread.port);
+        }
+        /* start an activatable lookup service simulation */
+        if (lookupServiceID == null) {
+            lookupServiceID = lookupProxy.getServiceID();
+        }
+        if( (lookupProxy == null) || (lookupServiceID == null) ) {
+            throw new ActivationException("failure creating lookup service");
+        }
+	// the code block was a noop for unicastPort > 0, because 
+	// setUnicastPort does nothing if the argument is unicastPort
+//          if(unicastPort > 0) {
+//              /* Change the locator port for this lookup service. */
+//              setUnicastPort(unicastPort);
+//          } else {
+//              /* Port is already set (randomly). need to set only the locator. */
+//              lookupProxy.setLocator(lookupLocator);
+//          }//endif
+	// change to set unconditionally
+        lookupProxy.setLocator(lookupLocator);
+
+	/* add new configration entries from the davis reggie implementation */
+	Configuration config = qaConfig.getConfiguration();
+	MethodConstraints discoveryConstraints = null;
+	try {
+	    try {
+	        multicastInterfaces = (NetworkInterface[]) config.getEntry(
+	    	    "test", "multicastInterfaces", NetworkInterface[].class);
+	        multicastInterfacesSpecified = true;
+	    } catch (NoSuchEntryException e) {
+	        List l = Collections.list(NetworkInterface.getNetworkInterfaces());
+	        multicastInterfaces = (NetworkInterface[])
+		    l.toArray(new NetworkInterface[l.size()]);
+	        multicastInterfacesSpecified = false;
+	    }
+//	    multicastAnnouncementInterval = Config.getLongEntry(
+//	        config, "test", "multicastAnnouncementInterval",
+//	        multicastAnnouncementInterval, 1, Long.MAX_VALUE);
+	    multicastAnnouncementInterval = 
+	        qaConfig.getLongConfigVal("net.jini.discovery.announce", 120000);
+	    discoveryConstraints = 
+	        (MethodConstraints) config.getEntry(
+		    "test", "discoveryConstraints",
+		    MethodConstraints.class, null);
+	    if (discoveryConstraints == null) {
+	        discoveryConstraints = 
+		    new BasicMethodConstraints(InvocationConstraints.EMPTY);
+	    }
+	    try {
+	        multicastRequestSubjectChecker =
+		    (ClientSubjectChecker) Config.getNonNullEntry(
+		        config, "test", "multicastRequestSubjectChecker",
+		        ClientSubjectChecker.class);
+	    } catch (NoSuchEntryException e) {
+	        // leave null
+	    }
+	    try {
+	        unicastDiscoverySubjectChecker =
+		    (ClientSubjectChecker) Config.getNonNullEntry(
+		        config, "test", "unicastDiscoverySubjectChecker",
+		        ClientSubjectChecker.class);
+	    } catch (NoSuchEntryException e) {
+	        // leave null
+	    }
+	} catch (ConfigurationException ce) {
+	    throw new RuntimeException("Configuration error", ce);
+	}
+	protocol2 = Discovery.getProtocol2(null);
+	multicastRequestConstraints = DiscoveryConstraints.process(
+	    discoveryConstraints.getConstraints(
+		DiscoveryConstraints.multicastRequestMethod));
+	multicastAnnouncementConstraints = DiscoveryConstraints.process(
+	    discoveryConstraints.getConstraints(
+		DiscoveryConstraints.multicastAnnouncementMethod));
+	unicastDiscoveryConstraints = DiscoveryConstraints.process(
+	    discoveryConstraints.getConstraints(
+		DiscoveryConstraints.unicastDiscoveryMethod));
+
+	try {
+            DEFAULT_MULTICAST_TTL = Config.getIntEntry(
+	        config, "multicast", "ttl", 1, 0, 15);
+	} catch (ConfigurationException ce) {
+            DEFAULT_MULTICAST_TTL = 1;
+	}
+
+        /* create the discovery-related threads */
+        multicastRequestThread = new MulticastThread();
+        multicastAnnouncementThread = new AnnounceThread();
+        
+    }//end init
+    
+    public void start(){
+        /* start the threads */
+        unicastRequestThread.start();
+        multicastRequestThread.start();
+        multicastAnnouncementThread.start();
+    }
+
+}//end class DiscoveryProtocolSimulator
+
+



Mime
View raw message