@AdviseWith(adviceClasses = {Jdk14LogImplAdvice.class})
  @Test
  public void testSendDatagramWithCallback() throws Exception {

    // Submitted callback

    Pipe readPipe = Pipe.open();
    Pipe writePipe = Pipe.open();

    GatheringByteChannel gatheringByteChannel = writePipe.sink();
    ScatteringByteChannel scatteringByteChannel = readPipe.source();

    RegistrationReference registrationReference =
        _selectorIntraband.registerChannel(writePipe.source(), readPipe.sink());

    Object attachment = new Object();

    RecordCompletionHandler<Object> recordCompletionHandler = new RecordCompletionHandler<Object>();

    _selectorIntraband.sendDatagram(
        registrationReference,
        Datagram.createRequestDatagram(_type, _data),
        attachment,
        EnumSet.of(CompletionType.SUBMITTED),
        recordCompletionHandler);

    Datagram receiveDatagram = IntrabandTestUtil.readDatagramFully(scatteringByteChannel);

    recordCompletionHandler.waitUntilSubmitted();

    Assert.assertSame(attachment, recordCompletionHandler.getAttachment());
    Assert.assertEquals(_type, receiveDatagram.getType());

    ByteBuffer dataByteBuffer = receiveDatagram.getDataByteBuffer();

    Assert.assertArrayEquals(_data, dataByteBuffer.array());

    CaptureHandler captureHandler = null;

    try {

      // Callback timeout, with log

      captureHandler =
          JDKLoggerTestUtil.configureJDKLogger(BaseIntraband.class.getName(), Level.WARNING);

      List<LogRecord> logRecords = captureHandler.getLogRecords();

      recordCompletionHandler = new RecordCompletionHandler<Object>();

      _selectorIntraband.sendDatagram(
          registrationReference,
          Datagram.createRequestDatagram(_type, _data),
          attachment,
          EnumSet.of(CompletionType.DELIVERED),
          recordCompletionHandler,
          10,
          TimeUnit.MILLISECONDS);

      Selector selector = _selectorIntraband.selector;

      recordCompletionHandler.waitUntilTimeouted(selector);

      Assert.assertSame(attachment, recordCompletionHandler.getAttachment());
      Assert.assertEquals(1, logRecords.size());

      IntrabandTestUtil.assertMessageStartWith(
          logRecords.get(0), "Removed timeout response waiting datagram");

      // Callback timeout, without log

      logRecords = captureHandler.resetLogLevel(Level.OFF);

      recordCompletionHandler = new RecordCompletionHandler<Object>();

      _selectorIntraband.sendDatagram(
          registrationReference,
          Datagram.createRequestDatagram(_type, _data),
          attachment,
          EnumSet.of(CompletionType.DELIVERED),
          recordCompletionHandler,
          10,
          TimeUnit.MILLISECONDS);

      recordCompletionHandler.waitUntilTimeouted(selector);

      Assert.assertSame(attachment, recordCompletionHandler.getAttachment());
      Assert.assertTrue(logRecords.isEmpty());
    } finally {
      if (captureHandler != null) {
        captureHandler.close();
      }
    }

    // Callback timeout, completion handler causes NPE

    captureHandler =
        JDKLoggerTestUtil.configureJDKLogger(SelectorIntraband.class.getName(), Level.SEVERE);

    try {
      List<LogRecord> logRecords = captureHandler.getLogRecords();

      recordCompletionHandler =
          new RecordCompletionHandler<Object>() {

            @Override
            public void timedOut(Object attachment) {
              super.timedOut(attachment);

              throw new NullPointerException();
            }
          };

      Jdk14LogImplAdvice.reset();

      Selector selector = _selectorIntraband.selector;

      try {
        _selectorIntraband.sendDatagram(
            registrationReference,
            Datagram.createRequestDatagram(_type, _data),
            attachment,
            EnumSet.of(CompletionType.DELIVERED),
            recordCompletionHandler,
            10,
            TimeUnit.MILLISECONDS);
      } finally {
        recordCompletionHandler.waitUntilTimeouted(selector);

        Jdk14LogImplAdvice.waitUntilErrorCalled();
      }

      Assert.assertFalse(selector.isOpen());
      Assert.assertEquals(1, logRecords.size());

      IntrabandTestUtil.assertMessageStartWith(
          logRecords.get(0), SelectorIntraband.class + ".threadFactory-1 exiting exceptionally");

      gatheringByteChannel.close();
      scatteringByteChannel.close();
    } finally {
      captureHandler.close();
    }
  }
  @Test
  public void testSendDatagramWithoutCallback() throws Exception {

    // Single datagram sending

    Pipe readPipe = Pipe.open();
    Pipe writePipe = Pipe.open();

    GatheringByteChannel gatheringByteChannel = writePipe.sink();
    ScatteringByteChannel scatteringByteChannel = readPipe.source();

    SelectionKeyRegistrationReference registrationReference =
        (SelectionKeyRegistrationReference)
            _selectorIntraband.registerChannel(writePipe.source(), readPipe.sink());

    Thread wakeUpThread = new Thread(new WakeUpRunnable(_selectorIntraband));

    wakeUpThread.start();

    Selector selector = _selectorIntraband.selector;

    synchronized (selector) {
      wakeUpThread.interrupt();
      wakeUpThread.join();

      Datagram requestDatagram = Datagram.createRequestDatagram(_type, _data);

      _selectorIntraband.sendDatagram(registrationReference, requestDatagram);

      SelectionKey writeSelectionKey = registrationReference.writeSelectionKey;

      ChannelContext channelContext = (ChannelContext) writeSelectionKey.attachment();

      Queue<Datagram> sendingQueue = channelContext.getSendingQueue();

      Assert.assertEquals(1, sendingQueue.size());
      Assert.assertSame(requestDatagram, sendingQueue.peek());
    }

    Datagram receiveDatagram = IntrabandTestUtil.readDatagramFully(scatteringByteChannel);

    Assert.assertEquals(_type, receiveDatagram.getType());

    ByteBuffer dataByteBuffer = receiveDatagram.getDataByteBuffer();

    Assert.assertArrayEquals(_data, dataByteBuffer.array());

    // Two datagrams continuous sending

    Datagram requestDatagram1 = Datagram.createRequestDatagram(_type, _data);
    Datagram requestDatagram2 = Datagram.createRequestDatagram(_type, _data);

    wakeUpThread = new Thread(new WakeUpRunnable(_selectorIntraband));

    wakeUpThread.start();

    synchronized (selector) {
      wakeUpThread.interrupt();
      wakeUpThread.join();

      _selectorIntraband.sendDatagram(registrationReference, requestDatagram1);
      _selectorIntraband.sendDatagram(registrationReference, requestDatagram2);

      SelectionKey writeSelectionKey = registrationReference.writeSelectionKey;

      ChannelContext channelContext = (ChannelContext) writeSelectionKey.attachment();

      Queue<Datagram> sendingQueue = channelContext.getSendingQueue();

      Assert.assertEquals(2, sendingQueue.size());

      Datagram[] datagrams = sendingQueue.toArray(new Datagram[2]);

      Assert.assertSame(requestDatagram1, datagrams[0]);
      Assert.assertSame(requestDatagram2, datagrams[1]);
    }

    Datagram receiveDatagram1 = IntrabandTestUtil.readDatagramFully(scatteringByteChannel);

    Assert.assertEquals(_type, receiveDatagram1.getType());

    dataByteBuffer = receiveDatagram1.getDataByteBuffer();

    Assert.assertArrayEquals(_data, dataByteBuffer.array());

    Datagram receiveDatagram2 = IntrabandTestUtil.readDatagramFully(scatteringByteChannel);

    Assert.assertEquals(_type, receiveDatagram2.getType());

    dataByteBuffer = receiveDatagram2.getDataByteBuffer();

    Assert.assertArrayEquals(_data, dataByteBuffer.array());

    // Two datagrams delay sending

    requestDatagram1 = Datagram.createRequestDatagram(_type, _data);
    requestDatagram2 = Datagram.createRequestDatagram(_type, _data);

    wakeUpThread = new Thread(new WakeUpRunnable(_selectorIntraband));

    wakeUpThread.start();

    SelectionKey writeSelectionKey = registrationReference.writeSelectionKey;

    ChannelContext channelContext = (ChannelContext) writeSelectionKey.attachment();

    Queue<Datagram> sendingQueue = channelContext.getSendingQueue();

    synchronized (writeSelectionKey) {
      synchronized (selector) {
        wakeUpThread.interrupt();
        wakeUpThread.join();

        _selectorIntraband.sendDatagram(registrationReference, requestDatagram1);

        Assert.assertEquals(1, sendingQueue.size());
        Assert.assertSame(requestDatagram1, sendingQueue.peek());
      }

      receiveDatagram1 = IntrabandTestUtil.readDatagramFully(scatteringByteChannel);

      Assert.assertEquals(_type, receiveDatagram1.getType());

      dataByteBuffer = receiveDatagram1.getDataByteBuffer();

      Assert.assertArrayEquals(_data, dataByteBuffer.array());

      Thread pollingThread = _selectorIntraband.pollingThread;

      while (pollingThread.getState() == Thread.State.RUNNABLE) ;

      _selectorIntraband.sendDatagram(registrationReference, requestDatagram2);

      Assert.assertEquals(1, sendingQueue.size());
      Assert.assertSame(requestDatagram2, sendingQueue.peek());
    }

    receiveDatagram2 = IntrabandTestUtil.readDatagramFully(scatteringByteChannel);

    Assert.assertEquals(_type, receiveDatagram2.getType());

    dataByteBuffer = receiveDatagram2.getDataByteBuffer();

    Assert.assertArrayEquals(_data, dataByteBuffer.array());

    // Huge datagram sending

    int hugeBufferSize = 1024 * 1024 * 10;

    ByteBuffer hugeBuffer = ByteBuffer.allocate(hugeBufferSize);

    for (int i = 0; i < hugeBufferSize; i++) {
      hugeBuffer.put(i, (byte) i);
    }

    _selectorIntraband.sendDatagram(
        registrationReference, Datagram.createRequestDatagram(_type, hugeBuffer));

    receiveDatagram = DatagramHelper.createReceiveDatagram();

    channelContext = (ChannelContext) writeSelectionKey.attachment();

    int count = 0;

    while (!DatagramHelper.readFrom(receiveDatagram, scatteringByteChannel)) {

      count++;
    }

    Assert.assertTrue(count > 0);

    sendingQueue = channelContext.getSendingQueue();

    Assert.assertTrue(sendingQueue.isEmpty());

    dataByteBuffer = receiveDatagram.getDataByteBuffer();

    Assert.assertArrayEquals(hugeBuffer.array(), dataByteBuffer.array());

    unregisterChannels(registrationReference);

    gatheringByteChannel.close();
    scatteringByteChannel.close();
  }
  @AdviseWith(adviceClasses = {Jdk14LogImplAdvice.class})
  @Test
  public void testReceiveDatagram() throws Exception {
    Pipe readPipe = Pipe.open();
    Pipe writePipe = Pipe.open();

    GatheringByteChannel gatheringByteChannel = writePipe.sink();
    ScatteringByteChannel scatteringByteChannel = readPipe.source();

    SelectionKeyRegistrationReference registrationReference =
        (SelectionKeyRegistrationReference)
            _selectorIntraband.registerChannel(writePipe.source(), readPipe.sink());

    long sequenceId = 100;

    CaptureHandler captureHandler = null;

    try {

      // Receive ACK response, no ACK request, with log

      captureHandler =
          JDKLoggerTestUtil.configureJDKLogger(BaseIntraband.class.getName(), Level.WARNING);

      List<LogRecord> logRecords = captureHandler.getLogRecords();

      Jdk14LogImplAdvice.reset();

      try {
        DatagramHelper.writeTo(
            DatagramHelper.createACKResponseDatagram(sequenceId), gatheringByteChannel);
      } finally {
        Jdk14LogImplAdvice.waitUntilWarnCalled();
      }

      Assert.assertEquals(1, logRecords.size());

      IntrabandTestUtil.assertMessageStartWith(
          logRecords.get(0), "Dropped ownerless ACK response ");

      // Receive ACK response, no ACK request, without log

      logRecords = captureHandler.resetLogLevel(Level.OFF);

      Jdk14LogImplAdvice.reset();

      try {
        DatagramHelper.writeTo(
            DatagramHelper.createACKResponseDatagram(sequenceId), gatheringByteChannel);
      } finally {
        Jdk14LogImplAdvice.waitUntilIsWarnEnableCalled();
      }

      Assert.assertTrue(logRecords.isEmpty());

      // Receive ACK response, with ACK request

      Datagram requestDatagram = Datagram.createRequestDatagram(_type, _data);

      DatagramHelper.setAttachment(requestDatagram, new Object());

      RecordCompletionHandler<Object> recordCompletionHandler =
          new RecordCompletionHandler<Object>();

      DatagramHelper.setCompletionHandler(requestDatagram, recordCompletionHandler);

      DatagramHelper.setSequenceId(requestDatagram, sequenceId);
      DatagramHelper.setTimeout(requestDatagram, 10000);

      BaseIntrabandHelper.addResponseWaitingDatagram(_selectorIntraband, requestDatagram);

      DatagramHelper.writeTo(
          DatagramHelper.createACKResponseDatagram(sequenceId), gatheringByteChannel);

      recordCompletionHandler.waitUntilDelivered();

      Assert.assertSame(
          DatagramHelper.getAttachment(requestDatagram), recordCompletionHandler.getAttachment());

      // Receive response, no request, with log

      logRecords = captureHandler.resetLogLevel(Level.WARNING);

      Jdk14LogImplAdvice.reset();

      try {
        DatagramHelper.writeTo(
            Datagram.createResponseDatagram(requestDatagram, _data), gatheringByteChannel);
      } finally {
        Jdk14LogImplAdvice.waitUntilWarnCalled();
      }

      Assert.assertEquals(1, logRecords.size());

      IntrabandTestUtil.assertMessageStartWith(logRecords.get(0), "Dropped ownerless response ");

      // Receive response, no request, without log

      logRecords = captureHandler.resetLogLevel(Level.OFF);

      Jdk14LogImplAdvice.reset();

      try {
        requestDatagram = Datagram.createRequestDatagram(_type, _data);

        DatagramHelper.setSequenceId(requestDatagram, sequenceId);

        DatagramHelper.writeTo(
            Datagram.createResponseDatagram(requestDatagram, _data), gatheringByteChannel);
      } finally {
        Jdk14LogImplAdvice.waitUntilIsWarnEnableCalled();
      }

      Assert.assertTrue(logRecords.isEmpty());

      // Receive response, with request, with replied completion handler

      requestDatagram = Datagram.createRequestDatagram(_type, _data);

      DatagramHelper.setAttachment(requestDatagram, new Object());

      recordCompletionHandler = new RecordCompletionHandler<Object>();

      DatagramHelper.setCompletionHandler(requestDatagram, recordCompletionHandler);

      DatagramHelper.setCompletionTypes(requestDatagram, EnumSet.of(CompletionType.REPLIED));
      DatagramHelper.setSequenceId(requestDatagram, sequenceId);
      DatagramHelper.setTimeout(requestDatagram, 10000);

      BaseIntrabandHelper.addResponseWaitingDatagram(_selectorIntraband, requestDatagram);

      DatagramHelper.writeTo(
          Datagram.createResponseDatagram(requestDatagram, _data), gatheringByteChannel);

      recordCompletionHandler.waitUntilReplied();

      Assert.assertSame(
          DatagramHelper.getAttachment(requestDatagram), recordCompletionHandler.getAttachment());

      // Receive response, with request, without replied completion
      // handler, with log

      logRecords = captureHandler.resetLogLevel(Level.WARNING);

      requestDatagram = Datagram.createRequestDatagram(_type, _data);

      DatagramHelper.setCompletionTypes(requestDatagram, EnumSet.noneOf(CompletionType.class));

      recordCompletionHandler = new RecordCompletionHandler<Object>();

      DatagramHelper.setCompletionHandler(requestDatagram, recordCompletionHandler);

      DatagramHelper.setSequenceId(requestDatagram, sequenceId);
      DatagramHelper.setTimeout(requestDatagram, 10000);

      BaseIntrabandHelper.addResponseWaitingDatagram(_selectorIntraband, requestDatagram);

      Jdk14LogImplAdvice.reset();

      try {
        DatagramHelper.writeTo(
            Datagram.createResponseDatagram(requestDatagram, _data), gatheringByteChannel);
      } finally {
        Jdk14LogImplAdvice.waitUntilWarnCalled();
      }

      Assert.assertEquals(1, logRecords.size());

      IntrabandTestUtil.assertMessageStartWith(logRecords.get(0), "Dropped unconcerned response ");

      // Receive response, with request, without replied completion
      // handler, without log

      logRecords = captureHandler.resetLogLevel(Level.OFF);

      requestDatagram = Datagram.createRequestDatagram(_type, _data);

      DatagramHelper.setCompletionTypes(requestDatagram, EnumSet.noneOf(CompletionType.class));

      recordCompletionHandler = new RecordCompletionHandler<Object>();

      DatagramHelper.setCompletionHandler(requestDatagram, recordCompletionHandler);

      DatagramHelper.setSequenceId(requestDatagram, sequenceId);
      DatagramHelper.setTimeout(requestDatagram, 10000);

      BaseIntrabandHelper.addResponseWaitingDatagram(_selectorIntraband, requestDatagram);

      Jdk14LogImplAdvice.reset();

      try {
        DatagramHelper.writeTo(
            Datagram.createResponseDatagram(requestDatagram, _data), gatheringByteChannel);
      } finally {
        Jdk14LogImplAdvice.waitUntilIsWarnEnableCalled();
      }

      Assert.assertTrue(logRecords.isEmpty());

      // Receive request, requires ACK, no datagram receive handler,
      // with log

      logRecords = captureHandler.resetLogLevel(Level.WARNING);

      requestDatagram = Datagram.createRequestDatagram(_type, _data);

      DatagramHelper.setAckRequest(requestDatagram);
      DatagramHelper.setSequenceId(requestDatagram, sequenceId);

      Jdk14LogImplAdvice.reset();

      try {
        DatagramHelper.writeTo(requestDatagram, gatheringByteChannel);
      } finally {
        Jdk14LogImplAdvice.waitUntilWarnCalled();
      }

      Datagram ackResponseDatagram = IntrabandTestUtil.readDatagramFully(scatteringByteChannel);

      Assert.assertEquals(sequenceId, DatagramHelper.getSequenceId(ackResponseDatagram));
      Assert.assertTrue(DatagramHelper.isAckResponse(ackResponseDatagram));

      ByteBuffer dataByteBuffer = ackResponseDatagram.getDataByteBuffer();

      Assert.assertEquals(0, dataByteBuffer.capacity());

      Assert.assertEquals(1, logRecords.size());

      IntrabandTestUtil.assertMessageStartWith(logRecords.get(0), "Dropped ownerless request ");

      // Receive request, no datagram receive handler, without log

      logRecords = captureHandler.resetLogLevel(Level.OFF);

      requestDatagram = Datagram.createRequestDatagram(_type, _data);

      DatagramHelper.setSequenceId(requestDatagram, sequenceId);

      Jdk14LogImplAdvice.reset();

      try {
        DatagramHelper.writeTo(requestDatagram, gatheringByteChannel);
      } finally {
        Jdk14LogImplAdvice.waitUntilIsWarnEnableCalled();
      }

      Assert.assertTrue(logRecords.isEmpty());

      // Receive request, with datagram receive handler,

      logRecords = captureHandler.resetLogLevel(Level.SEVERE);

      requestDatagram = Datagram.createRequestDatagram(_type, _data);

      DatagramHelper.setSequenceId(requestDatagram, sequenceId);

      RecordDatagramReceiveHandler recordDatagramReceiveHandler =
          new RecordDatagramReceiveHandler();

      _selectorIntraband.registerDatagramReceiveHandler(_type, recordDatagramReceiveHandler);

      Jdk14LogImplAdvice.reset();

      try {
        DatagramHelper.writeTo(requestDatagram, gatheringByteChannel);
      } finally {
        Jdk14LogImplAdvice.waitUntilErrorCalled();
      }

      Datagram receiveDatagram = recordDatagramReceiveHandler.getReceiveDatagram();

      Assert.assertEquals(sequenceId, DatagramHelper.getSequenceId(receiveDatagram));
      Assert.assertEquals(_type, receiveDatagram.getType());

      dataByteBuffer = receiveDatagram.getDataByteBuffer();

      Assert.assertArrayEquals(_data, dataByteBuffer.array());
      Assert.assertEquals(1, logRecords.size());

      IntrabandTestUtil.assertMessageStartWith(logRecords.get(0), "Unable to dispatch");

      unregisterChannels(registrationReference);

      gatheringByteChannel.close();
      scatteringByteChannel.close();
    } finally {
      if (captureHandler != null) {
        captureHandler.close();
      }
    }
  }