• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 Google Inc.  All rights reserved.
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 
17 package com.google.polo.wire.xml;
18 
19 import java.io.ByteArrayInputStream;
20 import java.io.ByteArrayOutputStream;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.OutputStream;
24 
25 /**
26  * Representation of a message sent by the XML protocol.
27  */
28 public class XmlMessageWrapper {
29 
30     /**
31      * Number of bytes in the header for the "receiver id" field.
32      */
33     private static final int HEADER_FIELD_RECEIVER_ID_LENGTH = 32;
34 
35     /**
36      * Number of bytes in the header for the "payload length" field.
37      */
38     private static final int HEADER_FIELD_PAYLOAD_LENGTH = 4;
39 
40     /**
41      * Number of bytes in the header for the "protocol version" field.
42      */
43     private static final int HEADER_FIELD_PROTOCOL_VERSION_LENGTH = 2;
44 
45     /**
46      * Number of bytes in the header reserved for future use.
47      */
48     private static final int HEADER_FIELD_PADDING_LENGTH = 25;
49 
50     private static final int HEADER_SIZE = 64;
51 
52     /**
53      * The id of the receiver.
54      */
55     private String mReceiverId;
56 
57     /**
58      * Protocol version.
59      */
60     private int mProtocolVersion;
61 
62     /**
63      * Creator ID
64      */
65     private byte mCreatorId;
66 
67     /**
68      * XML message.
69      */
70     private byte[] mPayload;
71 
XmlMessageWrapper(String recieverId, int protocolVersion, byte creatorId, byte[] payload)72     public XmlMessageWrapper(String recieverId, int protocolVersion,
73             byte creatorId, byte[] payload) {
74         mReceiverId = recieverId;
75         mProtocolVersion = protocolVersion;
76         mCreatorId = creatorId;
77         mPayload = payload;
78     }
79 
80     /**
81      * Writes the serialized form of this message to an {@link OutputStream}
82      *
83      * @param  outputStream  the destination output stream
84      * @throws IOException  if an error occurred during write
85      */
serializeToOutputStream(OutputStream outputStream)86     public void serializeToOutputStream(OutputStream outputStream)
87             throws IOException {
88         // Receiver ID
89         outputStream.write(stringToBytesPadded(mReceiverId,
90                 HEADER_FIELD_RECEIVER_ID_LENGTH));
91 
92         // Payload length
93         outputStream.write(intToBigEndianIntBytes(mPayload.length));
94 
95         // Protocol version
96         outputStream.write(intToBigEndianShortBytes(mProtocolVersion));
97 
98         // Creator ID
99         outputStream.write(mCreatorId);
100 
101         // Padding
102         byte[] pad = new byte[HEADER_FIELD_PADDING_LENGTH];
103         outputStream.write(pad);
104 
105         // Payload
106         outputStream.write(mPayload);
107     }
108 
109     /**
110      * Returns the serialized form of this message in a newly-allocated byte
111      * array.
112      *
113      * @return  a new byte array
114      * @throws  IOException  if an error occurred during write
115      */
serializeToByteArray()116     public byte[] serializeToByteArray() throws IOException {
117         int len = mPayload.length + HEADER_SIZE;
118         ByteArrayOutputStream outputStream = new ByteArrayOutputStream(len);
119         serializeToOutputStream(outputStream);
120         return outputStream.toByteArray();
121     }
122 
123     /**
124      * Construct a new {@link XmlMessageWrapper} from an InputStream.
125      *
126      * @param stream  the {@link InputStream} to read
127      * @return  a new {@link XmlMessageWrapper}
128      * @throws IOException  if an error occurs during read
129      */
fromInputStream(InputStream stream)130     public static XmlMessageWrapper fromInputStream(InputStream stream)
131             throws IOException {
132         String receiverId = new String(readBytes(stream,
133                 HEADER_FIELD_RECEIVER_ID_LENGTH));
134         receiverId = receiverId.replace("\0", "");
135 
136         byte[] payloadLenBytes = readBytes(stream, HEADER_FIELD_PAYLOAD_LENGTH);
137         long payloadLen = intBigEndianBytesToLong(payloadLenBytes);
138 
139         int protocolVersion = shortBigEndianBytesToInt(readBytes(stream,
140                 HEADER_FIELD_PROTOCOL_VERSION_LENGTH));
141 
142         byte createorId = readBytes(stream, 1)[0];
143         byte[] padding = readBytes(stream, HEADER_FIELD_PADDING_LENGTH);
144         byte[] payload = readBytes(stream, (int)payloadLen);
145 
146         return new XmlMessageWrapper(receiverId, protocolVersion, createorId,
147                 payload);
148 
149     }
150 
151     /**
152      * Get creator id to indicate the program is playing on TV1 or TV2.
153      */
getCreatorId()154     public byte getCreatorId() {
155         return mCreatorId;
156     }
157 
getPayload()158     public byte[] getPayload() {
159         return mPayload;
160     }
161 
162     /**
163      * Get the message payload as an {@link InputStream}.
164      */
getPayloadStream()165     public InputStream getPayloadStream() {
166         return new ByteArrayInputStream(mPayload);
167     }
168 
169     /**
170      * Converts a 4-byte array of bytes to an unsigned long value.
171      */
intBigEndianBytesToLong(byte[] input)172     private static final long intBigEndianBytesToLong(byte[] input) {
173         assert (input.length == 4);
174         long ret = (long)(input[0]) & 0xff;
175         ret <<= 8;
176         ret |= (long)(input[1]) & 0xff;
177         ret <<= 8;
178         ret |= (long)(input[2]) & 0xff;
179         ret <<= 8;
180         ret |= (long)(input[3]) & 0xff;
181         return ret;
182     }
183 
184     /**
185      * Converts an integer value to the big endian 4-byte representation.
186      */
intToBigEndianIntBytes(int intVal)187     public static final byte[] intToBigEndianIntBytes(int intVal) {
188         byte[] outBuf = new byte[4];
189         outBuf[0] = (byte)((intVal >> 24) & 0xff);
190         outBuf[1] = (byte)((intVal >> 16) & 0xff);
191         outBuf[2] = (byte)((intVal >> 8) & 0xff);
192         outBuf[3] = (byte)(intVal & 0xff);
193         return outBuf;
194     }
195 
196     /**
197      * Converts a 2-byte array of bytes to an unsigned long value.
198      */
shortBigEndianBytesToInt(byte[] input)199     public static final int shortBigEndianBytesToInt(byte[] input) {
200         assert (input.length == 2);
201         int ret = (input[0]) & 0xff;
202         ret <<= 8;
203         ret |= input[1] & 0xff;
204         return ret;
205     }
206 
207     /**
208      * Converts an integer value to the 2-byte short representation.  The two
209      * most significant bytes are ignored.
210      */
intToBigEndianShortBytes(int intVal)211     public static final byte[] intToBigEndianShortBytes(int intVal) {
212         byte[] outBuf = new byte[2];
213         outBuf[0] = (byte)((intVal >> 8) & 0xff);
214         outBuf[1] = (byte)(intVal & 0xff);
215         return outBuf;
216     }
217 
218     /**
219      * Converts a string to a byte sequence of exactly byteLen bytes,
220      * padding with null characters if needed.
221      *
222      * @param byteLen  the size of the byte array to return
223      * @return  a byte array
224      */
stringToBytesPadded(String string, int byteLen)225     public static final byte[] stringToBytesPadded(String string, int byteLen) {
226         byte[] outBuf = new byte[byteLen];
227         byte[] stringBytes = string.getBytes();
228 
229         for (int i=0; i < outBuf.length; i++) {
230             if (i < stringBytes.length) {
231                 outBuf[i] = stringBytes[i];
232             } else {
233                 outBuf[i] = '\0';
234             }
235         }
236         return outBuf;
237     }
238 
239     /**
240      * Reads an exact number of bytes from an input stream.
241      *
242      * @param stream  the stream to read
243      * @param numBytes  the number of bytes desired
244      * @return  a byte array of results
245      * @throws IOException  if an error occurred during read, or stream closed
246      */
readBytes(InputStream stream, int numBytes)247     private static byte[] readBytes(InputStream stream, int numBytes)
248             throws IOException {
249         byte buffer[] = new byte[numBytes];
250         int bytesRead = 0;
251 
252         while (bytesRead < numBytes) {
253             int inc = stream.read(buffer, bytesRead, numBytes - bytesRead);
254             if (inc < 0) {
255                 throw new IOException("Stream closed while reading.");
256             }
257             bytesRead += inc;
258         }
259 
260         return buffer;
261     }
262 }
263