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.client;
20  
21  import java.nio.ByteBuffer;
22  import java.util.Iterator;
23  import java.util.List;
24  
25  import org.eclipse.jetty.client.api.ContentResponse;
26  import org.eclipse.jetty.client.api.Request;
27  import org.eclipse.jetty.client.api.Response;
28  import org.eclipse.jetty.client.api.Result;
29  import org.eclipse.jetty.http.HttpField;
30  import org.eclipse.jetty.util.Callback;
31  import org.eclipse.jetty.util.IteratingNestedCallback;
32  import org.eclipse.jetty.util.log.Log;
33  import org.eclipse.jetty.util.log.Logger;
34  
35  public class ResponseNotifier
36  {
37      private static final Logger LOG = Log.getLogger(ResponseNotifier.class);
38  
39      public void notifyBegin(List<Response.ResponseListener> listeners, Response response)
40      {
41          // Optimized to avoid allocations of iterator instances
42          for (int i = 0; i < listeners.size(); ++i)
43          {
44              Response.ResponseListener listener = listeners.get(i);
45              if (listener instanceof Response.BeginListener)
46                  notifyBegin((Response.BeginListener)listener, response);
47          }
48      }
49  
50      private void notifyBegin(Response.BeginListener listener, Response response)
51      {
52          try
53          {
54              listener.onBegin(response);
55          }
56          catch (Throwable x)
57          {
58              LOG.info("Exception while notifying listener " + listener, x);
59          }
60      }
61  
62      public boolean notifyHeader(List<Response.ResponseListener> listeners, Response response, HttpField field)
63      {
64          boolean result = true;
65          // Optimized to avoid allocations of iterator instances
66          for (int i = 0; i < listeners.size(); ++i)
67          {
68              Response.ResponseListener listener = listeners.get(i);
69              if (listener instanceof Response.HeaderListener)
70                  result &= notifyHeader((Response.HeaderListener)listener, response, field);
71          }
72          return result;
73      }
74  
75      private boolean notifyHeader(Response.HeaderListener listener, Response response, HttpField field)
76      {
77          try
78          {
79              return listener.onHeader(response, field);
80          }
81          catch (Throwable x)
82          {
83              LOG.info("Exception while notifying listener " + listener, x);
84              return false;
85          }
86      }
87  
88      public void notifyHeaders(List<Response.ResponseListener> listeners, Response response)
89      {
90          // Optimized to avoid allocations of iterator instances
91          for (int i = 0; i < listeners.size(); ++i)
92          {
93              Response.ResponseListener listener = listeners.get(i);
94              if (listener instanceof Response.HeadersListener)
95                  notifyHeaders((Response.HeadersListener)listener, response);
96          }
97      }
98  
99      private void notifyHeaders(Response.HeadersListener listener, Response response)
100     {
101         try
102         {
103             listener.onHeaders(response);
104         }
105         catch (Throwable x)
106         {
107             LOG.info("Exception while notifying listener " + listener, x);
108         }
109     }
110 
111     public void notifyContent(List<Response.ResponseListener> listeners, Response response, ByteBuffer buffer, Callback callback)
112     {
113         // Here we use an IteratingNestedCallback not to avoid the stack overflow, but to
114         // invoke the listeners one after the other. When all of them have invoked the
115         // callback they got passed, the callback passed to this method is finally invoked.
116         ContentCallback contentCallback = new ContentCallback(listeners, response, buffer, callback);
117         contentCallback.iterate();
118     }
119 
120     private void notifyContent(Response.AsyncContentListener listener, Response response, ByteBuffer buffer, Callback callback)
121     {
122         try
123         {
124             listener.onContent(response, buffer, callback);
125         }
126         catch (Throwable x)
127         {
128             LOG.info("Exception while notifying listener " + listener, x);
129         }
130     }
131 
132     public void notifySuccess(List<Response.ResponseListener> listeners, Response response)
133     {
134         // Optimized to avoid allocations of iterator instances
135         for (int i = 0; i < listeners.size(); ++i)
136         {
137             Response.ResponseListener listener = listeners.get(i);
138             if (listener instanceof Response.SuccessListener)
139                 notifySuccess((Response.SuccessListener)listener, response);
140         }
141     }
142 
143     private void notifySuccess(Response.SuccessListener listener, Response response)
144     {
145         try
146         {
147             listener.onSuccess(response);
148         }
149         catch (Throwable x)
150         {
151             LOG.info("Exception while notifying listener " + listener, x);
152         }
153     }
154 
155     public void notifyFailure(List<Response.ResponseListener> listeners, Response response, Throwable failure)
156     {
157         // Optimized to avoid allocations of iterator instances
158         for (int i = 0; i < listeners.size(); ++i)
159         {
160             Response.ResponseListener listener = listeners.get(i);
161             if (listener instanceof Response.FailureListener)
162                 notifyFailure((Response.FailureListener)listener, response, failure);
163         }
164     }
165 
166     private void notifyFailure(Response.FailureListener listener, Response response, Throwable failure)
167     {
168         try
169         {
170             listener.onFailure(response, failure);
171         }
172         catch (Throwable x)
173         {
174             LOG.info("Exception while notifying listener " + listener, x);
175         }
176     }
177 
178     public void notifyComplete(List<Response.ResponseListener> listeners, Result result)
179     {
180         // Optimized to avoid allocations of iterator instances
181         for (int i = 0; i < listeners.size(); ++i)
182         {
183             Response.ResponseListener listener = listeners.get(i);
184             if (listener instanceof Response.CompleteListener)
185                 notifyComplete((Response.CompleteListener)listener, result);
186         }
187     }
188 
189     private void notifyComplete(Response.CompleteListener listener, Result result)
190     {
191         try
192         {
193             listener.onComplete(result);
194         }
195         catch (Throwable x)
196         {
197             LOG.info("Exception while notifying listener " + listener, x);
198         }
199     }
200 
201     public void forwardSuccess(List<Response.ResponseListener> listeners, Response response)
202     {
203         notifyBegin(listeners, response);
204         for (Iterator<HttpField> iterator = response.getHeaders().iterator(); iterator.hasNext();)
205         {
206             HttpField field = iterator.next();
207             if (!notifyHeader(listeners, response, field))
208                 iterator.remove();
209         }
210         notifyHeaders(listeners, response);
211         if (response instanceof ContentResponse)
212             notifyContent(listeners, response, ByteBuffer.wrap(((ContentResponse)response).getContent()), Callback.NOOP);
213         notifySuccess(listeners, response);
214     }
215 
216     public void forwardSuccessComplete(List<Response.ResponseListener> listeners, Request request, Response response)
217     {
218         forwardSuccess(listeners, response);
219         notifyComplete(listeners, new Result(request, response));
220     }
221 
222     public void forwardFailure(List<Response.ResponseListener> listeners, Response response, Throwable failure)
223     {
224         notifyBegin(listeners, response);
225         for (Iterator<HttpField> iterator = response.getHeaders().iterator(); iterator.hasNext();)
226         {
227             HttpField field = iterator.next();
228             if (!notifyHeader(listeners, response, field))
229                 iterator.remove();
230         }
231         notifyHeaders(listeners, response);
232         if (response instanceof ContentResponse)
233             notifyContent(listeners, response, ByteBuffer.wrap(((ContentResponse)response).getContent()), Callback.NOOP);
234         notifyFailure(listeners, response, failure);
235     }
236 
237     public void forwardFailureComplete(List<Response.ResponseListener> listeners, Request request, Throwable requestFailure, Response response, Throwable responseFailure)
238     {
239         forwardFailure(listeners, response, responseFailure);
240         notifyComplete(listeners, new Result(request, requestFailure, response, responseFailure));
241     }
242 
243     private class ContentCallback extends IteratingNestedCallback
244     {
245         private final List<Response.ResponseListener> listeners;
246         private final Response response;
247         private final ByteBuffer buffer;
248         private int index;
249 
250         private ContentCallback(List<Response.ResponseListener> listeners, Response response, ByteBuffer buffer, Callback callback)
251         {
252             super(callback);
253             this.listeners = listeners;
254             this.response = response;
255             // Slice the buffer to avoid that listeners peek into data they should not look at.
256             this.buffer = buffer.slice();
257         }
258 
259         @Override
260         protected Action process() throws Exception
261         {
262             if (index == listeners.size())
263                 return Action.SUCCEEDED;
264 
265             Response.ResponseListener listener = listeners.get(index);
266             if (listener instanceof Response.AsyncContentListener)
267             {
268                 // The buffer was sliced, so we always clear it
269                 // (clear => position=0, limit=capacity) before
270                 // passing it to the listener that may consume it.
271                 buffer.clear();
272                 ResponseNotifier.this.notifyContent((Response.AsyncContentListener)listener, response, buffer, this);
273                 return Action.SCHEDULED;
274             }
275             else
276             {
277                 succeeded();
278                 return Action.SCHEDULED;
279             }
280         }
281 
282         @Override
283         public void succeeded()
284         {
285             ++index;
286             super.succeeded();
287         }
288     }
289 }