<div dir="ltr"><div><div><div>thanks a lot - it seems to work but:<br><br></div>1. Synchronized is bad :) anyway, you wrote that there is only one thread so synchronization is not needed<br></div>2. Main issue: it&#39;s not bi-directional full-duplex (like WebSockets) i.e. you are not able to receive and send data at the same time, it&#39;s still synchronous<br>
<br></div>many thanks anyway - nice try :)<br></div><div class="gmail_extra"><br><br><div class="gmail_quote">On Fri, Apr 4, 2014 at 9:02 AM, Stuart Douglas <span dir="ltr">&lt;<a href="mailto:stuart.w.douglas@gmail.com" target="_blank">stuart.w.douglas@gmail.com</a>&gt;</span> wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Try something like the code below:<br>
<br>
<br>
public class AsyncEchoUpgradeServlet extends HttpServlet {<br>
<br>
    @Override<br>
    protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {<br>
        req.upgrade(Handler.class);<br>
    }<br>
<br>
    public static class Handler implements HttpUpgradeHandler {<br>
<br>
        @Override<br>
        public void init(final WebConnection wc) {<br>
            Listener listener = new Listener(wc);<br>
            try {<br>
                //we have to set the write listener before the read listener<br>
                //otherwise the output stream could be written to before it is<br>
                //in async mode<br>
                wc.getOutputStream().<u></u>setWriteListener(listener);<br>
                wc.getInputStream().<u></u>setReadListener(listener);<br>
            } catch (IOException e) {<br>
                UndertowLogger.REQUEST_IO_<u></u>LOGGER.ioException(e);<br>
            }<br>
        }<br>
<br>
        @Override<br>
        public void destroy() {<br>
<br>
        }<br>
    }<br>
<br>
<br>
    private static class Listener implements WriteListener, ReadListener {<br>
<br>
        private final WebConnection connection;<br>
        private final Queue&lt;String&gt; queue = new ArrayDeque&lt;String&gt;();<br>
<br>
        private Listener(final WebConnection connection) {<br>
            this.connection = connection;<br>
        }<br>
<br>
        @Override<br>
        public synchronized void onDataAvailable() throws IOException {<br>
            byte[] data = new byte[100];<br>
            while (connection.getInputStream().<u></u>isReady()) {<br>
                int read;<br>
                if ((read = connection.getInputStream().<u></u>read(data)) != -1) {<br>
                    queue.add(new String(data, 0, read));<br>
                }<br>
                onWritePossible();<br>
            }<br>
        }<br>
<br>
        @Override<br>
        public void onAllDataRead() throws IOException {<br>
<br>
        }<br>
<br>
        @Override<br>
        public synchronized void onWritePossible() throws IOException {<br>
<br>
            while (!queue.isEmpty() &amp;&amp; connection.getOutputStream().<u></u>isReady()) {<br>
                String data = queue.poll();<br>
                connection.getOutputStream().<u></u>write(data.getBytes());<br>
            }<br>
        }<br>
<br>
        @Override<br>
        public void onError(final Throwable t) {<br>
<br>
        }<br>
    }<br>
}<br>
<br>
<br>
<br>
Przemyslaw Bielicki wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div><div class="h5">
Hi Stuart,<br>
<br>
thx for the explanations. There is a problem anyway - if you set both<br>
read and write listeners from the handler it won&#39;t work at all as in the<br>
write listener ServletOutputStream.isReady() will be returning true all<br>
the time (why not? the output is ready to send some data at the same<br>
time in is ready to read some data), and the read listener will be<br>
ignored (as the tread is focused on the writer). The solution here is<br>
not to have while (out.isReady()) loop in the writer but a simple check<br>
if (out.isReady()), but then you have to call onWritePossible yourself.<br>
It means you need to pass a write listener reference to the read<br>
listener - it&#39;s smells more than a bit.<br>
<br>
I think I have my conclusion, that is: it is impossible to achieve<br>
full-duplex (thus multiplexing) in an upgraded protocol using Servlet<br>
3.1 API as there is &quot;only ever one IO thread per connection&quot; (I didn&#39;t<br>
find such limitation in the specification - can you point out the<br>
section in which it is said there should be on thread per connection?).<br>
In order to achieve multiplexing one have to explicitly create a<br>
separate thread dealing with either reading or writing the data.<br>
Also, using Servlet 3.1 it is impossible to achieve non-blocking read<br>
and write and multiplexing on the same connection, even using a separate<br>
thread.<br>
<br>
I will contact the specification owners to see their opinion.<br>
<br>
Many thanks,<br>
Przemyslaw<br>
<br>
<br>
On Fri, Apr 4, 2014 at 12:33 AM, Stuart Douglas<br></div></div><div class="">
&lt;<a href="mailto:stuart.w.douglas@gmail.com" target="_blank">stuart.w.douglas@gmail.com</a> &lt;mailto:<a href="mailto:stuart.w.douglas@gmail.com" target="_blank">stuart.w.douglas@<u></u>gmail.com</a>&gt;&gt; wrote:<br>

<br>
<br>
<br>
    Przemyslaw Bielicki wrote:<br>
<br>
        I tried - exactly the same results.<br>
<br>
        Another weird observation is that ServletOutputStream.isReady() is<br>
        returning true even after the connection is closed<br></div>
        (ServletInputStream.__<u></u>isFinished() is correctly returning true).<div class=""><br>
<br>
        Here&#39;s the scenario that works but I can write back the data<br>
        only once:<br>
        1. In HttpUpgradeHandler I set only  the ReadListener<br>
        2. I switch the protocol and send some data<br>
        3. ReadListener gets activated i.e. onDataAvailable() is called.<br>
        4. I process the input data, read as much as possible and put<br>
        the input<br>
        into the queue<br>
        5. From within ReadListener I set the WriteListener<br></div>
        6. WriteListener.onWritePossible(<u></u>__) gets called and I process<div class=""><br>
        the data -<br>
        I clean the queue<br></div>
        7. As long as I&#39;m in WriteListener.onWritePossible(<u></u>__)<div class=""><br>
        (while.out.isReady() is constantly returning true, which is a<br>
        correct<br>
        bahavior) the ReadListener is on-hold. I can send as much data<br>
        as I like<br>
        but onDataAvailable() is not called<br></div>
        8. Only when I leave WriteListener.onWritePossible(<u></u>__) method the<div class=""><br>
        ReadListener.onDataAvailable() is called again and I can consume the<br>
        input data again.<br>
        9. I can process the input data again i.e. put it into the queue but<br></div>
        WriteListener.onWritePossible(<u></u>__) is never called again. When I<div class=""><br>
        try to<br>
        reset it I get IllegalStateException<br>
<br>
        Either the specification or implementation seem not very mature....<br>
        Wildfly behavior is consistent with the one of Tomcat.<br>
<br>
<br>
    As Remy said this is expected.<br>
    Basically there is only ever one IO thread per connection, so only<br>
    one method will be active at a time.<br>
<br>
    The reason why your listener method is not being called again would<br>
    become apparent if you look at the javadoc for the read/write listeners:<br>
<br>
    Subsequently the container will invoke this method if and only<br></div>
    if {@link javax.servlet.__<u></u>ServletOutputStream#isReady()} method<div class=""><br>
    has been called and has returned &lt;code&gt;false&lt;/code&gt;.<br>
<br>
    Basically what this means is that the listener is only invoked if<br>
    isReady() returns false at some point. If you have read some data<br>
    and then you want to echo it you should call the onWritePossible<br>
    method yourself, after you have received the data.<br>
<br>
    Stuart<br>
<br>
<br>
<br>
<br>
        At the moment I conclude that the non-blocking write is not<br>
        possible in<br>
        Servlet 3.1.<br>
<br>
        I would appreciate if someone can provide an example that<br>
        actually works<br>
        or explain why the weird behavior I observe is correct (is it?)<br>
<br>
        Cheers,<br>
        Przemyslaw<br>
<br>
<br>
<br>
<br>
        On Thu, Apr 3, 2014 at 6:18 AM, Stuart Douglas<br>
        &lt;<a href="mailto:stuart.w.douglas@gmail.com" target="_blank">stuart.w.douglas@gmail.com</a> &lt;mailto:<a href="mailto:stuart.w.douglas@gmail.com" target="_blank">stuart.w.douglas@<u></u>gmail.com</a>&gt;<br></div>

        &lt;mailto:<a href="mailto:stuart.w.douglas@" target="_blank">stuart.w.douglas@</a>__<a href="http://gmail.com" target="_blank">gma<u></u>il.com</a><div class=""><br>
        &lt;mailto:<a href="mailto:stuart.w.douglas@gmail.com" target="_blank">stuart.w.douglas@<u></u>gmail.com</a>&gt;&gt;&gt; wrote:<br>
<br>
             Can you try with the latest development build of Wildfly (from<br></div>
        <a href="https://ci.jboss.org/hudson/____job/WildFly-latest-master/" target="_blank">https://ci.jboss.org/hudson/__<u></u>__job/WildFly-latest-master/</a><br>
        &lt;<a href="https://ci.jboss.org/hudson/__job/WildFly-latest-master/" target="_blank">https://ci.jboss.org/hudson/_<u></u>_job/WildFly-latest-master/</a>&gt;<div class=""><br>
        &lt;<a href="https://ci.jboss.org/hudson/__job/WildFly-latest-master/" target="_blank">https://ci.jboss.org/hudson/_<u></u>_job/WildFly-latest-master/</a><br>
        &lt;<a href="https://ci.jboss.org/hudson/job/WildFly-latest-master/" target="_blank">https://ci.jboss.org/hudson/<u></u>job/WildFly-latest-master/</a>&gt;&gt;).<br>
<br>
<br>
             There have been some fixes in this area, so your problem<br>
        may have<br>
             already been fixed.<br>
<br>
             Stuart<br>
<br>
<br>
             PB wrote:<br>
<br>
                 Hi,<br>
<br>
                 I&#39;m testing the HTTP Upgrade feature of WF 8.0 and I&#39;m<br>
        facing<br>
                 some banal<br>
                 problem. Basically my ReadListener is NEVER called.<br>
                 Here&#39;s the code:<br>
<br>
                 @WebServlet(urlPatterns = &quot;/upgrade&quot;)<br>
                 public class UpgradeServlet extends HttpServlet {<br>
                     @Override<br>
                     protected void doGet(HttpServletRequest req,<br>
        HttpServletResponse<br>
                 resp) throws ServletException, IOException {<br>
                       if<br>
<br></div>
        (&quot;upgrade&quot;.equalsIgnoreCase(__<u></u>__req.getHeader(&quot;Connection&quot;))<u></u>) {<br>
                         req.upgrade(EchoHandler.class)<u></u>____;<div><div class="h5"><br>
<br>
                       }<br>
                     }<br>
                 }<br>
<br>
                 public class EchoHandler implements HttpUpgradeHandler {<br>
                     @Override<br>
                     public void init(WebConnection wc) {<br>
                       try {<br>
                         ServletInputStream in = wc.getInputStream();<br>
                         ServletOutputStream out = wc.getOutputStream();<br>
<br>
                         BlockingQueue&lt;String&gt; queue = new<br>
                 LinkedBlockingQueue&lt;String&gt;();<br>
                         in.setReadListener(new EchoReadListener(queue,<br>
        in));<br>
                         out.setWriteListener(new<br>
        EchoWriteListener(queue, out));<br>
                       } catch (IOException e) {<br>
                         throw new IllegalStateException(e);<br>
                       }<br>
                     }<br>
<br>
                 public class EchoReadListener implements ReadListener {<br>
                     @Override<br>
                     public void onDataAvailable() throws IOException {<br>
                       while (in.isReady()) {<br>
                         int length = in.read(buffer);<br>
                         String input = new String(buffer, 0, length);<br>
                         if (false == queue.offer(input)) {<br>
                           System.err.println(&quot;&#39;&quot; + input + &quot;&#39; input was<br>
        ignored&quot;);<br>
                         }<br>
                       }<br>
                     }<br>
<br>
                 I&#39;m connecting to WF using telnet and sending the<br>
        upgrade request:<br>
                 GET /example-webapp/upgrade HTTP/1.1<br>
                 Host: localhost<br>
                 Connection: upgrade<br>
                 Upgrade: echo<br>
<br>
                 and I&#39;m getting correct response:<br>
<br>
                 HTTP/1.1 101 Switching Protocols<br>
                 Connection: Upgrade<br>
                 X-Powered-By: Undertow 1<br>
                 Server: Wildfly 8<br>
                 Content-Length: 0<br>
<br>
                 which means that from now on the protocol between my telnet<br>
                 client and<br>
                 WF is pure TCP.<br>
                 So, I start typing some text, hit Enter and.... nothing<br>
        happens.<br>
                 onDataAvailable() is NEVER called. More so, this makes<br>
        WF totally<br>
                 irresponsive - my whole webapp is dead.<br>
<br>
                 I believe, I&#39;m doing something wrong - any ideas what<br>
        exactly?<br>
                 There is<br>
                 also a slight chance that Upgrade feature in WF is<br>
        f****d :)<br>
                 Anyway, WF should not block even in case my upgraded<br>
        protocol is not<br>
                 working correctly?<br>
<br>
                 Many thanks,<br>
                 Przemyslaw<br>
<br></div></div>
                 ______________________________<u></u>_____________________<div class=""><br>
                 wildfly-dev mailing list<br>
        <a href="mailto:wildfly-dev@lists.jboss.org" target="_blank">wildfly-dev@lists.jboss.org</a> &lt;mailto:<a href="mailto:wildfly-dev@lists.jboss.org" target="_blank">wildfly-dev@lists.<u></u>jboss.org</a>&gt;<br></div>

        &lt;mailto:<a href="mailto:wildfly-dev@lists." target="_blank">wildfly-dev@lists.</a>__<a href="http://jboss.org" target="_blank">jb<u></u>oss.org</a><br>
        &lt;mailto:<a href="mailto:wildfly-dev@lists.jboss.org" target="_blank">wildfly-dev@lists.<u></u>jboss.org</a>&gt;&gt;<br>
        <a href="https://lists.jboss.org/____mailman/listinfo/wildfly-dev" target="_blank">https://lists.jboss.org/____<u></u>mailman/listinfo/wildfly-dev</a><br>
        &lt;<a href="https://lists.jboss.org/__mailman/listinfo/wildfly-dev" target="_blank">https://lists.jboss.org/__<u></u>mailman/listinfo/wildfly-dev</a>&gt;<br>
        &lt;<a href="https://lists.jboss.org/__mailman/listinfo/wildfly-dev" target="_blank">https://lists.jboss.org/__<u></u>mailman/listinfo/wildfly-dev</a><br>
        &lt;<a href="https://lists.jboss.org/mailman/listinfo/wildfly-dev" target="_blank">https://lists.jboss.org/<u></u>mailman/listinfo/wildfly-dev</a>&gt;&gt;<div class=""><br>
<br>
<br>
<br>
______________________________<u></u>_________________<br>
wildfly-dev mailing list<br>
<a href="mailto:wildfly-dev@lists.jboss.org" target="_blank">wildfly-dev@lists.jboss.org</a><br>
<a href="https://lists.jboss.org/mailman/listinfo/wildfly-dev" target="_blank">https://lists.jboss.org/<u></u>mailman/listinfo/wildfly-dev</a><br>
</div></blockquote>
</blockquote></div><br></div>