thanks a lot - it seems to work but:

1. Synchronized is bad :) anyway, you wrote that there is only one thread so synchronization is not needed
2. Main issue: it's not bi-directional full-duplex (like WebSockets) i.e. you are not able to receive and send data at the same time, it's still synchronous

many thanks anyway - nice try :)


On Fri, Apr 4, 2014 at 9:02 AM, Stuart Douglas <stuart.w.douglas@gmail.com> wrote:
Try something like the code below:


public class AsyncEchoUpgradeServlet extends HttpServlet {

    @Override
    protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
        req.upgrade(Handler.class);
    }

    public static class Handler implements HttpUpgradeHandler {

        @Override
        public void init(final WebConnection wc) {
            Listener listener = new Listener(wc);
            try {
                //we have to set the write listener before the read listener
                //otherwise the output stream could be written to before it is
                //in async mode
                wc.getOutputStream().setWriteListener(listener);
                wc.getInputStream().setReadListener(listener);
            } catch (IOException e) {
                UndertowLogger.REQUEST_IO_LOGGER.ioException(e);
            }
        }

        @Override
        public void destroy() {

        }
    }


    private static class Listener implements WriteListener, ReadListener {

        private final WebConnection connection;
        private final Queue<String> queue = new ArrayDeque<String>();

        private Listener(final WebConnection connection) {
            this.connection = connection;
        }

        @Override
        public synchronized void onDataAvailable() throws IOException {
            byte[] data = new byte[100];
            while (connection.getInputStream().isReady()) {
                int read;
                if ((read = connection.getInputStream().read(data)) != -1) {
                    queue.add(new String(data, 0, read));
                }
                onWritePossible();
            }
        }

        @Override
        public void onAllDataRead() throws IOException {

        }

        @Override
        public synchronized void onWritePossible() throws IOException {

            while (!queue.isEmpty() && connection.getOutputStream().isReady()) {
                String data = queue.poll();
                connection.getOutputStream().write(data.getBytes());
            }
        }

        @Override
        public void onError(final Throwable t) {

        }
    }
}



Przemyslaw Bielicki wrote:
Hi Stuart,

thx for the explanations. There is a problem anyway - if you set both
read and write listeners from the handler it won't work at all as in the
write listener ServletOutputStream.isReady() will be returning true all
the time (why not? the output is ready to send some data at the same
time in is ready to read some data), and the read listener will be
ignored (as the tread is focused on the writer). The solution here is
not to have while (out.isReady()) loop in the writer but a simple check
if (out.isReady()), but then you have to call onWritePossible yourself.
It means you need to pass a write listener reference to the read
listener - it's smells more than a bit.

I think I have my conclusion, that is: it is impossible to achieve
full-duplex (thus multiplexing) in an upgraded protocol using Servlet
3.1 API as there is "only ever one IO thread per connection" (I didn't
find such limitation in the specification - can you point out the
section in which it is said there should be on thread per connection?).
In order to achieve multiplexing one have to explicitly create a
separate thread dealing with either reading or writing the data.
Also, using Servlet 3.1 it is impossible to achieve non-blocking read
and write and multiplexing on the same connection, even using a separate
thread.

I will contact the specification owners to see their opinion.

Many thanks,
Przemyslaw


On Fri, Apr 4, 2014 at 12:33 AM, Stuart Douglas
<stuart.w.douglas@gmail.com <mailto:stuart.w.douglas@gmail.com>> wrote:



    Przemyslaw Bielicki wrote:

        I tried - exactly the same results.

        Another weird observation is that ServletOutputStream.isReady() is
        returning true even after the connection is closed
        (ServletInputStream.__isFinished() is correctly returning true).


        Here's the scenario that works but I can write back the data
        only once:
        1. In HttpUpgradeHandler I set only  the ReadListener
        2. I switch the protocol and send some data
        3. ReadListener gets activated i.e. onDataAvailable() is called.
        4. I process the input data, read as much as possible and put
        the input
        into the queue
        5. From within ReadListener I set the WriteListener
        6. WriteListener.onWritePossible(__) gets called and I process

        the data -
        I clean the queue
        7. As long as I'm in WriteListener.onWritePossible(__)

        (while.out.isReady() is constantly returning true, which is a
        correct
        bahavior) the ReadListener is on-hold. I can send as much data
        as I like
        but onDataAvailable() is not called
        8. Only when I leave WriteListener.onWritePossible(__) method the

        ReadListener.onDataAvailable() is called again and I can consume the
        input data again.
        9. I can process the input data again i.e. put it into the queue but
        WriteListener.onWritePossible(__) is never called again. When I

        try to
        reset it I get IllegalStateException

        Either the specification or implementation seem not very mature....
        Wildfly behavior is consistent with the one of Tomcat.


    As Remy said this is expected.
    Basically there is only ever one IO thread per connection, so only
    one method will be active at a time.

    The reason why your listener method is not being called again would
    become apparent if you look at the javadoc for the read/write listeners:

    Subsequently the container will invoke this method if and only
    if {@link javax.servlet.__ServletOutputStream#isReady()} method

    has been called and has returned <code>false</code>.

    Basically what this means is that the listener is only invoked if
    isReady() returns false at some point. If you have read some data
    and then you want to echo it you should call the onWritePossible
    method yourself, after you have received the data.

    Stuart




        At the moment I conclude that the non-blocking write is not
        possible in
        Servlet 3.1.

        I would appreciate if someone can provide an example that
        actually works
        or explain why the weird behavior I observe is correct (is it?)

        Cheers,
        Przemyslaw




        On Thu, Apr 3, 2014 at 6:18 AM, Stuart Douglas
        <stuart.w.douglas@gmail.com <mailto:stuart.w.douglas@gmail.com>
        <mailto:stuart.w.douglas@__gmail.com

        <mailto:stuart.w.douglas@gmail.com>>> wrote:

             Can you try with the latest development build of Wildfly (from
        https://ci.jboss.org/hudson/____job/WildFly-latest-master/
        <https://ci.jboss.org/hudson/__job/WildFly-latest-master/>

        <https://ci.jboss.org/hudson/__job/WildFly-latest-master/
        <https://ci.jboss.org/hudson/job/WildFly-latest-master/>>).


             There have been some fixes in this area, so your problem
        may have
             already been fixed.

             Stuart


             PB wrote:

                 Hi,

                 I'm testing the HTTP Upgrade feature of WF 8.0 and I'm
        facing
                 some banal
                 problem. Basically my ReadListener is NEVER called.
                 Here's the code:

                 @WebServlet(urlPatterns = "/upgrade")
                 public class UpgradeServlet extends HttpServlet {
                     @Override
                     protected void doGet(HttpServletRequest req,
        HttpServletResponse
                 resp) throws ServletException, IOException {
                       if

        ("upgrade".equalsIgnoreCase(____req.getHeader("Connection"))) {
                         req.upgrade(EchoHandler.class)____;


                       }
                     }
                 }

                 public class EchoHandler implements HttpUpgradeHandler {
                     @Override
                     public void init(WebConnection wc) {
                       try {
                         ServletInputStream in = wc.getInputStream();
                         ServletOutputStream out = wc.getOutputStream();

                         BlockingQueue<String> queue = new
                 LinkedBlockingQueue<String>();
                         in.setReadListener(new EchoReadListener(queue,
        in));
                         out.setWriteListener(new
        EchoWriteListener(queue, out));
                       } catch (IOException e) {
                         throw new IllegalStateException(e);
                       }
                     }

                 public class EchoReadListener implements ReadListener {
                     @Override
                     public void onDataAvailable() throws IOException {
                       while (in.isReady()) {
                         int length = in.read(buffer);
                         String input = new String(buffer, 0, length);
                         if (false == queue.offer(input)) {
                           System.err.println("'" + input + "' input was
        ignored");
                         }
                       }
                     }

                 I'm connecting to WF using telnet and sending the
        upgrade request:
                 GET /example-webapp/upgrade HTTP/1.1
                 Host: localhost
                 Connection: upgrade
                 Upgrade: echo

                 and I'm getting correct response:

                 HTTP/1.1 101 Switching Protocols
                 Connection: Upgrade
                 X-Powered-By: Undertow 1
                 Server: Wildfly 8
                 Content-Length: 0

                 which means that from now on the protocol between my telnet
                 client and
                 WF is pure TCP.
                 So, I start typing some text, hit Enter and.... nothing
        happens.
                 onDataAvailable() is NEVER called. More so, this makes
        WF totally
                 irresponsive - my whole webapp is dead.

                 I believe, I'm doing something wrong - any ideas what
        exactly?
                 There is
                 also a slight chance that Upgrade feature in WF is
        f****d :)
                 Anyway, WF should not block even in case my upgraded
        protocol is not
                 working correctly?

                 Many thanks,
                 Przemyslaw

                 ___________________________________________________

                 wildfly-dev mailing list
        wildfly-dev@lists.jboss.org <mailto:wildfly-dev@lists.jboss.org>
        <mailto:wildfly-dev@lists.__jboss.org
        <mailto:wildfly-dev@lists.jboss.org>>
        https://lists.jboss.org/____mailman/listinfo/wildfly-dev
        <https://lists.jboss.org/__mailman/listinfo/wildfly-dev>
        <https://lists.jboss.org/__mailman/listinfo/wildfly-dev
        <https://lists.jboss.org/mailman/listinfo/wildfly-dev>>




_______________________________________________
wildfly-dev mailing list
wildfly-dev@lists.jboss.org
https://lists.jboss.org/mailman/listinfo/wildfly-dev