/**
* 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.nonblocking;
import com.liferay.portal.kernel.nio.intraband.BaseIntraband;
import com.liferay.portal.kernel.nio.intraband.BaseIntrabandHelper;
import com.liferay.portal.kernel.nio.intraband.ChannelContext;
import com.liferay.portal.kernel.nio.intraband.ClosedIntrabandException;
import com.liferay.portal.kernel.nio.intraband.CompletionHandler.CompletionType;
import com.liferay.portal.kernel.nio.intraband.Datagram;
import com.liferay.portal.kernel.nio.intraband.DatagramHelper;
import com.liferay.portal.kernel.nio.intraband.IntrabandTestUtil;
import com.liferay.portal.kernel.nio.intraband.RecordCompletionHandler;
import com.liferay.portal.kernel.nio.intraband.RecordDatagramReceiveHandler;
import com.liferay.portal.kernel.nio.intraband.RegistrationReference;
import com.liferay.portal.kernel.test.CaptureHandler;
import com.liferay.portal.kernel.test.JDKLoggerTestUtil;
import com.liferay.portal.kernel.test.rule.AggregateTestRule;
import com.liferay.portal.kernel.test.rule.CodeCoverageAssertor;
import com.liferay.portal.kernel.test.rule.NewEnv;
import com.liferay.portal.kernel.util.Time;
import com.liferay.portal.test.rule.AdviseWith;
import com.liferay.portal.test.rule.AspectJNewEnvTestRule;
import java.io.IOException;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.Channel;
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.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.channels.spi.SelectorProvider;
import java.nio.charset.Charset;
import java.util.EnumSet;
import java.util.List;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import org.aspectj.lang.annotation.Aspect;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
/**
* @author Shuyang Zhou
*/
public class SelectorIntrabandTest {
@ClassRule
@Rule
public static final AggregateTestRule aggregateTestRule =
new AggregateTestRule(
new CodeCoverageAssertor() {
@Override
public void appendAssertClasses(List<Class<?>> assertClasses) {
assertClasses.add(SelectionKeyRegistrationReference.class);
}
},
AspectJNewEnvTestRule.INSTANCE);
@Before
public void setUp() throws Exception {
_selectorIntraband = new SelectorIntraband(_DEFAULT_TIMEOUT);
}
@After
public void tearDown() throws Exception {
_selectorIntraband.close();
}
@Test
public void testCreateAndDestroy() throws Exception {
try (CaptureHandler captureHandler =
JDKLoggerTestUtil.configureJDKLogger(
SelectorIntraband.class.getName(), Level.INFO)) {
// Close selector, with log
List<LogRecord> logRecords = captureHandler.getLogRecords();
Thread wakeUpThread = new Thread(
new WakeUpRunnable(_selectorIntraband));
wakeUpThread.start();
Thread pollingThread = _selectorIntraband.pollingThread;
Selector selector = _selectorIntraband.selector;
synchronized (selector) {
wakeUpThread.interrupt();
wakeUpThread.join();
while (pollingThread.getState() != Thread.State.BLOCKED);
selector.close();
}
pollingThread.join();
Assert.assertEquals(logRecords.toString(), 1, logRecords.size());
String pollingThreadName = pollingThread.getName();
LogRecord logRecord = logRecords.get(0);
Assert.assertEquals(
pollingThreadName.concat(
" exiting gracefully on selector closure"),
logRecord.getMessage());
// Close selector, without log
_selectorIntraband = new SelectorIntraband(_DEFAULT_TIMEOUT);
logRecords = captureHandler.resetLogLevel(Level.OFF);
wakeUpThread = new Thread(new WakeUpRunnable(_selectorIntraband));
wakeUpThread.start();
pollingThread = _selectorIntraband.pollingThread;
selector = _selectorIntraband.selector;
synchronized (selector) {
wakeUpThread.interrupt();
wakeUpThread.join();
while (pollingThread.getState() != Thread.State.BLOCKED);
selector.close();
}
pollingThread.join();
Assert.assertTrue(logRecords.isEmpty());
}
}
@AdviseWith(adviceClasses = {Jdk14LogImplAdvice.class})
@NewEnv(type = NewEnv.Type.CLASSLOADER)
@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;
try (CaptureHandler captureHandler =
JDKLoggerTestUtil.configureJDKLogger(
BaseIntraband.class.getName(), Level.WARNING)) {
// Receive ACK response, no ACK request, with log
List<LogRecord> logRecords = captureHandler.getLogRecords();
Jdk14LogImplAdvice.reset();
try {
DatagramHelper.writeTo(
DatagramHelper.createACKResponseDatagram(sequenceId),
gatheringByteChannel);
}
finally {
Jdk14LogImplAdvice.waitUntilWarnCalled();
}
Assert.assertEquals(logRecords.toString(), 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<>();
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(logRecords.toString(), 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<>();
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<>();
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(logRecords.toString(), 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<>();
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(logRecords.toString(), 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(logRecords.toString(), 1, logRecords.size());
IntrabandTestUtil.assertMessageStartWith(
logRecords.get(0), "Unable to dispatch");
_unregisterChannels(registrationReference);
gatheringByteChannel.close();
scatteringByteChannel.close();
}
}
@Test
public void testRegisterChannelDuplex() throws Exception {
// Channel is null
try {
_selectorIntraband.registerChannel(null);
Assert.fail();
}
catch (NullPointerException npe) {
Assert.assertEquals("Channel is null", npe.getMessage());
}
// Channel is not of type GatheringByteChannel
try {
_selectorIntraband.registerChannel(
IntrabandTestUtil.<Channel>createProxy(Channel.class));
Assert.fail();
}
catch (IllegalArgumentException iae) {
Assert.assertEquals(
"Channel is not of type GatheringByteChannel",
iae.getMessage());
}
// Channel is not of type ScatteringByteChannel
try {
_selectorIntraband.registerChannel(
IntrabandTestUtil.<Channel>createProxy(
GatheringByteChannel.class));
Assert.fail();
}
catch (IllegalArgumentException iae) {
Assert.assertEquals(
"Channel is not of type ScatteringByteChannel",
iae.getMessage());
}
// Channel is not of type SelectableChannel
try {
_selectorIntraband.registerChannel(
IntrabandTestUtil.<Channel>createProxy(
ScatteringByteChannel.class, GatheringByteChannel.class));
Assert.fail();
}
catch (IllegalArgumentException iae) {
Assert.assertEquals(
"Channel is not of type SelectableChannel", iae.getMessage());
}
// Channel is not valid for reading
try {
_selectorIntraband.registerChannel(
new MockDuplexSelectableChannel(false, true));
Assert.fail();
}
catch (IllegalArgumentException iae) {
Assert.assertEquals(
"Channel is not valid for reading", iae.getMessage());
}
// Channel is not valid for writing
try {
_selectorIntraband.registerChannel(
new MockDuplexSelectableChannel(true, false));
Assert.fail();
}
catch (IllegalArgumentException iae) {
Assert.assertEquals(
"Channel is not valid for writing", iae.getMessage());
}
SocketChannel[] peerSocketChannels =
IntrabandTestUtil.createSocketChannelPeers();
try {
SocketChannel socketChannel = peerSocketChannels[0];
// Interruptted on register
final Thread mainThread = Thread.currentThread();
Thread wakeUpThread = new Thread(
new WakeUpRunnable(_selectorIntraband));
Thread interruptThread = new Thread() {
@Override
public void run() {
while (mainThread.getState() != Thread.State.WAITING);
mainThread.interrupt();
}
};
wakeUpThread.start();
Selector selector = _selectorIntraband.selector;
synchronized (selector) {
wakeUpThread.interrupt();
wakeUpThread.join();
interruptThread.start();
try {
_selectorIntraband.registerChannel(socketChannel);
Assert.fail();
}
catch (IOException ioe) {
Throwable cause = ioe.getCause();
Assert.assertTrue(cause instanceof InterruptedException);
}
interruptThread.join();
}
_selectorIntraband.close();
_selectorIntraband = new SelectorIntraband(_DEFAULT_TIMEOUT);
// Normal register
SelectionKeyRegistrationReference
selectionKeyRegistrationReference =
(SelectionKeyRegistrationReference)
_selectorIntraband.registerChannel(socketChannel);
Assert.assertNotNull(selectionKeyRegistrationReference);
Assert.assertSame(
selectionKeyRegistrationReference.readSelectionKey,
selectionKeyRegistrationReference.writeSelectionKey);
SelectionKey selectionKey =
selectionKeyRegistrationReference.readSelectionKey;
Assert.assertTrue(selectionKey.isValid());
Assert.assertEquals(
SelectionKey.OP_READ, selectionKey.interestOps());
Assert.assertNotNull(selectionKey.attachment());
selectionKey.interestOps(
SelectionKey.OP_READ | SelectionKey.OP_WRITE);
selector = _selectorIntraband.selector;
selector.wakeup();
while (selectionKey.interestOps() != SelectionKey.OP_READ);
// Concurrent cancelling
wakeUpThread = new Thread(new WakeUpRunnable(_selectorIntraband));
wakeUpThread.start();
synchronized (selector) {
wakeUpThread.interrupt();
wakeUpThread.join();
selectionKey.interestOps(
SelectionKey.OP_READ | SelectionKey.OP_WRITE);
SocketChannel peerSocketChannel = peerSocketChannels[1];
peerSocketChannel.write(ByteBuffer.allocate(1));
Socket socket = peerSocketChannel.socket();
socket.shutdownOutput();
}
while (selectionKey.isValid());
// Register after close
_selectorIntraband.close();
try {
_selectorIntraband.registerChannel(socketChannel);
Assert.fail();
}
catch (ClosedIntrabandException cie) {
}
}
finally {
peerSocketChannels[0].close();
peerSocketChannels[1].close();
}
}
@Test
public void testRegisterChannelReadWrite() throws Exception {
// Scattering byte channel is null
try {
_selectorIntraband.registerChannel(null, null);
Assert.fail();
}
catch (NullPointerException npe) {
Assert.assertEquals(
"Scattering byte channel is null", npe.getMessage());
}
// Gathering byte channel is null
try {
_selectorIntraband.registerChannel(
IntrabandTestUtil.<ScatteringByteChannel>createProxy(
ScatteringByteChannel.class),
null);
Assert.fail();
}
catch (NullPointerException npe) {
Assert.assertEquals(
"Gathering byte channel is null", npe.getMessage());
}
// Scattering byte channel is not of type SelectableChannel
try {
_selectorIntraband.registerChannel(
IntrabandTestUtil.<ScatteringByteChannel>createProxy(
ScatteringByteChannel.class),
IntrabandTestUtil.<GatheringByteChannel>createProxy(
GatheringByteChannel.class));
Assert.fail();
}
catch (IllegalArgumentException iae) {
Assert.assertEquals(
"Scattering byte channel is not of type SelectableChannel",
iae.getMessage());
}
// Gathering byte channel is not of type SelectableChannel
try {
_selectorIntraband.registerChannel(
new MockDuplexSelectableChannel(false, false),
IntrabandTestUtil.<GatheringByteChannel>createProxy(
GatheringByteChannel.class));
Assert.fail();
}
catch (IllegalArgumentException iae) {
Assert.assertEquals(
"Gathering byte channel is not of type SelectableChannel",
iae.getMessage());
}
// Scattering byte channel is not valid for reading
try {
_selectorIntraband.registerChannel(
new MockDuplexSelectableChannel(false, true),
new MockDuplexSelectableChannel(true, true));
Assert.fail();
}
catch (IllegalArgumentException iae) {
Assert.assertEquals(
"Scattering byte channel is not valid for reading",
iae.getMessage());
}
// Gathering byte channel is not valid for writing
try {
_selectorIntraband.registerChannel(
new MockDuplexSelectableChannel(true, true),
new MockDuplexSelectableChannel(true, false));
Assert.fail();
}
catch (IllegalArgumentException iae) {
Assert.assertEquals(
"Gathering byte channel is not valid for writing",
iae.getMessage());
}
// Interruptted on register
Pipe pipe = Pipe.open();
try (SourceChannel sourceChannel = pipe.source();
SinkChannel sinkChannel = pipe.sink()) {
final Thread mainThread = Thread.currentThread();
Thread wakeUpThread = new Thread(
new WakeUpRunnable(_selectorIntraband));
Thread interruptThread = new Thread() {
@Override
public void run() {
while (mainThread.getState() != Thread.State.WAITING);
mainThread.interrupt();
}
};
wakeUpThread.start();
Selector selector = _selectorIntraband.selector;
synchronized (selector) {
wakeUpThread.interrupt();
wakeUpThread.join();
interruptThread.start();
try {
_selectorIntraband.registerChannel(
sourceChannel, sinkChannel);
Assert.fail();
}
catch (IOException ioe) {
Throwable cause = ioe.getCause();
Assert.assertTrue(cause instanceof InterruptedException);
}
interruptThread.join();
}
_selectorIntraband.close();
// Normal register
_selectorIntraband = new SelectorIntraband(_DEFAULT_TIMEOUT);
SelectionKeyRegistrationReference
selectionKeyRegistrationReference =
(SelectionKeyRegistrationReference)
_selectorIntraband.registerChannel(
sourceChannel, sinkChannel);
Assert.assertNotNull(selectionKeyRegistrationReference);
SelectionKey readSelectionKey =
selectionKeyRegistrationReference.readSelectionKey;
Assert.assertTrue(readSelectionKey.isValid());
Assert.assertEquals(
SelectionKey.OP_READ, readSelectionKey.interestOps());
Assert.assertNotNull(readSelectionKey.attachment());
SelectionKey writeSelectionKey =
selectionKeyRegistrationReference.writeSelectionKey;
Assert.assertTrue(writeSelectionKey.isValid());
Assert.assertEquals(0, writeSelectionKey.interestOps());
Assert.assertNotNull(writeSelectionKey.attachment());
Assert.assertSame(
readSelectionKey.attachment(), writeSelectionKey.attachment());
writeSelectionKey.interestOps(SelectionKey.OP_WRITE);
selector = _selectorIntraband.selector;
selector.wakeup();
while (writeSelectionKey.interestOps() != 0);
_unregisterChannels(selectionKeyRegistrationReference);
// Register after close
_selectorIntraband.close();
try {
_selectorIntraband.registerChannel(sourceChannel, sinkChannel);
Assert.fail();
}
catch (ClosedIntrabandException cie) {
}
}
}
@AdviseWith(adviceClasses = {Jdk14LogImplAdvice.class})
@NewEnv(type = NewEnv.Type.CLASSLOADER)
@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<>();
_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());
try (CaptureHandler captureHandler1 =
JDKLoggerTestUtil.configureJDKLogger(
BaseIntraband.class.getName(), Level.WARNING)) {
// Callback timeout, with log
List<LogRecord> logRecords = captureHandler1.getLogRecords();
recordCompletionHandler = new RecordCompletionHandler<>();
_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(logRecords.toString(), 1, logRecords.size());
IntrabandTestUtil.assertMessageStartWith(
logRecords.get(0), "Removed timeout response waiting datagram");
// Callback timeout, without log
logRecords = captureHandler1.resetLogLevel(Level.OFF);
recordCompletionHandler = new RecordCompletionHandler<>();
_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());
}
// Callback timeout, completion handler causes NPE
try (CaptureHandler captureHandler1 =
JDKLoggerTestUtil.configureJDKLogger(
SelectorIntraband.class.getName(), Level.SEVERE)) {
List<LogRecord> logRecords1 = captureHandler1.getLogRecords();
recordCompletionHandler = new RecordCompletionHandler<Object>() {
@Override
public void timedOut(Object attachment) {
super.timedOut(attachment);
throw new NullPointerException();
}
};
Jdk14LogImplAdvice.reset();
Selector selector = _selectorIntraband.selector;
Datagram datagram = Datagram.createRequestDatagram(_TYPE, _data);
try {
_selectorIntraband.sendDatagram(
registrationReference, datagram, attachment,
EnumSet.of(CompletionType.DELIVERED),
recordCompletionHandler, 10, TimeUnit.MILLISECONDS);
}
finally {
try (CaptureHandler captureHandler2 =
JDKLoggerTestUtil.configureJDKLogger(
BaseIntraband.class.getName(), Level.WARNING)) {
recordCompletionHandler.waitUntilTimeouted(selector);
List<LogRecord> logRecords2 =
captureHandler2.getLogRecords();
Assert.assertEquals(
logRecords2.toString(), 1, logRecords2.size());
LogRecord logRecord = logRecords2.get(0);
Assert.assertEquals(
"Removed timeout response waiting datagram " + datagram,
logRecord.getMessage());
}
Jdk14LogImplAdvice.waitUntilErrorCalled();
}
Assert.assertFalse(selector.isOpen());
Assert.assertEquals(logRecords1.toString(), 1, logRecords1.size());
IntrabandTestUtil.assertMessageStartWith(
logRecords1.get(0),
SelectorIntraband.class +
".threadFactory-1 exiting exceptionally");
gatheringByteChannel.close();
scatteringByteChannel.close();
}
}
@Test
public void testSendDatagramWithoutCallback() throws Exception {
// Single datagram sending
Pipe readPipe = Pipe.open();
Pipe writePipe = Pipe.open();
try (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();
while ((writeSelectionKey.interestOps() & SelectionKey.OP_WRITE) !=
0);
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);
}
}
@Aspect
public static class Jdk14LogImplAdvice {
public static volatile CountDownLatch errorCalledCountDownLatch =
new CountDownLatch(1);
public static volatile CountDownLatch
isWarnEnabledCalledCountDownLatch = new CountDownLatch(1);
public static volatile CountDownLatch warnCalledCountDownLatch =
new CountDownLatch(1);
public static void reset() {
errorCalledCountDownLatch = new CountDownLatch(1);
isWarnEnabledCalledCountDownLatch = new CountDownLatch(1);
warnCalledCountDownLatch = new CountDownLatch(1);
}
public static void waitUntilErrorCalled() throws InterruptedException {
errorCalledCountDownLatch.await();
}
public static void waitUntilIsWarnEnableCalled()
throws InterruptedException {
isWarnEnabledCalledCountDownLatch.await();
}
public static void waitUntilWarnCalled() throws InterruptedException {
warnCalledCountDownLatch.await();
}
@org.aspectj.lang.annotation.After(
"execution(* com.liferay.portal.kernel.log.Jdk14LogImpl.error(" +
"Object, Throwable))"
)
public void error() {
errorCalledCountDownLatch.countDown();
}
@org.aspectj.lang.annotation.After(
"execution(* com.liferay.portal.kernel.log.Jdk14LogImpl." +
"isWarnEnabled())"
)
public void isWarnEnabled() {
isWarnEnabledCalledCountDownLatch.countDown();
}
@org.aspectj.lang.annotation.After(
"execution(* com.liferay.portal.kernel.log.Jdk14LogImpl.warn(" +
"Object))"
)
public void warn1() {
warnCalledCountDownLatch.countDown();
}
@org.aspectj.lang.annotation.After(
"execution(* com.liferay.portal.kernel.log.Jdk14LogImpl.warn(" +
"Object, Throwable))"
)
public void warn2() {
warnCalledCountDownLatch.countDown();
}
}
private void _unregisterChannels(
SelectionKeyRegistrationReference registrationReference)
throws Exception {
registrationReference.cancelRegistration();
SelectorIntraband defaultIntraband =
(SelectorIntraband)registrationReference.getIntraband();
Selector selector = defaultIntraband.selector;
Set<SelectionKey> keys = selector.keys();
while (!keys.isEmpty()) {
selector.wakeup();
}
SelectionKey readSelectionKey = registrationReference.readSelectionKey;
SelectionKey writeSelectionKey =
registrationReference.writeSelectionKey;
try (SelectableChannel readSelectableChannel =
readSelectionKey.channel();
SelectableChannel writeSelectableChannel =
writeSelectionKey.channel()) {
while (readSelectableChannel.keyFor(selector) != null);
while (writeSelectableChannel.keyFor(selector) != null);
}
}
private static final String _DATA_STRING =
SelectorIntrabandTest.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 SelectorIntraband _selectorIntraband;
private static class MockDuplexSelectableChannel
extends SelectableChannel
implements GatheringByteChannel, ScatteringByteChannel {
public MockDuplexSelectableChannel(boolean readable, boolean writable) {
_readable = readable;
_writable = writable;
}
@Override
public Object blockingLock() {
throw new UnsupportedOperationException();
}
@Override
public SelectableChannel configureBlocking(boolean block) {
throw new UnsupportedOperationException();
}
@Override
public boolean isBlocking() {
throw new UnsupportedOperationException();
}
@Override
public boolean isRegistered() {
throw new UnsupportedOperationException();
}
@Override
public SelectionKey keyFor(Selector selector) {
throw new UnsupportedOperationException();
}
@Override
public SelectorProvider provider() {
throw new UnsupportedOperationException();
}
@Override
public int read(ByteBuffer byteBuffer) {
throw new UnsupportedOperationException();
}
@Override
public long read(ByteBuffer[] byteBuffers) {
throw new UnsupportedOperationException();
}
@Override
public long read(ByteBuffer[] byteBuffers, int offset, int length) {
throw new UnsupportedOperationException();
}
@Override
public SelectionKey register(
Selector selector, int ops, Object attachment) {
throw new UnsupportedOperationException();
}
@Override
public int validOps() {
int ops = 0;
if (_readable) {
ops |= SelectionKey.OP_READ;
}
if (_writable) {
ops |= SelectionKey.OP_WRITE;
}
return ops;
}
@Override
public int write(ByteBuffer byteBuffer) {
throw new UnsupportedOperationException();
}
@Override
public long write(ByteBuffer[] byteBuffers) {
throw new UnsupportedOperationException();
}
@Override
public long write(ByteBuffer[] byteBuffers, int offset, int length) {
throw new UnsupportedOperationException();
}
@Override
protected void implCloseChannel() {
throw new UnsupportedOperationException();
}
private final boolean _readable;
private final boolean _writable;
}
private static class WakeUpRunnable implements Runnable {
public WakeUpRunnable(SelectorIntraband selectorIntraband) {
_selectorIntraband = selectorIntraband;
}
@Override
public void run() {
Thread currentThread = Thread.currentThread();
while (!currentThread.isInterrupted()) {
_selectorIntraband.selector.wakeup();
}
}
private final SelectorIntraband _selectorIntraband;
}
}