/**
* 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.blocking;
import com.liferay.portal.kernel.nio.intraband.BaseIntraband;
import com.liferay.portal.kernel.nio.intraband.ChannelContext;
import com.liferay.portal.kernel.nio.intraband.Datagram;
import com.liferay.portal.kernel.nio.intraband.RegistrationReference;
import com.liferay.portal.kernel.util.NamedThreadFactory;
import java.io.IOException;
import java.nio.channels.Channel;
import java.nio.channels.GatheringByteChannel;
import java.nio.channels.ScatteringByteChannel;
import java.nio.channels.SelectableChannel;
import java.util.Queue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
/**
* @author Shuyang Zhou
*/
public class ExecutorIntraband extends BaseIntraband {
public ExecutorIntraband(long defaultTimeout) {
super(defaultTimeout);
}
@Override
public void close() throws InterruptedException, IOException {
executorService.shutdownNow();
executorService.awaitTermination(defaultTimeout, TimeUnit.MILLISECONDS);
super.close();
}
@Override
public RegistrationReference registerChannel(Channel channel) {
if (channel == null) {
throw new NullPointerException("Channel is null");
}
if (!(channel instanceof GatheringByteChannel)) {
throw new IllegalArgumentException(
"Channel is not of type GatheringByteChannel");
}
if (!(channel instanceof ScatteringByteChannel)) {
throw new IllegalArgumentException(
"Channel is not of type ScatteringByteChannel");
}
if (channel instanceof SelectableChannel) {
SelectableChannel selectableChannel = (SelectableChannel)channel;
if (!selectableChannel.isBlocking()) {
throw new IllegalArgumentException(
"Channel is of type SelectableChannel and configured in " +
"nonblocking mode");
}
}
ensureOpen();
return doRegisterChannel(
(ScatteringByteChannel)channel, (GatheringByteChannel)channel);
}
@Override
public RegistrationReference registerChannel(
ScatteringByteChannel scatteringByteChannel,
GatheringByteChannel gatheringByteChannel) {
if (gatheringByteChannel == null) {
throw new NullPointerException("Gathering byte channel is null");
}
if (scatteringByteChannel == null) {
throw new NullPointerException("Scattering byte channel is null");
}
if (scatteringByteChannel instanceof SelectableChannel) {
SelectableChannel selectableChannel =
(SelectableChannel)scatteringByteChannel;
if (!selectableChannel.isBlocking()) {
throw new IllegalArgumentException(
"Scattering byte channel is of type SelectableChannel " +
"and configured in nonblocking mode");
}
}
if (gatheringByteChannel instanceof SelectableChannel) {
SelectableChannel selectableChannel =
(SelectableChannel)gatheringByteChannel;
if (!selectableChannel.isBlocking()) {
throw new IllegalArgumentException(
"Gathering byte channel is of type SelectableChannel and " +
"configured in nonblocking mode");
}
}
ensureOpen();
return doRegisterChannel(scatteringByteChannel, gatheringByteChannel);
}
protected RegistrationReference doRegisterChannel(
ScatteringByteChannel scatteringByteChannel,
GatheringByteChannel gatheringByteChannel) {
BlockingQueue<Datagram> sendingQueue = new LinkedBlockingQueue<>();
ChannelContext channelContext = new ChannelContext(sendingQueue);
ReadingCallable readingCallable = new ReadingCallable(
scatteringByteChannel, channelContext);
WritingCallable writingCallable = new WritingCallable(
gatheringByteChannel, channelContext);
// Submit the polling jobs, no dispatch will happen until latches are
// open. This ensures a thread safe publication of
// ChannelContext#_registrationReference.
Future<Void> readFuture = executorService.submit(readingCallable);
Future<Void> writeFuture = executorService.submit(writingCallable);
FutureRegistrationReference futureRegistrationReference =
new FutureRegistrationReference(
this, channelContext, readFuture, writeFuture);
channelContext.setRegistrationReference(futureRegistrationReference);
readingCallable.openLatch();
writingCallable.openLatch();
return futureRegistrationReference;
}
@Override
protected void doSendDatagram(
RegistrationReference registrationReference, Datagram datagram) {
FutureRegistrationReference futureRegistrationReference =
(FutureRegistrationReference)registrationReference;
ChannelContext channelContext =
futureRegistrationReference.channelContext;
Queue<Datagram> sendingQueue = channelContext.getSendingQueue();
sendingQueue.offer(datagram);
}
protected static final ThreadFactory THREAD_FACTORY =
new NamedThreadFactory(
ExecutorIntraband.class + ".threadFactory", Thread.NORM_PRIORITY,
ExecutorIntraband.class.getClassLoader());
protected final ExecutorService executorService =
Executors.newCachedThreadPool(THREAD_FACTORY);
protected class ReadingCallable implements Callable<Void> {
public ReadingCallable(
ScatteringByteChannel scatteringByteChannel,
ChannelContext channelContext) {
_scatteringByteChannel = scatteringByteChannel;
_channelContext = channelContext;
_countDownLatch = new CountDownLatch(1);
}
@Override
public Void call() throws Exception {
_countDownLatch.await();
while (_scatteringByteChannel.isOpen()) {
handleReading(_scatteringByteChannel, _channelContext);
}
return null;
}
public void openLatch() {
_countDownLatch.countDown();
}
private final ChannelContext _channelContext;
private final CountDownLatch _countDownLatch;
private final ScatteringByteChannel _scatteringByteChannel;
}
protected class WritingCallable implements Callable<Void> {
public WritingCallable(
GatheringByteChannel gatheringByteChannel,
ChannelContext channelContext) {
_gatheringByteChannel = gatheringByteChannel;
_channelContext = channelContext;
_countDownLatch = new CountDownLatch(1);
}
@Override
public Void call() throws Exception {
_countDownLatch.await();
try {
BlockingQueue<Datagram> sendingQueue =
(BlockingQueue<Datagram>)_channelContext.getSendingQueue();
while (true) {
Datagram datagram = sendingQueue.take();
_channelContext.setWritingDatagram(datagram);
if (!handleWriting(
_gatheringByteChannel, _channelContext)) {
if (_gatheringByteChannel.isOpen()) {
// Still open but no longer writable, typical
// behavior of nonblocking channel
throw new IllegalStateException(
_gatheringByteChannel +
" behaved in nonblocking way.");
}
else {
break;
}
}
cleanUpTimeoutResponseWaitingDatagrams();
}
}
catch (InterruptedException ie) {
}
return null;
}
public void openLatch() {
_countDownLatch.countDown();
}
private final ChannelContext _channelContext;
private final CountDownLatch _countDownLatch;
private final GatheringByteChannel _gatheringByteChannel;
}
}