View Javadoc

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 }