org.xnio.channels.Channels Maven / Gradle / Ivy
/*
* JBoss, Home of Professional Open Source
*
* Copyright 2008 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.xnio.channels;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOError;
import java.io.InterruptedIOException;
import java.nio.channels.Channel;
import java.nio.channels.FileChannel;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Locale;
import org.xnio.Buffers;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.WritableByteChannel;
import java.nio.channels.GatheringByteChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.ScatteringByteChannel;
import java.util.concurrent.TimeUnit;
import org.xnio.ChannelListener;
import org.xnio.Option;
import org.xnio.XnioIoThread;
/**
* A utility class containing static methods to support channel usage.
*
* @apiviz.exclude
*/
public final class Channels {
private Channels() {
}
/**
* Simple utility method to execute a blocking flush on a writable channel. The method blocks until there are no
* remaining bytes in the send queue.
*
* @param channel the writable channel
* @throws IOException if an I/O exception occurs
*
* @since 2.0
*/
public static void flushBlocking(SuspendableWriteChannel channel) throws IOException {
while (! channel.flush()) {
channel.awaitWritable();
}
}
/**
* Simple utility method to execute a blocking flush on a writable channel. The method blocks until there are no
* remaining bytes in the send queue or the timeout is reached.
*
* @param channel the writable channel
* @param time the amount of time to wait
* @param unit the unit of time to wait
* @return true if the channel was successfully flushed, false if the timeout was reached
* @throws IOException if an I/O exception occurs
*
* @since 3.8
*/
public static boolean flushBlocking(SuspendableWriteChannel channel, long time, TimeUnit unit) throws IOException {
// In the fast path, the timeout is not used because bytes can be flushed without blocking.
if (channel.flush()) {
return true;
}
long remaining = unit.toNanos(time);
long now = System.nanoTime();
do {
// awaitWritable may return spuriously so looping is required.
channel.awaitWritable(remaining, TimeUnit.NANOSECONDS);
// flush prior to recalculating remaining time to avoid a nanoTime
// invocation in the optimal path.
if (channel.flush()) {
return true;
}
// Nanotime must only be used in comparison with another nanotime value
// This implementation allows us to avoid immediate subsequent nanoTime calls
} while ((remaining -= Math.max(-now + (now = System.nanoTime()), 0L)) > 0L);
return false;
}
/**
* Simple utility method to execute a blocking write shutdown on a writable channel. The method blocks until the
* channel's output side is fully shut down.
*
* @param channel the writable channel
* @throws IOException if an I/O exception occurs
*
* @since 2.0
*/
public static void shutdownWritesBlocking(SuspendableWriteChannel channel) throws IOException {
channel.shutdownWrites();
flushBlocking(channel);
}
/**
* Simple utility method to execute a blocking write shutdown on a writable channel. The method blocks until the
* channel's output side is fully shut down or the timeout is reached.
*
* @param channel the writable channel
* @param time the amount of time to wait
* @param unit the unit of time to wait
* @return true if the channel was successfully flushed, false if the timeout was reached
* @throws IOException if an I/O exception occurs
*
* @since 3.8
*/
public static boolean shutdownWritesBlocking(SuspendableWriteChannel channel, long time, TimeUnit unit) throws IOException {
channel.shutdownWrites();
return flushBlocking(channel, time, unit);
}
/**
* Simple utility method to execute a blocking write on a byte channel. The method blocks until the bytes in the
* buffer have been fully written. To ensure that the data is sent, the {@link #flushBlocking(SuspendableWriteChannel)}
* method should be called after all writes are complete.
*
* @param channel the channel to write on
* @param buffer the data to write
* @param the channel type
* @return the number of bytes written
* @throws IOException if an I/O exception occurs
* @since 1.2
*/
public static int writeBlocking(C channel, ByteBuffer buffer) throws IOException {
int t = 0;
while (buffer.hasRemaining()) {
final int res = channel.write(buffer);
if (res == 0) {
channel.awaitWritable();
} else {
t += res;
}
}
return t;
}
/**
* Simple utility method to execute a blocking write on a byte channel with a timeout. The method blocks until
* either the bytes in the buffer have been fully written, or the timeout expires, whichever comes first.
*
* @param channel the channel to write on
* @param buffer the data to write
* @param time the amount of time to wait
* @param unit the unit of time to wait
* @param the channel type
* @return the number of bytes written
* @throws IOException if an I/O exception occurs
* @since 1.2
*/
public static int writeBlocking(C channel, ByteBuffer buffer, long time, TimeUnit unit) throws IOException {
long remaining = unit.toNanos(time);
long now = System.nanoTime();
int t = 0;
while (buffer.hasRemaining() && remaining > 0L) {
int res = channel.write(buffer);
if (res == 0) {
channel.awaitWritable(remaining, TimeUnit.NANOSECONDS);
remaining -= Math.max(-now + (now = System.nanoTime()), 0L);
} else {
t += res;
}
}
return t;
}
/**
* Simple utility method to execute a blocking write on a gathering byte channel. The method blocks until the
* bytes in the buffer have been fully written.
*
* @param channel the channel to write on
* @param buffers the data to write
* @param offs the index of the first buffer to write
* @param len the number of buffers to write
* @param the channel type
* @return the number of bytes written
* @throws IOException if an I/O exception occurs
* @since 1.2
*/
public static long writeBlocking(C channel, ByteBuffer[] buffers, int offs, int len) throws IOException {
long t = 0;
while (Buffers.hasRemaining(buffers, offs, len)) {
final long res = channel.write(buffers, offs, len);
if (res == 0) {
channel.awaitWritable();
} else {
t += res;
}
}
return t;
}
/**
* Simple utility method to execute a blocking write on a gathering byte channel with a timeout. The method blocks until all
* the bytes are written, or until the timeout occurs.
*
* @param channel the channel to write on
* @param buffers the data to write
* @param offs the index of the first buffer to write
* @param len the number of buffers to write
* @param time the amount of time to wait
* @param unit the unit of time to wait
* @param the channel type
* @return the number of bytes written
* @throws IOException if an I/O exception occurs
* @since 1.2
*/
public static long writeBlocking(C channel, ByteBuffer[] buffers, int offs, int len, long time, TimeUnit unit) throws IOException {
long remaining = unit.toNanos(time);
long now = System.nanoTime();
long t = 0;
while (Buffers.hasRemaining(buffers, offs, len) && remaining > 0L) {
long res = channel.write(buffers, offs, len);
if (res == 0) {
channel.awaitWritable(remaining, TimeUnit.NANOSECONDS);
remaining -= Math.max(-now + (now = System.nanoTime()), 0L);
} else {
t += res;
}
}
return t;
}
/**
* Simple utility method to execute a blocking send on a message channel. The method blocks until the message is written.
*
* @param channel the channel to write on
* @param buffer the data to write
* @param the channel type
* @throws IOException if an I/O exception occurs
* @since 1.2
*/
public static void sendBlocking(C channel, ByteBuffer buffer) throws IOException {
while (! channel.send(buffer)) {
channel.awaitWritable();
}
}
/**
* Simple utility method to execute a blocking send on a message channel with a timeout. The method blocks until the channel
* is writable, and then the message is written.
*
* @param channel the channel to write on
* @param buffer the data to write
* @param time the amount of time to wait
* @param unit the unit of time to wait
* @param the channel type
* @return the write result
* @throws IOException if an I/O exception occurs
* @since 1.2
*/
public static boolean sendBlocking(C channel, ByteBuffer buffer, long time, TimeUnit unit) throws IOException {
long remaining = unit.toNanos(time);
long now = System.nanoTime();
while (remaining > 0L) {
if (!channel.send(buffer)) {
channel.awaitWritable(remaining, TimeUnit.NANOSECONDS);
remaining -= Math.max(-now + (now = System.nanoTime()), 0L);
} else {
return true;
}
}
return false;
}
/**
* Simple utility method to execute a blocking gathering send on a message channel. The method blocks until the message is written.
*
* @param channel the channel to write on
* @param buffers the data to write
* @param offs the index of the first buffer to write
* @param len the number of buffers to write
* @param the channel type
* @throws IOException if an I/O exception occurs
* @since 1.2
*/
public static void sendBlocking(C channel, ByteBuffer[] buffers, int offs, int len) throws IOException {
while (! channel.send(buffers, offs, len)) {
channel.awaitWritable();
}
}
/**
* Simple utility method to execute a blocking gathering send on a message channel with a timeout. The method blocks until either
* the message is written or the timeout expires.
*
* @param channel the channel to write on
* @param buffers the data to write
* @param offs the index of the first buffer to write
* @param len the number of buffers to write
* @param time the amount of time to wait
* @param unit the unit of time to wait
* @param the channel type
* @return {@code true} if the message was written before the timeout
* @throws IOException if an I/O exception occurs
* @since 1.2
*/
public static boolean sendBlocking(C channel, ByteBuffer[] buffers, int offs, int len, long time, TimeUnit unit) throws IOException {
long remaining = unit.toNanos(time);
long now = System.nanoTime();
while (remaining > 0L) {
if (!channel.send(buffers, offs, len)) {
channel.awaitWritable(remaining, TimeUnit.NANOSECONDS);
remaining -= Math.max(-now + (now = System.nanoTime()), 0L);
} else {
return true;
}
}
return false;
}
/**
* Simple utility method to execute a blocking read on a readable byte channel. This method blocks until the
* channel is readable, and then the message is read.
*
* @param channel the channel to read from
* @param buffer the buffer into which bytes are to be transferred
* @param the channel type
* @return the number of bytes read
* @throws IOException if an I/O exception occurs
* @since 1.2
*/
public static int readBlocking(C channel, ByteBuffer buffer) throws IOException {
int res;
while ((res = channel.read(buffer)) == 0 && buffer.hasRemaining()) {
channel.awaitReadable();
}
return res;
}
/**
* Simple utility method to execute a blocking read on a readable byte channel with a timeout. This method blocks until the
* channel is readable, and then the message is read.
*
* @param channel the channel to read from
* @param buffer the buffer into which bytes are to be transferred
* @param time the amount of time to wait
* @param unit the unit of time to wait
* @param the channel type
* @return the number of bytes read
* @throws IOException if an I/O exception occurs
* @since 1.2
*/
public static int readBlocking(C channel, ByteBuffer buffer, long time, TimeUnit unit) throws IOException {
// In the fast path, the timeout is not used because bytes can be read without blocking.
int res = channel.read(buffer);
if (res != 0) {
return res;
}
long remaining = unit.toNanos(time);
long now = System.nanoTime();
while (buffer.hasRemaining() && remaining > 0) {
// awaitReadable may return spuriously, so looping is required.
channel.awaitReadable(remaining, TimeUnit.NANOSECONDS);
// read prior to recalculating remaining time to avoid a nanoTime
// invocation in the optimal path.
res = channel.read(buffer);
if (res != 0) {
return res;
}
// Nanotime must only be used in comparison with another nanotime value
// This implementation allows us to avoid immediate subsequent nanoTime calls
remaining -= Math.max(-now + (now = System.nanoTime()), 0L);
}
return res;
}
/**
* Simple utility method to execute a blocking read on a scattering byte channel. This method blocks until the
* channel is readable, and then the message is read.
*
* @param channel the channel to read from
* @param buffers the buffers into which bytes are to be transferred
* @param offs the first buffer to use
* @param len the number of buffers to use
* @param the channel type
* @return the number of bytes read
* @throws IOException if an I/O exception occurs
* @since 1.2
*/
public static long readBlocking(C channel, ByteBuffer[] buffers, int offs, int len) throws IOException {
long res;
while ((res = channel.read(buffers, offs, len)) == 0) {
channel.awaitReadable();
}
return res;
}
/**
* Simple utility method to execute a blocking read on a scattering byte channel with a timeout. This method blocks until the
* channel is readable, and then the message is read.
*
* @param channel the channel to read from
* @param buffers the buffers into which bytes are to be transferred
* @param offs the first buffer to use
* @param len the number of buffers to use
* @param time the amount of time to wait
* @param unit the unit of time to wait
* @param the channel type
* @return the number of bytes read
* @throws IOException if an I/O exception occurs
* @since 1.2
*/
public static long readBlocking(C channel, ByteBuffer[] buffers, int offs, int len, long time, TimeUnit unit) throws IOException {
// In the fast path, the timeout is not used because bytes can be read
// without blocking or interaction with the precise clock.
long res = channel.read(buffers, offs, len);
if (res != 0L) {
return res;
}
long remaining = unit.toNanos(time);
long now = System.nanoTime();
while (Buffers.hasRemaining(buffers, offs, len) && remaining > 0) {
// awaitReadable may return spuriously, looping is required.
channel.awaitReadable(remaining, TimeUnit.NANOSECONDS);
// read prior to recalculating remaining time to avoid a nanoTime
// invocation in the optimal path.
res = channel.read(buffers, offs, len);
if (res != 0) {
return res;
}
// Nanotime must only be used in comparison with another nanotime value
// This implementation allows us to avoid immediate subsequent nanoTime calls
remaining -= Math.max(-now + (now = System.nanoTime()), 0L);
}
return res;
}
/**
* Simple utility method to execute a blocking receive on a readable message channel. This method blocks until the
* channel is readable, and then the message is received.
*
* @param channel the channel to read from
* @param buffer the buffer into which bytes are to be transferred
* @param the channel type
* @return the number of bytes read
* @throws IOException if an I/O exception occurs
* @since 1.2
*/
public static int receiveBlocking(C channel, ByteBuffer buffer) throws IOException {
int res;
while ((res = channel.receive(buffer)) == 0) {
channel.awaitReadable();
}
return res;
}
/**
* Simple utility method to execute a blocking receive on a readable message channel with a timeout. This method blocks until the
* channel is readable, and then the message is received.
*
* @param channel the channel to read from
* @param buffer the buffer into which bytes are to be transferred
* @param time the amount of time to wait
* @param unit the unit of time to wait
* @param the channel type
* @return the number of bytes read
* @throws IOException if an I/O exception occurs
* @since 1.2
*/
public static int receiveBlocking(C channel, ByteBuffer buffer, long time, TimeUnit unit) throws IOException {
// In the fast path, the timeout is not used because bytes can be read without blocking.
int res = channel.receive(buffer);
if (res != 0) {
return res;
}
long remaining = unit.toNanos(time);
long now = System.nanoTime();
while (buffer.hasRemaining() && remaining > 0) {
// awaitReadable may return spuriously, looping is required.
channel.awaitReadable(remaining, TimeUnit.NANOSECONDS);
// read prior to recalculating remaining time to avoid a nanoTime
// invocation in the optimal path.
res = channel.receive(buffer);
if (res != 0) {
return res;
}
// Nanotime must only be used in comparison with another nanotime value
// This implementation allows us to avoid immediate subsequent nanoTime calls
remaining -= Math.max(-now + (now = System.nanoTime()), 0L);
}
return res;
}
/**
* Simple utility method to execute a blocking receive on a readable message channel. This method blocks until the
* channel is readable, and then the message is received.
*
* @param channel the channel to read from
* @param buffers the buffers into which bytes are to be transferred
* @param offs the first buffer to use
* @param len the number of buffers to use
* @param the channel type
* @return the number of bytes read
* @throws IOException if an I/O exception occurs
* @since 1.2
*/
public static long receiveBlocking(C channel, ByteBuffer[] buffers, int offs, int len) throws IOException {
long res;
while ((res = channel.receive(buffers, offs, len)) == 0) {
channel.awaitReadable();
}
return res;
}
/**
* Simple utility method to execute a blocking receive on a readable message channel with a timeout. This method blocks until the
* channel is readable, and then the message is received.
*
* @param channel the channel to read from
* @param buffers the buffers into which bytes are to be transferred
* @param offs the first buffer to use
* @param len the number of buffers to use
* @param time the amount of time to wait
* @param unit the unit of time to wait
* @param the channel type
* @return the number of bytes read
* @throws IOException if an I/O exception occurs
* @since 1.2
*/
public static long receiveBlocking(C channel, ByteBuffer[] buffers, int offs, int len, long time, TimeUnit unit) throws IOException {
// In the fast path, the timeout is not used because bytes can be read without blocking.
long res = channel.receive(buffers, offs, len);
if (res != 0L) {
return res;
}
long remaining = unit.toNanos(time);
long now = System.nanoTime();
while (Buffers.hasRemaining(buffers, offs, len) && remaining > 0) {
// awaitReadable may return spuriously, looping is required.
channel.awaitReadable(remaining, TimeUnit.NANOSECONDS);
res = channel.receive(buffers, offs, len);
if (res != 0) {
return res;
}
// Nanotime must only be used in comparison with another nanotime value
// This implementation allows us to avoid immediate subsequent nanoTime calls
remaining -= Math.max(-now + (now = System.nanoTime()), 0L);
}
return res;
}
/**
* Simple utility method to execute a blocking accept on an accepting channel. This method blocks until
* an accept is possible, and then returns the accepted connection.
*
* @param channel the accepting channel
* @param the connection channel type
* @param the accepting channel type
* @return the accepted channel
* @throws IOException if an I/O error occurs
* @since 3.0
*/
public static > C acceptBlocking(A channel) throws IOException {
C accepted;
while ((accepted = channel.accept()) == null) {
channel.awaitAcceptable();
}
return accepted;
}
/**
* Simple utility method to execute a blocking accept on an accepting channel, with a timeout. This method blocks until
* an accept is possible, and then returns the accepted connection.
*
* @param channel the accepting channel
* @param time the amount of time to wait
* @param unit the unit of time to wait
* @param the connection channel type
* @param the accepting channel type
* @return the accepted channel, or {@code null} if the timeout occurred before a connection was accepted
* @throws IOException if an I/O error occurs
* @since 3.0
*/
public static > C acceptBlocking(A channel, long time, TimeUnit unit) throws IOException {
final C accepted = channel.accept();
if (accepted == null) {
channel.awaitAcceptable(time, unit);
return channel.accept();
} else {
return accepted;
}
}
/**
* Transfer bytes between two channels efficiently, blocking if necessary.
*
* @param destination the destination channel
* @param source the source file channel
* @param startPosition the start position in the source file
* @param count the number of bytes to transfer
* @throws IOException if an I/O error occurs
*/
public static void transferBlocking(StreamSinkChannel destination, FileChannel source, long startPosition, final long count) throws IOException {
long remaining = count;
long res;
while (remaining > 0L) {
while ((res = destination.transferFrom(source, startPosition, remaining)) == 0L) {
try {
destination.awaitWritable();
} catch (InterruptedIOException e) {
final long bytes = count - remaining;
if (bytes > (long) Integer.MAX_VALUE) {
e.bytesTransferred = -1;
} else {
e.bytesTransferred = (int) bytes;
}
}
}
remaining -= res;
startPosition += res;
}
}
/**
* Transfer bytes between two channels efficiently, blocking if necessary.
*
* @param destination the destination file channel
* @param source the source channel
* @param startPosition the start position in the destination file
* @param count the number of bytes to transfer
* @throws IOException if an I/O error occurs
*/
public static void transferBlocking(FileChannel destination, StreamSourceChannel source, long startPosition, final long count) throws IOException {
long remaining = count;
long res;
while (remaining > 0L) {
while ((res = source.transferTo(startPosition, remaining, destination)) == 0L) {
try {
source.awaitReadable();
} catch (InterruptedIOException e) {
final long bytes = count - remaining;
if (bytes > (long) Integer.MAX_VALUE) {
e.bytesTransferred = -1;
} else {
e.bytesTransferred = (int) bytes;
}
}
}
remaining -= res;
startPosition += res;
}
}
/**
* Transfer bytes between two channels efficiently, blocking if necessary.
*
* @param destination the destination channel
* @param source the source channel
* @param throughBuffer the buffer to transfer through,
* @param count the number of bytes to transfer
* @return the number of bytes actually transferred (will be fewer than {@code count} if EOF was reached)
* @throws IOException if the transfer fails
*/
public static long transferBlocking(StreamSinkChannel destination, StreamSourceChannel source, ByteBuffer throughBuffer, long count) throws IOException {
long t = 0L;
long res;
while (t < count) {
try {
while ((res = source.transferTo(count, throughBuffer, destination)) == 0L) {
if (throughBuffer.hasRemaining()) {
writeBlocking(destination, throughBuffer);
} else {
source.awaitReadable();
}
}
t += res;
} catch (InterruptedIOException e) {
int transferred = e.bytesTransferred;
t += transferred;
if (transferred < 0 || t > (long) Integer.MAX_VALUE) {
e.bytesTransferred = -1;
} else {
e.bytesTransferred = (int) t;
}
throw e;
}
if (res == -1L) {
return t == 0L ? -1L : t;
}
}
return t;
}
/**
* Set the close listener for a channel (type-safe).
*
* @param channel the channel
* @param listener the listener to set
* @param the channel type
*/
public static void setCloseListener(T channel, ChannelListener super T> listener) {
@SuppressWarnings("unchecked")
ChannelListener.Setter extends T> setter = (ChannelListener.Setter extends T>) channel.getCloseSetter();
setter.set(listener);
}
/**
* Set the accept listener for a channel (type-safe).
*
* @param channel the channel
* @param listener the listener to set
* @param the channel type
*/
public static > void setAcceptListener(T channel, ChannelListener super T> listener) {
@SuppressWarnings("unchecked")
ChannelListener.Setter extends T> setter = (ChannelListener.Setter extends T>) channel.getAcceptSetter();
setter.set(listener);
}
/**
* Set the read listener for a channel (type-safe).
*
* @param channel the channel
* @param listener the listener to set
* @param the channel type
*/
public static void setReadListener(T channel, ChannelListener super T> listener) {
@SuppressWarnings("unchecked")
ChannelListener.Setter extends T> setter = (ChannelListener.Setter extends T>) channel.getReadSetter();
setter.set(listener);
}
/**
* Set the write listener for a channel (type-safe).
*
* @param channel the channel
* @param listener the listener to set
* @param the channel type
*/
public static void setWriteListener(T channel, ChannelListener super T> listener) {
@SuppressWarnings("unchecked")
ChannelListener.Setter extends T> setter = (ChannelListener.Setter extends T>) channel.getWriteSetter();
setter.set(listener);
}
/**
* Create a wrapper for a byte channel which does not expose other methods.
*
* @param original the original
* @return the wrapped channel
*/
public static ByteChannel wrapByteChannel(final ByteChannel original) {
return new ByteChannel() {
public int read(final ByteBuffer dst) throws IOException {
return original.read(dst);
}
public boolean isOpen() {
return original.isOpen();
}
public void close() throws IOException {
original.close();
}
public int write(final ByteBuffer src) throws IOException {
return original.write(src);
}
public long write(final ByteBuffer[] srcs, final int offset, final int length) throws IOException {
return original.write(srcs, offset, length);
}
public long write(final ByteBuffer[] srcs) throws IOException {
return original.write(srcs);
}
public long read(final ByteBuffer[] dsts, final int offset, final int length) throws IOException {
return original.read(dsts, offset, length);
}
public long read(final ByteBuffer[] dsts) throws IOException {
return original.read(dsts);
}
};
}
/**
* Get an option value from a configurable target. If the method throws an exception then the default value
* is returned.
*
* @param configurable the configurable target
* @param option the option
* @param defaultValue the default value
* @param the option value type
* @return the value
*/
public static T getOption(Configurable configurable, Option option, T defaultValue) {
try {
final T value = configurable.getOption(option);
return value == null ? defaultValue : value;
} catch (IOException e) {
return defaultValue;
}
}
/**
* Get an option value from a configurable target. If the method throws an exception then the default value
* is returned.
*
* @param configurable the configurable target
* @param option the option
* @param defaultValue the default value
* @return the value
*/
public static boolean getOption(Configurable configurable, Option option, boolean defaultValue) {
try {
final Boolean value = configurable.getOption(option);
return value == null ? defaultValue : value.booleanValue();
} catch (IOException e) {
return defaultValue;
}
}
/**
* Get an option value from a configurable target. If the method throws an exception then the default value
* is returned.
*
* @param configurable the configurable target
* @param option the option
* @param defaultValue the default value
* @return the value
*/
public static int getOption(Configurable configurable, Option option, int defaultValue) {
try {
final Integer value = configurable.getOption(option);
return value == null ? defaultValue : value.intValue();
} catch (IOException e) {
return defaultValue;
}
}
/**
* Get an option value from a configurable target. If the method throws an exception then the default value
* is returned.
*
* @param configurable the configurable target
* @param option the option
* @param defaultValue the default value
* @return the value
*/
public static long getOption(Configurable configurable, Option option, long defaultValue) {
try {
final Long value = configurable.getOption(option);
return value == null ? defaultValue : value.longValue();
} catch (IOException e) {
return defaultValue;
}
}
/**
* Unwrap a nested channel type. If the channel does not wrap the target type, {@code null} is returned.
*
* @param targetType the class to unwrap
* @param channel the channel
* @param the type to unwrap
* @return the unwrapped type, or {@code null} if the given type is not wrapped
* @see WrappedChannel
*/
public static T unwrap(Class targetType, Channel channel) {
for (;;) {
if (channel == null) {
return null;
} else if (targetType.isInstance(channel)) {
return targetType.cast(channel);
} else if (channel instanceof WrappedChannel) {
channel = ((WrappedChannel>)channel).getChannel();
} else {
return null;
}
}
}
private static final FileChannel NULL_FILE_CHANNEL;
private static final ByteBuffer DRAIN_BUFFER = ByteBuffer.allocateDirect(16384);
/**
* Attempt to drain the given number of bytes from the stream source channel.
*
* @param channel the channel to drain
* @param count the number of bytes
* @return the number of bytes drained, 0 if reading the channel would block, or -1 if the EOF was reached
* @throws IOException if an error occurs
*/
public static long drain(StreamSourceChannel channel, long count) throws IOException {
long total = 0L, lres;
int ires;
ByteBuffer buffer = null;
for (;;) {
if (count == 0L) return total;
if (NULL_FILE_CHANNEL != null) {
while (count > 0) {
if ((lres = channel.transferTo(0, count, NULL_FILE_CHANNEL)) == 0L) {
break;
}
total += lres;
count -= lres;
}
// jump out quick if we drained the fast way
if (total > 0L) return total;
}
if (buffer == null) buffer = DRAIN_BUFFER.duplicate();
if ((long) buffer.limit() > count) buffer.limit((int) count);
ires = channel.read(buffer);
buffer.clear();
switch (ires) {
case -1: return total == 0L ? -1L : total;
case 0: return total;
default: total += (long) ires; count -= (long) ires;
}
}
}
/**
* Attempt to drain the given number of bytes from the readable byte channel.
*
* @param channel the channel to drain
* @param count the number of bytes
* @return the number of bytes drained, 0 if reading the channel would block, or -1 if the EOF was reached
* @throws IOException if an error occurs
*/
public static long drain(ReadableByteChannel channel, long count) throws IOException {
if (channel instanceof StreamSourceChannel) {
return drain((StreamSourceChannel) channel, count);
} else {
long total = 0L, lres;
int ires;
ByteBuffer buffer = null;
for (;;) {
if (count == 0L) return total;
if (NULL_FILE_CHANNEL != null) {
while (count > 0) {
if ((lres = NULL_FILE_CHANNEL.transferFrom(channel, 0, count)) == 0L) {
break;
}
total += lres;
count -= lres;
}
// jump out quick if we drained the fast way
if (total > 0L) return total;
}
if (buffer == null) buffer = DRAIN_BUFFER.duplicate();
if ((long) buffer.limit() > count) buffer.limit((int) count);
ires = channel.read(buffer);
buffer.clear();
switch (ires) {
case -1: return total == 0L ? -1L : total;
case 0: return total;
default: total += (long) ires; count -= (long) ires;
}
}
}
}
/**
* Attempt to drain the given number of bytes from the file channel. This does nothing more than force a
* read of bytes in the file.
*
* @param channel the channel to drain
* @param position the position to drain from
* @param count the number of bytes
* @return the number of bytes drained, 0 if reading the channel would block, or -1 if the EOF was reached
* @throws IOException if an error occurs
*/
public static long drain(FileChannel channel, long position, long count) throws IOException {
if (channel instanceof StreamSourceChannel) {
return drain((StreamSourceChannel) channel, count);
} else {
long total = 0L, lres;
int ires;
ByteBuffer buffer = null;
for (;;) {
if (count == 0L) return total;
if (NULL_FILE_CHANNEL != null) {
while (count > 0) {
if ((lres = channel.transferTo(position, count, NULL_FILE_CHANNEL)) == 0L) {
break;
}
total += lres;
count -= lres;
}
// jump out quick if we drained the fast way
if (total > 0L) return total;
}
if (buffer == null) buffer = DRAIN_BUFFER.duplicate();
if ((long) buffer.limit() > count) buffer.limit((int) count);
ires = channel.read(buffer);
buffer.clear();
switch (ires) {
case -1: return total == 0L ? -1L : total;
case 0: return total;
default: total += (long) ires;
}
}
}
}
/**
* Resume reads asynchronously. Queues a task on the channel's I/O thread to resume. Note that if a channel
* has multiple threads associated with it, the results may not be desirable.
*
* @param channel the channel to resume
*/
public static void resumeReadsAsync(final SuspendableReadChannel channel) {
final XnioIoThread ioThread = channel.getIoThread();
if (ioThread == Thread.currentThread()) {
channel.resumeReads();
} else {
ioThread.execute(new Runnable() {
public void run() {
channel.resumeReads();
}
});
}
}
/**
* Resume writes asynchronously. Queues a task on the channel's I/O thread to resume. Note that if a channel
* has multiple threads associated with it, the results may not be desirable.
*
* @param channel the channel to resume
*/
public static void resumeWritesAsync(final SuspendableWriteChannel channel) {
final XnioIoThread ioThread = channel.getIoThread();
if (ioThread == Thread.currentThread()) {
channel.resumeWrites();
} else {
ioThread.execute(new Runnable() {
public void run() {
channel.resumeWrites();
}
});
}
}
/**
* Writes out the data in the buffer to the channel. If all the data is written out
* then the channel will have its writes shutdown.
*
* @param channel The channel
* @param src The buffer
* @return The number of bytes written
* @throws IOException
*/
public static int writeFinalBasic(StreamSinkChannel channel, ByteBuffer src) throws IOException {
int res = channel.write(src);
if(!src.hasRemaining()) {
channel.shutdownWrites();
}
return res;
}
/**
* Writes out the data in the buffer to the channel. If all the data is written out
* then the channel will have its writes shutdown.
*
* @param channel The channel
* @param srcs The buffers
* @param offset The offset into the srcs array
* @param length The number buffers to write
* @return The number of bytes written
* @throws IOException
*/
public static long writeFinalBasic(StreamSinkChannel channel, ByteBuffer[] srcs, int offset, int length) throws IOException {
final long res = channel.write(srcs, offset, length);
if (!Buffers.hasRemaining(srcs, offset, length)) {
channel.shutdownWrites();
}
return res;
}
static {
NULL_FILE_CHANNEL = AccessController.doPrivileged(new PrivilegedAction() {
public FileChannel run() {
final String osName = System.getProperty("os.name", "unknown").toLowerCase(Locale.US);
try {
if (osName.contains("windows")) {
return new FileOutputStream("NUL:").getChannel();
} else {
return new FileOutputStream("/dev/null").getChannel();
}
} catch (FileNotFoundException e) {
throw new IOError(e);
}
}
});
}
}