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.spdy.api;
20  
21  import java.nio.ByteBuffer;
22  import java.nio.charset.Charset;
23  import java.util.concurrent.TimeUnit;
24  import java.util.concurrent.atomic.AtomicInteger;
25  
26  /**
27   * <p>A container for DATA frames metadata and content bytes.</p>
28   * <p>Specialized subclasses (like {@link StringDataInfo}) may be used by applications
29   * to send specific types of content.</p>
30   * <p>Applications may send multiple instances of {@link DataInfo}, usually of the same
31   * type, via {@link Stream#data(DataInfo)}. The last instance must have the
32   * {@link #isClose() close flag} set, so that the client knows that no more content is
33   * expected.</p>
34   * <p>Receivers of {@link DataInfo} via {@link StreamFrameListener#onData(Stream, DataInfo)}
35   * have two different APIs to read the data content bytes: a {@link #readInto(ByteBuffer) read}
36   * API that does not interact with flow control, and a {@link #consumeInto(ByteBuffer) drain}
37   * API that interacts with flow control.</p>
38   * <p>Flow control is defined so that when the sender wants to sends a number of bytes larger
39   * than the {@link Settings.ID#INITIAL_WINDOW_SIZE} value, it will stop sending as soon as it
40   * has sent a number of bytes equal to the window size. The receiver has to <em>consume</em>
41   * the data bytes that it received in order to tell the sender to send more bytes.</p>
42   * <p>Consuming the data bytes can be done only via {@link #consumeInto(ByteBuffer)} or by a combination
43   * of {@link #readInto(ByteBuffer)} and {@link #consume(int)} (possibly at different times).</p>
44   */
45  public abstract class DataInfo extends Info
46  {
47      /**
48       * <p>Flag that indicates that this {@link DataInfo} is the last frame in the stream.</p>
49       *
50       * @see #isClose()
51       * @see #getFlags()
52       */
53      public final static byte FLAG_CLOSE = 1;
54  
55      private final AtomicInteger consumed = new AtomicInteger();
56      private boolean close;
57  
58      /**
59       * <p>Creates a new {@link DataInfo} with the given close flag and no compression flag.</p>
60       *
61       * @param close the value of the close flag
62       */
63      public DataInfo(boolean close)
64      {
65          setClose(close);
66      }
67  
68      /**
69       * <p>Creates a new {@link DataInfo} with the given close flag and no compression flag.</p>
70       *
71       * @param timeout
72       * @param unit
73       * @param close the value of the close flag
74       */
75      protected DataInfo(long timeout, TimeUnit unit, boolean close)
76      {
77          super(timeout, unit);
78          this.close = close;
79      }
80  
81      /**
82       * @return the value of the close flag
83       * @see #setClose(boolean)
84       */
85      public boolean isClose()
86      {
87          return close;
88      }
89  
90      /**
91       * @param close the value of the close flag
92       * @see #isClose()
93       */
94      public void setClose(boolean close)
95      {
96          this.close = close;
97      }
98  
99      /**
100      * @return the close and compress flags as integer
101      * @see #FLAG_CLOSE
102      */
103     public byte getFlags()
104     {
105         return isClose() ? FLAG_CLOSE : 0;
106     }
107 
108     /**
109      * @return the total number of content bytes
110      * @see #available()
111      */
112     public abstract int length();
113 
114     /**
115      * <p>Returns the available content bytes that can be read via {@link #readInto(ByteBuffer)}.</p>
116      * <p>Each invocation to {@link #readInto(ByteBuffer)} modifies the value returned by this method,
117      * until no more content bytes are available.</p>
118      *
119      * @return the available content bytes
120      * @see #readInto(ByteBuffer)
121      */
122     public abstract int available();
123 
124     /**
125      * <p>Copies the content bytes of this {@link DataInfo} into the given {@link ByteBuffer}.</p>
126      * <p>If the given {@link ByteBuffer} cannot contain the whole content of this {@link DataInfo}
127      * then after the read {@link #available()} will return a positive value, and further content
128      * may be retrieved by invoking again this method with a new output buffer.</p>
129      *
130      * @param output the {@link ByteBuffer} to copy the bytes into
131      * @return the number of bytes copied
132      * @see #available()
133      * @see #consumeInto(ByteBuffer)
134      */
135     public abstract int readInto(ByteBuffer output);
136 
137     /**
138      * <p>Copies the content bytes of this {@link DataInfo} into the given byte array.</p>
139      * <p>If the given byte array cannot contain the whole content of this {@link DataInfo}
140      * then after the read {@link #available()} will return a positive value, and further content
141      * may be retrieved by invoking again this method with a new byte array.</p>
142      *
143      * @param bytes the byte array to copy the bytes into
144      * @param offset the index of the byte array to start copying
145      * @param length the number of bytes to copy
146      * @return the number of bytes copied
147      */
148     public abstract int readInto(byte[] bytes, int offset, int length);
149 
150     /**
151      * <p>Reads and consumes the content bytes of this {@link DataInfo} into the given {@link ByteBuffer}.</p>
152      *
153      * @param output the {@link ByteBuffer} to copy the bytes into
154      * @return the number of bytes copied
155      * @see #consume(int)
156      */
157     public int consumeInto(ByteBuffer output)
158     {
159         int read = readInto(output);
160         consume(read);
161         return read;
162     }
163 
164     /**
165      * <p>Reads and consumes the content bytes of this {@link DataInfo} into the given byte array,
166      * starting from index {@code offset} for {@code length} bytes.</p>
167      *
168      * @param bytes the byte array to copy the bytes into
169      * @param offset the offset of the byte array to start copying
170      * @param length the number of bytes to copy
171      * @return the number of bytes copied
172      */
173     public int consumeInto(byte[] bytes, int offset, int length)
174     {
175         int read = readInto(bytes, offset, length);
176         consume(read);
177         return read;
178     }
179 
180     /**
181      * <p>Consumes the given number of bytes from this {@link DataInfo}.</p>
182      *
183      * @param delta the number of bytes consumed
184      */
185     public void consume(int delta)
186     {
187         if (delta < 0)
188             throw new IllegalArgumentException();
189         int read = length() - available();
190         int newConsumed = consumed() + delta;
191 //        if (newConsumed > read)
192 //            throw new IllegalStateException("Consuming without reading: consumed " + newConsumed + " but only read " + read);
193         consumed.addAndGet(delta);
194     }
195 
196     /**
197      * @return the number of bytes consumed
198      */
199     public int consumed()
200     {
201         return consumed.get();
202     }
203 
204     /**
205      *
206      * @param charset the charset used to convert the bytes
207      * @param consume whether to consume the content
208      * @return a String with the content of this {@link DataInfo}
209      */
210     public String asString(String charset, boolean consume)
211     {
212         return asString(Charset.forName(charset), consume);
213     }
214 
215     /**
216      *
217      * @param charset the charset used to convert the bytes
218      * @param consume whether to consume the content
219      * @return a String with the content of this {@link DataInfo}
220      */
221     public String asString(Charset charset, boolean consume)
222     {
223         ByteBuffer buffer = asByteBuffer(consume);
224         return charset.decode(buffer).toString();
225     }
226 
227     /**
228      * @return a byte array with the content of this {@link DataInfo}
229      * @param consume whether to consume the content
230      */
231     public byte[] asBytes(boolean consume)
232     {
233         ByteBuffer buffer = asByteBuffer(consume);
234         byte[] result = new byte[buffer.remaining()];
235         buffer.get(result);
236         return result;
237     }
238 
239     /**
240      * @return a {@link ByteBuffer} with the content of this {@link DataInfo}
241      * @param consume whether to consume the content
242      */
243     public ByteBuffer asByteBuffer(boolean consume)
244     {
245         ByteBuffer buffer = allocate(available());
246         if (consume)
247             consumeInto(buffer);
248         else
249             readInto(buffer);
250         buffer.flip();
251         return buffer;
252     }
253 
254     protected ByteBuffer allocate(int size)
255     {
256         return ByteBuffer.allocate(size);
257     }
258 
259     @Override
260     public String toString()
261     {
262         return String.format("DATA @%x available=%d consumed=%d close=%b", hashCode(), available(), consumed(), isClose());
263     }
264 }