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