mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 14:54:52 +02:00
8198372: API to create a SelectableChannel to a FileDescriptor
Reviewed-by: chegar, bpb
This commit is contained in:
parent
7523687071
commit
cf9438f418
5 changed files with 575 additions and 1 deletions
|
@ -242,6 +242,7 @@ module java.base {
|
|||
exports sun.nio.ch to
|
||||
java.management,
|
||||
jdk.crypto.cryptoki,
|
||||
jdk.net,
|
||||
jdk.sctp,
|
||||
jdk.unsupported;
|
||||
exports sun.nio.cs to
|
||||
|
|
263
src/jdk.net/share/classes/jdk/nio/Channels.java
Normal file
263
src/jdk.net/share/classes/jdk/nio/Channels.java
Normal file
|
@ -0,0 +1,263 @@
|
|||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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 General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.nio;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.SelectableChannel;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.nio.channels.spi.AbstractSelectableChannel;
|
||||
import java.nio.channels.spi.SelectorProvider;
|
||||
import java.util.Objects;
|
||||
|
||||
import sun.nio.ch.IOUtil;
|
||||
import sun.nio.ch.Net;
|
||||
import sun.nio.ch.SelChImpl;
|
||||
import sun.nio.ch.SelectionKeyImpl;
|
||||
import sun.nio.ch.SelectorProviderImpl;
|
||||
|
||||
/**
|
||||
* Defines static methods to create {@link java.nio.channels.Channel channels}.
|
||||
*
|
||||
* <p> Unless otherwise specified, passing a {@code null} argument to any of the
|
||||
* methods defined here will cause a {@code NullPointerException} to be thrown.
|
||||
*
|
||||
* @since 11
|
||||
*/
|
||||
|
||||
public final class Channels {
|
||||
private Channels() { }
|
||||
|
||||
/**
|
||||
* An object used to coordinate the closing of a selectable channel created
|
||||
* by {@link Channels#readWriteSelectableChannel readWriteSelectableChannel}.
|
||||
*
|
||||
* @since 11
|
||||
*/
|
||||
public interface SelectableChannelCloser {
|
||||
|
||||
/**
|
||||
* Closes a selectable channel.
|
||||
*
|
||||
* <p> This method is invoked by the channel's close method in order to
|
||||
* perform the actual work of closing the channel. This method is only
|
||||
* invoked if the channel has not yet been closed, and it is never
|
||||
* invoked more than once by the channel's close implementation.
|
||||
*
|
||||
* <p> An implementation of this method must arrange for any other
|
||||
* thread that is blocked in an I/O operation upon the channel to return
|
||||
* immediately, either by throwing an exception or by returning normally.
|
||||
* If the channel is {@link SelectableChannel#isRegistered registered}
|
||||
* with one or more {@link java.nio.channels.Selector Selector}s then
|
||||
* the file descriptor should not be released until the {@link
|
||||
* #implReleaseChannel implReleaseChannel} method is invoked. </p>
|
||||
*
|
||||
* @param sc
|
||||
* The selectable channel
|
||||
*
|
||||
* @throws IOException
|
||||
* If an I/O error occurs while closing the file descriptor
|
||||
*
|
||||
* @see java.nio.channels.spi.AbstractInterruptibleChannel#implCloseChannel
|
||||
*/
|
||||
void implCloseChannel(SelectableChannel sc) throws IOException;
|
||||
|
||||
/**
|
||||
* Release the file descriptor and any resources for a selectable
|
||||
* channel that closed while registered with one or more {@link
|
||||
* java.nio.channels.Selector Selector}s.
|
||||
*
|
||||
* <p> This method is for cases where a channel is closed when
|
||||
* {@link java.nio.channels.SelectableChannel#isRegistered registered}
|
||||
* with one or more {@code Selector}s. A channel may remain registered
|
||||
* for some time after it is closed. This method is invoked when the
|
||||
* channel is eventually deregistered from the last {@code Selector}
|
||||
* that it was registered with. It is invoked at most once.
|
||||
*
|
||||
* @apiNote This method is invoked while synchronized on the selector
|
||||
* and its selected-key set. Great care must be taken to avoid deadlocks
|
||||
* with other threads that also synchronize on these objects.
|
||||
*
|
||||
* @param sc
|
||||
* The closed selectable channel
|
||||
*
|
||||
* @throws IOException
|
||||
* If an I/O error occurs
|
||||
*
|
||||
* @see java.nio.channels.spi.AbstractSelector#deregister
|
||||
*/
|
||||
void implReleaseChannel(SelectableChannel sc) throws IOException;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a selectable channel to a file descriptor that supports an
|
||||
* {@link SelectableChannel#validOps() operation-set} of
|
||||
* {@link SelectionKey#OP_READ OP_READ} and
|
||||
* {@link SelectionKey#OP_WRITE OP_WRITE}. The selectable channel will be
|
||||
* created by the default {@link SelectorProvider}.
|
||||
*
|
||||
* <p> The given file descriptor is a socket or resource that can be
|
||||
* multiplexed by a {@link java.nio.channels.Selector} for read and write
|
||||
* readiness. Great care is required to coordinate direct use of the file
|
||||
* descriptor with the use of the selectable channel. In particular,
|
||||
* changing the blocking mode or closing the file descriptor without careful
|
||||
* coordination will result in unspecified and unsafe side effects. The
|
||||
* given {@link SelectableChannelCloser SelectableChannelCloser} is invoked to
|
||||
* close the file descriptor and to coordinate the closing when the channel
|
||||
* is registered with a {@code Selector}. </p>
|
||||
*
|
||||
* <p> If there is a security manager set then its
|
||||
* {@link SecurityManager#checkRead(FileDescriptor) checkRead} and
|
||||
* {@link SecurityManager#checkWrite(FileDescriptor) checkWrite} methods
|
||||
* are invoked to check that the caller has permission to both read from and
|
||||
* write to the file descriptor. </p>
|
||||
*
|
||||
* @implNote This method throws {@code UnsupportedOperationException} if
|
||||
* the default {@code SelectorProvider} is not the JDK built-in implementation.
|
||||
*
|
||||
* @param fd
|
||||
* The file descriptor
|
||||
* @param closer
|
||||
* The object to close the channel
|
||||
*
|
||||
* @return The selectable channel
|
||||
*
|
||||
* @throws IllegalArgumentException
|
||||
* If the file descriptor is not {@link FileDescriptor#valid() valid}
|
||||
* @throws SecurityException
|
||||
* If denied by the security manager
|
||||
*/
|
||||
public static SelectableChannel readWriteSelectableChannel(FileDescriptor fd,
|
||||
SelectableChannelCloser closer) {
|
||||
Objects.requireNonNull(closer);
|
||||
if (!fd.valid())
|
||||
throw new IllegalArgumentException("file descriptor is not valid");
|
||||
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
sm.checkRead(fd);
|
||||
sm.checkWrite(fd);
|
||||
}
|
||||
|
||||
SelectorProvider provider = SelectorProvider.provider();
|
||||
if (!(provider instanceof SelectorProviderImpl))
|
||||
throw new UnsupportedOperationException("custom SelectorProvider");
|
||||
|
||||
return new ReadWriteChannelImpl((SelectorProviderImpl)provider, fd, closer);
|
||||
}
|
||||
|
||||
private static final class ReadWriteChannelImpl
|
||||
extends AbstractSelectableChannel implements SelChImpl
|
||||
{
|
||||
private final FileDescriptor fd;
|
||||
private final int fdVal;
|
||||
private final SelectableChannelCloser closer;
|
||||
|
||||
ReadWriteChannelImpl(SelectorProviderImpl provider,
|
||||
FileDescriptor fd,
|
||||
SelectableChannelCloser closer) {
|
||||
super(provider);
|
||||
this.fd = fd;
|
||||
this.fdVal = IOUtil.fdVal(fd);
|
||||
this.closer = closer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileDescriptor getFD() {
|
||||
return fd;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFDVal() {
|
||||
return fdVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int validOps() {
|
||||
return (SelectionKey.OP_READ | SelectionKey.OP_WRITE);
|
||||
}
|
||||
|
||||
private boolean translateReadyOps(int ops,
|
||||
int initialOps,
|
||||
SelectionKeyImpl ski) {
|
||||
int intOps = ski.nioInterestOps();
|
||||
int oldOps = ski.nioReadyOps();
|
||||
int newOps = initialOps;
|
||||
|
||||
if ((ops & (Net.POLLERR | Net.POLLHUP)) != 0) {
|
||||
newOps = intOps;
|
||||
ski.nioReadyOps(newOps);
|
||||
return (newOps & ~oldOps) != 0;
|
||||
}
|
||||
|
||||
if (((ops & Net.POLLIN) != 0) &&
|
||||
((intOps & SelectionKey.OP_READ) != 0))
|
||||
newOps |= SelectionKey.OP_READ;
|
||||
|
||||
if (((ops & Net.POLLOUT) != 0) &&
|
||||
((intOps & SelectionKey.OP_WRITE) != 0))
|
||||
newOps |= SelectionKey.OP_WRITE;
|
||||
|
||||
ski.nioReadyOps(newOps);
|
||||
return (newOps & ~oldOps) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl ski) {
|
||||
return translateReadyOps(ops, ski.nioReadyOps(), ski);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl ski) {
|
||||
return translateReadyOps(ops, 0, ski);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int translateInterestOps(int ops) {
|
||||
int newOps = 0;
|
||||
if ((ops & SelectionKey.OP_READ) != 0)
|
||||
newOps |= Net.POLLIN;
|
||||
if ((ops & SelectionKey.OP_WRITE) != 0)
|
||||
newOps |= Net.POLLOUT;
|
||||
return newOps;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void implConfigureBlocking(boolean block) throws IOException {
|
||||
IOUtil.configureBlocking(fd, block);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void implCloseSelectableChannel() throws IOException {
|
||||
closer.implCloseChannel(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void kill() throws IOException {
|
||||
closer.implReleaseChannel(this);
|
||||
}
|
||||
}
|
||||
}
|
32
src/jdk.net/share/classes/jdk/nio/package-info.java
Normal file
32
src/jdk.net/share/classes/jdk/nio/package-info.java
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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 General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Defines JDK-specific {@link java.nio.channels.Channel channel} APIs.
|
||||
*
|
||||
* @since 11
|
||||
*/
|
||||
|
||||
package jdk.nio;
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -31,5 +31,6 @@
|
|||
*/
|
||||
module jdk.net {
|
||||
exports jdk.net;
|
||||
exports jdk.nio;
|
||||
}
|
||||
|
||||
|
|
277
test/jdk/jdk/nio/Basic.java
Normal file
277
test/jdk/jdk/nio/Basic.java
Normal file
|
@ -0,0 +1,277 @@
|
|||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code 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 General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8198372
|
||||
* @modules jdk.net java.base/sun.nio.ch:+open
|
||||
* @run testng Basic
|
||||
* @summary Basic tests for jdk.nio.Channels
|
||||
*/
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.SelectableChannel;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.nio.channels.Selector;
|
||||
import java.nio.channels.ServerSocketChannel;
|
||||
import java.nio.channels.SocketChannel;
|
||||
|
||||
import jdk.nio.Channels;
|
||||
import jdk.nio.Channels.SelectableChannelCloser;
|
||||
|
||||
import sun.nio.ch.IOUtil;
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
@Test
|
||||
public class Basic {
|
||||
|
||||
/**
|
||||
* A loopback connection
|
||||
*/
|
||||
static class Connection implements Closeable {
|
||||
private final SocketChannel sc1;
|
||||
private final SocketChannel sc2;
|
||||
|
||||
private Connection(SocketChannel sc1, SocketChannel sc2) {
|
||||
this.sc1 = sc1;
|
||||
this.sc2 = sc2;
|
||||
}
|
||||
|
||||
static Connection open() throws IOException {
|
||||
try (ServerSocketChannel ssc = ServerSocketChannel.open()) {
|
||||
InetAddress lb = InetAddress.getLoopbackAddress();
|
||||
ssc.bind(new InetSocketAddress(lb, 0));
|
||||
SocketChannel sc1 = SocketChannel.open(ssc.getLocalAddress());
|
||||
SocketChannel sc2 = ssc.accept();
|
||||
return new Connection(sc1, sc2);
|
||||
}
|
||||
}
|
||||
|
||||
SocketChannel channel1() {
|
||||
return sc1;
|
||||
}
|
||||
|
||||
SocketChannel channel2() {
|
||||
return sc2;
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
try {
|
||||
sc1.close();
|
||||
} finally {
|
||||
sc2.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A SelectableChannelCloser that tracks if the implCloseChannel and
|
||||
* implReleaseChannel methods are invoked
|
||||
*/
|
||||
static class Closer implements SelectableChannelCloser {
|
||||
int closeCount;
|
||||
SelectableChannel invokedToClose;
|
||||
int releaseCount;
|
||||
SelectableChannel invokedToRelease;
|
||||
|
||||
@Override
|
||||
public void implCloseChannel(SelectableChannel sc) {
|
||||
closeCount++;
|
||||
invokedToClose = sc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void implReleaseChannel(SelectableChannel sc) {
|
||||
releaseCount++;
|
||||
invokedToRelease = sc;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic test of channel registered with Selector
|
||||
*/
|
||||
public void testSelect() throws IOException {
|
||||
Selector sel = Selector.open();
|
||||
try (Connection connection = Connection.open()) {
|
||||
|
||||
// create channel with the file descriptor from one end of the connection
|
||||
FileDescriptor fd = getFD(connection.channel1());
|
||||
SelectableChannel ch = Channels.readWriteSelectableChannel(fd, new Closer());
|
||||
|
||||
// register for read events, channel should not be selected
|
||||
ch.configureBlocking(false);
|
||||
SelectionKey key = ch.register(sel, SelectionKey.OP_READ);
|
||||
int n = sel.selectNow();
|
||||
assertTrue(n == 0);
|
||||
|
||||
// write bytes to other end of connection
|
||||
SocketChannel peer = connection.channel2();
|
||||
ByteBuffer msg = ByteBuffer.wrap("hello".getBytes("UTF-8"));
|
||||
int nwrote = peer.write(msg);
|
||||
assertTrue(nwrote >= 0);
|
||||
|
||||
// channel should be selected
|
||||
n = sel.select();
|
||||
assertTrue(n == 1);
|
||||
assertTrue(sel.selectedKeys().contains(key));
|
||||
assertTrue(key.isReadable());
|
||||
assertFalse(key.isWritable());
|
||||
sel.selectedKeys().clear();
|
||||
|
||||
// change interest set for writing, channel should be selected
|
||||
key.interestOps(SelectionKey.OP_WRITE);
|
||||
n = sel.select();
|
||||
assertTrue(n == 1);
|
||||
assertTrue(sel.selectedKeys().contains(key));
|
||||
assertTrue(key.isWritable());
|
||||
assertFalse(key.isReadable());
|
||||
sel.selectedKeys().clear();
|
||||
|
||||
// change interest set for reading + writing, channel should be selected
|
||||
key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
|
||||
n = sel.select();
|
||||
assertTrue(n == 1);
|
||||
assertTrue(sel.selectedKeys().contains(key));
|
||||
assertTrue(key.isWritable());
|
||||
assertTrue(key.isReadable());
|
||||
sel.selectedKeys().clear();
|
||||
|
||||
// change interest set to 0 to deregister, channel should not be selected
|
||||
key.interestOps(0);
|
||||
n = sel.selectNow();
|
||||
assertTrue(n == 0);
|
||||
|
||||
} finally {
|
||||
sel.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the SelectableChannelCloser implCloseChannel method is invoked.
|
||||
*/
|
||||
public void testImplCloseChannel() throws IOException {
|
||||
try (Connection connection = Connection.open()) {
|
||||
FileDescriptor fd = getFD(connection.channel1());
|
||||
Closer closer = new Closer();
|
||||
SelectableChannel ch = Channels.readWriteSelectableChannel(fd, closer);
|
||||
|
||||
// close channel twice, checking that the closer is invoked only once
|
||||
for (int i=0; i<2; i++) {
|
||||
ch.close();
|
||||
|
||||
// implCloseChannel should been invoked once
|
||||
assertTrue(closer.closeCount == 1);
|
||||
assertTrue(closer.invokedToClose == ch);
|
||||
|
||||
// implReleaseChannel should not have been invoked
|
||||
assertTrue(closer.releaseCount == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the SelectableChannelCloser implReleaseChannel method is invoked.
|
||||
*/
|
||||
public void testImplReleaseChannel() throws IOException {
|
||||
Selector sel = Selector.open();
|
||||
try (Connection connection = Connection.open()) {
|
||||
FileDescriptor fd = getFD(connection.channel1());
|
||||
Closer closer = new Closer();
|
||||
SelectableChannel ch = Channels.readWriteSelectableChannel(fd, closer);
|
||||
|
||||
// register with Selector, invoking selectNow to ensure registered
|
||||
ch.configureBlocking(false);
|
||||
ch.register(sel, SelectionKey.OP_WRITE);
|
||||
sel.selectNow();
|
||||
|
||||
// close channel
|
||||
ch.close();
|
||||
|
||||
// implCloseChannel should have been invoked
|
||||
assertTrue(closer.closeCount == 1);
|
||||
assertTrue(closer.invokedToClose == ch);
|
||||
|
||||
// implReleaseChannel should not have been invoked
|
||||
assertTrue(closer.releaseCount == 0);
|
||||
|
||||
// flush the selector
|
||||
sel.selectNow();
|
||||
|
||||
// implReleaseChannel should have been invoked
|
||||
assertTrue(closer.releaseCount == 1);
|
||||
assertTrue(closer.invokedToRelease == ch);
|
||||
|
||||
} finally {
|
||||
sel.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testInvalidFileDescriptor() throws IOException {
|
||||
FileDescriptor fd = IOUtil.newFD(-1);
|
||||
Channels.readWriteSelectableChannel(fd, new SelectableChannelCloser() {
|
||||
@Override
|
||||
public void implCloseChannel(SelectableChannel sc) { }
|
||||
@Override
|
||||
public void implReleaseChannel(SelectableChannel sc) { }
|
||||
});
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = NullPointerException.class)
|
||||
public void testNullFileDescriptor() throws IOException {
|
||||
Channels.readWriteSelectableChannel(null, new SelectableChannelCloser() {
|
||||
@Override
|
||||
public void implCloseChannel(SelectableChannel sc) { }
|
||||
@Override
|
||||
public void implReleaseChannel(SelectableChannel sc) { }
|
||||
});
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = NullPointerException.class)
|
||||
public void testNullCloser() throws IOException {
|
||||
try (Connection connection = Connection.open()) {
|
||||
FileDescriptor fd = getFD(connection.channel1());
|
||||
Channels.readWriteSelectableChannel(fd, null);
|
||||
}
|
||||
}
|
||||
|
||||
private static FileDescriptor getFD(SocketChannel sc) {
|
||||
try {
|
||||
Class<?> clazz = sc.getClass();
|
||||
Field f = clazz.getDeclaredField("fd");
|
||||
f.setAccessible(true);
|
||||
return (FileDescriptor) f.get(sc);
|
||||
} catch (Exception e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue