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.InputStream; 22 import java.nio.ByteBuffer; 23 import java.util.Iterator; 24 import java.util.NoSuchElementException; 25 26 import org.eclipse.jetty.client.api.ContentProvider; 27 import org.eclipse.jetty.util.BufferUtil; 28 29 /** 30 * A {@link ContentProvider} for an {@link InputStream}. 31 * <p /> 32 * The input stream is read once and therefore fully consumed. 33 * Invocations to the {@link #iterator()} method after the first will return an "empty" iterator 34 * because the stream has been consumed on the first invocation. 35 * <p /> 36 * It is possible to specify, at the constructor, a buffer size used to read content from the 37 * stream, by default 4096 bytes. 38 * <p /> 39 * However, it is possible for subclasses to override {@link #onRead(byte[], int, int)} to copy 40 * the content read from the stream to another location (for example a file), and be able to 41 * support multiple invocations of {@link #iterator()}, returning the iterator provided by this 42 * class on the first invocation, and an iterator on the bytes copied to the other location 43 * for subsequent invocations. 44 */ 45 public class InputStreamContentProvider implements ContentProvider 46 { 47 private final InputStream stream; 48 private final int bufferSize; 49 50 public InputStreamContentProvider(InputStream stream) 51 { 52 this(stream, 4096); 53 } 54 55 public InputStreamContentProvider(InputStream stream, int bufferSize) 56 { 57 this.stream = stream; 58 this.bufferSize = bufferSize; 59 } 60 61 @Override 62 public long getLength() 63 { 64 return -1; 65 } 66 67 /** 68 * Callback method invoked just after having read from the stream, 69 * but before returning the iteration element (a {@link ByteBuffer} 70 * to the caller. 71 * <p /> 72 * Subclasses may override this method to copy the content read from 73 * the stream to another location (a file, or in memory if the content 74 * is known to fit). 75 * 76 * @param buffer the byte array containing the bytes read 77 * @param offset the offset from where bytes should be read 78 * @param length the length of the bytes read 79 * @return a {@link ByteBuffer} wrapping the byte array 80 */ 81 protected ByteBuffer onRead(byte[] buffer, int offset, int length) 82 { 83 if (length <= 0) 84 return BufferUtil.EMPTY_BUFFER; 85 return ByteBuffer.wrap(buffer, offset, length); 86 } 87 88 @Override 89 public Iterator<ByteBuffer> iterator() 90 { 91 return new Iterator<ByteBuffer>() 92 { 93 private final byte[] bytes = new byte[bufferSize]; 94 private Exception failure; 95 private ByteBuffer buffer; 96 97 @Override 98 public boolean hasNext() 99 { 100 try 101 { 102 int read = stream.read(bytes); 103 if (read > 0) 104 { 105 buffer = onRead(bytes, 0, read); 106 return true; 107 } 108 else if (read < 0) 109 { 110 return false; 111 } 112 else 113 { 114 buffer = BufferUtil.EMPTY_BUFFER; 115 return true; 116 } 117 } 118 catch (Exception x) 119 { 120 if (failure == null) 121 { 122 failure = x; 123 // Signal we have more content to cause a call to 124 // next() which will throw NoSuchElementException. 125 return true; 126 } 127 return false; 128 } 129 } 130 131 @Override 132 public ByteBuffer next() 133 { 134 ByteBuffer result = buffer; 135 buffer = null; 136 if (failure != null) 137 throw (NoSuchElementException)new NoSuchElementException().initCause(failure); 138 if (result == null) 139 throw new NoSuchElementException(); 140 return result; 141 } 142 143 @Override 144 public void remove() 145 { 146 throw new UnsupportedOperationException(); 147 } 148 }; 149 } 150 }