Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,40 @@

import java.io.Closeable;
import java.lang.reflect.Proxy;
import java.nio.channels.AsynchronousChannel;
import java.nio.channels.ByteChannel;
import java.nio.channels.Channel;
import java.nio.channels.GatheringByteChannel;
import java.nio.channels.InterruptibleChannel;
import java.nio.channels.NetworkChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.ScatteringByteChannel;
import java.nio.channels.SeekableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.util.LinkedHashSet;
import java.util.Objects;
import java.util.Set;

/**
* Creates a close-shielding proxy for a {@link Channel}.
*
* <p>
* The returned proxy will implement all {@link Channel} sub-interfaces that the delegate implements.
* </p>
* <p>The returned proxy implements all {@link Channel} sub-interfaces that are both supported by this implementation and actually implemented by the given
* delegate.</p>
*
* <p>The following interfaces are supported:</p>
*
* <ul>
* <li>{@link AsynchronousChannel}</li>
* <li>{@link ByteChannel}</li>
* <li>{@link Channel}</li>
* <li>{@link GatheringByteChannel}</li>
* <li>{@link InterruptibleChannel}</li>
* <li>{@link NetworkChannel}</li>
* <li>{@link ReadableByteChannel}</li>
* <li>{@link ScatteringByteChannel}</li>
* <li>{@link SeekableByteChannel}</li>
* <li>{@link WritableByteChannel}</li>
* </ul>
*
* @see Channel
* @see Closeable
Expand All @@ -44,7 +67,7 @@ private static Set<Class<?>> collectChannelInterfaces(final Class<?> type, final
// Visit interfaces
while (currentType != null) {
for (final Class<?> iface : currentType.getInterfaces()) {
if (Channel.class.isAssignableFrom(iface) && out.add(iface)) {
if (CloseShieldChannelHandler.isSupported(iface) && out.add(iface)) {
collectChannelInterfaces(iface, out);
}
}
Expand All @@ -57,8 +80,10 @@ private static Set<Class<?>> collectChannelInterfaces(final Class<?> type, final
* Wraps a channel to shield it from being closed.
*
* @param channel The underlying channel to shield, not {@code null}.
* @param <T> Any Channel type (interface or class).
* @param <T> A supported channel type.
* @return A proxy that shields {@code close()} and enforces closed semantics on other calls.
* @throws ClassCastException if {@code T} is not a supported channel type.
* @throws NullPointerException if {@code channel} is {@code null}.
*/
@SuppressWarnings({ "unchecked", "resource" }) // caller closes
public static <T extends Channel> T wrap(final T channel) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,45 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.nio.channels.AsynchronousChannel;
import java.nio.channels.ByteChannel;
import java.nio.channels.Channel;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.GatheringByteChannel;
import java.nio.channels.InterruptibleChannel;
import java.nio.channels.NetworkChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.ScatteringByteChannel;
import java.nio.channels.SeekableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.util.Collections;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;

final class CloseShieldChannelHandler implements InvocationHandler {

private static final Set<Class<?>> SUPPORTED_INTERFACES;

static {
final Set<Class<?>> interfaces = new HashSet<>();
interfaces.add(AsynchronousChannel.class);
interfaces.add(ByteChannel.class);
interfaces.add(Channel.class);
interfaces.add(GatheringByteChannel.class);
interfaces.add(InterruptibleChannel.class);
interfaces.add(NetworkChannel.class);
interfaces.add(ReadableByteChannel.class);
interfaces.add(ScatteringByteChannel.class);
interfaces.add(SeekableByteChannel.class);
interfaces.add(WritableByteChannel.class);
SUPPORTED_INTERFACES = Collections.unmodifiableSet(interfaces);
}

static boolean isSupported(final Class<?> interfaceClass) {
return SUPPORTED_INTERFACES.contains(interfaceClass);
}

/**
* Tests whether the given method is allowed to be called after the shield is closed.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,13 @@
import static org.mockito.Mockito.when;

import java.io.IOException;
import java.nio.channels.AsynchronousByteChannel;
import java.nio.channels.AsynchronousChannel;
import java.nio.channels.ByteChannel;
import java.nio.channels.Channel;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.FileChannel;
import java.nio.channels.GatheringByteChannel;
import java.nio.channels.InterruptibleChannel;
import java.nio.channels.MulticastChannel;
import java.nio.channels.NetworkChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.ScatteringByteChannel;
Expand All @@ -65,14 +63,11 @@ class CloseShieldChannelTest {
static Stream<Class<? extends Channel>> testedInterfaces() {
// @formatter:off
return Stream.of(
AsynchronousByteChannel.class,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello @ppkarwasz
Why have these interfaces been removed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They would require additional code to work correctly.

AsynchronousByteChannel.read and AsynchronousByteChannel.write for example do not throw an exception after close(), but report the problem through Future or CompletionHandler.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahhh, thank you for the clarification! 😊

AsynchronousChannel.class,
ByteChannel.class,
Channel.class,
GatheringByteChannel.class,
InterruptibleChannel.class,
MulticastChannel.class,
NetworkChannel.class,
NetworkChannel.class,
ReadableByteChannel.class,
ScatteringByteChannel.class,
Expand Down