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 return true;
124 }
125 return false;
126 }
127 }
128
129 @Override
130 public ByteBuffer next()
131 {
132 ByteBuffer result = buffer;
133 buffer = null;
134 if (failure != null)
135 throw (NoSuchElementException)new NoSuchElementException().initCause(failure);
136 if (result == null)
137 throw new NoSuchElementException();
138 return result;
139 }
140
141 @Override
142 public void remove()
143 {
144 throw new UnsupportedOperationException();
145 }
146 };
147 }
148 }