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 * @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 }