1. /*
  2. * @(#)Channels.java 1.23 03/12/19
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package java.nio.channels;
  8. import java.io.FileInputStream;
  9. import java.io.FileOutputStream;
  10. import java.io.InputStream;
  11. import java.io.OutputStream;
  12. import java.io.Reader;
  13. import java.io.Writer;
  14. import java.io.IOException;
  15. import java.nio.ByteBuffer;
  16. import java.nio.CharBuffer;
  17. import java.nio.BufferOverflowException;
  18. import java.nio.BufferUnderflowException;
  19. import java.nio.charset.Charset;
  20. import java.nio.charset.CharsetDecoder;
  21. import java.nio.charset.CharsetEncoder;
  22. import java.nio.charset.CoderResult;
  23. import java.nio.charset.UnsupportedCharsetException;
  24. import java.nio.channels.spi.AbstractInterruptibleChannel;
  25. import sun.nio.ch.ChannelInputStream;
  26. import sun.nio.cs.StreamDecoder;
  27. import sun.nio.cs.StreamEncoder;
  28. /**
  29. * Utility methods for channels and streams.
  30. *
  31. * <p> This class defines static methods that support the interoperation of the
  32. * stream classes of the <tt>{@link java.io}</tt> package with the channel
  33. * classes of this package. </p>
  34. *
  35. *
  36. * @author Mark Reinhold
  37. * @author Mike McCloskey
  38. * @author JSR-51 Expert Group
  39. * @version 1.23, 03/12/19
  40. * @since 1.4
  41. */
  42. public final class Channels {
  43. private Channels() { } // No instantiation
  44. private static int write(WritableByteChannel ch, ByteBuffer bb)
  45. throws IOException
  46. {
  47. if (ch instanceof SelectableChannel) {
  48. SelectableChannel sc = (SelectableChannel)ch;
  49. synchronized (sc.blockingLock()) {
  50. if (!sc.isBlocking())
  51. throw new IllegalBlockingModeException();
  52. return ch.write(bb);
  53. }
  54. } else {
  55. return ch.write(bb);
  56. }
  57. }
  58. // -- Byte streams from channels --
  59. /**
  60. * Constructs a stream that reads bytes from the given channel.
  61. *
  62. * <p> The <tt>read</tt> methods of the resulting stream will throw an
  63. * {@link IllegalBlockingModeException} if invoked while the underlying
  64. * channel is in non-blocking mode. The stream will not be buffered, and
  65. * it will not support the {@link InputStream#mark mark} or {@link
  66. * InputStream#reset reset} methods. The stream will be safe for access by
  67. * multiple concurrent threads. Closing the stream will in turn cause the
  68. * channel to be closed. </p>
  69. *
  70. * @param ch
  71. * The channel from which bytes will be read
  72. *
  73. * @return A new input stream
  74. */
  75. public static InputStream newInputStream(ReadableByteChannel ch) {
  76. return new sun.nio.ch.ChannelInputStream(ch);
  77. }
  78. /**
  79. * Constructs a stream that writes bytes to the given channel.
  80. *
  81. * <p> The <tt>write</tt> methods of the resulting stream will throw an
  82. * {@link IllegalBlockingModeException} if invoked while the underlying
  83. * channel is in non-blocking mode. The stream will not be buffered. The
  84. * stream will be safe for access by multiple concurrent threads. Closing
  85. * the stream will in turn cause the channel to be closed. </p>
  86. *
  87. * @param ch
  88. * The channel to which bytes will be written
  89. *
  90. * @return A new output stream
  91. */
  92. public static OutputStream newOutputStream(final WritableByteChannel ch) {
  93. return new OutputStream() {
  94. private ByteBuffer bb = null;
  95. private byte[] bs = null; // Invoker's previous array
  96. private byte[] b1 = null;
  97. public synchronized void write(int b) throws IOException {
  98. if (b1 == null)
  99. b1 = new byte[1];
  100. b1[0] = (byte)b;
  101. this.write(b1);
  102. }
  103. public synchronized void write(byte[] bs, int off, int len)
  104. throws IOException
  105. {
  106. if ((off < 0) || (off > bs.length) || (len < 0) ||
  107. ((off + len) > bs.length) || ((off + len) < 0)) {
  108. throw new IndexOutOfBoundsException();
  109. } else if (len == 0) {
  110. return;
  111. }
  112. ByteBuffer bb = ((this.bs == bs)
  113. ? this.bb
  114. : ByteBuffer.wrap(bs));
  115. bb.limit(Math.min(off + len, bb.capacity()));
  116. bb.position(off);
  117. this.bb = bb;
  118. this.bs = bs;
  119. Channels.write(ch, bb);
  120. }
  121. public void close() throws IOException {
  122. ch.close();
  123. }
  124. };
  125. }
  126. // -- Channels from streams --
  127. /**
  128. * Constructs a channel that reads bytes from the given stream.
  129. *
  130. * <p> The resulting channel will not be buffered; it will simply redirect
  131. * its I/O operations to the given stream. Closing the channel will in
  132. * turn cause the stream to be closed. </p>
  133. *
  134. * @param in
  135. * The stream from which bytes are to be read
  136. *
  137. * @return A new readable byte channel
  138. */
  139. public static ReadableByteChannel newChannel(final InputStream in) {
  140. if (in instanceof FileInputStream) {
  141. String inClass = in.getClass().toString();
  142. if (inClass.equals("java.io.FileInputStream"))
  143. return ((FileInputStream)in).getChannel();
  144. }
  145. return new ReadableByteChannelImpl(in);
  146. }
  147. private static class ReadableByteChannelImpl
  148. extends AbstractInterruptibleChannel // Not really interruptible
  149. implements ReadableByteChannel
  150. {
  151. InputStream in;
  152. private static final int TRANSFER_SIZE = 8192;
  153. private byte buf[] = new byte[0];
  154. private boolean open = true;
  155. private Object readLock = new Object();
  156. ReadableByteChannelImpl(InputStream in) {
  157. this.in = in;
  158. }
  159. public int read(ByteBuffer dst) throws IOException {
  160. int len = dst.remaining();
  161. int totalRead = 0;
  162. int bytesRead = 0;
  163. synchronized (readLock) {
  164. while (totalRead < len) {
  165. int bytesToRead = Math.min((len - totalRead),
  166. TRANSFER_SIZE);
  167. if (buf.length < bytesToRead)
  168. buf = new byte[bytesToRead];
  169. if ((totalRead > 0) && !(in.available() > 0))
  170. break; // block at most once
  171. try {
  172. begin();
  173. bytesRead = in.read(buf, 0, bytesToRead);
  174. } finally {
  175. end(bytesRead > 0);
  176. }
  177. if (bytesRead < 0)
  178. break;
  179. else
  180. totalRead += bytesRead;
  181. dst.put(buf, 0, bytesRead);
  182. }
  183. if ((bytesRead < 0) && (totalRead == 0))
  184. return -1;
  185. return totalRead;
  186. }
  187. }
  188. protected void implCloseChannel() throws IOException {
  189. in.close();
  190. open = false;
  191. }
  192. }
  193. /**
  194. * Constructs a channel that writes bytes to the given stream.
  195. *
  196. * <p> The resulting channel will not be buffered; it will simply redirect
  197. * its I/O operations to the given stream. Closing the channel will in
  198. * turn cause the stream to be closed. </p>
  199. *
  200. * @param out
  201. * The stream to which bytes are to be written
  202. *
  203. * @return A new writable byte channel
  204. */
  205. public static WritableByteChannel newChannel(final OutputStream out) {
  206. if (out instanceof FileOutputStream) {
  207. String outClass = out.getClass().toString();
  208. if (outClass.equals("java.io.FileOutputStream"))
  209. return ((FileOutputStream)out).getChannel();
  210. }
  211. return new WritableByteChannelImpl(out);
  212. }
  213. private static class WritableByteChannelImpl
  214. extends AbstractInterruptibleChannel // Not really interruptible
  215. implements WritableByteChannel
  216. {
  217. OutputStream out;
  218. private static final int TRANSFER_SIZE = 8192;
  219. private byte buf[] = new byte[0];
  220. private boolean open = true;
  221. private Object writeLock = new Object();
  222. WritableByteChannelImpl(OutputStream out) {
  223. this.out = out;
  224. }
  225. public int write(ByteBuffer src) throws IOException {
  226. int len = src.remaining();
  227. int totalWritten = 0;
  228. synchronized (writeLock) {
  229. while (totalWritten < len) {
  230. int bytesToWrite = Math.min((len - totalWritten),
  231. TRANSFER_SIZE);
  232. if (buf.length < bytesToWrite)
  233. buf = new byte[bytesToWrite];
  234. src.get(buf, 0, bytesToWrite);
  235. try {
  236. begin();
  237. out.write(buf, 0, bytesToWrite);
  238. } finally {
  239. end(bytesToWrite > 0);
  240. }
  241. totalWritten += bytesToWrite;
  242. }
  243. return totalWritten;
  244. }
  245. }
  246. protected void implCloseChannel() throws IOException {
  247. out.close();
  248. open = false;
  249. }
  250. }
  251. // -- Character streams from channels --
  252. /**
  253. * Constructs a reader that decodes bytes from the given channel using the
  254. * given decoder.
  255. *
  256. * <p> The resulting stream will contain an internal input buffer of at
  257. * least <tt>minBufferCap</tt> bytes. The stream's <tt>read</tt> methods
  258. * will, as needed, fill the buffer by reading bytes from the underlying
  259. * channel; if the channel is in non-blocking mode when bytes are to be
  260. * read then an {@link IllegalBlockingModeException} will be thrown. The
  261. * resulting stream will not otherwise be buffered, and it will not support
  262. * the {@link Reader#mark mark} or {@link Reader#reset reset} methods.
  263. * Closing the stream will in turn cause the channel to be closed. </p>
  264. *
  265. * @param ch
  266. * The channel from which bytes will be read
  267. *
  268. * @param dec
  269. * The charset decoder to be used
  270. *
  271. * @param minBufferCap
  272. * The minimum capacity of the internal byte buffer,
  273. * or <tt>-1</tt> if an implementation-dependent
  274. * default capacity is to be used
  275. *
  276. * @return A new reader
  277. */
  278. public static Reader newReader(ReadableByteChannel ch,
  279. CharsetDecoder dec,
  280. int minBufferCap)
  281. {
  282. dec.reset();
  283. return StreamDecoder.forDecoder(ch, dec, minBufferCap);
  284. }
  285. /**
  286. * Constructs a reader that decodes bytes from the given channel according
  287. * to the named charset.
  288. *
  289. * <p> An invocation of this method of the form
  290. *
  291. * <blockquote><pre>
  292. * Channels.newReader(ch, csname)</pre></blockquote>
  293. *
  294. * behaves in exactly the same way as the expression
  295. *
  296. * <blockquote><pre>
  297. * Channels.newReader(ch,
  298. * Charset.forName(csName)
  299. * .newDecoder(),
  300. * -1);</pre></blockquote>
  301. *
  302. * @param ch
  303. * The channel from which bytes will be read
  304. *
  305. * @param csName
  306. * The name of the charset to be used
  307. *
  308. * @return A new reader
  309. *
  310. * @throws UnsupportedCharsetException
  311. * If no support for the named charset is available
  312. * in this instance of the Java virtual machine
  313. */
  314. public static Reader newReader(ReadableByteChannel ch,
  315. String csName)
  316. {
  317. return newReader(ch, Charset.forName(csName).newDecoder(), -1);
  318. }
  319. /**
  320. * Constructs a writer that encodes characters using the given encoder and
  321. * writes the resulting bytes to the given channel.
  322. *
  323. * <p> The resulting stream will contain an internal output buffer of at
  324. * least <tt>minBufferCap</tt> bytes. The stream's <tt>write</tt> methods
  325. * will, as needed, flush the buffer by writing bytes to the underlying
  326. * channel; if the channel is in non-blocking mode when bytes are to be
  327. * written then an {@link IllegalBlockingModeException} will be thrown.
  328. * The resulting stream will not otherwise be buffered. Closing the stream
  329. * will in turn cause the channel to be closed. </p>
  330. *
  331. * @param ch
  332. * The channel to which bytes will be written
  333. *
  334. * @param enc
  335. * The charset encoder to be used
  336. *
  337. * @param minBufferCap
  338. * The minimum capacity of the internal byte buffer,
  339. * or <tt>-1</tt> if an implementation-dependent
  340. * default capacity is to be used
  341. *
  342. * @return A new writer
  343. */
  344. public static Writer newWriter(final WritableByteChannel ch,
  345. final CharsetEncoder enc,
  346. final int minBufferCap)
  347. {
  348. enc.reset();
  349. return StreamEncoder.forEncoder(ch, enc, minBufferCap);
  350. }
  351. /**
  352. * Constructs a writer that encodes characters according to the named
  353. * charset and writes the resulting bytes to the given channel.
  354. *
  355. * <p> An invocation of this method of the form
  356. *
  357. * <blockquote><pre>
  358. * Channels.newWriter(ch, csname)</pre></blockquote>
  359. *
  360. * behaves in exactly the same way as the expression
  361. *
  362. * <blockquote><pre>
  363. * Channels.newWriter(ch,
  364. * Charset.forName(csName)
  365. * .newEncoder(),
  366. * -1);</pre></blockquote>
  367. *
  368. * @param ch
  369. * The channel to which bytes will be written
  370. *
  371. * @param csName
  372. * The name of the charset to be used
  373. *
  374. * @return A new writer
  375. *
  376. * @throws UnsupportedCharsetException
  377. * If no support for the named charset is available
  378. * in this instance of the Java virtual machine
  379. */
  380. public static Writer newWriter(WritableByteChannel ch,
  381. String csName)
  382. {
  383. return newWriter(ch, Charset.forName(csName).newEncoder(), -1);
  384. }
  385. }