/* * @(#)ConnectionBaseAdapter.java 1.7 06/10/10 * * Copyright 1990-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER * * This program 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 program 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 at /legal/license.txt). * * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa * Clara, CA 95054 or visit www.sun.com if you need additional * information or have any questions. */ package com.sun.cdc.io; import com.sun.cdc.io.ConnectionBaseInterface; import com.sun.cdc.io.GeneralBase; //import com.sun.midp.midlet.*; //import com.sun.midp.security.*; import java.io.*; import javax.microedition.io.*; /** * Protocol classes extend this class to gain some of the common functionality * needed to implement a CDC Generic Connection. * <p> * The common functionality includes:</p> * <ul> * <li>Supplies the input and output stream classes for a StreamConnection</li> * <li>Limits the number of streams opened according to mode, but the limit * can be overridden. Read-write allows 1 input and 1 output, write-only * allows 1 output, read-only allows 1 input</li> * <li>Only "disconnects" when the connection and all streams are closed</li> * <li>Throws I/O exceptions when used after being closed</li> * <li>Provides a more efficient implementation of * {@link InputStream#read(byte[], int, int)}, which is called by * {@link InputStream#read()} * <li>Provides a more efficient implementation of * {@link OutputStream#write(byte[], int, int)}, which is called by * {@link OutputStream#write(int)} * </ul> * <p align="center"> * <b>Class Relationship Diagram</b></p> * <p align="center"> * <img src="doc-files/ConnectionBaseAdapter.gif" border=0></p> * * @version 3.0 9/1/2000 */ public abstract class ConnectionBaseAdapter implements ConnectionBaseInterface, StreamConnection { /** Flag indicating if the connection is open. */ protected boolean connectionOpen = false; /** Number of input streams that were opened. */ protected static int iStreams = 0; /** * Maximum number of open input streams. Set this * to zero to prevent openInputStream from giving out a stream in * write-only mode. */ protected int maxIStreams = 1; /** Number of output streams were opened. */ protected static int oStreams = 0; /** * Maximum number of output streams. Set this * to zero to prevent openOutputStream from giving out a stream in * read-only mode. */ protected int maxOStreams = 1; /** * Check for required permission and open a connection to a target. * * @param name URL for the connection, without the * without the protocol part * @param mode I/O access mode, see {@link Connector} * @param timeouts flag to indicate that the caller * wants timeout exceptions * @return this Connection object * * @exception IllegalArgumentException If a parameter is invalid. * @exception ConnectionNotFoundException If the connection cannot * be found. * @exception IOException If some other kind of I/O error occurs. */ public Connection openPrim(String name, int mode, boolean timeouts) throws IOException { /// checkForPermission(null, name); // Give the subclass a chance to check /// checkForPermission(name); // Give the subclass a chance to check switch (mode) { case Connector.READ: case Connector.WRITE: case Connector.READ_WRITE: break; default: throw new IllegalArgumentException("Illegal mode"); } connect(name, mode, timeouts); connectionOpen = true; return this; } /** * Overridden by Protocols to check for permissions. * This implementation always throws a security exception. * The subclass is responsible for checking permissions and * maintaining the state (in private local fields) as to whether * it was granted. * * @param token security token of the calling class or null * @param name the URL of the connection without the protocol * * @exception SecurityException if permissions are not granted * @exception InterruptedIOException if I/O associated with permissions is interrupted */ /// protected void checkForPermission(SecurityToken token, String name) protected void checkForPermission() throws SecurityException, InterruptedIOException { throw new SecurityException("Permission not granted"); } /** * Utility method to check for the required permission, and handle * prompts, etc. A SecurityToken may be supplied, in which case * the permission must be allowed by the token. If the token is * <code>null</code> then the permission is checked in the current * app. If there is no app then the permission * is allowed. A SecurityException is thrown when the permission * is not granted. * * @param token security token of the calling class or null * @param requiredPermission the permission that is needed * @param name resource to insert into the permission question * @param protocol the protocol string used in the resource name * * @exception SecurityException if the permission is not granted * @exception InterruptedIOException if another thread interrupts the * calling thread while this method is waiting to preempt the * display. */ /// protected final void checkForPermission(SecurityToken token, /// int requiredPermission, /// String name, String protocol) /// protected final void checkForPermission() /// throws InterruptedIOException ///{ /// If a security token was supplied, use it for the check /// if (token != null) { /// token.checkIfPermissionAllowed(requiredPermission); /// return; ///} /// Scheduler scheduler; /// MIDletSuite midletSuite; /// scheduler = Scheduler.getScheduler(); /// midletSuite = scheduler.getMIDletSuite(); /// there is no suite running when installing from the command line /// if (midletSuite != null) { /// if (protocol != null) { /// name = protocol + ":" + name; /// } /// try { /// midletSuite.checkForPermission(requiredPermission, name); /// } catch (InterruptedException ie) { /// throw new InterruptedIOException( /// "Interrupted while trying to ask the user permission"); /// } ///} ///} /** * Check for the required permission and open a connection to a target. * This method can be used with permissions greater than * the current app. * * @param token security token of the calling class * @param name URL for the connection, without the * without the protocol part * @param mode I/O access mode, see {@link Connector} * @param timeouts flag to indicate that the caller * wants timeout exceptions * @return this Connection object * * @exception IllegalArgumentException If a parameter is invalid. * @exception ConnectionNotFoundException If the connection cannot * be found. * @exception IOException If some other kind of I/O error occurs. */ /// public Connection openPrim(SecurityToken token, String name, int mode, /// boolean timeouts) throws IOException { /// checkForPermission(token, name); /// public Connection openPrim(String name, int mode, /// boolean timeouts) throws IOException { /// checkForPermission(); /// return openPrim(name, mode, timeouts); /// } /** * Check for required permission and open a connection to a target. * This method can be used with permissions greater than * the current app. Assume read/write and no timeouts. * * @param token security token of the calling class * @param name URL for the connection, without the * without the protocol part * @return this Connection object * * @exception IllegalArgumentException If a parameter is invalid. * @exception ConnectionNotFoundException If the connection cannot * be found. * @exception IOException If some other kind of I/O error occurs. */ /// public Connection openPrim(SecurityToken token, String name) /// throws IOException { /// return openPrim(token, name, Connector.READ_WRITE, false); public Connection openPrim(String name) throws IOException { return openPrim(name, Connector.READ_WRITE, false); } /** * Returns an input stream. * * @return an input stream for writing bytes to this port. * @exception IOException if an I/O error occurs when creating the * output stream. */ public InputStream openInputStream() throws IOException { InputStream i; ensureOpen(); /* Fix for CR 6246819: Comment out MIDP code that limits streams so that multiple streams are supported for CDC */ /*if (maxIStreams == 0) { throw new IOException("no more input streams available"); }*/ i = new BaseInputStream(this); //maxIStreams--; iStreams++; return i; } /** * Open and return a data input stream for a connection. * * @return An input stream * @exception IOException If an I/O error occurs */ public DataInputStream openDataInputStream() throws IOException { return new DataInputStream(openInputStream()); } /** * Returns an output stream. * * @return an output stream for writing bytes to this port. * @exception IOException if an I/O error occurs when creating the * output stream. */ public OutputStream openOutputStream() throws IOException { OutputStream o; ensureOpen(); /* Fix for CR 6246819: Comment out MIDP code that limits streams so that multiple streams are supported for CDC */ /*if (maxOStreams == 0) { throw new IOException("no more output streams available"); }*/ o = new BaseOutputStream(this); //maxOStreams--; oStreams++; return o; } /** * Open and return a data output stream for a connection. * * @return An input stream * @exception IOException If an I/O error occurs */ public DataOutputStream openDataOutputStream() throws IOException { return new DataOutputStream(openOutputStream()); } /** * Close the connection. * * @exception IOException if an I/O error occurs when closing the * connection. */ public void close() throws IOException { if (connectionOpen) { connectionOpen = false; closeCommon(); } } /** * Called once by each child input stream. * If the input stream is marked open, it will be marked closed and * the if the connection and output stream are closed the disconnect * method will be called. * * @exception IOException if the subclass throws one */ protected void closeInputStream() throws IOException { if (iStreams>0) { iStreams--; closeCommon(); } } /** * Called once by each child output stream. * If the output stream is marked open, it will be marked closed and * the if the connection and input stream are closed the disconnect * method will be called. * * @exception IOException if the subclass throws one */ protected void closeOutputStream() throws IOException { if (oStreams>0) { oStreams--; closeCommon(); } } /** * Disconnect if the connection and all the streams and the closed. * * @exception IOException if an I/O error occurs when closing the * connection. */ void closeCommon() throws IOException { if (!connectionOpen && iStreams == 0 && oStreams == 0) { disconnect(); } } /** * Check if the connection is open. * * @exception IOException is thrown, if the stream is not open. */ protected void ensureOpen() throws IOException { if (!connectionOpen) { throw new IOException("Connection closed"); } } /** * Check if the streams are open. * * @exception IOException is thrown, if the stream is still open. */ protected void ensureNoStreamsOpen() throws IOException { if ((iStreams > 0) || (oStreams > 0)) { throw new IOException("Stream is still open"); } } /** * Connect to a target. * * @param name URL for the connection, without the protocol * part * @param mode I/O access mode, see {@link Connector} * @param timeouts flag to indicate that the called wants * timeout exceptions * * @exception IllegalArgumentException If a parameter is invalid. * @exception ConnectionNotFoundException If the connection cannot be * found. * @exception IOException If some other kind of I/O error occurs. */ protected abstract void connect(String name, int mode, boolean timeouts) throws IOException; /** * Free up the connection resources. * * @exception IOException if an I/O error occurs. */ protected abstract void disconnect() throws IOException; /** * Reads up to <code>len</code> bytes of data from the input stream into * an array of bytes, blocks until at least one byte is available. * * @param b the buffer into which the data is read. * @param off the start offset in array <code>b</code> * at which the data is written. * @param len the maximum number of bytes to read. * @return the total number of bytes read into the buffer, or * <code>-1</code> if there is no more data because the end of * the stream has been reached. * @exception IOException if an I/O error occurs. */ protected abstract int readBytes(byte b[], int off, int len) throws IOException; /** * Returns the number of bytes that can be read (or skipped over) from * this input stream without blocking by the next caller of a method for * this input stream. The next caller might be the same thread or * another thread. This classes implementation always returns * <code>0</code>. It is up to subclasses to override this method. * * @return the number of bytes that can be read from this input stream * without blocking. * @exception IOException if an I/O error occurs. */ public int available() throws IOException { return 0; } /** * Writes <code>len</code> bytes from the specified byte array * starting at offset <code>off</code> to this output stream. * <p> * Polling the native code is done here to allow for simple * asynchronous native code to be written. Not all implementations * work this way (they block in the native code) but the same * Java code works for both. * * @param b the data. * @param off the start offset in the data. * @param len the number of bytes to write. * @return number of bytes written * @exception IOException if an I/O error occurs. In particular, * an <code>IOException</code> is thrown if the output * stream is closed. */ protected abstract int writeBytes(byte b[], int off, int len) throws IOException; /** * Forces any buffered output bytes to be written out. * The general contract of <code>flush</code> is * that calling it is an indication that, if any bytes previously * written that have been buffered by the connection, * should immediately be written to their intended destination. * <p> * The <code>flush</code> method of <code>ConnectionBaseAdapter</code> * does nothing. * * @exception IOException if an I/O error occurs. */ protected void flush() throws IOException { } /** * Tests if input stream for a connection supports the <code>mark</code> and * <code>reset</code> methods. * * <p> The <code>markSupported</code> method of * <code>ConnectionBaseAdapter</code> returns <code>false</code>. * * <p> Subclasses should override this method if they support own mark/reset * functionality. * * @return <code>true</code> if input stream for this connection supports * the <code>mark</code> and <code>reset</code> methods; * <code>false</code> otherwise. * @see java.io.InputStream#mark(int) * @see java.io.InputStream#reset() */ public boolean markSupported() { return false; } /** * Marks the current position in input stream for a connection. * A subsequent call to the <code>reset</code> method repositions this * stream at the last marked position so that subsequent reads re-read * the same bytes. * * <p> The <code>mark</code> method of <code>ConnectionBaseAdapter</code> * does nothing. * * <p> Subclasses should override this method if they support own mark/reset * functionality. * * @param readlimit the maximum limit of bytes that can be read before * the mark position becomes invalid. * @see java.io.InputStream#reset() */ public synchronized void mark(int readlimit) {} /** * Repositions input stream for a connection to the position at the time the * <code>mark</code> method was last called on this input stream. * * <p> The method <code>reset</code> for <code>ConnectionBaseAdapter</code> * class does nothing and always throws an <code>IOException</code>. * * <p> Subclasses should override this method if they support own mark/reset * functionality. * * @exception IOException if this stream has not been marked or if the * mark has been invalidated. * @see java.io.InputStream#reset() * @see java.io.InputStream#mark(int) * @see java.io.IOException */ public synchronized void reset() throws IOException { throw new IOException("mark/reset not supported"); } } /** * Input stream for the connection */ class BaseInputStream extends InputStream { /** Pointer to the connection */ private ConnectionBaseAdapter parent; /** Buffer for single char reads */ byte[] buf = new byte[1]; /** * Buffer for mark/reset funtionality support. * <code>null</code> value indicates <code>mark</code> was not called or * <code>readlimit</code> value of the last <code>mark</code> was exceeded. */ byte[] markBuf = null; /** The size of data stored in <code>markBuf</code>. */ int markSize = 0; /** Current position in <code>markBuf</code> to read data from. */ int markPos = 0; /** * Indicates whether <code>reset</code> method was called. * If so, data is read from <code>markBuf</code> buffer, * otherwise via <code>parent.readBytes</code> method. */ boolean isReadFromBuffer = false; /** * Constructs a BaseInputStream for a ConnectionBaseAdapter. * * @param parent pointer to the connection object * * @exception IOException if an I/O error occurs. */ BaseInputStream(ConnectionBaseAdapter parent) throws IOException { this.parent = parent; } /** * Check the stream is open * * @exception InterruptedIOException if it is not. */ private void ensureOpen() throws InterruptedIOException { if (parent == null) { throw new InterruptedIOException("Stream closed"); } } /** * Returns the number of bytes that can be read (or skipped over) from * this input stream without blocking by the next caller of a method for * this input stream. The next caller might be the same thread or * another thread. * * <p>The <code>available</code> method always returns <code>0</code> if * {@link ConnectionBaseAdapter#available()} is * not overridden by the subclass. * * @return the number of bytes that can be read from this input stream * without blocking. * @exception IOException if an I/O error occurs. */ public int available() throws IOException { ensureOpen(); return parent.available(); } /** * Reads the next byte of data from the input stream. The value byte is * returned as an <code>int</code> in the range <code>0</code> to * <code>255</code>. If no byte is available because the end of the stream * has been reached, the value <code>-1</code> is returned. This method * blocks until input data is available, the end of the stream is detected, * or an exception is thrown. * * @return the next byte of data, or <code>-1</code> if the end of the * stream is reached. * @exception IOException if an I/O error occurs. */ public int read() throws IOException { if (read(buf, 0, 1) > 0) { return (buf[0] & 0xFF); } return -1; } /** * Reads up to <code>len</code> bytes of data from the input stream into * an array of bytes. An attempt is made to read as many as * <code>len</code> bytes, but a smaller number may be read, possibly * zero. The number of bytes actually read is returned as an integer. * * <p> This method blocks until input data is available, end of file is * detected, or an exception is thrown. * * <p> If <code>b</code> is <code>null</code>, a * <code>NullPointerException</code> is thrown. * * <p> If <code>off</code> is negative, or <code>len</code> is negative, or * <code>off+len</code> is greater than the length of the array * <code>b</code>, then an <code>IndexOutOfBoundsException</code> is * thrown. * * <p> If <code>len</code> is zero, then no bytes are read and * <code>0</code> is returned; otherwise, there is an attempt to read at * least one byte. If no byte is available because the stream is at end of * file, the value <code>-1</code> is returned; otherwise, at least one * byte is read and stored into <code>b</code>. * * <p> The first byte read is stored into element <code>b[off]</code>, the * next one into <code>b[off+1]</code>, and so on. The number of bytes read * is, at most, equal to <code>len</code>. Let <i>k</i> be the number of * bytes actually read; these bytes will be stored in elements * <code>b[off]</code> through <code>b[off+</code><i>k</i><code>-1]</code>, * leaving elements <code>b[off+</code><i>k</i><code>]</code> through * <code>b[off+len-1]</code> unaffected. * * <p> In every case, elements <code>b[0]</code> through * <code>b[off]</code> and elements <code>b[off+len]</code> through * <code>b[b.length-1]</code> are unaffected. * * <p> If the first byte cannot be read for any reason other than end of * file, then an <code>IOException</code> is thrown. In particular, an * <code>IOException</code> is thrown if the input stream has been closed. * * @param b the buffer into which the data is read. * @param off the start offset in array <code>b</code> * at which the data is written. * @param len the maximum number of bytes to read. * @return the total number of bytes read into the buffer, or * <code>-1</code> if there is no more data because the end of * the stream has been reached. * @exception IOException if an I/O error occurs. * @see java.io.InputStream#read() */ public int read(byte b[], int off, int len) throws IOException { int test; ensureOpen(); if (len == 0) { return 0; } /* * test the parameters so the subclass will not have to. * this will avoid crashes in the native code */ test = b[off] + b[len - 1] + b[off + len - 1]; // use parent's mark/reset functionality // if the parent supports the own one if (parent.markSupported()) { return parent.readBytes(b, off, len); } // read data from mark buffer if reset method was called if (isReadFromBuffer) { int dataSize = markSize - markPos; if (dataSize > 0) { int copySize = (dataSize > len) ? len : dataSize; System.arraycopy(markBuf, markPos, b, off, copySize); markPos += copySize; // read data directly from the stream // if size of data in the buffer is not enough int readSize = 0; if (copySize < len) { readSize = parent.readBytes( b, off + copySize, len - copySize); // check if eos is reached if (readSize == -1) { readSize = 0; } else { // check the mark buffer overflow if (markSize + readSize > markBuf.length) { markBuf = null; // cache the data in the mark buffer } else { System.arraycopy( b, off + copySize, markBuf, markSize, readSize); markSize += readSize; } } isReadFromBuffer = false; } return copySize + readSize; } else { isReadFromBuffer = false; } } int readSize = parent.readBytes(b, off, len); // fill mark buffer if exists if (markBuf != null) { if (readSize > 0) { // check the mark buffer overflow if (markSize + readSize > markBuf.length) { markBuf = null; // cache the data in the mark buffer } else { System.arraycopy(b, off, markBuf, markSize, readSize); markSize += readSize; } } } return readSize; } /** * Closes this input stream and releases any system resources associated * with the stream. * * @exception IOException if an I/O error occurs. */ public void close() throws IOException { if (parent != null) { parent.closeInputStream(); parent = null; } // free buffer used by mark/reset operations if it was allocated markBuf = null; } /** * Tests if this input stream supports the <code>mark</code> and * <code>reset</code> methods. * * <p>The <code>markSupported</code> method of * <code>BaseInputStream</code> returns <code>true</code>. * * @return always <code>true</code> * * @see BaseInputStream#mark(int) * @see BaseInputStream#reset() * @see java.io.InputStream#markSupported() * @see java.io.InputStream#mark(int) * @see java.io.InputStream#reset() */ public boolean markSupported() { return true; } /** * Marks the current position in this input stream. A subsequent call to * the <code>reset</code> method repositions this stream at the last marked * position so that subsequent reads re-read the same bytes. * * <p> The <code>readlimit</code> arguments tells this input stream to * allow that many bytes to be read before the mark position gets * invalidated. * * <p> The stream remembers all the bytes read after the call to * <code>mark</code> and stands ready to supply those same bytes again * if and whenever the method <code>reset</code> is called. * However, the stream is not required to remember any data at all if more * than <code>readlimit</code> bytes are read from the stream before * <code>reset</code> is called. * * @param readlimit the maximum limit of bytes that can be read before * the mark position becomes invalid. * @see BaseInputStream#reset() * @see java.io.InputStream#reset() * @see java.io.InputStream#mark(int) */ public synchronized void mark(int readlimit) { // check whether the stream is closed if (parent == null) { return; } // use parent's mark/reset functionality // if the parent supports the own one if (parent.markSupported()) { parent.mark(readlimit); } else { byte[] oldBuf = markBuf; // copy relevant data from old buffer if any if (isReadFromBuffer) { int oldDataSize = markSize - markPos; if (readlimit < oldDataSize) { readlimit = oldDataSize; } markBuf = new byte[readlimit]; System.arraycopy(oldBuf, markPos, markBuf, 0, oldDataSize); markSize = oldDataSize; } else { markBuf = new byte[readlimit]; markSize = 0; } markPos = 0; } } /** * Repositions this stream to the position at the time the * <code>mark</code> method was last called on this input stream. * * <p> If the method <code>mark</code> has not been called since * the stream was created, or the number of bytes read from the stream * since <code>mark</code> was last called is larger than the argument * to <code>mark</code> at that last call, then an * <code>IOException</code> is thrown. * * <p> If such an <code>IOException</code> is not thrown, then the * stream is reset to a state such that all the bytes read since the * most recent call to <code>mark</code> will be resupplied * to subsequent callers of the <code>read</code> method, followed by * any bytes that otherwise would have been the next input data as of * the time of the call to <code>reset</code>. * * @exception IOException if this stream has not been marked or if the * mark has been invalidated; * or if the stream is closed * @see BaseInputStream#mark(int) * @see java.io.InputStream#mark(int) * @see java.io.InputStream#reset(int) * @see java.io.IOException */ public synchronized void reset() throws IOException { ensureOpen(); // use parent's mark/reset functionality // if the parent supports the own one if (parent.markSupported()) { parent.reset(); } else { if (markBuf == null) { throw new IOException("Invalid mark position"); } markPos = 0; isReadFromBuffer = true; } } } /** * Output stream for the connection */ class BaseOutputStream extends OutputStream { /** Pointer to the connection */ ConnectionBaseAdapter parent; /** Buffer for single char writes */ byte[] buf = new byte[1]; /** * Constructs a BaseOutputStream for an ConnectionBaseAdapter. * * @param p parent connection */ BaseOutputStream(ConnectionBaseAdapter p) { parent = p; } /** * Check the stream is open * * @exception InterruptedIOException if it is not. */ private void ensureOpen() throws InterruptedIOException { if (parent == null) { throw new InterruptedIOException("Stream closed"); } } /** * Writes the specified byte to this output stream. The general * contract for <code>write</code> is that one byte is written * to the output stream. The byte to be written is the eight * low-order bits of the argument <code>b</code>. The 24 * high-order bits of <code>b</code> are ignored. * * @param b the <code>byte</code>. * @exception IOException if an I/O error occurs. In particular, * an <code>IOException</code> may be thrown if the * output stream has been closed. */ public void write(int b) throws IOException { buf[0] = (byte)b; write(buf, 0, 1); } /** * Writes <code>len</code> bytes from the specified byte array * starting at offset <code>off</code> to this output stream. * The general contract for <code>write(b, off, len)</code> is that * some of the bytes in the array <code>b</code> are written to the * output stream in order; element <code>b[off]</code> is the first * byte written and <code>b[off+len-1]</code> is the last byte written * by this operation. * <p> * If <code>b</code> is <code>null</code>, a * <code>NullPointerException</code> is thrown. * <p> * If <code>off</code> is negative, or <code>len</code> is negative, or * <code>off+len</code> is greater than the length of the array * <code>b</code>, then an <tt>IndexOutOfBoundsException</tt> is thrown. * * @param b the data. * @param off the start offset in the data. * @param len the number of bytes to write. * @exception IOException if an I/O error occurs. In particular, * an <code>IOException</code> is thrown if the output * stream is closed. */ public void write(byte b[], int off, int len) throws IOException { int test; int bytesWritten; ensureOpen(); if (len == 0) { return; } /* * test the parameters here so subclases do not have to, * this will avoid a crash in the native code */ test = b[off] + b[len - 1] + b[off + len - 1]; /* * Polling the native code is done here to allow for simple * asynchronous native code to be written. Not all implementations * work this way (they block in the native code) but the same * Java code works for both. */ for (bytesWritten = 0; ; ) { try { bytesWritten += parent.writeBytes(b, off + bytesWritten, len - bytesWritten); } finally { if (parent == null) { throw new InterruptedIOException("Stream closed"); } } if (bytesWritten == len) { break; } /// GeneralBase.iowait(); } } /** * Flushes this output stream and forces any buffered output bytes * to be written out. The general contract of <code>flush</code> is * that calling it is an indication that, if any bytes previously * written have been buffered by the implementation of the output * stream, such bytes should immediately be written to their * intended destination. * * @exception IOException if an I/O error occurs. */ public void flush() throws IOException { ensureOpen(); parent.flush(); } /** * Closes this output stream and releases any system resources * associated with this stream. The general contract of <code>close</code> * is that it closes the output stream. A closed stream cannot perform * output operations and cannot be reopened. * * @exception IOException if an I/O error occurs. */ public void close() throws IOException { if (parent != null) { parent.closeOutputStream(); parent = null; } } }