View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2015 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.util;
20  
21  import java.io.Closeable;
22  import java.io.IOException;
23  import java.io.OutputStream;
24  import java.nio.ByteBuffer;
25  import java.util.Iterator;
26  
27  import org.eclipse.jetty.client.AsyncContentProvider;
28  import org.eclipse.jetty.client.api.ContentProvider;
29  import org.eclipse.jetty.client.api.Request;
30  import org.eclipse.jetty.client.api.Response;
31  import org.eclipse.jetty.util.Callback;
32  
33  /**
34   * A {@link ContentProvider} that provides content asynchronously through an {@link OutputStream}
35   * similar to {@link DeferredContentProvider}.
36   * <p>
37   * {@link OutputStreamContentProvider} can only be used in conjunction with
38   * {@link Request#send(Response.CompleteListener)} (and not with its blocking counterpart {@link Request#send()})
39   * because it provides content asynchronously.
40   * <p>
41   * The deferred content is provided once by writing to the {@link #getOutputStream() output stream}
42   * and then fully consumed.
43   * Invocations to the {@link #iterator()} method after the first will return an "empty" iterator
44   * because the stream has been consumed on the first invocation.
45   * However, it is possible for subclasses to support multiple invocations of {@link #iterator()}
46   * by overriding {@link #write(ByteBuffer)} and {@link #close()}, copying the bytes and making them
47   * available for subsequent invocations.
48   * <p>
49   * Content must be provided by writing to the {@link #getOutputStream() output stream}, that must be
50   * {@link OutputStream#close() closed} when all content has been provided.
51   * <p>
52   * Example usage:
53   * <pre>
54   * HttpClient httpClient = ...;
55   *
56   * // Use try-with-resources to autoclose the output stream
57   * OutputStreamContentProvider content = new OutputStreamContentProvider();
58   * try (OutputStream output = content.getOutputStream())
59   * {
60   *     httpClient.newRequest("localhost", 8080)
61   *             .content(content)
62   *             .send(new Response.CompleteListener()
63   *             {
64   *                 &#64;Override
65   *                 public void onComplete(Result result)
66   *                 {
67   *                     // Your logic here
68   *                 }
69   *             });
70   *
71   *     // At a later time...
72   *     output.write("some content".getBytes());
73   * }
74   * </pre>
75   */
76  public class OutputStreamContentProvider implements AsyncContentProvider, Callback, Closeable
77  {
78      private final DeferredContentProvider deferred = new DeferredContentProvider();
79      private final OutputStream output = new DeferredOutputStream();
80  
81      @Override
82      public boolean isNonBlocking()
83      {
84          return deferred.isNonBlocking();
85      }
86      
87      @Override
88      public long getLength()
89      {
90          return deferred.getLength();
91      }
92  
93      @Override
94      public Iterator<ByteBuffer> iterator()
95      {
96          return deferred.iterator();
97      }
98  
99      @Override
100     public void setListener(Listener listener)
101     {
102         deferred.setListener(listener);
103     }
104 
105     public OutputStream getOutputStream()
106     {
107         return output;
108     }
109 
110     protected void write(ByteBuffer buffer)
111     {
112         deferred.offer(buffer);
113     }
114 
115     @Override
116     public void close()
117     {
118         deferred.close();
119     }
120 
121     @Override
122     public void succeeded()
123     {
124         deferred.succeeded();
125     }
126 
127     @Override
128     public void failed(Throwable failure)
129     {
130         deferred.failed(failure);
131     }
132 
133     private class DeferredOutputStream extends OutputStream
134     {
135         @Override
136         public void write(int b) throws IOException
137         {
138             write(new byte[]{(byte)b}, 0, 1);
139         }
140 
141         @Override
142         public void write(byte[] b, int off, int len) throws IOException
143         {
144             OutputStreamContentProvider.this.write(ByteBuffer.wrap(b, off, len));
145             flush();
146         }
147 
148         @Override
149         public void flush() throws IOException
150         {
151             deferred.flush();
152         }
153 
154         @Override
155         public void close() throws IOException
156         {
157             OutputStreamContentProvider.this.close();
158         }
159     }
160 }