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             // TODO: handle callback
213             notifyContent(listeners, response, ByteBuffer.wrap(((ContentResponse)response).getContent()), new Callback.Adapter());
214         notifySuccess(listeners, response);
215     }
216 
217     public void forwardSuccessComplete(List<Response.ResponseListener> listeners, Request request, Response response)
218     {
219         forwardSuccess(listeners, response);
220         notifyComplete(listeners, new Result(request, response));
221     }
222 
223     public void forwardFailure(List<Response.ResponseListener> listeners, Response response, Throwable failure)
224     {
225         notifyBegin(listeners, response);
226         for (Iterator<HttpField> iterator = response.getHeaders().iterator(); iterator.hasNext();)
227         {
228             HttpField field = iterator.next();
229             if (!notifyHeader(listeners, response, field))
230                 iterator.remove();
231         }
232         notifyHeaders(listeners, response);
233         if (response instanceof ContentResponse)
234             // TODO: handle callback
235             notifyContent(listeners, response, ByteBuffer.wrap(((ContentResponse)response).getContent()), new Callback.Adapter());
236         notifyFailure(listeners, response, failure);
237     }
238 
239     public void forwardFailureComplete(List<Response.ResponseListener> listeners, Request request, Throwable requestFailure, Response response, Throwable responseFailure)
240     {
241         forwardFailure(listeners, response, responseFailure);
242         notifyComplete(listeners, new Result(request, requestFailure, response, responseFailure));
243     }
244 
245     private class ContentCallback extends IteratingNestedCallback
246     {
247         private final List<Response.ResponseListener> listeners;
248         private final Response response;
249         private final ByteBuffer buffer;
250         private int index;
251 
252         private ContentCallback(List<Response.ResponseListener> listeners, Response response, ByteBuffer buffer, Callback callback)
253         {
254             super(callback);
255             this.listeners = listeners;
256             this.response = response;
257             // Slice the buffer to avoid that listeners peek into data they should not look at.
258             this.buffer = buffer.slice();
259         }
260 
261         @Override
262         protected Action process() throws Exception
263         {
264             if (index == listeners.size())
265                 return Action.SUCCEEDED;
266 
267             Response.ResponseListener listener = listeners.get(index);
268             if (listener instanceof Response.AsyncContentListener)
269             {
270                 // The buffer was sliced, so we always clear it
271                 // (clear => position=0, limit=capacity) before
272                 // passing it to the listener that may consume it.
273                 buffer.clear();
274                 ResponseNotifier.this.notifyContent((Response.AsyncContentListener)listener, response, buffer, this);
275                 return Action.SCHEDULED;
276             }
277             else
278             {
279                 succeeded();
280                 return Action.SCHEDULED;
281             }
282         }
283 
284         @Override
285         public void succeeded()
286         {
287             ++index;
288             super.succeeded();
289         }
290     }
291 }