| // |
| // ======================================================================== |
| // Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd. |
| // ------------------------------------------------------------------------ |
| // All rights reserved. This program and the accompanying materials |
| // are made available under the terms of the Eclipse Public License v1.0 |
| // and Apache License v2.0 which accompanies this distribution. |
| // |
| // The Eclipse Public License is available at |
| // http://www.eclipse.org/legal/epl-v10.html |
| // |
| // The Apache License v2.0 is available at |
| // http://www.opensource.org/licenses/apache2.0.php |
| // |
| // You may elect to redistribute this code under either of these licenses. |
| // ======================================================================== |
| // |
| |
| package org.eclipse.jetty.client.util; |
| |
| import java.io.Closeable; |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.nio.ByteBuffer; |
| import java.util.Iterator; |
| |
| import org.eclipse.jetty.client.AsyncContentProvider; |
| import org.eclipse.jetty.client.api.ContentProvider; |
| import org.eclipse.jetty.client.api.Request; |
| import org.eclipse.jetty.client.api.Response; |
| import org.eclipse.jetty.util.Callback; |
| |
| /** |
| * A {@link ContentProvider} that provides content asynchronously through an {@link OutputStream} |
| * similar to {@link DeferredContentProvider}. |
| * <p> |
| * {@link OutputStreamContentProvider} can only be used in conjunction with |
| * {@link Request#send(Response.CompleteListener)} (and not with its blocking counterpart {@link Request#send()}) |
| * because it provides content asynchronously. |
| * <p> |
| * The deferred content is provided once by writing to the {@link #getOutputStream() output stream} |
| * and then fully consumed. |
| * Invocations to the {@link #iterator()} method after the first will return an "empty" iterator |
| * because the stream has been consumed on the first invocation. |
| * However, it is possible for subclasses to support multiple invocations of {@link #iterator()} |
| * by overriding {@link #write(ByteBuffer)} and {@link #close()}, copying the bytes and making them |
| * available for subsequent invocations. |
| * <p> |
| * Content must be provided by writing to the {@link #getOutputStream() output stream}, that must be |
| * {@link OutputStream#close() closed} when all content has been provided. |
| * <p> |
| * Example usage: |
| * <pre> |
| * HttpClient httpClient = ...; |
| * |
| * // Use try-with-resources to autoclose the output stream |
| * OutputStreamContentProvider content = new OutputStreamContentProvider(); |
| * try (OutputStream output = content.getOutputStream()) |
| * { |
| * httpClient.newRequest("localhost", 8080) |
| * .content(content) |
| * .send(new Response.CompleteListener() |
| * { |
| * @Override |
| * public void onComplete(Result result) |
| * { |
| * // Your logic here |
| * } |
| * }); |
| * |
| * // At a later time... |
| * output.write("some content".getBytes()); |
| * } |
| * </pre> |
| */ |
| public class OutputStreamContentProvider implements AsyncContentProvider, Callback, Closeable |
| { |
| private final DeferredContentProvider deferred = new DeferredContentProvider(); |
| private final OutputStream output = new DeferredOutputStream(); |
| |
| @Override |
| public boolean isNonBlocking() |
| { |
| return deferred.isNonBlocking(); |
| } |
| |
| @Override |
| public long getLength() |
| { |
| return deferred.getLength(); |
| } |
| |
| @Override |
| public Iterator<ByteBuffer> iterator() |
| { |
| return deferred.iterator(); |
| } |
| |
| @Override |
| public void setListener(Listener listener) |
| { |
| deferred.setListener(listener); |
| } |
| |
| public OutputStream getOutputStream() |
| { |
| return output; |
| } |
| |
| protected void write(ByteBuffer buffer) |
| { |
| deferred.offer(buffer); |
| } |
| |
| @Override |
| public void close() |
| { |
| deferred.close(); |
| } |
| |
| @Override |
| public void succeeded() |
| { |
| deferred.succeeded(); |
| } |
| |
| @Override |
| public void failed(Throwable failure) |
| { |
| deferred.failed(failure); |
| } |
| |
| private class DeferredOutputStream extends OutputStream |
| { |
| @Override |
| public void write(int b) throws IOException |
| { |
| write(new byte[]{(byte)b}, 0, 1); |
| } |
| |
| @Override |
| public void write(byte[] b, int off, int len) throws IOException |
| { |
| OutputStreamContentProvider.this.write(ByteBuffer.wrap(b, off, len)); |
| flush(); |
| } |
| |
| @Override |
| public void flush() throws IOException |
| { |
| deferred.flush(); |
| } |
| |
| @Override |
| public void close() throws IOException |
| { |
| OutputStreamContentProvider.this.close(); |
| } |
| } |
| } |