View Javadoc

1   /*
2      Copyright 2007 Ramon Servadei
3   
4      Licensed under the Apache License, Version 2.0 (the "License");
5      you may not use this file except in compliance with the License.
6      You may obtain a copy of the License at
7   
8          http://www.apache.org/licenses/LICENSE-2.0
9   
10     Unless required by applicable law or agreed to in writing, software
11     distributed under the License is distributed on an "AS IS" BASIS,
12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13     See the License for the specific language governing permissions and
14     limitations under the License.
15   */
16  package fulmine.protocol.specification;
17  
18  import static fulmine.util.Utils.CLOSE_BRACE;
19  
20  import java.util.Collection;
21  
22  import fulmine.model.component.IComponent;
23  import fulmine.model.container.IContainer;
24  import fulmine.protocol.wire.IWireIdentityRegistry;
25  import fulmine.protocol.wire.WireIdentity;
26  import fulmine.protocol.wire.operation.BasicOperation;
27  import fulmine.protocol.wire.operation.IOperationScope;
28  
29  /**
30   * A utility class for writing fulmine delta (FD) protocol frames.
31   * <p>
32   * Please refer to the "Fulmine Delta Transmission Protocol" specification for a
33   * complete description of the fulmine serialisation technique.
34   * 
35   * @see IFrameConstants
36   * @author Ramon Servadei
37   * 
38   */
39  public final class FrameWriter implements IFrameWriter
40  {
41  
42      /** Standard constructor. */
43      public FrameWriter()
44      {
45          super();
46      }
47  
48      /**
49       * Write a container's current state changes into an FD frame.
50       * 
51       * @param scope
52       *            the scope to use for the write operation
53       * @param container
54       *            the container to write as a frame
55       * @param completeState
56       *            whether to write the complete state or only changes
57       * @return the byte[] for the FD frame representing the state of the
58       *         container
59       */
60      public static byte[] write(final IOperationScope scope,
61          final IContainer container, final boolean completeState)
62      {
63          final int[] headerBufferPosition = new int[1];
64          final byte[][] headerBuffer = new byte[1][1024];
65  
66          final int[] dataBufferPosition = new int[1];
67          final byte[][] dataBuffer = new byte[1][1024];
68  
69          // a container can only have a SWF wire identity and this is also the
70          // container's identity
71          container.writeState(scope, WireIdentity.get(container.getIdentity()),
72              headerBuffer, headerBufferPosition, dataBuffer, dataBufferPosition,
73              completeState);
74          scope.validate();
75  
76          final byte[] frame =
77              mergeBuffers(headerBufferPosition, headerBuffer,
78                  dataBufferPosition, dataBuffer);
79          return frame;
80      }
81  
82      /**
83       * Write a container's complete state into an FD frame.
84       * 
85       * @param container
86       *            the container to write as a frame
87       * @return the byte[] for the FD frame representing the state of the
88       *         container
89       */
90      public byte[] writeComplete(final IContainer container)
91      {
92          return write(new BasicOperation(
93              container.getContext().getPermissionProfile()), container, true);
94      }
95  
96      /**
97       * Write a container's state into an FD frame.
98       * 
99       * @param container
100      *            the container to write as a frame
101      * @return the byte[] for the FD frame representing the state of the
102      *         container
103      */
104     public byte[] write(final IContainer container)
105     {
106         return write(new BasicOperation(
107             container.getContext().getPermissionProfile()), container, false);
108     }
109 
110     /**
111      * Write only a container's meta state into an FD frame. The meta state
112      * includes the current state (active/inactive)
113      * 
114      * @param container
115      *            the container to write
116      * @return the byte[] for the FD frame representing the meta state of the
117      *         container
118      */
119     public byte[] writeMeta(final IContainer container)
120     {
121         final int[] headerBufferPosition = new int[1];
122         final byte[][] headerBuffer = new byte[1][1024];
123 
124         final int[] dataBufferPosition = new int[1];
125         final byte[][] dataBuffer = new byte[1][1024];
126 
127         writeHeaderAndData(container, null, null, new BasicOperation(null),
128             headerBuffer, headerBufferPosition, dataBuffer, dataBufferPosition,
129             false);
130 
131         final byte[] frame =
132             mergeBuffers(headerBufferPosition, headerBuffer,
133                 dataBufferPosition, dataBuffer);
134         return frame;
135     }
136 
137     /**
138      * Write a nested container's state into a nested FD frame. Nested FD frames
139      * have the following structure:
140      * 
141      * <pre>
142      * -----------------------------------------------------------------
143      * |  nested frame preamble  |            nested frame             |
144      * -----------------------------------------------------------------
145      * |   4 byte    |  4 byte   | header size bytes | data size bytes |
146      * -----------------------------------------------------------------
147      * | header size | data size |       header      |       data      |
148      * -----------------------------------------------------------------
149      * </pre>
150      * 
151      * Notice that there is no container identifier or any of the other fields
152      * found in the general container header.
153      * 
154      * @param fields
155      *            the fields of the container to write into the frame
156      * @param registry
157      *            used to get the IWF wire codes for the fields
158      * @param scope
159      *            the scope of the write operation
160      * @param completeState
161      *            whether to write the complete state or only changes
162      * @return the byte[] for the nested FD frame representing the state of the
163      *         nested container
164      */
165     public static byte[] writeNested(
166         final Collection<? extends IComponent> components,
167         final IWireIdentityRegistry registry, final IOperationScope scope,
168         final boolean completeState)
169     {
170 
171         final int[] headerBufferPosition = new int[1];
172         final byte[][] headerBuffer = new byte[1][1024];
173 
174         final int[] dataBufferPosition = new int[1];
175         final byte[][] dataBuffer = new byte[1][1024];
176 
177         // make space for the header size and data size portions of the nested
178         // frame preamble
179         headerBufferPosition[0] = IFrameConstants.NESTED_FRAME_PREAMBLE_LENGTH;
180 
181         doWriteComponents(components, registry, scope, headerBufferPosition,
182             headerBuffer, dataBufferPosition, dataBuffer, completeState);
183 
184         ByteWriter.writeIntegerAsBytes(headerBufferPosition[0]
185             - IFrameConstants.NESTED_FRAME_PREAMBLE_LENGTH, headerBuffer, 0,
186             IFrameConstants.HEADER_SIZE_LENGTH);
187         ByteWriter.writeIntegerAsBytes(dataBufferPosition[0], headerBuffer,
188             IFrameConstants.HEADER_SIZE_LENGTH,
189             IFrameConstants.HEADER_SIZE_LENGTH);
190 
191         final byte[] frame =
192             mergeBuffers(headerBufferPosition, headerBuffer,
193                 dataBufferPosition, dataBuffer);
194         return frame;
195     }
196 
197     @SuppressWarnings("boxing")
198     private static void doWriteComponents(
199         final Collection<? extends IComponent> components,
200         final IWireIdentityRegistry registry, final IOperationScope scope,
201         final int[] headerBufferPosition, final byte[][] headerBuffer,
202         final int[] dataBufferPosition, final byte[][] dataBuffer,
203         final boolean completeState)
204     {
205         for (IComponent nestedComponent : components)
206         {
207             try
208             {
209                 if (nestedComponent == null)
210                 {
211                     StringBuffer sb = new StringBuffer();
212                     java.util.List<Object> l =
213                         java.util.Arrays.asList(new Object[] { components,
214                             registry, scope, headerBufferPosition,
215                             headerBuffer, dataBufferPosition, dataBuffer,
216                             completeState });
217                     sb.append("FrameWriter.doWriteComponents(").append(l).append(
218                         CLOSE_BRACE);
219                     System.err.println(sb.toString());
220                     Thread.dumpStack();
221                 }
222                 else
223                 {
224                     final String identity = nestedComponent.getIdentity();
225                     nestedComponent.writeState(scope,
226                         registry.getWireIdentityFor(identity), headerBuffer,
227                         headerBufferPosition, dataBuffer, dataBufferPosition,
228                         completeState);
229                 }
230             }
231             catch (IllegalArgumentException e)
232             {
233                 throw new IllegalArgumentException(
234                     "Could not write component: " + nestedComponent, e);
235             }
236         }
237     }
238 
239     public static byte[] mergeBuffers(final int[] headerBufferPosition,
240         final byte[][] headerBuffer, final int[] dataBufferPosition,
241         final byte[][] dataBuffer)
242     {
243         final byte[] frame =
244             new byte[headerBufferPosition[0] + dataBufferPosition[0]];
245         System.arraycopy(headerBuffer[0], 0, frame, 0, headerBufferPosition[0]);
246         System.arraycopy(dataBuffer[0], 0, frame, headerBufferPosition[0],
247             dataBufferPosition[0]);
248         return frame;
249     }
250 
251     /**
252      * Write the container and fields to the header and data buffers. The header
253      * and data buffers make up an FD frame.
254      * 
255      * @param container
256      *            the container to write into the header and data buffers of the
257      *            frame
258      * @param fields
259      *            the fields of the container to write into the frame, can be
260      *            <code>null</code>
261      * @param registry
262      *            used to get the IWF wire codes for the fields
263      * @param scope
264      *            the scope of the write operation
265      * @param headerBuffer
266      *            headerBuffer[0] holds the buffer for the header of the frame
267      * @param headerBufferPosition
268      *            headerBufferPosition[0] points to the end of the header data
269      *            in headerDataBuffer[0]
270      * @param dataBuffer
271      *            dataBuffer[0] holds the buffer for the data of the frame
272      * @param dataBufferPosition
273      *            dataBufferPosition[0] points to the end of the data in
274      *            dataBuffer[0]
275      * @param completeState
276      *            whether to write the complete state or only changes
277      */
278     public static void writeHeaderAndData(final IContainer container,
279         final Collection<? extends IComponent> components,
280         final IWireIdentityRegistry registry, final IOperationScope scope,
281         final byte[][] headerBuffer, final int[] headerBufferPosition,
282         final byte[][] dataBuffer, final int[] dataBufferPosition,
283         final boolean completeState)
284     {
285         // identify the size of the frame preamble (the preamble is
286         // written after the data)
287         final byte[] bytesForString =
288             ByteWriter.getBytes(container.getIdentity());
289         final int byteCountForString = bytesForString.length;
290         final int preambleLength =
291             IFrameConstants.PREAMBLE_STATIC_SECTION_LENGTH
292                 + IFrameConstants.ID_SIZE_LENGTH + byteCountForString;
293         final int[] preamblePosition = new int[] { headerBufferPosition[0] };
294         headerBufferPosition[0] += preambleLength;
295 
296         if (components != null)
297         {
298             doWriteComponents(components, registry, scope,
299                 headerBufferPosition, headerBuffer, dataBufferPosition,
300                 dataBuffer, completeState);
301         }
302 
303         // now complete the preamble
304         // ID size
305         final int idSize =
306             ByteWriter.writeInteger(byteCountForString, headerBuffer,
307                 preamblePosition[0]);
308         if (idSize > 1)
309         {
310             throw new IllegalStateException("Identity is too long (255 max): '"
311                 + container.getIdentity() + "'");
312         }
313         preamblePosition[0] += idSize;
314         // ID
315         preamblePosition[0] +=
316             ByteWriter.writeBytes(bytesForString, headerBuffer,
317                 preamblePosition[0]);
318         // record type
319         final byte type = container.getType().value();
320         ByteWriter.writeIntegerAsBytes(type, headerBuffer, preamblePosition[0],
321             IFrameConstants.RECORD_TYPE_LENGTH);
322         preamblePosition[0] += IFrameConstants.RECORD_TYPE_LENGTH;
323         // write the state
324         final int state = container.getDataState().ordinal();
325         ByteWriter.writeIntegerAsBytes(state, headerBuffer,
326             preamblePosition[0], IFrameConstants.RECORD_STATE_LENGTH);
327         preamblePosition[0] += IFrameConstants.RECORD_STATE_LENGTH;
328         final int domain = container.getDomain().value();
329         ByteWriter.writeIntegerAsBytes(domain, headerBuffer,
330             preamblePosition[0], IFrameConstants.RECORD_DOMAIN_LENGTH);
331         preamblePosition[0] += IFrameConstants.RECORD_DOMAIN_LENGTH;
332         // unused part
333         preamblePosition[0] += IFrameConstants.UNUSED_LENGTH;
334         // header size
335         ByteWriter.writeIntegerAsBytes(
336             headerBufferPosition[0] - preambleLength, headerBuffer,
337             preamblePosition[0], IFrameConstants.HEADER_SIZE_LENGTH);
338         preamblePosition[0] += IFrameConstants.HEADER_SIZE_LENGTH;
339         // data size
340         ByteWriter.writeIntegerAsBytes(dataBufferPosition[0], headerBuffer,
341             preamblePosition[0], IFrameConstants.DATA_SIZE_LENGTH);
342         preamblePosition[0] += IFrameConstants.DATA_SIZE_LENGTH;
343     }
344 }