In Grizzly 1.9, it was possible to write data to the client using non-blocking I/O, however, when reading post data from within a GrizzlyAdapter implementation, all I/O in that case was blocking. In Grizzly 2.2.10, it's very simple using the InputStream/Reader from the request and read data in a non-blocking manner. As far as writing, all I/O will be written in non-blocking mode, but there is some extra features on the OutputStream vended by the Response instance that will be covered that expose advanced non-blocking operations.
The methods getOutputStream() and getWriter() on Response return instances of NIOOutputStream and NIOWriter. In addition to the methods defined by java.io.OutputStream and java.io.Writer, these entities both implement the NIOOutputSink interface.
/** * <p> * This interface defines methods to allow an {@link java.io.OutputStream} or * {@link java.io.Writer} to allow the developer to check with the runtime * whether or not it's possible to write a certain amount of data, or if it's * not possible, to be notified when it is. * </p> * * @since 2.0 */ public interface NIOOutputSink { /** * Instructs the <code>NIOOutputSink</code> to invoke the provided * {@link WriteHandler} when it is possible to write <code>length</code> * bytes. * * Note that once the {@link WriteHandler} has been notified, it will not * be considered for notification again at a later point in time. * * @param handler the {@link WriteHandler} that should be notified * when it's possible to write <code>length</code> bytes. * @param length the number of bytes that require writing. * * @throws IllegalStateException if this method is invoked and a handler * from a previous invocation is still present (due to not having yet been * notified). */ void notifyCanWrite(final WriteHandler handler, final int length); /** * @param length specifies the number of bytes that require writing * * @return <code>true</code> if a write to this <code>NIOOutputSink</code> * will succeed, otherwise returns <code>false</code>. */ boolean canWrite(final int length); }
The typical flow when using these methods is to call canWrite() with the desired length. Whether or not the desired amount of data will depened, primarily, on the configuration value of maxPendingWrites property on the NetworkListener being used to process the current request.
For example, if the client is on a slow connection, and the OS network buffer is full, the write queue maintained by the Transport implementation would start to full up. If an attempt to write is made that will exeed the queue limit, a PendingWriteQueueLimitExceededException will be thrown. However, if the developer calls canWrite() with the desired length, and the return value is false, then it is know there, that the write would fail. This condition can be gracefully be handled by calling notifyCanWrite() providing a WriteHandler and the desired length. Once the pending write queue drains enough to support the length, the WriteHandler will be notified to allow the developer to continue sending content to the client.
For Grizzly 2.2.10 HTTP applications that deal in primarily binary data and are using Grizzly Buffers, they can leverage an additional method available on the NIOOutputStream specified by the BinaryNIOutputSink interface. This interface allows the direct writing of Buffer instances. This optimizes away the need to copy bytes to a Buffer implementation under the covers.
/** * Adds the ability for binary based {@link NIOOutputSink}s to write a * {@link Buffer} instead of having to convert to those types supported by * {@link java.io.OutputStream}. * * @since 2.0 */ public interface BinaryNIOOutputSink extends NIOOutputSink { /** * Writes the contents of the specified {@link org.glassfish.grizzly.Buffer}. * * @param buffer the {@link org.glassfish.grizzly.Buffer to write} */ void write(final Buffer buffer) throws IOException; }
On the input, side, Grizzly provides a similar interface for non-blocking reads called NIOInputSource.
/** * <p> * This interface defines methods to allow an {@link InputStream} or * {@link Reader} to notify the developer <em>when</em> and <em>how much</em> * data is ready to be read without blocking. * </p> * * @since 2.0 */ public interface NIOInputSource { /** * <p> * Notify the specified {@link ReadHandler} when any number of bytes * can be read without blocking. * </p> * * <p> * Invoking this method is equivalent to calling: notifyAvailable(handler, 0). * </p> * * @param handler the {@link ReadHandler} to notify. * * @throws IllegalArgumentException if <code>handler</code> is <code>null</code>, * or if <code>size</code> is less than zero. * @throws IllegalStateException if an attempt is made to register a handler * before an existing registered handler has been invoked or if all request * data has already been read. * * @see ReadHandler#onDataAvailable() * @see ReadHandler#onAllDataRead() */ void notifyAvailable(final ReadHandler handler); /** * <p> * Notify the specified {@link ReadHandler} when the number of bytes that * can be read without blocking is greater or equal to the specified * <code>size</code>. * </p> * * @param handler the {@link ReadHandler} to notify. * @param size the least number of bytes that must be available before * the {@link ReadHandler} is invoked. If size is <code>0</code>, the * handler will be notified as soon as data is available no matter the * size. * * @throws IllegalArgumentException if <code>handler</code> is <code>null</code>, * or if <code>size</code> is less than zero. * @throws IllegalStateException if an attempt is made to register a handler * before an existing registered handler has been invoked or if all request * data has already been read. * * @see ReadHandler#onDataAvailable() * @see ReadHandler#onAllDataRead() */ void notifyAvailable(final ReadHandler handler, final int size); /** * @return <code>true</code> when all data for this particular request * has been read, otherwise returns <code>false</code>. */ boolean isFinished(); /** * @return the number of bytes (or characters) that may be obtained * without blocking. Note when dealing with characters, this method * will return an estimate on the number of characters available. */ int readyData(); /** * @return <code>true</code> if data can be obtained without blocking, * otherwise returns <code>false</code>. */ boolean isReady(); }
The general idea behind non-blocking writes holds true for non-blocking reads. The developer can check to see if data is available to be read without blocking by calling isReady() or checking for a non-zero return from readyData(). If no data can be read without blocking, use notifyAvailable(ReadHandler) or notifyAvailable(ReadHandler, int). When data is becomes available, the ReadHandler will be invoked. Note that if no length is provided to the notifyAvailable() methods, the ReadHandler will be invoked as soon as any data becomes available. In this case, it's a good idea to check how much can be read by another call to readyData().
For optimized reading of binary data, there is the specialized interface, BinaryNIOInputSource, that allows direct access to the Buffer used to store the incoming data:
/** * Adds the ability for binary based {@link NIOInputSource}s to obtain the * incoming {@link org.glassfish.grizzly.Buffer} directly without having to * use intermediate objects to copy the data to. * * @since 2.0 */ public interface BinaryNIOInputSource extends NIOInputSource { /** * <p> * Returns the underlying {@link org.glassfish.grizzly.Buffer} that backs this * <code>NIOInputSource</code>. * </p> * * @return the underlying {@link org.glassfish.grizzly.Buffer} that backs this * <code>NIOInputSource</code>. */ Buffer getBuffer(); }
One final word on NIOInputSource implementations with respect to the Request object. In order for these methods to function properly, developers must obtain the desired NIOInputSource by calling Request.getInputStream(false) or Request.getReader(false). Specifiy a value of false sets the NIOInputSources to work in non-blocking mode. If the a value of true is provided to the getRead() or getInputStream() methods, the NIOInputSource will be set to blocking mode. It's not possible to change the mode after the initial invocations of these methods.