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.fcgi.generator;
20  
21  import java.nio.ByteBuffer;
22  import java.util.ArrayDeque;
23  import java.util.Queue;
24  
25  import org.eclipse.jetty.io.EndPoint;
26  import org.eclipse.jetty.util.IteratingCallback;
27  import org.eclipse.jetty.util.log.Log;
28  import org.eclipse.jetty.util.log.Logger;
29  
30  public class Flusher
31  {
32      private static final Logger LOG = Log.getLogger(Flusher.class);
33  
34      private final Queue<Generator.Result> queue = new ArrayDeque<>();
35      private final IteratingCallback flushCallback = new FlushCallback();
36      private final EndPoint endPoint;
37  
38      public Flusher(EndPoint endPoint)
39      {
40          this.endPoint = endPoint;
41      }
42  
43      public void flush(Generator.Result... results)
44      {
45          for (Generator.Result result : results)
46              offer(result);
47          flushCallback.iterate();
48      }
49  
50      private void offer(Generator.Result result)
51      {
52          synchronized (this)
53          {
54              queue.offer(result);
55          }
56      }
57  
58      private Generator.Result poll()
59      {
60          synchronized (this)
61          {
62              return queue.poll();
63          }
64      }
65  
66      public void shutdown()
67      {
68          flush(new ShutdownResult());
69      }
70  
71      private class FlushCallback extends IteratingCallback
72      {
73          private Generator.Result active;
74  
75          @Override
76          protected Action process() throws Exception
77          {
78              // Look if other writes are needed.
79              Generator.Result result = poll();
80              if (result == null)
81              {
82                  // No more writes to do, return.
83                  return Action.IDLE;
84              }
85  
86              // Attempt to gather another result.
87              // Most often there is another result in the
88              // queue so this is a real optimization because
89              // it sends both results in just one TCP packet.
90              Generator.Result other = poll();
91              if (other != null)
92                  result = result.join(other);
93  
94              active = result;
95              ByteBuffer[] buffers = result.getByteBuffers();
96              endPoint.write(this, buffers);
97              return Action.SCHEDULED;
98          }
99  
100         @Override
101         protected void onCompleteSuccess()
102         {
103             // We never return Action.SUCCEEDED, so this method is never called.
104             throw new IllegalStateException();
105         }
106 
107         @Override
108         public void succeeded()
109         {
110             if (active != null)
111                 active.succeeded();
112             active = null;
113             super.succeeded();
114         }
115 
116         @Override
117         public void onCompleteFailure(Throwable x)
118         {
119             if (active != null)
120                 active.failed(x);
121             active = null;
122 
123             while (true)
124             {
125                 Generator.Result result = poll();
126                 if (result == null)
127                     break;
128                 result.failed(x);
129             }
130         }
131     }
132 
133     private class ShutdownResult extends Generator.Result
134     {
135         private ShutdownResult()
136         {
137             super(null, null);
138         }
139 
140         @Override
141         public void succeeded()
142         {
143             shutdown();
144         }
145 
146         @Override
147         public void failed(Throwable x)
148         {
149             shutdown();
150         }
151 
152         private void shutdown()
153         {
154             if (LOG.isDebugEnabled())
155                 LOG.debug("Shutting down {}", endPoint);
156             endPoint.shutdownOutput();
157         }
158     }
159 }