jsr340-experts@servlet-spec.java.net

[jsr340-experts] Re: Initial draft of NIO proposal

From: Shing Wai Chan <shing.wai.chan_at_oracle.com>
Date: Tue, 13 Sep 2011 12:24:14 -0700

>
>
> On 9/12/2011 10:44 PM, Greg Wilkins wrote:
>> Rajiv,
>>
>> here is my first impression feedback.
>>
>> + Good call to hide the NIO APIs. The functionality you are targeting
>> looks pretty good. However I guess some might still want the
>> ability to do IO with Buffers and Channels?
>
>
> We talked about doing that - however if we end up exposing the Buffer
> / ByteBuffer then we need to worry about
> the ByteBuffer related APIs and also Channels etc become tricky with
> filtering. I am open to exposing it if we have
> a good way of doing so.
>
>
>>
>> + NIO is a really silly name. Can we try to avoid calling things
>> NIOXxx and instead use AsyncIOXxx. Specially as we are not actually
>> exposing any NIO classes and implementations might use NIO2 etc.
>
> Am not particularly good with naming ;). Am open to naming suggestions.
>
>>
>> + Why create new methods ServletRequest.getNIOInputStream and
>> ServletResponse.getNIOOutputStream? The current getInputStream and
>> getOutputStream already return ServletInputStream and
>> ServletOutputStream abstract classes, so we could just add the methods
>> from the OutputSink and InputSource interfaces to those classes.
>> This would avoid adding methods to interfaces and breaking wrappers
>> and other extensions.
>
> So you are suggesting to move the InputSource and OutputSink methods to
> ServletInputStream and ServletOutputStream respectively? How would one
> distinguish between
> the traditional IO vs the non blocking API? I was trying to keep the
> two separate. One other thought
> was to have an overridden method for getInputStream that takes a
> boolean which says blocking
> or non-blocking. Keeping the two separate just seemed simpler at this
> time.
For Reader/Writer, it is one more step.
Note that there is an asymmetry in APIs.
In ServletRequest, we have
     ServletInputStream getInputStream();
     BufferedReader getReader();
In ServletResponse, we have
     ServletOutputStream getOutputStream();
     PrintWriter getWriter();

In this case, we may like to add two classes
     public class ServletReader extends BufferedReader {
         ....
     }

     public class ServletWriter extends PrintWriter {
         ....
     }

and changes the ServletRequest and ServletResponse to have
     ServletReader getReader();
     ServletWriter getWriter();

respectively.

It is a signature change here. But it should not break old applications.

Shing Wai Chan

>
>>
>> + The addReadListener and addWriteListener methods should just be
>> called addListener and the type of the listener passed used to
>> determine if IAE should be thrown or not. This would be more in line
>> with other addListeners in the API.
>
> Good point. Will make that change.
>
>>
>> + Can you explain more about the life cycle of the listeners? Are
>> these intended to work only with Async requests, or will they work for
>> any request? eg if I call response.addListener(myWriteListener),
>> could it be called back immediately while I'm still dispatched in the
>> servlet? or will any call back only occur if I call startAsync and
>> return from the dispatch?
>
> When I first wrote the sample - I had a startAsync but I think we
> should not enforce
> the users to have to startAsync. I was thinking that the listeners
> could be called
> asynchronously. Whether it is called immediately or not - my first
> reaction is to say yes
> it can be called immediately but I will need to think a little more
> about it.
>
> - Rajiv
>
>>
>>
>> cheers
>>
>>
>>
>>
>>
>>
>>
>> On 13 September 2011 02:52, Rajiv Mordani<rajiv.mordani_at_oracle.com>
>> wrote:
>>> Hi All,
>>> Please review and provide input on the initial draft proposal -
>>> It is
>>> similar to what is there in Grizzly and also Tomcat AIO. It does not
>>> expose
>>> any of the NIO constructs per say but I think that we don't
>>> necessarily need
>>> to expose them. Please let me know what you'll think.
>>>
>>> - Rajiv
>>>
>>> We will introduce new classes for readers, writers, input and out
>>> streams
>>> for NIO. These would be exposed from ServletRequest and
>>> ServletResponse.
>>>
>>> ReadListener: The listeners will be called back asynchronously.
>>>
>>> - Rajiv
>>>
>>> package javax.servlet;
>>>
>>> import java.util.EventListener;
>>>
>>> /**
>>> * @since Servlet 3.1
>>> */
>>>
>>> /**
>>> *<p>
>>> * This class represents a call-back mechanism that will notify
>>> implementations
>>> * as HTTP request data becomes available to read without blocking.
>>> *</p>
>>> *
>>>
>>> */
>>> public interface ReadListener extends EventListener {
>>>
>>> /**
>>> *<p>
>>> * Invoked when data is available to be read without blocking.
>>> *
>>> */
>>> public void onDataAvailable(ServletRequest request);
>>>
>>> /**
>>> *<p>
>>> * Invoked when all data for the current request has been read.
>>> *</p>
>>> *
>>> */
>>>
>>> public void onAllDataRead(ServletRequest request);
>>>
>>> /**
>>> *<p>
>>> * Invoked when an error occurs processing the request.
>>> *</p>
>>> */
>>> public void onError(Throwable t);
>>>
>>>
>>> }
>>>
>>> NIOInputSource
>>>
>>> package javax.servlet;
>>>
>>> /**
>>> *<p>
>>> * This interface defines methods to allow an {_at_link
>>> NIOInputStream} or
>>> * {_at_link NIOReader} to notify the developer<em>when</em> and<em>how
>>> much</em>
>>> * data is ready to be read without blocking.
>>> *</p>
>>> *
>>> * @since Servlet 3.1
>>> */
>>>
>>> public interface NIOInputSource {
>>>
>>> /**
>>> * @return the number of bytes (or characters) that may be
>>> obtained
>>> * without blocking.
>>> */
>>> public int dataAvailable();
>>>
>>> /**
>>> * @return<code>true</code> when all data for this particular
>>> request
>>> * has been read, otherwise returns<code>false</code>.
>>> */
>>> public boolean isFinished();
>>>
>>> /**
>>> * @return<code>true</code> if data can be obtained without
>>> blocking,
>>> * otherwise returns<code>false</code>.
>>> */
>>> public boolean isReady();
>>> }
>>>
>>> NIOInputStream
>>>
>>> package javax.servlet;
>>>
>>> import java.io.InputStream;
>>>
>>> /**
>>> * @since Servlet 3.1
>>> */
>>>
>>> public abstract class NIOInputStream extends InputStream implements
>>> NIOInputSource {
>>> }
>>>
>>> NIOReader
>>>
>>> package javax.servlet;
>>>
>>> import java.io.Reader;
>>>
>>> /**
>>> * @since Servlet 3.1
>>> */
>>>
>>> public abstract class NIOReader extends Reader implements
>>> NIOInputSource {
>>> }
>>>
>>> WriteListener
>>>
>>> package javax.servlet;
>>>
>>> import java.util.EventListener;
>>>
>>> /**
>>> *
>>> * Callback notification mechanism that signals to the developer it's
>>> possible
>>> * to write content.
>>> *
>>> * @since Servlet 3.1
>>> */
>>> public interface WriteListener extends EventListener {
>>>
>>> /**
>>> * This callback will be invoked when data can be written.
>>> *
>>> *
>>> * @throws Exception, {_at_link Exception} might be thrown by the
>>> custom
>>> * handler code. This exception will be delegated for
>>> processing to
>>> * {_at_link #onError(java.lang.Throwable)}.
>>> */
>>> public void onWritePossible(ServletResponse response);
>>>
>>> /**
>>> *<p>
>>> * Invoked when an error occurs processing the request
>>> asynchronously.
>>> *</p>
>>> */
>>> public void onError(final Throwable t);
>>>
>>> /**
>>> * Get the ServletResponse associated
>>> * @return
>>> */
>>> }
>>>
>>> NIOOutputSink
>>>
>>> package javax.servlet;
>>>
>>> /**
>>> *<p>
>>> * This interface defines methods to allow an {_at_link
>>> javax.servlet.NIOOutputStream} or
>>> * {_at_link javax.servlet.NIOWriter} to allow the developer to check
>>> with the
>>> runtime
>>> * whether or not it's possible to write data, or if it's not
>>> possible, to
>>> * be notified when it is.
>>> *</p>
>>> *
>>> * @since Servlet 3.1
>>> */
>>>
>>> public interface NIOOutputSink {
>>>
>>>
>>> /**
>>> *
>>> * @return<code>true</code> if a write to this
>>> <code>NIOOutputSink</code>
>>> * will succeed, otherwise returns<code>false</code>.
>>> */
>>> public boolean canWrite(int size);
>>>
>>> public void complete();
>>>
>>> }
>>>
>>> NIOOutputStream
>>>
>>> package javax.servlet;
>>>
>>> import java.io.OutputStream;
>>>
>>> /**
>>> * @since Servlet 3.1
>>> */
>>>
>>> public abstract class NIOOutputStream extends OutputStream implements
>>> NIOOutputSink {
>>> }
>>>
>>> NIOWriter
>>>
>>> package javax.servlet;
>>>
>>> import java.io.Writer;
>>>
>>> /**
>>> * @since Servlet 3.1
>>> */
>>>
>>> public abstract class NIOWriter extends Writer implements
>>> NIOOutputSink {
>>> }
>>>
>>> ServletRequest
>>>
>>> /**
>>> * Retrieves the body of the request as character data using
>>> * a<code>BufferedReader</code>. The reader translates the
>>> character
>>> * data according to the character encoding used on the body.
>>> * Either this method or {_at_link #getNIOInputStream} may be
>>> called to
>>> read the
>>> * body, not both.
>>> *
>>> * @return a<code>NIOReader</code> containing the body of
>>> the request
>>> *
>>> *
>>> * @exception IllegalStateException if {_at_link
>>> #getNIOInputStream} method
>>> * has been called on this request
>>> *
>>> *
>>> * @see #getNIOInputStream
>>> *
>>> * @since Servlet 3.1
>>> *
>>> */
>>>
>>> public NIOReader getNIOReader();
>>>
>>> /**
>>> * Retrieves the body of the request as binary data using
>>> * a {_at_link NIOInputStream}. Either this method or
>>> * {_at_link #getNIOReader} may be called to read the body in
>>> * a non-blocking way but, not both.
>>> *
>>> * @return a {_at_link javax.servlet.NIOInputStream} object
>>> containing
>>> * the body of the request
>>> *
>>> * @exception IllegalStateException if the {_at_link
>>> #getNIOReader} method
>>> * has already been called for this request
>>> *
>>> * @since Servlet 3.1
>>> */
>>>
>>> public NIOInputStream getNIOInputStream();
>>>
>>> /**
>>> * Instructs the<code>ServletRequest</code> to invoke the
>>> provided
>>> * {_at_link ReadListener} when it is possible to write
>>> *
>>> * @param listener the {_at_link ReadListener} that should be
>>> notified
>>> * when it's possible to read.
>>> *
>>> */
>>>
>>> public void addReadListener(ReadListener listener);
>>>
>>> ServletResponse
>>>
>>> /**
>>> * Returns a {_at_link NIOOutputStream} suitable for writing binary
>>> * data in the response.
>>> *
>>> * @return a {_at_link NIOOutputStream} for
>>> writing binary data
>>> *
>>> * @exception IllegalStateException if
>>> the<code>getNIOWriter</code>
>>> method
>>> * has been called on this response
>>> *
>>> * @see #getNIOWriter
>>> *
>>> */
>>> public NIOOutputStream getNIOOutputStream();
>>>
>>> /**
>>> * Returns a<code>NIOWriter</code> object that
>>> * can send character text to the client.
>>> *
>>> *
>>> * @return a<code>NIOWriter</code> object that
>>> * can return character data to the client
>>> *
>>> *
>>> * @see #getNIOOutputStream
>>> *
>>> */
>>> public NIOWriter getNIOWriter();
>>>
>>> /**
>>> * Instructs the<code>ServletResponse</code> to invoke the
>>> provided
>>> * {_at_link WriteListener} when it is possible to write
>>> *
>>> *
>>> * @param listener the {_at_link WriteListener} that should be
>>> notified
>>> * when it's possible to write.
>>> *
>>> */
>>>
>>> public void addWriteListener(WriteListener listener);
>>>
>>> Sample utilizing the API
>>>
>>> package servlet.samples.nio;
>>>
>>>
>>> import javax.servlet.*;
>>> import javax.servlet.http.HttpServlet;
>>> import javax.servlet.http.HttpServletRequest;
>>> import javax.servlet.http.HttpServletResponse;
>>> import java.io.IOException;
>>>
>>> public class NIOServletSample extends HttpServlet {
>>> private byte[] b = new byte[100];
>>> public void doGet(HttpServletRequest request, HttpServletResponse
>>> response) {
>>> request.addReadListener(new ReadListener() {
>>> public void onDataAvailable(ServletRequest request) {
>>> NIOInputStream nis = request.getNIOInputStream();
>>> try {
>>> nis.read(new byte[nis.dataAvailable()]);
>>> } catch (IOException e) {
>>> e.printStackTrace();
>>> }
>>> }
>>>
>>> public void onAllDataRead(ServletRequest request) {
>>> try {
>>> request.getNIOInputStream().close();
>>> } catch (IOException e) {
>>> e.printStackTrace();
>>> }
>>>
>>> }
>>>
>>> public void onError(Throwable t) {
>>>
>>> }
>>> });
>>> response.addWriteListener(new WriteListener() {
>>> public void onWritePossible(ServletResponse response) {
>>> NIOOutputStream nos = response.getNIOOutputStream();
>>> try {
>>> nos.write(b);
>>> nos.complete();
>>> } catch (IOException e) {
>>> e.printStackTrace();
>>> }
>>>
>>> }
>>>
>>> public void onError(Throwable t) {
>>>
>>> }
>>>
>>> });
>>>
>>> }
>>> }
>>>
>>>