/* * Copyright 2007-2010 Sun Microsystems, Inc. * * This file is part of Project Darkstar Server. * * Project Darkstar Server is free software: you can redistribute it * and/or modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation and * distributed hereunder to you. * * Project Darkstar Server 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 for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * Sun designates this particular file as subject to the "Classpath" * exception as provided by Sun in the LICENSE file that accompanied * this code. * * -- */ package com.sun.sgs.nio.channels; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; import java.io.Writer; import java.nio.ByteBuffer; import java.nio.channels.ClosedByInterruptException; import java.nio.channels.IllegalBlockingModeException; import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritableByteChannel; import java.nio.channels.spi.SelectorProvider; import java.nio.charset.CharsetDecoder; import java.nio.charset.CharsetEncoder; import java.nio.charset.UnsupportedCharsetException; import java.util.LinkedList; import java.util.List; import java.util.concurrent.ExecutionException; import com.sun.sgs.nio.channels.spi.AsynchronousChannelProvider; /** * Utility methods for channels and streams. * <p> * This class defines: * <ul> * <li>Static methods that support the interoperation of the stream * classes of the {@link java.io} package with the channel classes of * this package. * <li>Method to get the management interfaces for pools of channels * in the the Java virtual machine. * </ul> */ public final class Channels { /** Prevents instantiation of this class. */ private Channels() { } /** * Unwraps an {@code ExecutionException} and throws its cause. * * @param e the {@code ExecutionException} to unwrap * * @throws IOException if the cause was an {@code IOException} * @throws Error if the cause was an {@code Error} * @throws RuntimeException otherwise */ private static void launderExecutionException(ExecutionException e) throws IOException { Throwable t = e.getCause(); if (t instanceof IOException) { throw (IOException) t; } else if (t instanceof Error) { throw (Error) t; } else if (t instanceof RuntimeException) { throw (RuntimeException) t; } else { throw new RuntimeException(t.getMessage(), t); } } /** * Checks that the given offset and length are appropriate for the * array, otherwise throws {@link IndexOutOfBoundsException}. * * @param b the array * @param off the offset * @param len the length * @throws IndexOutOfBoundsException if the offset and length are * out of the array bounds */ private static void checkBounds(byte[] b, int off, int len) { if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0)) { throw new IndexOutOfBoundsException(); } } /** * Constructs a stream that reads bytes from the given channel. * <p> * The stream will not be buffered, and it will not support the * {@code mark} or {@code reset} methods. The stream will be safe for * access by multiple concurrent threads. Closing the stream will in * turn cause the channel to be closed. * * @param ch the channel from which bytes will be read * @return a new input stream */ public static InputStream newInputStream(final AsynchronousByteChannel ch) { return new InputStream() { @Override public synchronized int read() throws IOException { byte[] oneByte = new byte[1]; int rc = this.read(oneByte); return (rc == -1) ? -1 : oneByte[0]; } @Override public synchronized int read(byte[] b, int off, int len) throws IOException { checkBounds(b, off, len); if (len == 0) { return 0; } ByteBuffer buf = ByteBuffer.wrap(b); buf.position(off).limit(off + len); try { return ch.read(buf, null).get(); } catch (InterruptedException e) { ch.close(); Thread.currentThread().interrupt(); throw new ClosedByInterruptException(); } catch (ExecutionException e) { launderExecutionException(e); // always throws throw new AssertionError("unreachable"); } } @Override public void close() throws IOException { ch.close(); } }; } /** * Constructs a stream that writes bytes to the given channel. * <p> * The stream will not be buffered. The stream will be safe for access * by multiple concurrent threads. Closing the stream will in turn cause * the channel to be closed. * * @param ch the channel to which bytes will be written * @return a new output stream */ public static OutputStream newOutputStream(final AsynchronousByteChannel ch) { return new OutputStream() { @Override public synchronized void write(int b) throws IOException { byte[] oneByte = new byte[1]; oneByte[0] = (byte) b; write(oneByte); } @Override public synchronized void write(byte[] b, int off, int len) throws IOException { checkBounds(b, off, len); if (len == 0) { return; } ByteBuffer buf = ByteBuffer.wrap(b); buf.position(off).limit(off + len); try { while (buf.hasRemaining()) { ch.write(buf, null).get(); } } catch (InterruptedException e) { ch.close(); Thread.currentThread().interrupt(); throw new ClosedByInterruptException(); } catch (ExecutionException e) { launderExecutionException(e); // always throws throw new AssertionError("unreachable"); } } @Override public void close() throws IOException { ch.close(); } }; } /** * Returns a list of the {@link ChannelPoolMXBean} objects in the Java * virtual machine. * <p> * The list of {@code ChannelPoolMXBean} objects returned by this method * is an aggregation of the {@code ChannelPoolMXBean} objects obtained * from: * <ul> * <li>The system-wide default {@link SelectorProvider} if it * implements the {@link ManagedChannelFactory} interface. * <li>The system-wide default {@link AsynchronousChannelProvider} if * it implements the {@code ManagedChannelFactory} interface. * </ul> * The list of {@code ChannelPoolMXBeans} is returned in no * particular order, and the ordering may differ from one invocation to * the next. Whether the list is modifiable is implementation specific. * * @return a list of {@code ChannelPoolMXBean} objects */ public static List<ChannelPoolMXBean> getChannelPoolMXBeans() { List<ChannelPoolMXBean> result = new LinkedList<ChannelPoolMXBean>(); Object[] providers = new Object[] { SelectorProvider.provider(), AsynchronousChannelProvider.provider() }; for (Object provider : providers) { if (provider instanceof ManagedChannelFactory) { result.addAll( ((ManagedChannelFactory) provider).getChannelPoolMXBeans()); } } return result; } /** * Constructs a channel that reads bytes from the given stream. * * <p> The resulting channel will not be buffered; it will simply redirect * its I/O operations to the given stream. Closing the channel will in * turn cause the stream to be closed. </p> * * @param in * The stream from which bytes are to be read * * @return A new readable byte channel */ public static ReadableByteChannel newChannel(InputStream in) { return java.nio.channels.Channels.newChannel(in); } /** * Constructs a channel that writes bytes to the given stream. * * <p> The resulting channel will not be buffered; it will simply redirect * its I/O operations to the given stream. Closing the channel will in * turn cause the stream to be closed. </p> * * @param out * The stream to which bytes are to be written * * @return A new writable byte channel */ public static WritableByteChannel newChannel(OutputStream out) { return java.nio.channels.Channels.newChannel(out); } /** * Constructs a stream that reads bytes from the given channel. * * <p> The <tt>read</tt> methods of the resulting stream will throw an * {@link IllegalBlockingModeException} if invoked while the underlying * channel is in non-blocking mode. The stream will not be buffered, and * it will not support the {@link InputStream#mark mark} or {@link * InputStream#reset reset} methods. The stream will be safe for access by * multiple concurrent threads. Closing the stream will in turn cause the * channel to be closed. </p> * * @param ch * The channel from which bytes will be read * * @return A new input stream */ public static InputStream newInputStream(ReadableByteChannel ch) { return java.nio.channels.Channels.newInputStream(ch); } /** * Constructs a stream that writes bytes to the given channel. * * <p> The <tt>write</tt> methods of the resulting stream will throw an * {@link IllegalBlockingModeException} if invoked while the underlying * channel is in non-blocking mode. The stream will not be buffered. The * stream will be safe for access by multiple concurrent threads. Closing * the stream will in turn cause the channel to be closed. </p> * * @param ch * The channel to which bytes will be written * * @return A new output stream */ public static OutputStream newOutputStream(WritableByteChannel ch) { return java.nio.channels.Channels.newOutputStream(ch); } /** * Constructs a reader that decodes bytes from the given channel using the * given decoder. * * <p> The resulting stream will contain an internal input buffer of at * least <tt>minBufferCap</tt> bytes. The stream's <tt>read</tt> methods * will, as needed, fill the buffer by reading bytes from the underlying * channel; if the channel is in non-blocking mode when bytes are to be * read then an {@link IllegalBlockingModeException} will be thrown. The * resulting stream will not otherwise be buffered, and it will not support * the {@link Reader#mark mark} or {@link Reader#reset reset} methods. * Closing the stream will in turn cause the channel to be closed. </p> * * @param ch * The channel from which bytes will be read * * @param dec * The charset decoder to be used * * @param minBufferCap * The minimum capacity of the internal byte buffer, * or <tt>-1</tt> if an implementation-dependent * default capacity is to be used * * @return A new reader */ public static Reader newReader(ReadableByteChannel ch, CharsetDecoder dec, int minBufferCap) { return java.nio.channels.Channels.newReader(ch, dec, minBufferCap); } /** * Constructs a reader that decodes bytes from the given channel according * to the named charset. * * <p> An invocation of this method of the form * * <blockquote><pre> * Channels.newReader(ch, csname)</pre></blockquote> * * behaves in exactly the same way as the expression * * <blockquote><pre> * Channels.newReader(ch, * Charset.forName(csName) * .newDecoder(), * -1);</pre></blockquote> * * @param ch * The channel from which bytes will be read * * @param csName * The name of the charset to be used * * @return A new reader * * @throws UnsupportedCharsetException * If no support for the named charset is available * in this instance of the Java virtual machine */ public static Reader newReader(ReadableByteChannel ch, String csName) { return java.nio.channels.Channels.newReader(ch, csName); } /** * Constructs a writer that encodes characters using the given encoder and * writes the resulting bytes to the given channel. * * <p> The resulting stream will contain an internal output buffer of at * least <tt>minBufferCap</tt> bytes. The stream's <tt>write</tt> methods * will, as needed, flush the buffer by writing bytes to the underlying * channel; if the channel is in non-blocking mode when bytes are to be * written then an {@link IllegalBlockingModeException} will be thrown. * The resulting stream will not otherwise be buffered. Closing the stream * will in turn cause the channel to be closed. </p> * * @param ch * The channel to which bytes will be written * * @param enc * The charset encoder to be used * * @param minBufferCap * The minimum capacity of the internal byte buffer, * or <tt>-1</tt> if an implementation-dependent * default capacity is to be used * * @return A new writer */ public static Writer newWriter(WritableByteChannel ch, CharsetEncoder enc, int minBufferCap) { return java.nio.channels.Channels.newWriter(ch, enc, minBufferCap); } /** * Constructs a writer that encodes characters according to the named * charset and writes the resulting bytes to the given channel. * * <p> An invocation of this method of the form * * <blockquote><pre> * Channels.newWriter(ch, csname)</pre></blockquote> * * behaves in exactly the same way as the expression * * <blockquote><pre> * Channels.newWriter(ch, * Charset.forName(csName) * .newEncoder(), * -1);</pre></blockquote> * * @param ch * The channel to which bytes will be written * * @param csName * The name of the charset to be used * * @return A new writer * * @throws UnsupportedCharsetException * If no support for the named charset is available * in this instance of the Java virtual machine */ public static Writer newWriter(WritableByteChannel ch, String csName) { return java.nio.channels.Channels.newWriter(ch, csName); } }