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 }