View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.embedded;
20  
21  import java.io.IOException;
22  import java.util.concurrent.atomic.AtomicBoolean;
23  
24  import javax.servlet.AsyncContext;
25  import javax.servlet.ReadListener;
26  import javax.servlet.ServletException;
27  import javax.servlet.ServletInputStream;
28  import javax.servlet.ServletOutputStream;
29  import javax.servlet.WriteListener;
30  import javax.servlet.http.HttpServlet;
31  import javax.servlet.http.HttpServletRequest;
32  import javax.servlet.http.HttpServletResponse;
33  
34  
35  public class AsyncEchoServlet extends HttpServlet
36  {
37      private static final long serialVersionUID = 1L;
38  
39      @Override
40      protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
41      {
42          AsyncContext asyncContext = request.startAsync(request, response);
43          asyncContext.setTimeout(0);
44          Echoer echoer = new Echoer(asyncContext);
45          request.getInputStream().setReadListener(echoer);
46          response.getOutputStream().setWriteListener(echoer);
47      }
48  
49      private class Echoer implements ReadListener, WriteListener
50      {
51          private final byte[] buffer = new byte[4096];
52          private final AsyncContext asyncContext;
53          private final ServletInputStream input;
54          private final ServletOutputStream output;
55          private final AtomicBoolean complete = new AtomicBoolean(false);
56  
57          private Echoer(AsyncContext asyncContext) throws IOException
58          {
59              this.asyncContext = asyncContext;
60              this.input = asyncContext.getRequest().getInputStream();
61              this.output = asyncContext.getResponse().getOutputStream();
62          }
63          
64          @Override
65          public void onDataAvailable() throws IOException
66          {
67              handleAsyncIO();
68          }
69  
70          @Override
71          public void onAllDataRead() throws IOException
72          {
73              handleAsyncIO();
74          }
75  
76          @Override
77          public void onWritePossible() throws IOException
78          {
79              handleAsyncIO();
80          }
81          
82          private void handleAsyncIO() throws IOException
83          {
84              // This method is called:
85              //   1) after first registering a WriteListener (ready for first write)
86              //   2) after first registering a ReadListener iff write is ready
87              //   3) when a previous write completes after an output.isReady() returns false
88              //   4) from an input callback 
89             
90              // We should try to read, only if we are able to write!
91              while (true)
92              {
93                  if (!output.isReady())
94                      // Don't even try to read anything until it is possible to write something,
95                      // when onWritePossible will be called
96                      break;
97  
98                  if (!input.isReady())
99                      // Nothing available to read, so wait for another call to onDataAvailable
100                     break;
101                 
102                 int read = input.read(buffer);
103                 if (read<0)
104                 {
105                     if (complete.compareAndSet(false,true))
106                         asyncContext.complete();
107                     break;
108                 }
109                 else if (read>0)
110                 {
111                     output.write(buffer, 0, read);
112                 }
113             }
114         }
115 
116         @Override
117         public void onError(Throwable failure)
118         {
119             new Throwable("onError",failure).printStackTrace();
120             asyncContext.complete();
121         }
122     }
123 }