/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
package com.liferay.portal.kernel.nio.intraband;
import com.liferay.portal.kernel.io.BigEndianCodec;
import com.liferay.portal.kernel.nio.intraband.BaseIntraband.SendSyncDatagramCompletionHandler;
import com.liferay.portal.kernel.nio.intraband.CompletionHandler.CompletionType;
import com.liferay.portal.kernel.nio.intraband.test.MockIntraband;
import com.liferay.portal.kernel.nio.intraband.test.MockRegistrationReference;
import com.liferay.portal.kernel.test.CaptureHandler;
import com.liferay.portal.kernel.test.GCUtil;
import com.liferay.portal.kernel.test.JDKLoggerTestUtil;
import com.liferay.portal.kernel.test.ReflectionTestUtil;
import com.liferay.portal.kernel.test.SyncThrowableThread;
import com.liferay.portal.kernel.test.rule.CodeCoverageAssertor;
import com.liferay.portal.kernel.util.Time;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.GatheringByteChannel;
import java.nio.channels.Pipe;
import java.nio.channels.Pipe.SinkChannel;
import java.nio.channels.Pipe.SourceChannel;
import java.nio.channels.ScatteringByteChannel;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Test;
/**
* @author Shuyang Zhou
*/
public class BaseIntrabandTest {
@ClassRule
public static final CodeCoverageAssertor codeCoverageAssertor =
new CodeCoverageAssertor() {
@Override
public void appendAssertClasses(List<Class<?>> assertClasses) {
assertClasses.add(ChannelContext.class);
assertClasses.add(ClosedIntrabandException.class);
assertClasses.add(CompletionHandler.class);
Collections.addAll(
assertClasses,
CompletionHandler.class.getDeclaredClasses());
assertClasses.add(Datagram.class);
assertClasses.add(DatagramReceiveHandler.class);
}
};
@Test
public void testDatagramReceiveHandlerRegister() throws Exception {
// Length
AtomicReference<DatagramReceiveHandler[]>
datagramReceiveHandlersReference =
_mockIntraband.datagramReceiveHandlersReference;
DatagramReceiveHandler[] datagramReceiveHandlers =
datagramReceiveHandlersReference.get();
Assert.assertEquals(
Arrays.toString(datagramReceiveHandlers), 256,
datagramReceiveHandlers.length);
// Copy
Assert.assertNotSame(
datagramReceiveHandlers,
_mockIntraband.getDatagramReceiveHandlers());
// First register
DatagramReceiveHandler datagramReceiveHandler1 =
new RecordDatagramReceiveHandler();
Assert.assertNull(
_mockIntraband.registerDatagramReceiveHandler(
_TYPE, datagramReceiveHandler1));
Assert.assertSame(
datagramReceiveHandler1,
_mockIntraband.getDatagramReceiveHandlers()[_TYPE]);
// Second register
final DatagramReceiveHandler datagramReceiveHandler2 =
new RecordDatagramReceiveHandler();
Assert.assertSame(
datagramReceiveHandler1,
_mockIntraband.registerDatagramReceiveHandler(
_TYPE, datagramReceiveHandler2));
Assert.assertSame(
datagramReceiveHandler2,
_mockIntraband.getDatagramReceiveHandlers()[_TYPE]);
// Unregister
Assert.assertSame(
datagramReceiveHandler2,
_mockIntraband.unregisterDatagramReceiveHandler(_TYPE));
Assert.assertNull(_mockIntraband.getDatagramReceiveHandlers()[_TYPE]);
// Concurrent registering
final AtomicReference<DatagramReceiveHandler[]> atomicReference =
_mockIntraband.datagramReceiveHandlersReference;
DatagramReceiveHandler[] originalDatagramReceiveHandlers =
atomicReference.get();
atomicReference.set(new DatagramReceiveHandler[1024 * 1024]);
long valueOffset = ReflectionTestUtil.getFieldValue(
AtomicReference.class, "valueOffset");
try {
ReflectionTestUtil.setFieldValue(
AtomicReference.class, "valueOffset", valueOffset + 1);
FutureTask<Void> registerFutureTask = new FutureTask<>(
new Callable<Void>() {
@Override
public Void call() {
_mockIntraband.registerDatagramReceiveHandler(
_TYPE, datagramReceiveHandler2);
throw new AssertionError(
"Registering a datagram receive handle should " +
"fail with a NullPointerException");
}
});
Thread registerThread = new Thread(
registerFutureTask, "Register Thread");
registerThread.start();
FutureTask<Void> monitorFutureTask = new FutureTask<>(
new Callable<Void>() {
@Override
public Void call() throws InterruptedException {
for (int i = 0; i < 10; i++) {
GCUtil.gc(false, false);
}
atomicReference.set(null);
return null;
}
});
Thread monitorThread = new Thread(
monitorFutureTask, "Monitor Thread");
monitorThread.start();
monitorFutureTask.get(10, TimeUnit.MINUTES);
try {
registerFutureTask.get(10, TimeUnit.MINUTES);
Assert.fail();
}
catch (ExecutionException ee) {
Throwable throwable = ee.getCause();
Assert.assertSame(
NullPointerException.class, throwable.getClass());
}
}
finally {
ReflectionTestUtil.setFieldValue(
AtomicReference.class, "valueOffset", valueOffset);
atomicReference.set(originalDatagramReceiveHandlers);
}
_mockIntraband.close();
// Null after close
Assert.assertNull(datagramReceiveHandlersReference.get());
// Get after close
try {
_mockIntraband.getDatagramReceiveHandlers();
Assert.fail();
}
catch (ClosedIntrabandException cie) {
}
// Register after close
try {
_mockIntraband.registerDatagramReceiveHandler(
_TYPE, new RecordDatagramReceiveHandler());
Assert.fail();
}
catch (ClosedIntrabandException cie) {
}
// Unregister after close
try {
_mockIntraband.unregisterDatagramReceiveHandler(_TYPE);
Assert.fail();
}
catch (ClosedIntrabandException cie) {
}
}
@Test
public void testGenerateSequenceId() throws Exception {
// Overflow resetting
AtomicLong sequenceIdGenerator = _mockIntraband.sequenceIdGenerator;
sequenceIdGenerator.set(Long.MAX_VALUE);
Assert.assertEquals(
Long.MAX_VALUE, _mockIntraband.generateSequenceId());
Assert.assertEquals(0, _mockIntraband.generateSequenceId());
Assert.assertEquals(1, _mockIntraband.generateSequenceId());
}
@Test
public void testHandleReading() throws Exception {
try (CaptureHandler captureHandler =
JDKLoggerTestUtil.configureJDKLogger(
BaseIntraband.class.getName(), Level.FINE)) {
// IOException, new receive datagram, debug log
List<LogRecord> logRecords = captureHandler.getLogRecords();
ChannelContext channelContext = new ChannelContext(null);
MockRegistrationReference mockRegistrationReference =
new MockRegistrationReference(_mockIntraband);
channelContext.setRegistrationReference(mockRegistrationReference);
_mockIntraband.handleReading(
new MockScatteringByteChannel(false), channelContext);
Assert.assertFalse(mockRegistrationReference.isValid());
Assert.assertEquals(logRecords.toString(), 1, logRecords.size());
LogRecord logRecord = logRecords.get(0);
assertMessageStartWith(
logRecord, "Broken read channel, unregister ");
Assert.assertTrue(logRecord.getThrown() instanceof IOException);
// IOException, new receive datagram, info log
logRecords = captureHandler.resetLogLevel(Level.INFO);
channelContext = new ChannelContext(null);
mockRegistrationReference = new MockRegistrationReference(
_mockIntraband);
channelContext.setRegistrationReference(mockRegistrationReference);
_mockIntraband.handleReading(
new MockScatteringByteChannel(true), channelContext);
Assert.assertFalse(mockRegistrationReference.isValid());
Assert.assertEquals(logRecords.toString(), 1, logRecords.size());
logRecord = logRecords.get(0);
assertMessageStartWith(
logRecord, "Broken read channel, unregister ");
Assert.assertNull(logRecord.getThrown());
// IOException, existing receive datagram, without log
logRecords = captureHandler.resetLogLevel(Level.OFF);
channelContext = new ChannelContext(null);
channelContext.setReadingDatagram(Datagram.createReceiveDatagram());
mockRegistrationReference = new MockRegistrationReference(
_mockIntraband);
channelContext.setRegistrationReference(mockRegistrationReference);
_mockIntraband.handleReading(
new MockScatteringByteChannel(false), channelContext);
Assert.assertFalse(mockRegistrationReference.isValid());
Assert.assertTrue(logRecords.isEmpty());
// Slow reading of ownerless datagram, with log
logRecords = captureHandler.resetLogLevel(Level.WARNING);
Pipe pipe = Pipe.open();
try (SourceChannel sourceChannel = pipe.source();
final SinkChannel sinkChannel = pipe.sink()) {
Datagram requestDatagram = Datagram.createRequestDatagram(
_TYPE, _data);
requestDatagram.writeTo(sinkChannel);
final ByteBuffer byteBuffer = ByteBuffer.allocate(
_data.length + 14);
while (byteBuffer.hasRemaining()) {
sourceChannel.read(byteBuffer);
}
sourceChannel.configureBlocking(false);
sinkChannel.configureBlocking(false);
SyncThrowableThread<Void> syncThrowableThread =
new SyncThrowableThread<>(
new Callable<Void>() {
@Override
public Void call() throws Exception {
for (byte b : byteBuffer.array()) {
sinkChannel.write(
ByteBuffer.wrap(new byte[] {b}));
Thread.sleep(1);
}
return null;
}
});
syncThrowableThread.start();
channelContext = new ChannelContext(null);
Datagram receiveDatagram = Datagram.createReceiveDatagram();
channelContext.setReadingDatagram(receiveDatagram);
while (receiveDatagram == channelContext.getReadingDatagram()) {
_mockIntraband.handleReading(sourceChannel, channelContext);
}
syncThrowableThread.sync();
Assert.assertEquals(_TYPE, receiveDatagram.getType());
ByteBuffer dataByteBuffer = receiveDatagram.getDataByteBuffer();
Assert.assertArrayEquals(_data, dataByteBuffer.array());
Assert.assertEquals(
logRecords.toString(), 1, logRecords.size());
logRecord = logRecords.get(0);
assertMessageStartWith(logRecord, "Dropped ownerless request ");
// Read ownerless datagram, without log
logRecords = captureHandler.resetLogLevel(Level.OFF);
requestDatagram = Datagram.createRequestDatagram(_TYPE, _data);
requestDatagram.writeTo(sinkChannel);
receiveDatagram = Datagram.createReceiveDatagram();
channelContext.setReadingDatagram(receiveDatagram);
_mockIntraband.handleReading(sourceChannel, channelContext);
Assert.assertTrue(receiveDatagram.isRequest());
Assert.assertEquals(_TYPE, receiveDatagram.getType());
dataByteBuffer = receiveDatagram.getDataByteBuffer();
Assert.assertArrayEquals(_data, dataByteBuffer.array());
Assert.assertTrue(logRecords.isEmpty());
// Read ownerless ACK response, with log
logRecords = captureHandler.resetLogLevel(Level.WARNING);
long sequenceId = 100;
Datagram ackResponseDatagram =
Datagram.createACKResponseDatagram(sequenceId);
ackResponseDatagram.writeTo(sinkChannel);
receiveDatagram = Datagram.createReceiveDatagram();
channelContext.setReadingDatagram(receiveDatagram);
_mockIntraband.handleReading(sourceChannel, channelContext);
Assert.assertTrue(receiveDatagram.isAckResponse());
Assert.assertEquals(
logRecords.toString(), 1, logRecords.size());
logRecord = logRecords.get(0);
assertMessageStartWith(
logRecord, "Dropped ownerless ACK response ");
// Ownerless ACK response, without log
logRecords = captureHandler.resetLogLevel(Level.OFF);
ackResponseDatagram = Datagram.createACKResponseDatagram(
sequenceId);
ackResponseDatagram.writeTo(sinkChannel);
receiveDatagram = Datagram.createReceiveDatagram();
channelContext.setReadingDatagram(receiveDatagram);
_mockIntraband.handleReading(sourceChannel, channelContext);
Assert.assertTrue(receiveDatagram.isAckResponse());
Assert.assertTrue(logRecords.isEmpty());
// Normal ACK response
requestDatagram = Datagram.createRequestDatagram(_TYPE, _data);
requestDatagram.setSequenceId(sequenceId);
RecordCompletionHandler<Object> recordCompletionHandler =
new RecordCompletionHandler<>();
requestDatagram.completionHandler = recordCompletionHandler;
requestDatagram.timeout = 10000;
_mockIntraband.addResponseWaitingDatagram(requestDatagram);
ackResponseDatagram = Datagram.createACKResponseDatagram(
sequenceId);
ackResponseDatagram.writeTo(sinkChannel);
receiveDatagram = Datagram.createReceiveDatagram();
channelContext.setReadingDatagram(receiveDatagram);
_mockIntraband.handleReading(sourceChannel, channelContext);
recordCompletionHandler.waitUntilDelivered();
Assert.assertTrue(receiveDatagram.isAckResponse());
// Ownerless response, with log
logRecords = captureHandler.resetLogLevel(Level.WARNING);
Datagram responseDatagram = Datagram.createResponseDatagram(
requestDatagram, _data);
responseDatagram.writeTo(sinkChannel);
receiveDatagram = Datagram.createReceiveDatagram();
channelContext.setReadingDatagram(receiveDatagram);
_mockIntraband.handleReading(sourceChannel, channelContext);
Assert.assertTrue(receiveDatagram.isResponse());
Assert.assertEquals(0, receiveDatagram.getType());
dataByteBuffer = receiveDatagram.getDataByteBuffer();
Assert.assertArrayEquals(_data, dataByteBuffer.array());
Assert.assertEquals(
logRecords.toString(), 1, logRecords.size());
logRecord = logRecords.get(0);
assertMessageStartWith(
logRecord, "Dropped ownerless response ");
// Ownerless response, without log
logRecords = captureHandler.resetLogLevel(Level.OFF);
responseDatagram = Datagram.createResponseDatagram(
requestDatagram, _data);
responseDatagram.writeTo(sinkChannel);
receiveDatagram = Datagram.createReceiveDatagram();
channelContext.setReadingDatagram(receiveDatagram);
_mockIntraband.handleReading(sourceChannel, channelContext);
Assert.assertTrue(receiveDatagram.isResponse());
Assert.assertEquals(0, receiveDatagram.getType());
dataByteBuffer = receiveDatagram.getDataByteBuffer();
Assert.assertArrayEquals(_data, dataByteBuffer.array());
Assert.assertTrue(logRecords.isEmpty());
// Reply response
requestDatagram = Datagram.createRequestDatagram(_TYPE, _data);
requestDatagram.setSequenceId(sequenceId);
recordCompletionHandler = new RecordCompletionHandler<>();
requestDatagram.completionHandler = recordCompletionHandler;
requestDatagram.completionTypes =
BaseIntraband.REPLIED_ENUM_SET;
requestDatagram.timeout = 10000;
_mockIntraband.addResponseWaitingDatagram(requestDatagram);
responseDatagram = Datagram.createResponseDatagram(
requestDatagram, _data);
responseDatagram.writeTo(sinkChannel);
receiveDatagram = Datagram.createReceiveDatagram();
channelContext.setReadingDatagram(receiveDatagram);
_mockIntraband.handleReading(sourceChannel, channelContext);
recordCompletionHandler.waitUntilReplied();
Assert.assertTrue(receiveDatagram.isResponse());
Assert.assertEquals(0, receiveDatagram.getType());
dataByteBuffer = receiveDatagram.getDataByteBuffer();
Assert.assertArrayEquals(_data, dataByteBuffer.array());
// Unconcerned response, with log
logRecords = captureHandler.resetLogLevel(Level.WARNING);
requestDatagram = Datagram.createRequestDatagram(_TYPE, _data);
requestDatagram.setSequenceId(sequenceId);
recordCompletionHandler = new RecordCompletionHandler<>();
requestDatagram.completionHandler = recordCompletionHandler;
requestDatagram.completionTypes = EnumSet.noneOf(
CompletionType.class);
requestDatagram.timeout = 10000;
_mockIntraband.addResponseWaitingDatagram(requestDatagram);
responseDatagram = Datagram.createResponseDatagram(
requestDatagram, _data);
responseDatagram.writeTo(sinkChannel);
receiveDatagram = Datagram.createReceiveDatagram();
channelContext.setReadingDatagram(receiveDatagram);
_mockIntraband.handleReading(sourceChannel, channelContext);
Assert.assertTrue(receiveDatagram.isResponse());
Assert.assertEquals(0, receiveDatagram.getType());
dataByteBuffer = receiveDatagram.getDataByteBuffer();
Assert.assertArrayEquals(_data, dataByteBuffer.array());
Assert.assertEquals(
logRecords.toString(), 1, logRecords.size());
logRecord = logRecords.get(0);
assertMessageStartWith(
logRecord, "Dropped unconcerned response ");
// Unconcerned response, without log
logRecords = captureHandler.resetLogLevel(Level.OFF);
requestDatagram = Datagram.createRequestDatagram(_TYPE, _data);
requestDatagram.setSequenceId(sequenceId);
recordCompletionHandler = new RecordCompletionHandler<>();
requestDatagram.completionHandler = recordCompletionHandler;
requestDatagram.completionTypes = EnumSet.noneOf(
CompletionType.class);
requestDatagram.timeout = 10000;
_mockIntraband.addResponseWaitingDatagram(requestDatagram);
responseDatagram = Datagram.createResponseDatagram(
requestDatagram, _data);
responseDatagram.writeTo(sinkChannel);
receiveDatagram = Datagram.createReceiveDatagram();
channelContext.setReadingDatagram(receiveDatagram);
_mockIntraband.handleReading(sourceChannel, channelContext);
Assert.assertTrue(receiveDatagram.isResponse());
Assert.assertEquals(0, receiveDatagram.getType());
dataByteBuffer = receiveDatagram.getDataByteBuffer();
Assert.assertArrayEquals(_data, dataByteBuffer.array());
Assert.assertTrue(logRecords.isEmpty());
// Ownerless request with ACK requirement, with log
logRecords = captureHandler.resetLogLevel(Level.WARNING);
requestDatagram = Datagram.createRequestDatagram(_TYPE, _data);
requestDatagram.setAckRequest(true);
requestDatagram.setSequenceId(sequenceId);
requestDatagram.writeTo(sinkChannel);
channelContext = new ChannelContext(null);
receiveDatagram = Datagram.createReceiveDatagram();
channelContext.setReadingDatagram(receiveDatagram);
mockRegistrationReference = new MockRegistrationReference(
_mockIntraband);
channelContext.setRegistrationReference(
mockRegistrationReference);
_mockIntraband.handleReading(sourceChannel, channelContext);
Assert.assertTrue(receiveDatagram.isAckRequest());
Assert.assertTrue(receiveDatagram.isRequest());
Assert.assertEquals(_TYPE, receiveDatagram.getType());
dataByteBuffer = receiveDatagram.getDataByteBuffer();
Assert.assertArrayEquals(_data, dataByteBuffer.array());
Assert.assertEquals(
logRecords.toString(), 1, logRecords.size());
logRecord = logRecords.get(0);
assertMessageStartWith(logRecord, "Dropped ownerless request ");
Assert.assertSame(
mockRegistrationReference,
_mockIntraband.getRegistrationReference());
Datagram datagram = _mockIntraband.getDatagram();
Assert.assertEquals(sequenceId, datagram.getSequenceId());
Assert.assertTrue(datagram.isAckResponse());
// Request dispatching with failure
logRecords = captureHandler.resetLogLevel(Level.SEVERE);
RecordDatagramReceiveHandler recordDatagramReceiveHandler =
new RecordDatagramReceiveHandler();
_mockIntraband.registerDatagramReceiveHandler(
_TYPE, recordDatagramReceiveHandler);
requestDatagram = Datagram.createRequestDatagram(_TYPE, _data);
requestDatagram.setAckRequest(true);
requestDatagram.setSequenceId(sequenceId);
recordCompletionHandler = new RecordCompletionHandler<>();
requestDatagram.completionHandler = recordCompletionHandler;
requestDatagram.timeout = 10000;
_mockIntraband.addResponseWaitingDatagram(requestDatagram);
requestDatagram.writeTo(sinkChannel);
receiveDatagram = Datagram.createReceiveDatagram();
channelContext.setReadingDatagram(receiveDatagram);
_mockIntraband.handleReading(sourceChannel, channelContext);
Assert.assertTrue(receiveDatagram.isRequest());
Assert.assertEquals(_TYPE, receiveDatagram.getType());
dataByteBuffer = receiveDatagram.getDataByteBuffer();
Assert.assertArrayEquals(_data, dataByteBuffer.array());
Datagram recordDatagram =
recordDatagramReceiveHandler.getReceiveDatagram();
Assert.assertSame(receiveDatagram, recordDatagram);
Assert.assertEquals(_TYPE, recordDatagram.getType());
dataByteBuffer = recordDatagram.getDataByteBuffer();
Assert.assertArrayEquals(_data, dataByteBuffer.array());
Assert.assertEquals(
logRecords.toString(), 1, logRecords.size());
logRecord = logRecords.get(0);
assertMessageStartWith(logRecord, "Unable to dispatch");
Assert.assertTrue(
logRecord.getThrown() instanceof RuntimeException);
// Request dispatching without failure
logRecords = captureHandler.resetLogLevel(Level.SEVERE);
recordDatagramReceiveHandler = new RecordDatagramReceiveHandler(
false);
_mockIntraband.registerDatagramReceiveHandler(
_TYPE, recordDatagramReceiveHandler);
requestDatagram = Datagram.createRequestDatagram(_TYPE, _data);
requestDatagram.setAckRequest(true);
requestDatagram.setSequenceId(sequenceId);
recordCompletionHandler = new RecordCompletionHandler<>();
requestDatagram.completionHandler = recordCompletionHandler;
requestDatagram.timeout = 10000;
_mockIntraband.addResponseWaitingDatagram(requestDatagram);
requestDatagram.writeTo(sinkChannel);
receiveDatagram = Datagram.createReceiveDatagram();
channelContext.setReadingDatagram(receiveDatagram);
_mockIntraband.handleReading(sourceChannel, channelContext);
Assert.assertTrue(receiveDatagram.isRequest());
Assert.assertEquals(_TYPE, receiveDatagram.getType());
dataByteBuffer = receiveDatagram.getDataByteBuffer();
Assert.assertArrayEquals(_data, dataByteBuffer.array());
recordDatagram =
recordDatagramReceiveHandler.getReceiveDatagram();
Assert.assertSame(receiveDatagram, recordDatagram);
Assert.assertEquals(_TYPE, recordDatagram.getType());
dataByteBuffer = recordDatagram.getDataByteBuffer();
Assert.assertArrayEquals(_data, dataByteBuffer.array());
Assert.assertTrue(logRecords.isEmpty());
}
}
}
@Test
public void testHandleWriting() throws Exception {
ChannelContext channelContext = null;
Datagram requestDatagram = null;
RecordCompletionHandler<Object> recordCompletionHandler = null;
try (CaptureHandler captureHandler =
JDKLoggerTestUtil.configureJDKLogger(
BaseIntraband.class.getName(), Level.FINE)) {
// IOException, new send datagram, debug log
List<LogRecord> logRecords = captureHandler.getLogRecords();
channelContext = new ChannelContext(new LinkedList<Datagram>());
MockRegistrationReference mockRegistrationReference =
new MockRegistrationReference(_mockIntraband);
channelContext.setRegistrationReference(mockRegistrationReference);
channelContext.setWritingDatagram(
Datagram.createRequestDatagram(_TYPE, _data));
Assert.assertFalse(
_mockIntraband.handleWriting(
new MockGatheringByteChannel(), channelContext));
Assert.assertFalse(mockRegistrationReference.isValid());
Assert.assertEquals(logRecords.toString(), 1, logRecords.size());
LogRecord logRecord = logRecords.get(0);
assertMessageStartWith(
logRecord, "Broken write channel, unregister ");
Assert.assertTrue(logRecord.getThrown() instanceof IOException);
// IOException, new send datagram, info log
logRecords = captureHandler.resetLogLevel(Level.INFO);
channelContext = new ChannelContext(new LinkedList<Datagram>());
mockRegistrationReference = new MockRegistrationReference(
_mockIntraband);
channelContext.setRegistrationReference(mockRegistrationReference);
channelContext.setWritingDatagram(
Datagram.createRequestDatagram(_TYPE, _data));
Assert.assertFalse(
_mockIntraband.handleWriting(
new MockGatheringByteChannel(), channelContext));
Assert.assertFalse(mockRegistrationReference.isValid());
Assert.assertEquals(logRecords.toString(), 1, logRecords.size());
logRecord = logRecords.get(0);
assertMessageStartWith(
logRecord, "Broken write channel, unregister ");
Assert.assertNull(logRecord.getThrown());
// IOException, exist send datagram, with CompletionHandler,
// without log
logRecords = captureHandler.resetLogLevel(Level.OFF);
channelContext = new ChannelContext(null);
mockRegistrationReference = new MockRegistrationReference(
_mockIntraband);
channelContext.setRegistrationReference(mockRegistrationReference);
requestDatagram = Datagram.createRequestDatagram(_TYPE, _data);
recordCompletionHandler = new RecordCompletionHandler<>();
requestDatagram.completionHandler = recordCompletionHandler;
channelContext.setWritingDatagram(requestDatagram);
Assert.assertFalse(
_mockIntraband.handleWriting(
new MockGatheringByteChannel(), channelContext));
Assert.assertFalse(mockRegistrationReference.isValid());
recordCompletionHandler.waitUntilFailed();
Assert.assertNotNull(recordCompletionHandler.getIOException());
Assert.assertTrue(logRecords.isEmpty());
}
// Huge datagram write
Pipe pipe = Pipe.open();
Queue<Datagram> sendingQueue = new LinkedList<>();
try (SourceChannel sourceChannel = pipe.source();
SinkChannel sinkChannel = pipe.sink()) {
sourceChannel.configureBlocking(false);
sinkChannel.configureBlocking(false);
int bufferSize = 1024 * 1024 * 10;
ByteBuffer sendByteBuffer = ByteBuffer.allocate(bufferSize);
ByteBuffer receiveByteBuffer = ByteBuffer.allocate(bufferSize + 14);
channelContext = new ChannelContext(new LinkedList<Datagram>());
channelContext.setWritingDatagram(
Datagram.createRequestDatagram(_TYPE, sendByteBuffer));
int count = 0;
while (!_mockIntraband.handleWriting(sinkChannel, channelContext)) {
count++;
sourceChannel.read(receiveByteBuffer);
Assert.assertTrue(sendByteBuffer.hasRemaining());
}
sourceChannel.read(receiveByteBuffer);
Assert.assertFalse(sendByteBuffer.hasRemaining());
Assert.assertFalse(receiveByteBuffer.hasRemaining());
Assert.assertTrue(count > 0);
sourceChannel.configureBlocking(true);
sinkChannel.configureBlocking(true);
// Submitted callback
channelContext = new ChannelContext(new LinkedList<Datagram>());
requestDatagram = Datagram.createRequestDatagram(_TYPE, _data);
Object attachment = new Object();
requestDatagram.attachment = attachment;
recordCompletionHandler = new RecordCompletionHandler<>();
requestDatagram.completionHandler = recordCompletionHandler;
requestDatagram.completionTypes = EnumSet.of(
CompletionType.SUBMITTED);
channelContext.setWritingDatagram(requestDatagram);
Assert.assertTrue(
_mockIntraband.handleWriting(sinkChannel, channelContext));
recordCompletionHandler.waitUntilSubmitted();
Assert.assertSame(
attachment, recordCompletionHandler.getAttachment());
// Replied callback
channelContext = new ChannelContext(sendingQueue);
requestDatagram = Datagram.createRequestDatagram(_TYPE, _data);
requestDatagram.completionTypes = EnumSet.of(
CompletionType.REPLIED);
channelContext.setWritingDatagram(requestDatagram);
Assert.assertTrue(
_mockIntraband.handleWriting(sinkChannel, channelContext));
Assert.assertNull(requestDatagram.getDataByteBuffer());
String requestDatagramString = requestDatagram.toString();
Assert.assertTrue(requestDatagramString.contains("dataChunk=null"));
}
Assert.assertSame(sendingQueue, channelContext.getSendingQueue());
}
@Test
public void testResponseWaiting() throws InterruptedException {
// Add
Datagram requestDatagram = Datagram.createRequestDatagram(_TYPE, _data);
long sequenceId = 100;
requestDatagram.setSequenceId(sequenceId);
requestDatagram.timeout = 10000;
_mockIntraband.addResponseWaitingDatagram(requestDatagram);
Map<Long, Datagram> responseWaitingMap =
_mockIntraband.responseWaitingMap;
Assert.assertEquals(
responseWaitingMap.toString(), 1, responseWaitingMap.size());
Assert.assertSame(requestDatagram, responseWaitingMap.get(sequenceId));
Map<Long, Long> timeoutMap = _mockIntraband.timeoutMap;
Collection<Long> timeoutSequenceIds = timeoutMap.values();
Assert.assertEquals(
timeoutSequenceIds.toString(), 1, timeoutSequenceIds.size());
Assert.assertTrue(timeoutSequenceIds.contains(sequenceId));
// Remove, hit
Datagram responseDatagram = Datagram.createResponseDatagram(
requestDatagram, _data);
Assert.assertFalse(responseDatagram.isRequest());
_mockIntraband.removeResponseWaitingDatagram(responseDatagram);
Assert.assertTrue(responseWaitingMap.isEmpty());
Assert.assertTrue(timeoutSequenceIds.isEmpty());
// Remove, miss
_mockIntraband.removeResponseWaitingDatagram(responseDatagram);
Assert.assertTrue(responseWaitingMap.isEmpty());
Assert.assertTrue(timeoutSequenceIds.isEmpty());
try (CaptureHandler captureHandler =
JDKLoggerTestUtil.configureJDKLogger(
BaseIntraband.class.getName(), Level.WARNING)) {
// Clean up timeout, hit, with log
List<LogRecord> logRecords = captureHandler.getLogRecords();
Datagram requestDatagram1 = Datagram.createRequestDatagram(
_TYPE, _data);
requestDatagram1.setSequenceId(sequenceId);
RecordCompletionHandler<Object> recordCompletionHandler1 =
new RecordCompletionHandler<>();
requestDatagram1.completionHandler = recordCompletionHandler1;
requestDatagram1.timeout = 1;
_mockIntraband.addResponseWaitingDatagram(requestDatagram1);
Thread.sleep(10);
Datagram requestDatagram2 = Datagram.createRequestDatagram(
_TYPE, _data);
requestDatagram2.setSequenceId(sequenceId + 1);
RecordCompletionHandler<Object> recordCompletionHandler2 =
new RecordCompletionHandler<>();
requestDatagram2.completionHandler = recordCompletionHandler2;
requestDatagram2.timeout = 1;
_mockIntraband.addResponseWaitingDatagram(requestDatagram2);
Assert.assertEquals(
responseWaitingMap.toString(), 2, responseWaitingMap.size());
Assert.assertSame(
requestDatagram1, responseWaitingMap.get(sequenceId));
Assert.assertSame(
requestDatagram2, responseWaitingMap.get(sequenceId + 1));
Assert.assertEquals(
timeoutSequenceIds.toString(), 2, timeoutSequenceIds.size());
Assert.assertTrue(timeoutSequenceIds.contains(sequenceId));
Assert.assertTrue(timeoutSequenceIds.contains(sequenceId + 1));
Thread.sleep(10);
_mockIntraband.cleanUpTimeoutResponseWaitingDatagrams();
Assert.assertEquals(logRecords.toString(), 2, logRecords.size());
assertMessageStartWith(
logRecords.get(0),
"Removed timeout response waiting datagram ");
assertMessageStartWith(
logRecords.get(1),
"Removed timeout response waiting datagram ");
recordCompletionHandler1.waitUntilTimeouted();
recordCompletionHandler2.waitUntilTimeouted();
// Clean up timeout, hit, without log
logRecords = captureHandler.resetLogLevel(Level.OFF);
requestDatagram1 = Datagram.createRequestDatagram(_TYPE, _data);
requestDatagram1.setSequenceId(sequenceId);
recordCompletionHandler1 = new RecordCompletionHandler<>();
requestDatagram1.completionHandler = recordCompletionHandler1;
requestDatagram1.timeout = 1;
_mockIntraband.addResponseWaitingDatagram(requestDatagram1);
Thread.sleep(10);
requestDatagram2 = Datagram.createRequestDatagram(_TYPE, _data);
requestDatagram2.setSequenceId(sequenceId + 1);
recordCompletionHandler2 = new RecordCompletionHandler<>();
requestDatagram2.completionHandler = recordCompletionHandler2;
requestDatagram2.timeout = 1;
_mockIntraband.addResponseWaitingDatagram(requestDatagram2);
Assert.assertEquals(
responseWaitingMap.toString(), 2, responseWaitingMap.size());
Assert.assertSame(
requestDatagram1, responseWaitingMap.get(sequenceId));
Assert.assertSame(
requestDatagram2, responseWaitingMap.get(sequenceId + 1));
Assert.assertEquals(
timeoutSequenceIds.toString(), 2, timeoutSequenceIds.size());
Assert.assertTrue(timeoutSequenceIds.contains(sequenceId));
Assert.assertTrue(timeoutSequenceIds.contains(sequenceId + 1));
Thread.sleep(10);
_mockIntraband.cleanUpTimeoutResponseWaitingDatagrams();
Assert.assertTrue(logRecords.isEmpty());
recordCompletionHandler1.waitUntilTimeouted();
recordCompletionHandler2.waitUntilTimeouted();
// Clean up timeout, miss
_mockIntraband.cleanUpTimeoutResponseWaitingDatagrams();
}
}
@Test
public void testSendDatagramWithCallback() {
// Registration reference is null
try {
_mockIntraband.sendDatagram(null, null, null, null, null);
Assert.fail();
}
catch (NullPointerException npe) {
Assert.assertEquals(
"Registration reference is null", npe.getMessage());
}
// Registration reference is invalid
try {
RegistrationReference registrationReference =
new MockRegistrationReference(_mockIntraband);
registrationReference.cancelRegistration();
_mockIntraband.sendDatagram(
registrationReference, null, null, null, null);
Assert.fail();
}
catch (IllegalArgumentException iae) {
Assert.assertEquals(
"Registration reference is invalid", iae.getMessage());
}
// Datagram is null
try {
_mockIntraband.sendDatagram(
new MockRegistrationReference(_mockIntraband), null, null, null,
null);
Assert.fail();
}
catch (NullPointerException npe) {
Assert.assertEquals("Datagram is null", npe.getMessage());
}
// Completion type set is null
try {
_mockIntraband.sendDatagram(
new MockRegistrationReference(_mockIntraband),
Datagram.createRequestDatagram(_TYPE, _data), null, null, null);
Assert.fail();
}
catch (NullPointerException npe) {
Assert.assertEquals(
"Completion type set is null", npe.getMessage());
}
// Completion type set is empty
try {
_mockIntraband.sendDatagram(
new MockRegistrationReference(_mockIntraband),
Datagram.createRequestDatagram(_TYPE, _data), null,
EnumSet.noneOf(CompletionHandler.CompletionType.class), null);
Assert.fail();
}
catch (IllegalArgumentException iae) {
Assert.assertEquals(
"Completion type set is empty", iae.getMessage());
}
// Complete handler is null
try {
_mockIntraband.sendDatagram(
new MockRegistrationReference(_mockIntraband),
Datagram.createRequestDatagram(_TYPE, _data), null,
EnumSet.of(CompletionHandler.CompletionType.SUBMITTED), null);
Assert.fail();
}
catch (NullPointerException npe) {
Assert.assertEquals("Complete handler is null", npe.getMessage());
}
// Time unit is null
try {
_mockIntraband.sendDatagram(
new MockRegistrationReference(_mockIntraband),
Datagram.createRequestDatagram(_TYPE, _data), null,
EnumSet.of(CompletionHandler.CompletionType.SUBMITTED),
new RecordCompletionHandler<Object>(), 1000, null);
Assert.fail();
}
catch (NullPointerException npe) {
Assert.assertEquals("Time unit is null", npe.getMessage());
}
// Nonpositive timeout
Datagram requestDatagram = Datagram.createRequestDatagram(_TYPE, _data);
_mockIntraband.sendDatagram(
new MockRegistrationReference(_mockIntraband), requestDatagram,
null, EnumSet.of(CompletionHandler.CompletionType.DELIVERED),
new RecordCompletionHandler<Object>(), 0, TimeUnit.MILLISECONDS);
Datagram sentDatagram = _mockIntraband.getDatagram();
Assert.assertEquals(_DEFAULT_TIMEOUT, sentDatagram.timeout);
Map<Long, Datagram> responseWaitingMap =
_mockIntraband.responseWaitingMap;
Assert.assertEquals(
responseWaitingMap.toString(), 1, responseWaitingMap.size());
Assert.assertSame(
requestDatagram,
responseWaitingMap.remove(requestDatagram.getSequenceId()));
Map<Long, Long> timeoutMap = _mockIntraband.timeoutMap;
Collection<Long> timeoutSequenceIds = timeoutMap.values();
Assert.assertEquals(
timeoutSequenceIds.toString(), 1, timeoutSequenceIds.size());
Assert.assertTrue(
timeoutSequenceIds.remove(requestDatagram.getSequenceId()));
// Covert timeout
_mockIntraband.sendDatagram(
new MockRegistrationReference(_mockIntraband), requestDatagram,
null, EnumSet.of(CompletionHandler.CompletionType.REPLIED),
new RecordCompletionHandler<Object>(), 2, TimeUnit.SECONDS);
sentDatagram = _mockIntraband.getDatagram();
Assert.assertEquals(2000, sentDatagram.timeout);
Assert.assertEquals(
responseWaitingMap.toString(), 1, responseWaitingMap.size());
Assert.assertSame(
requestDatagram,
responseWaitingMap.remove(requestDatagram.getSequenceId()));
Assert.assertEquals(
timeoutSequenceIds.toString(), 1, timeoutSequenceIds.size());
Assert.assertTrue(
timeoutSequenceIds.remove(requestDatagram.getSequenceId()));
// Default timeout
_mockIntraband.sendDatagram(
new MockRegistrationReference(_mockIntraband), requestDatagram,
null, EnumSet.of(CompletionHandler.CompletionType.SUBMITTED),
new RecordCompletionHandler<Object>());
sentDatagram = _mockIntraband.getDatagram();
Assert.assertEquals(_DEFAULT_TIMEOUT, sentDatagram.timeout);
}
@Test
public void testSendDatagramWithoutCallback() {
// Registration reference is null
try {
_mockIntraband.sendDatagram(null, null);
Assert.fail();
}
catch (NullPointerException npe) {
Assert.assertEquals(
"Registration reference is null", npe.getMessage());
}
// Registration reference is invalid
try {
RegistrationReference registrationReference =
new MockRegistrationReference(_mockIntraband);
registrationReference.cancelRegistration();
_mockIntraband.sendDatagram(registrationReference, null);
Assert.fail();
}
catch (IllegalArgumentException iae) {
Assert.assertEquals(
"Registration reference is invalid", iae.getMessage());
}
// Datagram is null
try {
_mockIntraband.sendDatagram(
new MockRegistrationReference(_mockIntraband), null);
Assert.fail();
}
catch (NullPointerException npe) {
Assert.assertEquals("Datagram is null", npe.getMessage());
}
// Normal send
Datagram datagram = Datagram.createRequestDatagram(_TYPE, _data);
RegistrationReference registrationReference =
new MockRegistrationReference(_mockIntraband);
_mockIntraband.sendDatagram(registrationReference, datagram);
Assert.assertSame(
registrationReference, _mockIntraband.getRegistrationReference());
Assert.assertSame(datagram, _mockIntraband.getDatagram());
}
@Test
public void testSendSyncDatagram() throws Exception {
// Registration reference is null
try {
_mockIntraband.sendSyncDatagram(null, null);
Assert.fail();
}
catch (NullPointerException npe) {
Assert.assertEquals(
"Registration reference is null", npe.getMessage());
}
// Registration reference is invalid
try {
RegistrationReference registrationReference =
new MockRegistrationReference(_mockIntraband);
registrationReference.cancelRegistration();
_mockIntraband.sendSyncDatagram(registrationReference, null);
Assert.fail();
}
catch (IllegalArgumentException iae) {
Assert.assertEquals(
"Registration reference is invalid", iae.getMessage());
}
// Datagram is null
try {
_mockIntraband.sendSyncDatagram(
new MockRegistrationReference(_mockIntraband), null);
Assert.fail();
}
catch (NullPointerException npe) {
Assert.assertEquals("Datagram is null", npe.getMessage());
}
// Time unit is null
try {
_mockIntraband.sendSyncDatagram(
new MockRegistrationReference(_mockIntraband),
Datagram.createRequestDatagram(_TYPE, _data), 1000, null);
Assert.fail();
}
catch (NullPointerException npe) {
Assert.assertEquals("Time unit is null", npe.getMessage());
}
// Nonpositive timeout
try {
_mockIntraband.sendSyncDatagram(
new MockRegistrationReference(_mockIntraband),
Datagram.createRequestDatagram(_TYPE, _data), 0,
TimeUnit.MILLISECONDS);
Assert.fail();
}
catch (TimeoutException te) {
Assert.assertEquals("Result waiting timeout", te.getMessage());
}
Assert.assertEquals(
_DEFAULT_TIMEOUT, _mockIntraband.getDatagram().timeout);
// Covert timeout
Datagram requestDatagram = Datagram.createRequestDatagram(_TYPE, _data);
try {
_mockIntraband.sendSyncDatagram(
new MockRegistrationReference(_mockIntraband), requestDatagram,
2, TimeUnit.SECONDS);
Assert.fail();
}
catch (TimeoutException te) {
Assert.assertEquals("Result waiting timeout", te.getMessage());
}
Assert.assertEquals(2000, requestDatagram.timeout);
// Datagram writing IOException
IOException ioException = new IOException();
_mockIntraband.setIOException(ioException);
try {
_mockIntraband.sendSyncDatagram(
new MockRegistrationReference(_mockIntraband),
Datagram.createRequestDatagram(_TYPE, _data));
Assert.fail();
}
catch (IOException ioe) {
Assert.assertSame(ioException, ioe);
}
finally {
_mockIntraband.setIOException(null);
}
// Replied
final Datagram expectedDatagram = Datagram.createResponseDatagram(
requestDatagram, _data);
Intraband intraband = new MockIntraband(_DEFAULT_TIMEOUT) {
@Override
protected Datagram processDatagram(Datagram datagram) {
return expectedDatagram;
}
};
Datagram responseDatagram = intraband.sendSyncDatagram(
new MockRegistrationReference(_mockIntraband), requestDatagram);
Assert.assertSame(expectedDatagram, responseDatagram);
SendSyncDatagramCompletionHandler sendSyncDatagramCompletionHandler =
new SendSyncDatagramCompletionHandler();
sendSyncDatagramCompletionHandler.delivered(null);
sendSyncDatagramCompletionHandler.submitted(null);
sendSyncDatagramCompletionHandler.timedOut(null);
}
protected void assertMessageStartWith(
LogRecord logRecord, String messagePrefix) {
String message = logRecord.getMessage();
Assert.assertTrue(message.startsWith(messagePrefix));
}
private static final String _DATA_STRING =
BaseIntrabandTest.class.getName();
private static final long _DEFAULT_TIMEOUT = Time.SECOND;
private static final byte _TYPE = 1;
private final byte[] _data = _DATA_STRING.getBytes(
Charset.defaultCharset());
private final MockIntraband _mockIntraband = new MockIntraband(
_DEFAULT_TIMEOUT);
private static class MockGatheringByteChannel
implements GatheringByteChannel {
@Override
public void close() throws IOException {
throw new IOException();
}
@Override
public boolean isOpen() {
return true;
}
@Override
public int write(ByteBuffer byteBuffer) throws IOException {
throw new IOException();
}
@Override
public long write(ByteBuffer[] byteBuffers) throws IOException {
throw new IOException();
}
@Override
public long write(ByteBuffer[] byteBuffers, int offset, int length)
throws IOException {
throw new IOException();
}
}
private static class MockScatteringByteChannel
implements ScatteringByteChannel {
public MockScatteringByteChannel(boolean eofOnDataBufferReading) {
_eofOnDataBufferReading = eofOnDataBufferReading;
}
@Override
public void close() throws IOException {
throw new IOException();
}
@Override
public boolean isOpen() {
return true;
}
@Override
public int read(ByteBuffer byteBuffer) {
if (_eofOnDataBufferReading && (byteBuffer.capacity() == 14)) {
BigEndianCodec.putInt(byteBuffer.array(), 10, 1);
byteBuffer.position(byteBuffer.limit());
return 14;
}
else {
return -1;
}
}
@Override
public long read(ByteBuffer[] byteBuffers) {
return -1;
}
@Override
public long read(ByteBuffer[] byteBuffers, int offset, int length) {
return -1;
}
private final boolean _eofOnDataBufferReading;
}
}