protected void waitForBridgeView(
      int expected_size, long timeout, long interval, JChannel... channels) {
    long deadline = System.currentTimeMillis() + timeout;

    while (System.currentTimeMillis() < deadline) {
      boolean views_correct = true;
      for (JChannel ch : channels) {
        RELAY2 relay = (RELAY2) ch.getProtocolStack().findProtocol(RELAY2.class);
        View bridge_view = relay.getBridgeView(BRIDGE_CLUSTER);
        if (bridge_view == null || bridge_view.size() != expected_size) {
          views_correct = false;
          break;
        }
      }
      if (views_correct) break;
      Util.sleep(interval);
    }

    System.out.println("Bridge views:\n");
    for (JChannel ch : channels) {
      RELAY2 relay = (RELAY2) ch.getProtocolStack().findProtocol(RELAY2.class);
      View bridge_view = relay.getBridgeView(BRIDGE_CLUSTER);
      System.out.println(ch.getAddress() + ": " + bridge_view);
    }

    for (JChannel ch : channels) {
      RELAY2 relay = (RELAY2) ch.getProtocolStack().findProtocol(RELAY2.class);
      View bridge_view = relay.getBridgeView(BRIDGE_CLUSTER);
      assert bridge_view != null && bridge_view.size() == expected_size
          : ch.getAddress() + ": bridge view=" + bridge_view + ", expected=" + expected_size;
    }
  }
Exemple #2
0
    public List<PingData> get(long timeout) throws InterruptedException {
      long start_time = System.currentTimeMillis(), time_to_wait = timeout;

      promise.getLock().lock();
      try {
        while (time_to_wait > 0 && !promise.hasResult()) {
          // if num_expected_srv_rsps > 0, then it overrides num_expected_rsps
          if (num_expected_srv_rsps > 0) {
            int received_srv_rsps = getNumServerResponses(ping_rsps);
            if (received_srv_rsps >= num_expected_srv_rsps)
              return new LinkedList<PingData>(ping_rsps);
          } else if (ping_rsps.size() >= num_expected_rsps) {
            return new LinkedList<PingData>(ping_rsps);
          }

          if (break_on_coord_rsp && containsCoordinatorResponse(ping_rsps))
            return new LinkedList<PingData>(ping_rsps);

          promise.getCond().await(time_to_wait, TimeUnit.MILLISECONDS);
          time_to_wait = timeout - (System.currentTimeMillis() - start_time);
        }
        return new LinkedList<PingData>(ping_rsps);
      } finally {
        promise.getLock().unlock();
      }
    }
Exemple #3
0
  /**
   * An event is to be sent down the stack. The layer may want to examine its type and perform some
   * action on it, depending on the event's type. If the event is a message MSG, then the layer may
   * need to add a header to it (or do nothing at all) before sending it down the stack using <code>
   * PassDown</code>. In case of a GET_ADDRESS event (which tries to retrieve the stack's address
   * from one of the bottom layers), the layer may need to send a new response event back up the
   * stack using <code>up_prot.up()</code>. The PING protocol is interested in several different
   * down events, Event.FIND_INITIAL_MBRS - sent by the GMS layer and expecting a GET_MBRS_OK
   * Event.TMP_VIEW and Event.VIEW_CHANGE - a view change event Event.BECOME_SERVER - called after
   * client has joined and is fully working group member Event.CONNECT, Event.DISCONNECT.
   */
  @SuppressWarnings("unchecked")
  public Object down(Event evt) {

    switch (evt.getType()) {
      case Event.FIND_INITIAL_MBRS: // sent by GMS layer
      case Event.FIND_ALL_VIEWS:
        // sends the GET_MBRS_REQ to all members, waits 'timeout' ms or until 'num_initial_members'
        // have been retrieved
        long start = System.currentTimeMillis();
        boolean find_all_views = evt.getType() == Event.FIND_ALL_VIEWS;
        Promise<JoinRsp> promise = (Promise<JoinRsp>) evt.getArg();
        List<PingData> rsps = find_all_views ? findAllViews(promise) : findInitialMembers(promise);
        long diff = System.currentTimeMillis() - start;
        if (log.isTraceEnabled())
          log.trace("discovery took " + diff + " ms: responses: " + Util.printPingData(rsps));
        return rsps;

      case Event.TMP_VIEW:
      case Event.VIEW_CHANGE:
        List<Address> tmp;
        view = (View) evt.getArg();
        if ((tmp = view.getMembers()) != null) {
          synchronized (members) {
            members.clear();
            members.addAll(tmp);
          }
        }
        current_coord = !members.isEmpty() ? members.get(0) : null;
        is_coord = current_coord != null && local_addr != null && current_coord.equals(local_addr);

        return down_prot.down(evt);

      case Event.BECOME_SERVER: // called after client has joined and is fully working group member
        down_prot.down(evt);
        is_server = true;
        return null;

      case Event.SET_LOCAL_ADDRESS:
        local_addr = (Address) evt.getArg();
        return down_prot.down(evt);

      case Event.CONNECT:
      case Event.CONNECT_WITH_STATE_TRANSFER:
      case Event.CONNECT_USE_FLUSH:
      case Event.CONNECT_WITH_STATE_TRANSFER_USE_FLUSH:
        is_leaving = false;
        group_addr = (String) evt.getArg();
        Object ret = down_prot.down(evt);
        handleConnect();
        return ret;

      case Event.DISCONNECT:
        is_leaving = true;
        handleDisconnect();
        return down_prot.down(evt);

      default:
        return down_prot.down(evt); // Pass on to the layer below us
    }
  }
Exemple #4
0
  /**
   * Sends the new view and digest to all subgroup coordinors in coords. Each coord will in turn
   *
   * <ol>
   *   <li>broadcast the new view and digest to all the members of its subgroup (MergeView)
   *   <li>on reception of the view, if it is a MergeView, each member will set the digest and
   *       install the new view
   * </ol>
   */
  private void sendMergeView(
      Collection<Address> coords, MergeData combined_merge_data, MergeId merge_id) {
    if (coords == null || combined_merge_data == null) return;

    View view = combined_merge_data.view;
    Digest digest = combined_merge_data.digest;
    if (view == null || digest == null) {
      if (log.isErrorEnabled())
        log.error("view or digest is null, cannot send consolidated merge view/digest");
      return;
    }

    if (log.isDebugEnabled())
      log.debug(
          gms.local_addr + ": sending merge view " + view.getVid() + " to coordinators " + coords);

    gms.merge_ack_collector.reset(coords);
    int size = gms.merge_ack_collector.size();
    long timeout = gms.view_ack_collection_timeout;

    long start = System.currentTimeMillis();
    for (Address coord : coords) {
      Message msg = new Message(coord, null, null);
      GMS.GmsHeader hdr = new GMS.GmsHeader(GMS.GmsHeader.INSTALL_MERGE_VIEW);
      hdr.view = view;
      hdr.my_digest = digest;
      hdr.merge_id = merge_id;
      msg.putHeader(gms.getId(), hdr);
      gms.getDownProtocol().down(new Event(Event.MSG, msg));
    }

    // [JGRP-700] - FLUSH: flushing should span merge
    // if flush is in stack wait for acks from separated island coordinators
    if (gms.flushProtocolInStack) {
      try {
        gms.merge_ack_collector.waitForAllAcks(timeout);
        long stop = System.currentTimeMillis();
        if (log.isTraceEnabled())
          log.trace(
              "received all ACKs ("
                  + size
                  + ") for merge view "
                  + view
                  + " in "
                  + (stop - start)
                  + "ms");
      } catch (TimeoutException e) {
        log.warn(
            gms.local_addr
                + ": failed to collect all ACKs for merge ("
                + size
                + ") for view "
                + view
                + " after "
                + timeout
                + "ms, missing ACKs from "
                + gms.merge_ack_collector.printMissing());
      }
    }
  }
Exemple #5
0
    /**
     * Sends a MERGE_REQ to all coords and populates a list of MergeData (in merge_rsps). Returns
     * after coords.size() response have been received, or timeout msecs have elapsed (whichever is
     * first).
     *
     * <p>If a subgroup coordinator rejects the MERGE_REQ (e.g. because of participation in a
     * different merge), <em>that member will be removed from coords !</em>
     *
     * @param coords A map of coordinatgor addresses and associated membership lists
     * @param new_merge_id The new merge id
     * @param timeout Max number of msecs to wait for the merge responses from the subgroup coords
     */
    private boolean getMergeDataFromSubgroupCoordinators(
        Map<Address, Collection<Address>> coords, MergeId new_merge_id, long timeout) {
      boolean gotAllResponses;
      long start = System.currentTimeMillis();
      merge_rsps.reset(coords.keySet());
      if (log.isDebugEnabled())
        log.debug(gms.local_addr + ": sending MERGE_REQ to " + coords.keySet());

      for (Map.Entry<Address, Collection<Address>> entry : coords.entrySet()) {
        Address coord = entry.getKey();
        Collection<Address> mbrs = entry.getValue();
        Message msg = new Message(coord, null, null);
        msg.setFlag(Message.OOB);
        GMS.GmsHeader hdr = new GMS.GmsHeader(GMS.GmsHeader.MERGE_REQ, mbrs);
        hdr.mbr = gms.local_addr;
        hdr.merge_id = new_merge_id;
        msg.putHeader(gms.getId(), hdr);
        gms.getDownProtocol().down(new Event(Event.MSG, msg));
      }

      // wait until num_rsps_expected >= num_rsps or timeout elapsed
      merge_rsps.waitForAllResponses(timeout);
      gotAllResponses = merge_rsps.hasAllResponses();
      long stop = System.currentTimeMillis();
      if (log.isDebugEnabled())
        log.debug(
            gms.local_addr
                + ": collected "
                + merge_rsps.size()
                + " merge response(s) in "
                + (stop - start)
                + " ms");
      return gotAllResponses;
    }
Exemple #6
0
  public Results startTest() throws Throwable {
    System.out.println(
        "invoking "
            + num_msgs
            + " RPCs of "
            + Util.printBytes(msg_size)
            + ", sync="
            + sync
            + ", oob="
            + oob);
    int total_gets = 0, total_puts = 0;
    final AtomicInteger num_msgs_sent = new AtomicInteger(0);

    Invoker[] invokers = new Invoker[num_threads];
    for (int i = 0; i < invokers.length; i++)
      invokers[i] = new Invoker(members, num_msgs, num_msgs_sent);

    long start = System.currentTimeMillis();
    for (Invoker invoker : invokers) invoker.start();

    for (Invoker invoker : invokers) {
      invoker.join();
      total_gets += invoker.numGets();
      total_puts += invoker.numPuts();
    }

    long total_time = System.currentTimeMillis() - start;
    System.out.println("done (in " + total_time + " ms)");
    return new Results(total_gets, total_puts, total_time);
  }
 public static long sleep(long timeout) {
   // System.out.println("sleep()");
   long start = System.currentTimeMillis();
   Util.sleep(timeout);
   // throw new NullPointerException("boom");
   return System.currentTimeMillis() - start;
 }
Exemple #8
0
    public void receive(Message msg) {
      byte[] buf = msg.getRawBuffer();
      byte type = buf[msg.getOffset()];

      switch (type) {
        case START:
          ByteBuffer tmp = ByteBuffer.wrap(buf, 1 + msg.getOffset(), Global.LONG_SIZE);
          num_msgs = (int) tmp.getLong();
          print = num_msgs / 10;
          current_value.set(0);
          total_bytes.set(0);
          start = System.currentTimeMillis();
          break;
        case DATA:
          long new_val = current_value.incrementAndGet();
          total_bytes.addAndGet(msg.getLength() - Global.INT_SIZE);
          if (print > 0 && new_val % print == 0) System.out.println("received " + new_val);
          if (new_val >= num_msgs) {
            long time = System.currentTimeMillis() - start;
            double msgs_sec = (current_value.get() / (time / 1000.0));
            double throughput = total_bytes.get() / (time / 1000.0);
            System.out.println(
                String.format(
                    "\nreceived %d messages in %d ms (%.2f msgs/sec), throughput=%s",
                    current_value.get(), time, msgs_sec, Util.printBytes(throughput)));
            break;
          }
          break;
        default:
          System.err.println("Type " + type + " is invalid");
      }
    }
Exemple #9
0
 private void measureThrougput(long size) {
   if ((System.currentTimeMillis() - startTimeThroughput) > oneSecond) {
     control.throughput.setText("" + (throughput / 1024) + " KB/sec");
     startTimeThroughput = System.currentTimeMillis();
     throughput = 0;
   } else {
     throughput += size;
   }
 }
Exemple #10
0
 /** Set the digest and the send the state up to the application */
 protected void handleStateRsp(final Digest digest, Address sender, byte[] state) {
   try {
     if (isDigestNeeded()) {
       punchHoleFor(sender);
       closeBarrierAndSuspendStable(); // fix for https://jira.jboss.org/jira/browse/JGRP-1013
       if (digest != null)
         down_prot.down(
             new Event(Event.OVERWRITE_DIGEST, digest)); // set the digest (e.g. in NAKACK)
     }
     waiting_for_state_response = false;
     stop = System.currentTimeMillis();
     log.debug(
         "%s: received state, size=%s, time=%d milliseconds",
         local_addr, (state == null ? "0" : Util.printBytes(state.length)), stop - start);
     StateTransferResult result = new StateTransferResult(state);
     up_prot.up(new Event(Event.GET_STATE_OK, result));
     down_prot.down(
         new Event(Event.GET_VIEW_FROM_COORD)); // https://issues.jboss.org/browse/JGRP-1751
   } catch (Throwable t) {
     handleException(t);
   } finally {
     if (isDigestNeeded()) {
       closeHoleFor(sender);
       openBarrierAndResumeStable();
     }
   }
 }
Exemple #11
0
    protected synchronized boolean acquireTryLock(long timeout, boolean use_timeout)
        throws InterruptedException {
      if (denied) return false;
      if (!acquired) {
        is_trylock = true;
        this.timeout = timeout;
        if (owner == null) owner = getOwner();
        sendGrantLockRequest(name, owner, timeout, true);

        long target_time = use_timeout ? System.currentTimeMillis() + timeout : 0;
        boolean interrupted = false;
        while (!acquired && !denied) {
          if (use_timeout) {
            long wait_time = target_time - System.currentTimeMillis();
            if (wait_time <= 0) break;
            else {
              this.timeout = wait_time;
              try {
                this.wait(wait_time);
              } catch (InterruptedException e) {
                // If we were interrupted and haven't received a response yet then we try to
                // clean up the lock request and throw the exception
                if (!acquired && !denied) {
                  _unlock(true);
                  throw e;
                }
                // In the case that we were told if we acquired or denied the lock then return that,
                // but
                // make sure we set the interrupt status
                interrupted = true;
              }
            }
          } else {
            try {
              this.wait();
            } catch (InterruptedException e) {
              interrupted = true;
            }
          }
        }
        if (interrupted) Thread.currentThread().interrupt();
      }
      if (!acquired || denied) _unlock(true);
      return acquired && !denied;
    }
Exemple #12
0
  public Object down(Event evt) {
    switch (evt.getType()) {
      case Event.TMP_VIEW:
      case Event.VIEW_CHANGE:
        handleViewChange((View) evt.getArg());
        break;

      case Event.GET_STATE:
        Address target;
        StateTransferInfo info = (StateTransferInfo) evt.getArg();
        if (info.target == null) {
          target = determineCoordinator();
        } else {
          target = info.target;
          if (target.equals(local_addr)) {
            log.error("%s: cannot fetch state from myself", local_addr);
            target = null;
          }
        }
        if (target == null) {
          log.debug("%s: first member (no state)", local_addr);
          up_prot.up(new Event(Event.GET_STATE_OK, new StateTransferInfo()));
        } else {
          Message state_req =
              new Message(target)
                  .putHeader(this.id, new StateHeader(StateHeader.STATE_REQ))
                  .setFlag(Message.Flag.DONT_BUNDLE, Message.Flag.OOB, Message.Flag.SKIP_BARRIER);
          log.debug("%s: asking %s for state", local_addr, target);

          // suspend sending and handling of message garbage collection gossip messages,
          // fixes bugs #943480 and #938584). Wake up when state has been received
          /*if(log.isDebugEnabled())
              log.debug("passing down a SUSPEND_STABLE event");
          down_prot.down(new Event(Event.SUSPEND_STABLE, new Long(info.timeout)));*/
          waiting_for_state_response = true;
          start = System.currentTimeMillis();
          down_prot.down(new Event(Event.MSG, state_req));
        }
        return null; // don't pass down any further !

      case Event.CONFIG:
        Map<String, Object> config = (Map<String, Object>) evt.getArg();
        if (config != null && config.containsKey("flush_supported")) {
          flushProtocolInStack = true;
        }
        break;

      case Event.SET_LOCAL_ADDRESS:
        local_addr = (Address) evt.getArg();
        break;
    }

    return down_prot.down(evt); // pass on to the layer below us
  }
Exemple #13
0
    @Override
    public boolean awaitUntil(Date deadline) throws InterruptedException {
      long waitUntilTime = deadline.getTime();
      long currentTime = System.currentTimeMillis();

      long waitTime = waitUntilTime - currentTime;
      if (waitTime > 0) {
        return await(waitTime, TimeUnit.MILLISECONDS);
      } else {
        return false;
      }
    }
Exemple #14
0
    public void run() {
      long end_time, wait_time;
      List<Request> requests = new LinkedList<Request>();
      while (Thread.currentThread().equals(thread) && !suspended) {
        try {
          boolean keepGoing = false;
          end_time = System.currentTimeMillis() + max_bundling_time;
          do {
            Request firstRequest =
                (Request)
                    queue.remove(INTERVAL); // throws a TimeoutException if it runs into timeout
            requests.add(firstRequest);
            if (!view_bundling) break;
            if (queue.size() > 0) {
              Request nextReq = (Request) queue.peek();
              keepGoing = view_bundling && firstRequest.canBeProcessedTogether(nextReq);
            } else {
              wait_time = end_time - System.currentTimeMillis();
              if (wait_time > 0)
                queue.waitUntilClosed(
                    wait_time); // misnomer: waits until element has been added or q closed
              keepGoing =
                  queue.size() > 0 && firstRequest.canBeProcessedTogether((Request) queue.peek());
            }
          } while (keepGoing && System.currentTimeMillis() < end_time);

          try {
            process(requests);
          } finally {
            requests.clear();
          }
        } catch (QueueClosedException e) {
          break;
        } catch (TimeoutException e) {
          break;
        } catch (Throwable catchall) {
          Util.sleep(50);
        }
      }
    }
 protected void waitUntilStatus(
     String site_name,
     RELAY2.RouteStatus expected_status,
     long timeout,
     long interval,
     JChannel ch)
     throws Exception {
   RELAY2 relay = (RELAY2) ch.getProtocolStack().findProtocol(RELAY2.class);
   if (relay == null) throw new IllegalArgumentException("Protocol RELAY2 not found");
   Relayer.Route route = null;
   long deadline = System.currentTimeMillis() + timeout;
   while (System.currentTimeMillis() < deadline) {
     route = relay.getRoute(site_name);
     if (route != null && route.status() == expected_status) break;
     Util.sleep(interval);
   }
   assert route.status() == expected_status
       : "status="
           + (route != null ? route.status() : "n/a")
           + ", expected status="
           + expected_status;
 }
Exemple #16
0
    public void run() {
      // 1. Generate merge_id
      final MergeId new_merge_id = MergeId.create(gms.local_addr);
      final Collection<Address> coordsCopy = new ArrayList<Address>(coords.keySet());

      long start = System.currentTimeMillis();

      try {
        _run(new_merge_id, coordsCopy); // might remove members from coordsCopy
      } catch (Throwable ex) {
        if (log.isWarnEnabled()) log.warn(gms.local_addr + ": " + ex + ", merge is cancelled");
        sendMergeCancelledMessage(coordsCopy, new_merge_id);
        cancelMerge(
            new_merge_id); // the message above cancels the merge, too, but this is a 2nd line of
        // defense
      } finally {
        /* 5. if flush is in stack stop the flush for entire cluster [JGRP-700] - FLUSH: flushing should span merge */
        if (gms.flushProtocolInStack) gms.stopFlush();
        thread = null;
      }
      long diff = System.currentTimeMillis() - start;
      if (log.isDebugEnabled())
        log.debug(gms.local_addr + ": merge " + new_merge_id + " took " + diff + " ms");
    }
Exemple #17
0
  private class ReceiverThread extends Thread {
    volatile boolean running = true;
    Thread nullifier = null;
    private long startTimeThroughput = System.currentTimeMillis();
    private final long oneSecond = 1000;
    private long throughput = 1;

    public ReceiverThread() {
      nullifier =
          new Thread(
              new Runnable() {
                public void run() {
                  // nullifies throughput display
                  while (running) {
                    Util.sleep(2000);
                    if ((System.currentTimeMillis() - startTimeThroughput) > 2000) {
                      control.throughput.setText("0 KB/sec");
                    }
                  }
                }
              });
      nullifier.start();
    }

    public void shutDown() {
      running = false;
    }

    private void measureThrougput(long size) {
      if ((System.currentTimeMillis() - startTimeThroughput) > oneSecond) {
        control.throughput.setText("" + (throughput / 1024) + " KB/sec");
        startTimeThroughput = System.currentTimeMillis();
        throughput = 0;
      } else {
        throughput += size;
      }
    }

    public void run() {
      Object tmp;
      Message msg = null;
      int counter = 0;
      Vector v = new Vector();
      while (running) {
        Util.sleep(10);
        try {

          tmp = channel.receive(0);
          if (tmp == null) continue;

          if (tmp instanceof View) {
            View vw = (View) tmp;
            control.viewNumber.setText(String.valueOf(vw.getVid().getId()));
            control.numMessagesInLastView.setText(String.valueOf(counter));
            counter = 0;
            v.clear();
            continue;
          }

          if (!(tmp instanceof Message)) continue;

          msg = (Message) tmp;

          measureThrougput(msg.size());
          TotalPayload p = null;

          p = (TotalPayload) msg.getObject();
          v.addElement(new Integer(p.getRandomSequence()));
          int size = v.size();
          if (size % 50 == 0) {
            int value = 0;
            int i = 0;
            Iterator iter = v.iterator();
            while (iter.hasNext()) {
              i++;
              int seq = ((Integer) iter.next()).intValue();
              if (i % 2 == 0) {
                value *= seq;
              } else if (i % 3 == 0) {
                value -= seq;
              } else value += seq;
            }
            v.clear();
            value = Math.abs(value);
            int r = value % 85;
            int g = value % 170;
            int b = value % 255;
            colorPanel.setSeq(r, g, b);
          }
          counter++;
        } catch (ChannelNotConnectedException e) {
          e.printStackTrace();
        } catch (ChannelClosedException e) {
          e.printStackTrace();
        } catch (TimeoutException e) {
          e.printStackTrace();
        }
      }
    }
  }
Exemple #18
0
 long age() {
   return System.currentTimeMillis() - timestamp;
 }
Exemple #19
0
 void update() {
   timestamp = System.currentTimeMillis();
 }
Exemple #20
0
 long age() {
   return System.currentTimeMillis() - timestamp.longValue();
 }
Exemple #21
0
 void update() {
   timestamp.set(System.currentTimeMillis());
 }
Exemple #22
0
  /**
   * Sets the new view and sends a VIEW_CHANGE event up and down the stack. If the view is a
   * MergeView (subclass of View), then digest will be non-null and has to be set before installing
   * the view.
   */
  public void installView(View new_view, Digest digest) {
    ViewId vid = new_view.getVid();
    List<Address> mbrs = new_view.getMembers();
    ltime =
        Math.max(
            vid.getId(),
            ltime); // compute the logical time, regardless of whether the view is accepted

    // Discards view with id lower than or equal to our own. Will be installed without check if it
    // is the first view
    if (view != null) {
      ViewId view_id = view.getViewId();
      int rc = vid.compareToIDs(view_id);
      if (rc <= 0) {
        if (log.isWarnEnabled()
            && rc < 0
            && log_view_warnings) { // only scream if view is smaller, silently discard same views
          log.warn(
              local_addr
                  + ": received view < current view;"
                  + " discarding it (current vid: "
                  + view_id
                  + ", new vid: "
                  + vid
                  + ')');
        }
        return;
      }
    }

    /* Check for self-inclusion: if I'm not part of the new membership, I just discard it.
    This ensures that messages sent in view V1 are only received by members of V1 */
    if (!mbrs.contains(local_addr)) {
      if (log.isWarnEnabled() && log_view_warnings)
        log.warn(local_addr + ": not member of view " + new_view.getViewId() + "; discarding it");
      return;
    }

    if (digest != null) {
      if (new_view instanceof MergeView) mergeDigest(digest);
      else setDigest(digest);
    }

    if (log.isDebugEnabled()) log.debug(local_addr + ": installing view " + new_view);

    Event view_event;
    synchronized (members) {
      view = new View(new_view.getVid(), new_view.getMembers());
      view_event = new Event(Event.VIEW_CHANGE, new_view);

      // Set the membership. Take into account joining members
      if (!mbrs.isEmpty()) {
        members.set(mbrs);
        tmp_members.set(members);
        joining.removeAll(mbrs); // remove all members in mbrs from joining
        // remove all elements from 'leaving' that are not in 'mbrs'
        leaving.retainAll(mbrs);

        tmp_members.add(joining); // add members that haven't yet shown up in the membership
        tmp_members.remove(
            leaving); // remove members that haven't yet been removed from the membership

        // add to prev_members
        for (Address addr : mbrs) {
          if (!prev_members.contains(addr)) prev_members.add(addr);
        }
      }

      Address coord = determineCoordinator();
      if (coord != null && coord.equals(local_addr) && !haveCoordinatorRole()) {
        becomeCoordinator();
      } else {
        if (haveCoordinatorRole() && !local_addr.equals(coord)) {
          becomeParticipant();
          merge_ack_collector.reset(null); // we don't need this one anymore
        }
      }
    }

    // - Changed order of passing view up and down (http://jira.jboss.com/jira/browse/JGRP-347)
    // - Changed it back (bela Sept 4 2007): http://jira.jboss.com/jira/browse/JGRP-564
    // - Moved sending up view_event out of the synchronized block (bela Nov 2011)
    down_prot.down(view_event); // needed e.g. by failure detector or UDP
    up_prot.up(view_event);

    List<Address> tmp_mbrs = new_view.getMembers();
    ack_collector.retainAll(tmp_mbrs);
    merge_ack_collector.retainAll(tmp_mbrs);

    if (new_view instanceof MergeView) merger.forceCancelMerge();

    if (stats) {
      num_views++;
      prev_views.add(new Tuple<View, Long>(new_view, System.currentTimeMillis()));
    }
  }
public class JWhiteBoard extends ReceiverAdapter implements ActionListener, ChannelListener {
  protected String groupName = "";
  private JChannel channel = null;
  private int memberSize = 1;
  private JFrame mainFrame = null;
  private JPanel subPanel = null;
  private DrawPanel drawPanel = null;
  private JButton clearButton, leaveButton;
  private final Random random = new Random(System.currentTimeMillis());
  private final Font defaultFont = new Font("Helvetica", Font.PLAIN, 12);
  private final Color drawColor = Color.black;
  private static final Color backgroundColor = Color.lightGray;
  boolean noChannel = false;
  boolean jmx;
  private boolean useState = false;
  private long stateTimeout = 5000;
  private boolean use_unicasts = false;
  protected boolean send_own_state_on_merge = true;
  private final List<Address> members = new ArrayList<Address>();

  /**
   * Constructor 1
   *
   * @param props
   * @param no_channel
   * @param jmx
   * @param use_state
   * @param state_timeout
   * @param use_unicasts
   * @param name
   * @param send_own_state_on_merge
   * @param gen
   * @throws Exception
   */
  public JWhiteBoard(
      String props,
      boolean no_channel,
      boolean jmx,
      boolean use_state,
      long state_timeout,
      boolean use_unicasts,
      String name,
      boolean send_own_state_on_merge,
      AddressGenerator gen)
      throws Exception {
    this.noChannel = no_channel;
    this.jmx = jmx;
    this.useState = use_state;
    this.stateTimeout = state_timeout;
    this.use_unicasts = use_unicasts;
    if (no_channel) return;

    channel = new JChannel(props);
    if (gen != null) channel.addAddressGenerator(gen);
    if (name != null) channel.setName(name);
    channel.setReceiver(this);
    channel.addChannelListener(this);
    this.send_own_state_on_merge = send_own_state_on_merge;
  }

  /**
   * Constructor 2
   *
   * @param channel
   * @throws Exception
   */
  public JWhiteBoard(JChannel channel) throws Exception {
    this.channel = channel;
    channel.setReceiver(this);
    channel.addChannelListener(this);
  }

  /**
   * Constructor 3
   *
   * @param channel
   * @param use_state: Save state of Group
   * @param state_timeout: State time out
   * @throws Exception
   */
  public JWhiteBoard(JChannel channel, boolean use_state, long state_timeout) throws Exception {
    this.channel = channel;
    channel.setReceiver(this);
    channel.addChannelListener(this);
    this.useState = use_state;
    this.stateTimeout = state_timeout;
  }

  /**
   * Get the name of Group
   *
   * @return Group name
   */
  public String getGroupName() {
    return groupName;
  }

  /**
   * Set name for Group
   *
   * @param groupName
   */
  public void setGroupName(String groupName) {
    if (groupName != null) groupName = groupName;
  }

  /**
   * Main function
   *
   * @param args
   */
  public static void main(String[] args) {
    JWhiteBoard whiteBoard = null;
    String props = null;
    boolean no_channel = false;
    boolean jmx = true;
    boolean use_state = false;
    String group_name = null;
    long state_timeout = 5000;
    boolean use_unicasts = false;
    String name = null;
    boolean send_own_state_on_merge = true;
    AddressGenerator generator = null;

    // Get startup parameters for JWhiteBoard
    for (int i = 0; i < args.length; i++) {
      // Show help
      if ("-help".equals(args[i])) {
        help();
        return;
      }
      // Properties for Channel
      if ("-props".equals(args[i])) {
        props = args[++i];
        continue;
      }
      // If existed, use no channel
      if ("-no_channel".equals(args[i])) {
        no_channel = true;
        continue;
      }
      // Use Java Management Extensions or not
      if ("-jmx".equals(args[i])) {
        jmx = Boolean.parseBoolean(args[++i]);
        continue;
      }
      // If existed, set name for the Group
      if ("-clustername".equals(args[i])) {
        group_name = args[++i];
        continue;
      }
      // If existed, save Group's state
      if ("-state".equals(args[i])) {
        use_state = true;
        continue;
      }
      // If existed, set timeout for state
      if ("-timeout".equals(args[i])) {
        state_timeout = Long.parseLong(args[++i]);
        continue;
      }
      if ("-bind_addr".equals(args[i])) {
        System.setProperty("jgroups.bind_addr", args[++i]);
        continue;
      }
      if ("-use_unicasts".equals(args[i])) {
        use_unicasts = true;
        continue;
      }
      if ("-name".equals(args[i])) {
        name = args[++i];
        continue;
      }
      if ("-send_own_state_on_merge".equals(args[i])) {
        send_own_state_on_merge = Boolean.getBoolean(args[++i]);
        continue;
      }
      if ("-uuid".equals(args[i])) {
        generator = new OneTimeAddressGenerator(Long.valueOf(args[++i]));
        continue;
      }

      help();
      return;
    }

    try {
      whiteBoard =
          new JWhiteBoard(
              props,
              no_channel,
              jmx,
              use_state,
              state_timeout,
              use_unicasts,
              name,
              send_own_state_on_merge,
              generator);
      if (group_name == null) whiteBoard.setGroupName(group_name);
      whiteBoard.go();
    } catch (Throwable e) {
      e.printStackTrace(System.err);
      System.exit(0);
    }
  }

  /** Show help on Console screen */
  static void help() {
    System.out.print(
        "\nDraw [-help] [-no_channel] [-props <protocol stack definition>]"
            + " [-clustername <name>] [-state] [-timeout <state timeout>] [-use_unicasts] "
            + "[-bind_addr <addr>] [-jmx <true | false>] [-name <logical name>] [-send_own_state_on_merge true|false] "
            + "[-uuid <UUID>]");
    System.out.print(
        "-no_channel: doesn't use JGroups at all, any drawing will be relected on the "
            + "whiteboard directly");
    System.out.print(
        "-props: argument can be an old-style protocol stack specification, or it can be "
            + "a URL. In the latter case, the protocol specification will be read from the URL\n");
  }

  /**
   * Generate Random Color
   *
   * @return
   */
  private Color selectColor() {
    int red = Math.abs(random.nextInt()) % 255;
    int green = Math.abs(random.nextInt()) % 255;
    int blue = Math.abs(random.nextInt()) % 255;
    return new Color(red, blue, blue);
  }

  /**
   * Send message to members in members list only.
   *
   * @param buf
   * @throws Exception
   */
  private void sendToAll(byte[] buf) throws Exception {
    for (Address mbr : members) channel.send(new Message(mbr, buf));
  }

  /**
   * Init JWhiteBoard interface
   *
   * @throws Exception
   */
  public void go() throws Exception {
    if (!noChannel && !useState) channel.connect(groupName);
    mainFrame = new JFrame();
    mainFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    drawPanel = new DrawPanel(useState);
    drawPanel.setBackground(backgroundColor);
    subPanel = new JPanel();
    mainFrame.getContentPane().add("Center", drawPanel);
    clearButton = new JButton("Clean");
    clearButton.setFont(defaultFont);
    clearButton.addActionListener(this);
    leaveButton = new JButton("Exit");
    leaveButton.setFont(defaultFont);
    leaveButton.addActionListener(this);
    subPanel.add("South", clearButton);
    subPanel.add("South", leaveButton);
    mainFrame.getContentPane().add("South", subPanel);
    mainFrame.setBackground(backgroundColor);
    clearButton.setForeground(Color.blue);
    leaveButton.setForeground(Color.blue);
    mainFrame.pack();
    mainFrame.setLocation(15, 25);
    mainFrame.setBounds(new Rectangle(250, 250));

    if (!noChannel && useState) {
      channel.connect(groupName, null, stateTimeout);
    }
    mainFrame.setVisible(true);
  }

  /**
   * Set Frame's title
   *
   * @param title : frame's title
   */
  void setTitle(String title) {
    String tmp = "";
    if (noChannel) {
      mainFrame.setTitle("JWhiteBoard");
      return;
    }
    if (title != null) {
      mainFrame.setTitle(title);
    } else {
      if (channel.getAddress() != null) tmp += channel.getAddress();
      tmp += " (" + memberSize + ")";
      mainFrame.setTitle(tmp);
    }
  }

  void setTitle() {
    setTitle(null);
  }

  /** When receive a message, analyze message content and then execute the command: Draw or Clear */
  public void receive(Message msg) {
    byte[] buf = msg.getRawBuffer();
    if (buf == null) {
      System.err.println(
          "["
              + channel.getAddress()
              + "] received null buffer from "
              + msg.getSrc()
              + ", headers: "
              + msg.printHeaders());
      return;
    }

    try {
      DrawCommand comm =
          (DrawCommand)
              Util.streamableFromByteBuffer(
                  DrawCommand.class, buf, msg.getOffset(), msg.getLength());
      switch (comm.mode) {
        case DrawCommand.DRAW:
          if (drawPanel != null) drawPanel.drawPoint(comm);
          break;
        case DrawCommand.CLEAR:
          clearPanel();
        default:
          System.err.println("***** received invalid draw command " + comm.mode);
          break;
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  /** Execute when new member join or leave Group */
  public void viewAccepted(View v) {
    memberSize = v.size();
    if (mainFrame != null) setTitle();
    members.clear();
    members.addAll(v.getMembers());

    if (v instanceof MergeView) {
      System.out.println("** " + v);

      // This is a simple merge function, which fetches the state from the coordinator
      // on a merge and overwrites all of its own state
      if (useState && !members.isEmpty()) {
        Address coord = members.get(0);
        Address local_addr = channel.getAddress();
        if (local_addr != null && !local_addr.equals(coord)) {
          try {

            // make a copy of our state first
            Map<Point, Color> copy = null;
            if (send_own_state_on_merge) {
              synchronized (drawPanel.state) {
                copy = new LinkedHashMap<Point, Color>(drawPanel.state);
              }
            }
            System.out.println("fetching state from " + coord);
            channel.getState(coord, 5000);
            if (copy != null)
              sendOwnState(copy); // multicast my own state so everybody else has it too
          } catch (Exception e) {
            e.printStackTrace();
          }
        }
      }
    } else System.out.println("** View=" + v);
  }

  /** Get state of the Group */
  public void getState(OutputStream ostream) throws Exception {
    drawPanel.writeState(ostream);
  }

  /** Set state of the Group */
  public void setState(InputStream istream) throws Exception {
    drawPanel.readState(istream);
  }

  /* --------------- Callbacks --------------- */

  /** Clear all the content on a whiteboard */
  public void clearPanel() {
    if (drawPanel != null) drawPanel.clear();
  }

  /** Send Clear command to all members in Group */
  public void sendClearPanelMsg() {
    DrawCommand comm = new DrawCommand(DrawCommand.CLEAR);
    try {
      byte[] buf = Util.streamableToByteBuffer(comm);
      if (use_unicasts) sendToAll(buf);
      else channel.send(new Message(null, null, buf));
    } catch (Exception ex) {
      System.err.println(ex);
    }
  }

  /** Action when click [Clear] or [Leave] button */
  public void actionPerformed(ActionEvent e) {
    String command = e.getActionCommand();
    if ("Clear".equals(command)) {
      if (noChannel) {
        clearPanel();
        return;
      }
      sendClearPanelMsg();
    } else if ("Leave".equals(command)) {
      stop();
    } else System.out.println("Unknown action");
  }

  /** Leave Group and close JWhiteBoard */
  public void stop() {
    if (!noChannel) {
      try {
        channel.close();
      } catch (Exception ex) {
        System.err.println(ex);
      }
    }
    mainFrame.setVisible(false);
    mainFrame.dispose();
  }

  /**
   * Send State (content on WhiteBoard) to all members of Group
   *
   * @param copy
   */
  protected void sendOwnState(final Map<Point, Color> copy) {
    if (copy == null) return;
    for (Point point : copy.keySet()) {
      // we don't need the color: it is our draw_color anyway
      DrawCommand comm = new DrawCommand(DrawCommand.DRAW, point.x, point.y, drawColor.getRGB());
      try {
        byte[] buf = Util.streamableToByteBuffer(comm);
        if (use_unicasts) sendToAll(buf);
        else channel.send(new Message(null, buf));
      } catch (Exception ex) {
        System.err.println(ex);
      }
    }
  }

  /* ------------------------------ ChannelListener interface -------------------------- */

  /** Execute when connected to Group */
  public void channelConnected(Channel channel) {
    if (jmx) {
      Util.registerChannel((JChannel) channel, "jgroups");
    }
  }

  /** Execute when disconnected from Group */
  public void channelDisconnected(Channel channel) {
    if (jmx) {
      MBeanServer server = Util.getMBeanServer();
      if (server != null) {
        try {
          JmxConfigurator.unregisterChannel((JChannel) channel, server, groupName);
        } catch (Exception e) {
          e.printStackTrace();
        }
      }
    }
  }

  public void channelClosed(Channel channel) {}

  /* --------------------------- End of ChannelListener interface ---------------------- */

  /** DrawPanel */
  protected class DrawPanel extends JPanel implements MouseMotionListener {
    /** */
    private static final long serialVersionUID = 1L;

    protected final Dimension preferred_size = new Dimension(235, 170);
    protected Image img; // for drawing pixels
    protected Dimension d, imgsize;
    protected Graphics gr;
    protected final Map<Point, Color> state;

    /**
     * Constructor
     *
     * @param use_state
     */
    public DrawPanel(boolean use_state) {
      if (use_state) state = new LinkedHashMap<Point, Color>();
      else state = null;
      createOffscreenImage(false);
      addMouseMotionListener(this);
      addComponentListener(
          new ComponentAdapter() {
            public void componentResized(ComponentEvent e) {
              if (getWidth() <= 0 || getHeight() <= 0) return;
              createOffscreenImage(false);
            }
          });
    }

    /**
     * \ Draw saved State (color dots on panel) on WhiteBoard
     *
     * @param outstream
     * @throws IOException
     */
    public void writeState(OutputStream outstream) throws IOException {
      if (state == null) return;
      synchronized (state) {
        DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(outstream));
        // DataOutputStream dos=new DataOutputStream(outstream);
        dos.writeInt(state.size());
        for (Map.Entry<Point, Color> entry : state.entrySet()) {
          Point point = entry.getKey();
          Color col = entry.getValue();
          dos.writeInt(point.x);
          dos.writeInt(point.x);
          dos.writeInt(col.getRGB());
        }
        dos.flush();
        System.out.println("wrote " + state.size() + " elements");
      }
    }

    /**
     * Read State (color dots) from input stream
     *
     * @param instream
     * @throws IOException
     */
    public void readState(InputStream instream) throws IOException {
      DataInputStream in = new DataInputStream(new BufferedInputStream(instream));
      Map<Point, Color> new_state = new LinkedHashMap<Point, Color>();
      int num = in.readInt();
      for (int i = 0; i < num; i++) {
        Point point = new Point(in.readInt(), in.readInt());
        Color col = new Color(in.readInt());
        new_state.put(point, col);
      }

      synchronized (state) {
        state.clear();
        state.putAll(new_state);
        System.out.println("read " + state.size() + " elements");
        createOffscreenImage(true);
      }
    }

    final void createOffscreenImage(boolean discard_image) {
      d = getSize();
      if (discard_image) {
        img = null;
        imgsize = null;
      }
      if (img == null
          || imgsize == null
          || imgsize.width != d.width
          || imgsize.height != d.height) {
        img = createImage(d.width, d.height);
        if (img != null) {
          gr = img.getGraphics();
          if (gr != null && state != null) {
            drawState();
          }
        }
        imgsize = d;
      }
      repaint();
    }

    /* ---------------------- MouseMotionListener interface------------------------- */

    public void mouseMoved(MouseEvent e) {}

    /**
     * When do a mouse drag, get coordinates ( X and Y) of the mouse, then send Draw command as a
     * message to member of Group
     */
    public void mouseDragged(MouseEvent e) {
      int x = e.getX(), y = e.getX();
      DrawCommand comm = new DrawCommand(DrawCommand.DRAW, x, y, drawColor.getRGB());

      if (noChannel) {
        drawPoint(comm);
        return;
      }

      try {
        byte[] buf = Util.streamableToByteBuffer(comm);
        if (use_unicasts) sendToAll(buf);
        else channel.send(new Message(null, null, buf));
      } catch (Exception ex) {
        System.err.println(ex);
      }
    }

    /* ------------------- End of MouseMotionListener interface --------------------- */

    /**
     * Adds pixel to queue and calls repaint() whenever we have MAX_ITEMS pixels in the queue or
     * when MAX_TIME msecs have elapsed (whichever comes first). The advantage compared to just
     * calling repaint() after adding a pixel to the queue is that repaint() can most often draw
     * multiple points at the same time.
     */
    public void drawPoint(DrawCommand c) {
      if (c == null || gr == null) return;
      Color col = new Color(c.rgb);
      gr.setColor(col);
      gr.fillOval(c.x, c.y, 10, 10);
      repaint();
      if (state != null) {
        synchronized (state) {
          state.put(new Point(c.x, c.y), col);
        }
      }
    }

    /** Clear all contents */
    public void clear() {
      if (gr == null) return;
      gr.clearRect(0, 0, getSize().width, getSize().height);
      repaint();
      if (state != null) {
        synchronized (state) {
          state.clear();
        }
      }
    }

    /** Draw the entire panel from the state */
    @SuppressWarnings("rawtypes")
    public void drawState() {
      // clear();
      Map.Entry entry;
      Point pt;
      Color col;
      synchronized (state) {
        for (Iterator it = state.entrySet().iterator(); it.hasNext(); ) {
          entry = (Map.Entry) it.next();
          pt = (Point) entry.getKey();
          col = (Color) entry.getValue();
          gr.setColor(col);
          gr.fillOval(pt.x, pt.y, 10, 10);
        }
      }
      repaint();
    }

    public Dimension getPreferredSize() {
      return preferred_size;
    }

    public void paintComponent(Graphics g) {
      super.paintComponent(g);
      if (img != null) {
        g.drawImage(img, 0, 0, null);
      }
    }
  }
}