示例#1
0
  public static void replayNode(
      final Id id,
      final InetSocketAddress addr,
      InetSocketAddress bootaddress,
      final long startTime,
      final long randSeed)
      throws Exception {
    Environment env = new Environment();
    if (env.getParameters()
        .getBoolean("org.mpisws.p2p.testing.transportlayer.replay.Replayer_printlog"))
      printLog("0x" + id.toStringFull().substring(0, 6), env);

    //  Environment env = Environment.directEnvironment();
    System.out.println(
        id.toStringFull()
            + " "
            + addr.getAddress().getHostAddress()
            + " "
            + addr.getPort()
            + " "
            + bootaddress.getPort()
            + " "
            + startTime
            + " "
            + randSeed);
    env.destroy();
    new Replayer(id, addr, bootaddress, startTime, randSeed);
  }
示例#2
0
  @Test
  public void testGettersAndSetter() {
    // act
    content.setId(Id.build("butter"));

    // assert
    assertEquals(Id.build("butter"), content.getId());
  }
示例#3
0
  /**
   * produces a Id whose prefix up to row is identical to this, followed by a digit with value
   * column, followed by a suffix of digits with value suffixDigits.
   *
   * @param row the length of the prefix
   * @param column the value of the following digit
   * @param suffixDigit the value of the suffix digits
   * @param b power of 2 of the base
   * @return the resulting Id
   */
  public Id getDomainPrefix(int row, int column, int suffixDigit, int b) {
    Id res = new Id(Id);

    res.setDigit(row, column, b);
    for (int i = 0; i < row; i++) {
      res.setDigit(i, suffixDigit, b);
    }

    return build(res.Id);
  }
示例#4
0
  /**
   * Checks if this Id is between two given ids ccw (inclusive) and cw (exclusive) on the circle
   *
   * @param ccw the counterclockwise id
   * @param cw the clockwise id
   * @return true if this is between ccw (inclusive) and cw (exclusive), false otherwise
   */
  public boolean isBetween(Id ccw, Id cw) {
    if (ccw.equals(cw)) {
      return false;
    }

    if (ccw.clockwise(cw)) {
      return this.clockwise(cw) && !this.clockwise(ccw);
    } else {
      return !this.clockwise(ccw) || this.clockwise(cw);
    }
  }
示例#5
0
  /**
   * produces a set of ids (keys) that are evenly distributed around the id ring. One invocation
   * produces the i-th member of a set of size num. The set is evenly distributed around the ring,
   * with an offset given by this Id. The set is useful for constructing, for instance, Scribe trees
   * with disjoint sets of interior nodes.
   *
   * @param num the number of Ids in the set (must be <= 2^b)
   * @param b the routing base (as a power of 2)
   * @param i the index of the requested member of the set (0<=i<num; the 0-th member is this)
   * @return the resulting set member, or null in case of illegal arguments
   */
  public Id getAlternateId(int num, int b, int i) {
    if (num > (1 << b) || i < 0 || i >= num) {
      return null;
    }

    Id res = new Id(Id);

    int digit = res.getDigit(numDigits(b) - 1, b) + ((1 << b) / num) * i;
    res.setDigit(numDigits(b) - 1, digit, b);

    return build(res.Id);
  }
示例#6
0
  @Test
  public void testDeliverOfBackupMessageIfItIsNewer() {
    // setup
    content.getContentHeaders().put(DhtContentHeader.CONTENT_VERSION, Long.toString(2));
    backupMessage = new InsertBackupMessage(85, content, -1, nodeHandle, content.getId());

    // Make sure that the existing content is older
    KoalaMutableContent existingContent =
        new KoalaPiEntityContent(
            Id.build("ExisingContent"), "existingContent", new HashMap<String, String>());
    existingContent.getContentHeaders().put(DhtContentHeader.CONTENT_VERSION, Long.toString(1));

    GenericContinuationAnswer<KoalaMutableContent> g =
        new GenericContinuationAnswer<KoalaMutableContent>(existingContent);
    doAnswer(g)
        .when(storageManager)
        .getObject(eq(content.getId()), isA(StandardContinuation.class));

    ArgumentCaptor<GCPastMetadata> metadataArgument = ArgumentCaptor.forClass(GCPastMetadata.class);

    // act
    koalaGCPastImpl.deliver(content.getId(), backupMessage);

    // assert
    verify(storageManager)
        .store(
            eq(content.getId()),
            metadataArgument.capture(),
            eq(content),
            isA(StandardContinuation.class));
    assertEquals(2, metadataArgument.getValue().getExpiration());
  }
示例#7
0
  @Test
  public void testInsertGetsVersion() {
    // setup
    KoalaGCPastImpl localGcPastImpl =
        new KoalaGCPastImpl(
            node,
            storageManager,
            null,
            0,
            instance,
            new KoalaPastPolicy(),
            0,
            storageManager,
            KoalaNode.DEFAULT_NUMBER_OF_DHT_BACKUPS,
            koalaIdFactory,
            koalaPiEntityFactory) {
          @Override
          protected void sendRequest(
              rice.p2p.commonapi.Id id, PastMessage message, Continuation command) {
            sentId = id;
            sentMessage = message;
            callBackContinuation = command;
          }
        };
    PastContent content =
        new KoalaPiEntityContent(
            Id.build("bob"),
            "the builder",
            false,
            "text",
            0,
            NodeScope.AVAILABILITY_ZONE,
            "tv:series",
            18);
    Continuation command = mock(Continuation.class);

    // act
    localGcPastImpl.insert(content, command);

    // assert
    assertEquals(sentId, Id.build("bob"));
    assertTrue(sentMessage instanceof InsertRequestMessage);
    assertEquals(18, ((InsertRequestMessage) sentMessage).getExpiration());
    assertEquals(content, ((InsertRequestMessage) sentMessage).getContent());
    assertEquals(command, callBackContinuation);
  }
示例#8
0
  @Test
  public void testGetMultiContinuation() {
    // setup
    final PId idToQuery = new PiId(messageId.toStringFull(), 0);

    // act
    koalaGCPastImpl.get(idToQuery, continuation);
  }
示例#9
0
  @Test
  public void testGetAny() {
    // setup
    final PId idToQuery = new PiId(messageId.toStringFull(), 0);

    // act
    koalaGCPastImpl.getAny(idToQuery, continuation);

    // assert
    assertTrue(lookupContinuation instanceof KoalaPiEntityResultContinuation);
    assertEquals(messageId, lookupId);
  }
示例#10
0
  /** @param args */
  public static void main(String[] args) throws Exception {
    //    System.out.println(args[0]+" "+args[1]+" "+args[2]+" "+args[3]);
    String hex = args[0];
    InetAddress a = InetAddress.getByName(args[1]);
    int startPort = Integer.decode(args[2]).intValue();
    int bootPort = Integer.decode(args[3]).intValue();
    InetSocketAddress addr = new InetSocketAddress(a, startPort);
    InetSocketAddress bootaddress = new InetSocketAddress(a, bootPort);
    long startTime = Long.decode(args[4]).longValue();
    long randSeed = Long.decode(args[5]).longValue();

    replayNode(Id.build(hex), addr, bootaddress, startTime, randSeed);
  }
示例#11
0
  @Before
  public void before() {
    id = Id.build("CAFEBEEF");
    healthContent = "yellow";
    typeStr = "String";
    mockPast = mock(Past.class);
    mockNodeHandle = mock(NodeHandle.class);
    headers = new HashMap<String, String>();
    mockTimeSource = mock(TimeSource.class);
    mockEnvironment = mock(Environment.class);
    when(mockEnvironment.getTimeSource()).thenReturn(mockTimeSource);

    content = new KoalaPiEntityContent(id, healthContent, headers);
    content.setVersion(0);
  }
示例#12
0
  @Test
  public void testEquals() {
    KoalaMutableContent content =
        new KoalaPiEntityContent(id, healthContent, new HashMap<String, String>());
    KoalaMutableContent contentDiffContent =
        new KoalaPiEntityContent(id, healthContent + "boo", new HashMap<String, String>());
    KoalaMutableContent contentDiffId =
        new KoalaPiEntityContent(
            Id.build("different"), healthContent, new HashMap<String, String>());
    KoalaMutableContent contentSame =
        new KoalaPiEntityContent(id, healthContent, new HashMap<String, String>());

    assertEquals(contentSame, content);
    assertFalse(content.equals(contentDiffId));
    assertFalse(content.equals(contentDiffContent));
  }
示例#13
0
  @Test
  public void testGet() {
    final AtomicInteger count = new AtomicInteger(0);
    final PId idToQuery = new PiId(messageId.toStringFull(), 0);

    koalaGCPastImpl =
        new KoalaGCPastImpl(
            node,
            storageManager,
            null,
            0,
            instance,
            new KoalaPastPolicy(),
            0,
            storageManager,
            KoalaNode.DEFAULT_NUMBER_OF_DHT_BACKUPS,
            koalaIdFactory,
            koalaPiEntityFactory) {
          @Override
          protected Continuation getResponseContinuation(PastMessage msg) {
            if (msg.equals(message)) return continuation;
            return null;
          }

          @Override
          protected void getHandles(rice.p2p.commonapi.Id id, int max, Continuation command) {
            NodeHandleSet nodeSet = mock(NodeHandleSet.class);
            when(nodeSet.size()).thenReturn(3);
            command.receiveResult(nodeSet);
          }

          @Override
          public void lookupHandle(
              rice.p2p.commonapi.Id id, NodeHandle handle, Continuation command) {
            assertEquals(Id.build(idToQuery.toStringFull()), id);
            count.addAndGet(1);
          }
        };

    // act
    koalaGCPastImpl.get(idToQuery, continuation);

    // assert
    assertEquals(3, count.intValue());
  }
示例#14
0
  public void init() {
    invokedInsert = false;
    messageId = Id.build("1111567890123456789012345678901234560023");

    content =
        new KoalaPiEntityContent(
            messageId, "what", false, "type", 4, NodeScope.AVAILABILITY_ZONE, "url", 1);
    continuation = mock(Continuation.class);

    builder = mock(MessageBuilder.class);
    when(builder.buildMessage())
        .thenReturn(new GCInsertMessage(-1, content, 0L, nodeHandle, Id.build("dest")));

    Logger logger = mock(Logger.class);

    LogManager logManager = mock(LogManager.class);
    when(logManager.getLogger(isA(Class.class), eq(instance))).thenReturn(logger);

    Parameters parameters = mock(Parameters.class);

    RandomSource randomSource = mock(RandomSource.class);

    Environment environment = mock(Environment.class);
    when(environment.getLogManager()).thenReturn(logManager);
    when(environment.getParameters()).thenReturn(parameters);
    when(environment.getRandomSource()).thenReturn(randomSource);
    TimeSource timesource = mock(TimeSource.class);
    when(timesource.currentTimeMillis()).thenReturn(System.currentTimeMillis());
    when(environment.getTimeSource()).thenReturn(timesource);

    Endpoint endpoint = mock(Endpoint.class);
    doAnswer(
            new Answer<Object>() {
              @Override
              public Object answer(InvocationOnMock invocation) throws Throwable {
                koalaDeserializer = (MessageDeserializer) invocation.getArguments()[0];
                return null;
              }
            })
        .when(endpoint)
        .setDeserializer(isA(MessageDeserializer.class));

    koalaIdFactory = mock(KoalaIdFactory.class);
    when(koalaIdFactory.buildIdFromToString(anyString()))
        .thenAnswer(
            new Answer<Id>() {
              @Override
              public Id answer(InvocationOnMock invocation) throws Throwable {
                return Id.build((String) invocation.getArguments()[0]);
              }
            });

    when(koalaIdFactory.buildId(anyString()))
        .thenAnswer(
            new Answer<Id>() {
              @Override
              public Id answer(InvocationOnMock invocation) throws Throwable {
                return Id.build((String) invocation.getArguments()[0]);
              }
            });

    node = mock(PastryNode.class);
    when(node.getEnvironment()).thenReturn(environment);
    when(node.buildEndpoint(isA(Application.class), eq(instance))).thenReturn(endpoint);
    when(node.getIdFactory()).thenReturn(koalaIdFactory);

    sentBackupContent = new ArrayList<KoalaMutableContent>();
    backupIds = new HashSet<rice.p2p.commonapi.Id>();

    storageManager = mock(StorageManager.class);
    koalaPiEntityFactory = mock(KoalaPiEntityFactory.class);
  }
示例#15
0
@SuppressWarnings("unchecked")
public class KoalaGCPastImplTest {
  private KoalaGCPastImpl koalaGCPastImpl;
  private PastMessage message;
  private KoalaMutableContent content;
  private Continuation continuation;
  private boolean invokedInsert;

  private PastryNode node;
  private String instance;
  private StorageManager storageManager;
  private MessageDeserializer koalaDeserializer;
  private Continuation getHandlesContinuation;
  private rice.p2p.commonapi.Id getHandlesId;
  private NodeHandle nodeHandle;
  private PastMessage receivedInserRequestPastmessage;
  private Continuation receivedInserRequestContinuation;
  private MessageBuilder builder;
  private KoalaPiEntityFactory koalaPiEntityFactory;
  private Continuation lookupContinuation;
  private rice.p2p.commonapi.Id lookupId;
  private rice.p2p.commonapi.Id contentId;
  private double expectedRequiredhandlePercentage = .44;
  protected rice.p2p.commonapi.Id lookupHandlesId;
  protected double requiredHandlesPercentage;
  protected Continuation lookupHandlesContinuation;
  private Id dummyId =
      Id.build(
          "People are more violently opposed to fur than leather because it's safer to harass rich women than motorcycle gangs.");
  private KoalaIdFactory koalaIdFactory;
  private ArrayList<KoalaMutableContent> sentBackupContent;
  private HashSet<rice.p2p.commonapi.Id> backupIds;
  private PId realTestId;
  private InsertBackupMessage backupMessage;
  private boolean collectCalled = false;
  private rice.p2p.commonapi.Id sentId = null;
  private PastMessage sentMessage = null;
  private Continuation callBackContinuation = null;
  private Id messageId;

  @Before
  public void initKoalaGCPastImpl() {
    init();
    koalaGCPastImpl =
        new KoalaGCPastImpl(
            node,
            storageManager,
            null,
            0,
            instance,
            new KoalaPastPolicy(),
            0,
            storageManager,
            KoalaNode.DEFAULT_NUMBER_OF_DHT_BACKUPS,
            koalaIdFactory,
            koalaPiEntityFactory) {

          @Override
          public void insert(PastContent obj, long expiration, Continuation command) {
            invokedInsert = true;
          }

          @Override
          protected Continuation getResponseContinuation(PastMessage msg) {
            if (msg.equals(message)) return continuation;
            if (msg.equals(backupMessage)) return continuation;
            if (msg instanceof GCRefreshMessage) return continuation;

            return null;
          }

          @Override
          public void lookupHandle(
              rice.p2p.commonapi.Id id, NodeHandle handle, Continuation command) {}

          @Override
          public void lookup(rice.p2p.commonapi.Id id, boolean b, Continuation command) {
            lookupId = id;
            lookupContinuation = command;
          }

          @Override
          public void getHandles(rice.p2p.commonapi.Id id, int max, Continuation command) {
            getHandlesId = id;
            getHandlesContinuation = command;
          }

          @Override
          public void lookupHandles(
              rice.p2p.commonapi.Id id, int max, double requiredHandles, Continuation command) {
            lookupHandlesId = id;
            requiredHandlesPercentage = requiredHandles;
            lookupHandlesContinuation = command;
          }

          @Override
          protected void sendRequest(
              rice.p2p.commonapi.Id id, PastMessage message, Continuation command) {
            backupIds.add(id);
            KoalaMutableContent recievedBackupContent =
                (KoalaMutableContent) ((InsertBackupMessage) message).getContent();
            sentBackupContent.add(recievedBackupContent);
          }

          protected void collect(java.util.SortedMap map, Continuation command) {
            collectCalled = true;
          };
        };

    koalaGCPastImpl.setRequiredReadHandlesPercentage(expectedRequiredhandlePercentage);
  }

  @Before
  public void before() {
    nodeHandle = mock(NodeHandle.class);
  }

  public void init() {
    invokedInsert = false;
    messageId = Id.build("1111567890123456789012345678901234560023");

    content =
        new KoalaPiEntityContent(
            messageId, "what", false, "type", 4, NodeScope.AVAILABILITY_ZONE, "url", 1);
    continuation = mock(Continuation.class);

    builder = mock(MessageBuilder.class);
    when(builder.buildMessage())
        .thenReturn(new GCInsertMessage(-1, content, 0L, nodeHandle, Id.build("dest")));

    Logger logger = mock(Logger.class);

    LogManager logManager = mock(LogManager.class);
    when(logManager.getLogger(isA(Class.class), eq(instance))).thenReturn(logger);

    Parameters parameters = mock(Parameters.class);

    RandomSource randomSource = mock(RandomSource.class);

    Environment environment = mock(Environment.class);
    when(environment.getLogManager()).thenReturn(logManager);
    when(environment.getParameters()).thenReturn(parameters);
    when(environment.getRandomSource()).thenReturn(randomSource);
    TimeSource timesource = mock(TimeSource.class);
    when(timesource.currentTimeMillis()).thenReturn(System.currentTimeMillis());
    when(environment.getTimeSource()).thenReturn(timesource);

    Endpoint endpoint = mock(Endpoint.class);
    doAnswer(
            new Answer<Object>() {
              @Override
              public Object answer(InvocationOnMock invocation) throws Throwable {
                koalaDeserializer = (MessageDeserializer) invocation.getArguments()[0];
                return null;
              }
            })
        .when(endpoint)
        .setDeserializer(isA(MessageDeserializer.class));

    koalaIdFactory = mock(KoalaIdFactory.class);
    when(koalaIdFactory.buildIdFromToString(anyString()))
        .thenAnswer(
            new Answer<Id>() {
              @Override
              public Id answer(InvocationOnMock invocation) throws Throwable {
                return Id.build((String) invocation.getArguments()[0]);
              }
            });

    when(koalaIdFactory.buildId(anyString()))
        .thenAnswer(
            new Answer<Id>() {
              @Override
              public Id answer(InvocationOnMock invocation) throws Throwable {
                return Id.build((String) invocation.getArguments()[0]);
              }
            });

    node = mock(PastryNode.class);
    when(node.getEnvironment()).thenReturn(environment);
    when(node.buildEndpoint(isA(Application.class), eq(instance))).thenReturn(endpoint);
    when(node.getIdFactory()).thenReturn(koalaIdFactory);

    sentBackupContent = new ArrayList<KoalaMutableContent>();
    backupIds = new HashSet<rice.p2p.commonapi.Id>();

    storageManager = mock(StorageManager.class);
    koalaPiEntityFactory = mock(KoalaPiEntityFactory.class);
  }

  @Test
  public void testDeserializer() throws IOException {
    InputBuffer inBuf = mock(InputBuffer.class);
    when(inBuf.readByte()).thenReturn(new Byte((byte) 0));

    Object message =
        koalaDeserializer.deserialize(
            inBuf,
            InsertRequestMessage.TYPE,
            InsertRequestMessage.DEFAULT_PRIORITY,
            mock(NodeHandle.class));

    assertTrue(message instanceof InsertRequestMessage);
  }

  @Test
  public void testDeliverRandomMessage() throws Exception {
    // setup
    message = new InsertMessage(0, content, null, null);

    // act
    koalaGCPastImpl.deliver(null, message);

    // assert
    assertFalse(invokedInsert);
  }

  @Test
  public void testDeliverGCRefresh() throws Exception {
    // setup
    GCIdSet idSet = mock(GCIdSet.class);
    when(idSet.numElements()).thenReturn(1);
    GCId id = mock(GCId.class);
    when(idSet.asArray()).thenReturn(new GCId[] {id});
    message = new GCRefreshMessage(0, idSet, null, null);

    // act
    koalaGCPastImpl.deliver(null, message);

    // assert
    verify(continuation).receiveResult(AdditionalMatchers.aryEq(new Boolean[] {Boolean.TRUE}));
    verify(storageManager, never()).exists((Id) anyObject());
  }

  @Test
  public void testDeliverGCCollect() throws Exception {
    // setup
    message = new GCCollectMessage(0, null, null);

    // act
    koalaGCPastImpl.deliver(null, message);

    // assert
    assertFalse(collectCalled);
  }

  @Test
  public void testInsert() throws Exception {
    // setup
    koalaGCPastImpl =
        new KoalaGCPastImpl(
            node,
            storageManager,
            null,
            0,
            instance,
            new KoalaPastPolicy(),
            0,
            storageManager,
            KoalaNode.DEFAULT_NUMBER_OF_DHT_BACKUPS,
            koalaIdFactory,
            koalaPiEntityFactory) {
          @Override
          protected void sendRequest(
              rice.p2p.commonapi.Id id, PastMessage message, Continuation command) {
            contentId = id;
            receivedInserRequestContinuation = command;
            receivedInserRequestPastmessage = message;
          }
        };

    // act
    koalaGCPastImpl.insert(content, 0, continuation);

    // assert
    assertEquals(contentId, content.getId());
    assertTrue(receivedInserRequestPastmessage instanceof InsertRequestMessage);
    assertEquals(receivedInserRequestContinuation, continuation);
  }

  @Test
  public void testDoInsert() {
    final Object expectedResult = new Object();
    Continuation continuation = mock(Continuation.class);
    doAnswer(
            new Answer<Object>() {
              public Object answer(InvocationOnMock invocation) throws Throwable {
                // KoalaMutableContent existingContent = new KoalaPiEntityContent(dhtId, "what2",
                // new HashMap<String,
                // String>());
                KoalaMutableContent existingContent =
                    new KoalaPiEntityContent(messageId, "what2", new HashMap<String, String>());
                ((StandardContinuation) invocation.getArguments()[1])
                    .receiveResult(existingContent);
                return null;
              }
            })
        .when(storageManager)
        .getObject(eq(messageId), isA(StandardContinuation.class));

    doAnswer(
            new Answer<Object>() {
              @Override
              public Object answer(InvocationOnMock invocation) throws Throwable {
                ((StandardContinuation) invocation.getArguments()[3]).receiveResult(expectedResult);
                return null;
              }
            })
        .when(storageManager)
        .store(
            eq(messageId),
            (Serializable)
                argThat(
                    new ArgumentMatcher<GCPastMetadata>() {
                      @Override
                      public boolean matches(Object argument) {
                        if (!(argument instanceof GCPastMetadata)) return false;
                        GCPastMetadata koalaContentMetadata = (GCPastMetadata) argument;
                        return koalaContentMetadata.getExpiration() == content.getVersion();
                      }
                    }),
            argThat(
                new ArgumentMatcher<KoalaMutableContent>() {
                  @Override
                  public boolean matches(Object argument) {
                    if (!(argument instanceof KoalaMutableContent)) return false;
                    KoalaMutableContent koalaMutableContent = (KoalaMutableContent) argument;
                    rice.p2p.commonapi.Id id = koalaMutableContent.getId();
                    return id.equals(messageId);
                    // return id.toStringFull().endsWith("0001");
                  }
                }),
            isA(StandardContinuation.class));

    // act
    koalaGCPastImpl.doInsert(content.getId(), builder, continuation, false);

    // verify
    assertEquals(messageId, getHandlesId);
    assertTrue(getHandlesContinuation instanceof KoalaPastGetHandlesInsertContinuation);
  }

  @Test
  public void testDoInsertCheckInsertFails() {
    // setup
    Continuation continuation = mock(Continuation.class);

    doAnswer(
            new Answer<Object>() {
              public Object answer(InvocationOnMock invocation) throws Throwable {
                // KoalaMutableContent existingContent = new KoalaPiEntityContent(dhtId, "what2",
                // new HashMap<String,
                // String>());
                KoalaMutableContent existingContent =
                    new KoalaPiEntityContent(messageId, "what2", new HashMap<String, String>());
                existingContent
                    .getContentHeaders()
                    .put(DhtContentHeader.CONTENT_VERSION, Long.toString(2));
                ((StandardContinuation) invocation.getArguments()[1])
                    .receiveResult(existingContent);
                return null;
              }
            })
        .when(storageManager)
        .getObject(eq(messageId), isA(StandardContinuation.class));

    // act
    koalaGCPastImpl.doInsert(content.getId(), builder, continuation, false);

    // verify
    verify(continuation).receiveException(isA(PastException.class));
  }

  @Test
  public void testGet() {
    final AtomicInteger count = new AtomicInteger(0);
    final PId idToQuery = new PiId(messageId.toStringFull(), 0);

    koalaGCPastImpl =
        new KoalaGCPastImpl(
            node,
            storageManager,
            null,
            0,
            instance,
            new KoalaPastPolicy(),
            0,
            storageManager,
            KoalaNode.DEFAULT_NUMBER_OF_DHT_BACKUPS,
            koalaIdFactory,
            koalaPiEntityFactory) {
          @Override
          protected Continuation getResponseContinuation(PastMessage msg) {
            if (msg.equals(message)) return continuation;
            return null;
          }

          @Override
          protected void getHandles(rice.p2p.commonapi.Id id, int max, Continuation command) {
            NodeHandleSet nodeSet = mock(NodeHandleSet.class);
            when(nodeSet.size()).thenReturn(3);
            command.receiveResult(nodeSet);
          }

          @Override
          public void lookupHandle(
              rice.p2p.commonapi.Id id, NodeHandle handle, Continuation command) {
            assertEquals(Id.build(idToQuery.toStringFull()), id);
            count.addAndGet(1);
          }
        };

    // act
    koalaGCPastImpl.get(idToQuery, continuation);

    // assert
    assertEquals(3, count.intValue());
  }

  @Test
  public void testGetMultiContinuation() {
    // setup
    final PId idToQuery = new PiId(messageId.toStringFull(), 0);

    // act
    koalaGCPastImpl.get(idToQuery, continuation);
  }

  @Test
  public void testGetAny() {
    // setup
    final PId idToQuery = new PiId(messageId.toStringFull(), 0);

    // act
    koalaGCPastImpl.getAny(idToQuery, continuation);

    // assert
    assertTrue(lookupContinuation instanceof KoalaPiEntityResultContinuation);
    assertEquals(messageId, lookupId);
  }

  @Test(expected = IllegalArgumentException.class)
  public void testPutThrowsExceptionOnNonPersistable() {

    // act
    koalaGCPastImpl.put(new PiId("testPutException", 0), new NonPersistable(), continuation);
  }

  @Test
  public void testGetSuccessThreshold() {
    // setup
    koalaGCPastImpl.setSuccessfullInsertThreshold(94.3);

    // act
    assertEquals(94.3, koalaGCPastImpl.getSuccessfulInsertThreshold(), 0);
  }

  @Test
  public void testDeliverFetchHandleMessage() {
    // setup
    Id id = mock(Id.class);
    FetchHandleMessage message = mock(FetchHandleMessage.class);
    when(message.getId()).thenReturn(id);
    PId pid = mock(PId.class);
    when(koalaIdFactory.convertToPId(id)).thenReturn(pid);
    when(pid.forDht()).thenReturn(pid);
    when(koalaIdFactory.buildId(pid)).thenReturn(id);

    // act
    koalaGCPastImpl.deliver(null, message);

    // assert
    verify(storageManager).getObject(eq(id), isA(Continuation.class));
  }

  @Test
  public void testGetGlobalIds() {
    // setup
    KoalaIdFactory koalaIdFactory = new KoalaIdFactory(0, 0);
    koalaIdFactory.setKoalaPiEntityFactory(koalaPiEntityFactory);
    koalaGCPastImpl.setKoalaIdFactory(koalaIdFactory);
    PId testId = koalaGCPastImpl.getKoalaIdFactory().buildPId("R. Lopez");

    // act
    Set<String> globalIds = koalaGCPastImpl.generateBackupIds(4, NodeScope.GLOBAL, testId);

    // asssert
    assertEquals(4, globalIds.size());
    assertSetEndsWithBackupIdentifier(globalIds);
  }

  private void assertSetEndsWithBackupIdentifier(Set<String> ids) {
    for (String str : ids) {
      assertEquals(1, new BigInteger(str.substring(39), 16).mod(new BigInteger("2")).intValue());
    }
  }

  @Test
  public void testGetRegionalIds() {
    // setup
    KoalaIdFactory koalaIdFactory = new KoalaIdFactory(0, 0);
    koalaIdFactory.setKoalaPiEntityFactory(koalaPiEntityFactory);
    koalaGCPastImpl.setKoalaIdFactory(koalaIdFactory);
    PId testId = koalaGCPastImpl.getKoalaIdFactory().buildPId("R. Lopez");

    // act
    Set<String> ids = koalaGCPastImpl.generateBackupIds(4, NodeScope.REGION, testId);

    assertEquals(4, ids.size());
    assertSetEndsWithBackupIdentifier(ids);
    assertSetContains(ids, testId.getIdAsHex().substring(4, testId.getIdAsHex().length() - 1));
  }

  private void assertSetContains(Set<String> ids, String mustContain) {
    for (String str : ids) {
      assertTrue(str.toLowerCase().contains(mustContain.toLowerCase()));
    }
  }

  @Test
  public void testGetAvailabilityZoneIds() {
    // setup
    KoalaIdFactory koalaIdFactory = new KoalaIdFactory(0, 0);
    koalaIdFactory.setKoalaPiEntityFactory(koalaPiEntityFactory);
    koalaGCPastImpl.setKoalaIdFactory(koalaIdFactory);
    PId testId = koalaGCPastImpl.getKoalaIdFactory().buildPId("YEAHBOB");

    // act
    Set<String> ids = koalaGCPastImpl.generateBackupIds(4, NodeScope.AVAILABILITY_ZONE, testId);

    // assert
    assertEquals(4, ids.size());
    assertSetEndsWithBackupIdentifier(ids);
    assertSetContains(ids, testId.getIdAsHex().substring(5, testId.getIdAsHex().length() - 1));
  }

  @Test
  public void testGetGlobalBackupIDs() {
    // setup
    koalaGCPastImpl.setKoalaIdFactory(new KoalaIdFactory(0, 0));
    realTestId =
        (PId)
            koalaGCPastImpl
                .getKoalaIdFactory()
                .buildPIdFromHexString("1234567890123456789012345678901234567892");

    // act
    Set<String> ids = koalaGCPastImpl.generateBackupIds(4, NodeScope.GLOBAL, realTestId);

    // assert
    assertSetEndsWithBackupIdentifier(ids);
    assertSetContains(ids, "34567890123456789012345678901234560013");
  }

  @Test
  public void testBackupContent() {
    // setup
    PId pid = mock(PId.class);
    when(pid.asBackupId()).thenReturn(pid);
    when(pid.getIdAsHex()).thenReturn(content.getId().toStringFull());
    when(koalaIdFactory.convertToPId(eq(content.getId()))).thenReturn(pid);

    // act
    koalaGCPastImpl.backupContent(4, NodeScope.REGION, content);

    // assert
    assertEquals(4, backupIds.size());
    for (KoalaMutableContent sentContent : sentBackupContent) {
      assertEquals(sentContent.getBody(), content.getBody());
      assertEquals(
          sentContent.getContentHeaders().get(DhtContentHeader.CONTENT_TYPE),
          content.getContentHeaders().get(DhtContentHeader.CONTENT_TYPE));
      assertEquals(
          sentContent.getContentHeaders().get(DhtContentHeader.ID),
          sentContent.getId().toStringFull());
      assertEquals(sentContent.getVersion(), content.getVersion());
      assertNotSame(sentContent.getId(), content.getId());
    }
  }

  @Test
  public void testDeliverOfBackupMessageIfItIsNewer() {
    // setup
    content.getContentHeaders().put(DhtContentHeader.CONTENT_VERSION, Long.toString(2));
    backupMessage = new InsertBackupMessage(85, content, -1, nodeHandle, content.getId());

    // Make sure that the existing content is older
    KoalaMutableContent existingContent =
        new KoalaPiEntityContent(
            Id.build("ExisingContent"), "existingContent", new HashMap<String, String>());
    existingContent.getContentHeaders().put(DhtContentHeader.CONTENT_VERSION, Long.toString(1));

    GenericContinuationAnswer<KoalaMutableContent> g =
        new GenericContinuationAnswer<KoalaMutableContent>(existingContent);
    doAnswer(g)
        .when(storageManager)
        .getObject(eq(content.getId()), isA(StandardContinuation.class));

    ArgumentCaptor<GCPastMetadata> metadataArgument = ArgumentCaptor.forClass(GCPastMetadata.class);

    // act
    koalaGCPastImpl.deliver(content.getId(), backupMessage);

    // assert
    verify(storageManager)
        .store(
            eq(content.getId()),
            metadataArgument.capture(),
            eq(content),
            isA(StandardContinuation.class));
    assertEquals(2, metadataArgument.getValue().getExpiration());
  }

  @Test(expected = IllegalArgumentException.class)
  public void testFetchNotGCId() {
    // act
    koalaGCPastImpl.fetch(mock(Id.class), mock(NodeHandle.class), mock(Continuation.class));
  }

  @Test
  public void testShouldThrowExceptionIfBackupMessageIsOlderOrSameAsTheExistingBackedUpMessage() {
    backupMessage = new InsertBackupMessage(85, content, -1, nodeHandle, content.getId());

    GenericContinuationAnswer<KoalaMutableContent> g =
        new GenericContinuationAnswer<KoalaMutableContent>(content);
    doAnswer(g)
        .when(storageManager)
        .getObject(eq(content.getId()), isA(StandardContinuation.class));

    // act
    koalaGCPastImpl.deliver(content.getId(), backupMessage);

    // assert
    verify(continuation).receiveException(isA(KoalaContentVersionMismatchException.class));
  }

  @Test
  public void testFetchNotInStorage() throws Exception {
    // setup
    KoalaPastPolicy koalaPastPolicy = mock(KoalaPastPolicy.class);
    koalaGCPastImpl =
        new KoalaGCPastImpl(
            node,
            storageManager,
            null,
            0,
            instance,
            koalaPastPolicy,
            0,
            storageManager,
            KoalaNode.DEFAULT_NUMBER_OF_DHT_BACKUPS,
            koalaIdFactory,
            koalaPiEntityFactory);

    Id id = mock(Id.class);
    GCId gcId = mock(GCId.class);
    when(gcId.getId()).thenReturn(id);
    when(storageManager.exists(id)).thenReturn(false);
    NodeHandle hint = mock(NodeHandle.class);
    final CountDownLatch latch = new CountDownLatch(1);

    doAnswer(
            new Answer<Object>() {
              @Override
              public Object answer(InvocationOnMock invocation) throws Throwable {
                StandardContinuation continuation =
                    (StandardContinuation) invocation.getArguments()[4];
                continuation.receiveResult(content);
                latch.countDown();
                return null;
              }
            })
        .when(koalaPastPolicy)
        .fetch(
            eq(id), eq(hint), (Cache) isNull(), isA(Past.class), isA(StandardContinuation.class));

    Storage storage = mock(Storage.class);
    when(storageManager.getStorage()).thenReturn(storage);

    // act
    koalaGCPastImpl.fetch(gcId, hint, mock(Continuation.class));

    // assert
    assertTrue(latch.await(500, TimeUnit.MILLISECONDS));
    verify(storage).store(eq(id), isA(GCPastMetadata.class), eq(content), isA(Continuation.class));
  }

  @Test
  public void shouldNotStoreLocallyWhenReplicationFetchingIfDeletableAndDeleted() throws Exception {
    // setup
    KoalaPastPolicy koalaPastPolicy = mock(KoalaPastPolicy.class);
    koalaGCPastImpl =
        new KoalaGCPastImpl(
            node,
            storageManager,
            null,
            0,
            instance,
            koalaPastPolicy,
            0,
            storageManager,
            KoalaNode.DEFAULT_NUMBER_OF_DHT_BACKUPS,
            koalaIdFactory,
            koalaPiEntityFactory);

    Id id = mock(Id.class);
    GCId gcId = mock(GCId.class);
    when(gcId.getId()).thenReturn(id);
    when(storageManager.exists(id)).thenReturn(false);
    NodeHandle hint = mock(NodeHandle.class);
    final CountDownLatch latch = new CountDownLatch(1);

    doAnswer(
            new Answer<Object>() {
              @Override
              public Object answer(InvocationOnMock invocation) throws Throwable {
                StandardContinuation continuation =
                    (StandardContinuation) invocation.getArguments()[4];
                continuation.receiveResult(content);
                latch.countDown();
                return null;
              }
            })
        .when(koalaPastPolicy)
        .fetch(
            eq(id), eq(hint), (Cache) isNull(), isA(Past.class), isA(StandardContinuation.class));

    Storage storage = mock(Storage.class);
    when(storageManager.getStorage()).thenReturn(storage);

    String json = "what";
    content = new KoalaPiEntityContent(messageId, json, true, "text", 1, NodeScope.GLOBAL, "", 0);

    // act
    koalaGCPastImpl.fetch(gcId, hint, mock(Continuation.class));

    // assert
    assertTrue(latch.await(500, TimeUnit.MILLISECONDS));
    verify(storage, never())
        .store(eq(id), isA(GCPastMetadata.class), eq(content), isA(Continuation.class));
  }

  @Test
  public void shouldNotStoreLocallyWhenReplicationFetchingIfNullType() throws Exception {
    // setup
    KoalaPastPolicy koalaPastPolicy = mock(KoalaPastPolicy.class);
    koalaGCPastImpl =
        new KoalaGCPastImpl(
            node,
            storageManager,
            null,
            0,
            instance,
            koalaPastPolicy,
            0,
            storageManager,
            KoalaNode.DEFAULT_NUMBER_OF_DHT_BACKUPS,
            koalaIdFactory,
            koalaPiEntityFactory);

    Id id = mock(Id.class);
    GCId gcId = mock(GCId.class);
    when(gcId.getId()).thenReturn(id);
    when(storageManager.exists(id)).thenReturn(false);
    NodeHandle hint = mock(NodeHandle.class);
    final CountDownLatch latch = new CountDownLatch(1);

    doAnswer(
            new Answer<Object>() {
              @Override
              public Object answer(InvocationOnMock invocation) throws Throwable {
                StandardContinuation continuation =
                    (StandardContinuation) invocation.getArguments()[4];
                continuation.receiveResult(content);
                latch.countDown();
                return null;
              }
            })
        .when(koalaPastPolicy)
        .fetch(
            eq(id), eq(hint), (Cache) isNull(), isA(Past.class), isA(StandardContinuation.class));

    Storage storage = mock(Storage.class);
    when(storageManager.getStorage()).thenReturn(storage);

    String json = "what";
    content = new KoalaPiEntityContent(messageId, json, false, null, 1, NodeScope.GLOBAL, "", 0);

    // act
    koalaGCPastImpl.fetch(gcId, hint, mock(Continuation.class));

    // assert
    assertTrue(latch.await(500, TimeUnit.MILLISECONDS));
    verify(storage, never())
        .store(eq(id), isA(GCPastMetadata.class), eq(content), isA(Continuation.class));
  }

  @Test
  public void testGetLeafSet() {
    LeafSet leafset = mock(LeafSet.class);
    when(node.getLeafSet()).thenReturn(leafset);

    // act
    LeafSet result = koalaGCPastImpl.getLeafSet();

    // verify
    assertEquals(leafset, result);
  }

  @Test
  public void testReadObjectFromStorage() {
    // act
    koalaGCPastImpl.readObjectFromStorage(dummyId, continuation);

    // verify
    verify(storageManager).getObject(dummyId, continuation);
  }

  @Test
  public void testGetMessageContinuation() {
    // setup
    message = mock(PastMessage.class);

    // act
    Continuation actual = koalaGCPastImpl.getMessageResponseContinuation(message);

    // verify
    assertEquals(continuation, actual);
  }

  @Test
  public void testGetLocalNodeId() {
    // setup
    when(node.getId()).thenReturn(contentId);

    rice.p2p.commonapi.Id id = koalaGCPastImpl.getLocalNodeId();

    assertEquals(contentId, id);
  }

  @Test
  public void testGenerateBackups() {
    PId pid = new PiId("0234567890123456789012345678901234567890", 1);

    // act
    SortedSet<String> backupIds = koalaGCPastImpl.generateBackupIds(16, NodeScope.GLOBAL, pid);

    String firstBackup = backupIds.first();
    assertTrue(firstBackup.endsWith("1"));
    for (String id : backupIds) {
      assertEquals(firstBackup.substring(1), id.substring(1));
    }
  }

  @Test
  public void testInsertGetsVersion() {
    // setup
    KoalaGCPastImpl localGcPastImpl =
        new KoalaGCPastImpl(
            node,
            storageManager,
            null,
            0,
            instance,
            new KoalaPastPolicy(),
            0,
            storageManager,
            KoalaNode.DEFAULT_NUMBER_OF_DHT_BACKUPS,
            koalaIdFactory,
            koalaPiEntityFactory) {
          @Override
          protected void sendRequest(
              rice.p2p.commonapi.Id id, PastMessage message, Continuation command) {
            sentId = id;
            sentMessage = message;
            callBackContinuation = command;
          }
        };
    PastContent content =
        new KoalaPiEntityContent(
            Id.build("bob"),
            "the builder",
            false,
            "text",
            0,
            NodeScope.AVAILABILITY_ZONE,
            "tv:series",
            18);
    Continuation command = mock(Continuation.class);

    // act
    localGcPastImpl.insert(content, command);

    // assert
    assertEquals(sentId, Id.build("bob"));
    assertTrue(sentMessage instanceof InsertRequestMessage);
    assertEquals(18, ((InsertRequestMessage) sentMessage).getExpiration());
    assertEquals(content, ((InsertRequestMessage) sentMessage).getContent());
    assertEquals(command, callBackContinuation);
  }

  @Test
  public void testSetRequiredReadHandlesPercentage() {
    double d = 99.39;

    // act
    koalaGCPastImpl.setRequiredReadHandlesPercentage(d);

    // assert
    assertEquals(d, koalaGCPastImpl.getRequiredReadHandlesThreshold(), 0.0);
  }

  class NonPersistable extends PiEntityBase {
    public NonPersistable() {}

    @Override
    public String getType() {
      return "NonPersistable";
    }

    @Override
    public long getVersion() {
      return 0;
    }

    @Override
    public void incrementVersion() {}

    @Override
    public void setVersion(long version) {}

    @Override
    public String getUrl() {
      return "url:NonPersistable";
    }

    @Override
    public String getUriScheme() {
      return null;
    }
  }
}
示例#16
0
  public Replayer(
      final Id id,
      final InetSocketAddress addr,
      InetSocketAddress bootaddress,
      final long startTime,
      final long randSeed)
      throws Exception {
    this.bootaddress = bootaddress;
    Environment env = ReplayLayer.generateEnvironment(id.toString(), startTime, randSeed, null);

    final Parameters params = env.getParameters();

    params.setInt(
        "pastry_socket_scm_max_open_sockets",
        params.getInt(
            "org.mpisws.p2p.testing.transportlayer.replay_pastry_socket_scm_max_open_sockets"));

    params.setBoolean("pastry_socket_use_own_random", false);
    //    env.getParameters().setInt("rice.environment.random_loglevel", Logger.FINER);

    logger = env.getLogManager().getLogger(Replayer.class, null);

    //    env.getParameters().setInt("org.mpisws.p2p.transport.peerreview.replay_loglevel",
    // Logger.FINER);

    final Logger simLogger = env.getLogManager().getLogger(EventSimulator.class, null);

    final List<ReplayLayer<InetSocketAddress>> replayers =
        new ArrayList<ReplayLayer<InetSocketAddress>>();

    SocketPastryNodeFactory factory =
        new SocketPastryNodeFactory(
            new NodeIdFactory() {
              public Id generateNodeId() {
                return id;
              }
            },
            addr.getPort(),
            env) {

          //      @Override
          //      protected TransportLayer<MultiInetSocketAddress, ByteBuffer>
          // getPriorityTransportLayer(TransportLayer<MultiInetSocketAddress, ByteBuffer> trans,
          // LivenessProvider<MultiInetSocketAddress> liveness,
          // ProximityProvider<MultiInetSocketAddress> prox, PastryNode pn) {
          //        // get rid of the priorityLayer
          //        if
          // (params.getBoolean("org.mpisws.p2p.testing.transportlayer.replay.use_priority")) {
          //          return super.getPriorityTransportLayer(trans, liveness, prox, pn);
          //        } else {
          //          return trans;
          //        }
          //      }

          @Override
          public NodeHandle getLocalHandle(PastryNode pn, NodeHandleFactory nhf) {
            SocketNodeHandle ret = (SocketNodeHandle) super.getLocalHandle(pn, nhf);
            logger.log(ret.toStringFull());
            return ret;
          }

          @Override
          protected RandomSource cloneRandomSource(
              Environment rootEnvironment, Id nodeId, LogManager lman) {
            return rootEnvironment.getRandomSource();
          }

          @Override
          protected TransportLayer<InetSocketAddress, ByteBuffer> getWireTransportLayer(
              InetSocketAddress innermostAddress, PastryNode pn) throws IOException {
            Serializer<InetSocketAddress> serializer = new InetSocketAddressSerializer();

            HashProvider hashProv = new NullHashProvider();
            SecureHistoryFactory shFactory =
                new SecureHistoryFactoryImpl(hashProv, pn.getEnvironment());
            String logName = "0x" + id.toStringFull().substring(0, 6);
            SecureHistory hist = shFactory.open(logName, "r");

            ReplayLayer<InetSocketAddress> replay =
                new ReplayLayer<InetSocketAddress>(
                    serializer, hashProv, hist, addr, pn.getEnvironment());
            replay.registerEvent(Replayer.this, EVT_BOOT, EVT_SUBSCRIBE, EVT_PUBLISH);
            replayers.add(replay);
            return replay;
          }
        };

    // construct a node, passing the null boothandle on the first loop will
    // cause the node to start its own ring
    node = (PastryNode) factory.newNode();
    app = new MyScribeClient(node);

    ReplaySM sim = (ReplaySM) env.getSelectorManager();
    ReplayLayer<InetSocketAddress> replay = replayers.get(0);
    replay.makeProgress(); // get rid of INIT event
    sim.setVerifier(replay);

    sim.start();

    //    // this is an example of th enew way
    //    //PastryNode node = factory.newNode(nidFactory.generateNodeId());
    //    //node.getBootstrapper().boot(Collections.singleton(bootaddress));
    //
    //    // the node may require sending several messages to fully boot into the ring
    //    synchronized(node) {
    //      while(!node.isReady() && !node.joinFailed()) {
    //        // delay so we don't busy-wait
    //        node.wait(500);
    //
    //        // abort if can't join
    //        if (node.joinFailed()) {
    //          throw new IOException("Could not join the FreePastry ring.
    // Reason:"+node.joinFailedReason());
    //        }
    //      }
    //    }
    //
    //    System.out.println("Finished creating new node: " + node);
    //
    //    // construct a new scribe application
    //    MyScribeClient app = new MyScribeClient(node);
    //
    //    // for all the rest just subscribe
    //    app.subscribe();

    // now, print the tree
    //    env.getTimeSource().sleep(5000);

    try {
      env.getTimeSource().sleep(55000);
    } catch (InterruptedException ie) {
      return;
    }
    env.destroy();
  }
示例#17
0
  /**
   * checks to see if the previous node along the path was missing a RT entry if so, we send the
   * previous node the corresponding RT row to patch the hole
   *
   * @param msg the RouteMessage being routed
   * @param handle the next hop handle
   */
  private void checkForRouteTableHole(RouteMessage msg, NodeHandle handle) {
    if (logger.level <= Logger.FINEST)
      logger.log("checkForRouteTableHole(" + msg + "," + handle + ")");

    NodeHandle prevNode = msg.getPrevNode();
    if (prevNode == null) {
      if (logger.level <= Logger.FINER) logger.log("No prevNode defined in " + msg);
      return;
    }

    if (prevNode.equals(getNodeHandle())) {
      if (logger.level <= Logger.FINER) logger.log("prevNode is me in " + msg);
      return;
    }

    // we don't want to send the repair if they just routed in the leafset
    LeafSet ls = thePastryNode.getLeafSet();
    if (ls.overlaps()) return; // small network, don't bother
    if (ls.member(prevNode)) {
      // ok, it's in my leafset, so I'm in his, but make sure that it's not on the edge
      int index = ls.getIndex(prevNode);
      if ((index == ls.cwSize()) || (index == -ls.ccwSize())) {
        // it is the edge... continue with repair
      } else {
        return;
      }
    }

    Id prevId = prevNode.getNodeId();
    Id key = msg.getTarget();

    int diffDigit = prevId.indexOfMSDD(key, thePastryNode.getRoutingTable().baseBitLength());

    // if we both have the same prefix (in other words the previous node didn't make a prefix of
    // progress)
    if (diffDigit >= 0
        && diffDigit
            == thePastryNode
                .getNodeId()
                .indexOfMSDD(key, thePastryNode.getRoutingTable().baseBitLength())) {
      synchronized (lastTimeSentRouteTablePatch) {
        if (lastTimeSentRouteTablePatch.containsKey(prevNode)) {
          long lastTime = lastTimeSentRouteTablePatch.get(prevNode);
          if (lastTime
              > (thePastryNode.getEnvironment().getTimeSource().currentTimeMillis()
                  - ROUTE_TABLE_PATCH_THROTTLE)) {
            if (logger.level <= Logger.INFO)
              logger.log(
                  "not sending route table patch to "
                      + prevNode
                      + " because throttled.  Last Time:"
                      + lastTime);
            return;
          }
        }
        lastTimeSentRouteTablePatch.put(
            prevNode, thePastryNode.getEnvironment().getTimeSource().currentTimeMillis());
      }

      // the previous node is missing a RT entry, send the row
      // for now, we send the entire row for simplicity

      RouteSet[] row = thePastryNode.getRoutingTable().getRow(diffDigit);
      BroadcastRouteRow brr = new BroadcastRouteRow(thePastryNode.getLocalHandle(), row);

      if (prevNode.isAlive()) {
        if (logger.level <= Logger.FINE) {
          logger.log(
              "Found hole in " + prevNode + "'s routing table. Sending " + brr.toStringFull());
        }
        thePastryNode.send(prevNode, brr, null, options);
      }
    }
  }