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