Package com.sun.jini.test.share

Source Code of com.sun.jini.test.share.DiscoveryProtocolSimulator$AddressTask

/*
* 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 LookupLocator lookupLocator = null;
    /** The member groups to send */
    private String[] memberGroups = {};

    /** Thread to receive/process multicast requests from client or service */
    MulticastThread multicastRequestThread;
    /** Thread to receive/process unicast requests from client or service */
    UnicastThread unicastRequestThread;
    /** Thread to send multicast announcements to from client or service */
    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 LookupSimulatorProxyInterface lookupProxy;
    /** The service ID to assign to the lookup service that is sent */
    private 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 Object lockNAnnouncements = new Object();
    private Object lockLookupProxy    = new Object();
    private Object lockLookupLocator  = new Object();
    private Object lockMemberGroups   = new Object();

    /* new fields taken from the davis reggie */

    /** Network interfaces to use for multicast discovery */
    private NetworkInterface[] multicastInterfaces;
    /** Flag indicating whether network interfaces were explicitly specified */
    private boolean multicastInterfacesSpecified;
    private Discovery protocol2;
    /** Constraints specified for incoming multicast requests */
    private DiscoveryConstraints multicastRequestConstraints;
    /** Constraints specified for outgoing multicast announcements */
    private DiscoveryConstraints multicastAnnouncementConstraints;
    /** Constraints specified for handling unicast discovery */
    private DiscoveryConstraints unicastDiscoveryConstraints;
    /** Client subject checker to apply to incoming multicast requests */
    private ClientSubjectChecker multicastRequestSubjectChecker;
    /** Client subject checker to apply to unicast discovery attempts */
    private ClientSubjectChecker unicastDiscoverySubjectChecker;
    /** Interval to wait in between sending multicast announcements */
    private long multicastAnnouncementInterval = 1000 * 60 * 2;


    public DiscoveryProtocolSimulator(QAConfig config,
                                      String[] memberGroups,
              AdminManager manager)
                                       throws ActivationException, IOException, TestException
    {
        this(config,memberGroups,manager,0);
    }//end constructor
   
    public DiscoveryProtocolSimulator(QAConfig config,
                                      String[] memberGroups,
              AdminManager manager,
                                      int unicastPort)
                                       throws ActivationException, IOException, TestException
    {
        this.memberGroups = memberGroups;
        this.unicastPort  = unicastPort;
        // start LUS before switching identity to reggie
        lookupProxy = (LookupSimulatorProxyInterface) manager.startLookupService();
  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() {
        synchronized(lockLookupProxy) {
            return lookupProxy;
        }//end sync
    }//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 boolean interrupted = false;

  /* This is a workaround for Thread.interrupt not working on
   * MulticastSocket.receive on all platforms.
   */
  public synchronized void interrupt() {
      interrupted = true;
      socket.close();
  }

  public synchronized 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);
        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 ServerSocket listen;
  /** Listen port */
  public 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);
      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();
  }

  /** True if thread has been interrupted */
  private 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 synchronized void interrupt() {
      interrupted = true;
      try {
    (new Socket(InetAddress.getLocalHost(), port)).close();
      } catch (IOException e) {
      }
  }

  public synchronized 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 boolean interrupted = false;

  /* This is a workaround for Thread.interrupt not working due
   * to the logging system sometimes throwing away InterruptedIOException
   */
  public synchronized void interrupt() {
      interrupted = true;
      super.interrupt();
  }

  public synchronized 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 synchronized void run() {
      if (multicastInterfaces != null && multicastInterfaces.length == 0)
      {
    return;
      }
      try {
    while (!isInterrupted() && announce(memberGroups)) {
        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);
        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;
  }

        /* start the discovery-related threads */
        multicastRequestThread = new MulticastThread();
        multicastAnnouncementThread = new AnnounceThread();
        /* start the threads */
        unicastRequestThread.start();
        multicastRequestThread.start();
        multicastAnnouncementThread.start();
    }//end init

}//end class DiscoveryProtocolSimulator

TOP

Related Classes of com.sun.jini.test.share.DiscoveryProtocolSimulator$AddressTask

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.