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
+
+


Reply via email to