1 package com.ericsson.research.trap.impl;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36 import java.io.ByteArrayOutputStream;
37 import java.io.IOException;
38 import java.io.UnsupportedEncodingException;
39 import java.nio.ByteBuffer;
40 import java.nio.charset.Charset;
41 import java.util.zip.DeflaterOutputStream;
42 import java.util.zip.InflaterOutputStream;
43
44 import com.ericsson.research.trap.spi.TrapConstants;
45 import com.ericsson.research.trap.spi.TrapMessage;
46 import com.ericsson.research.trap.utils.StringUtil;
47
48 public class TrapMessageImpl implements TrapMessage
49 {
50
51 protected byte[] data = new byte[] {};
52 protected String authString = null;
53 protected Format format = TrapConstants.MESSAGE_FORMAT_DEFAULT;
54 protected Operation op = Operation.OK;
55 private int messageId = 0;
56 private boolean compressed = false;
57 private int channelId = 0;
58
59 public static final int HEADER_SIZE = 16;
60
61 public TrapMessageImpl()
62 {
63 }
64
65 public TrapMessageImpl(byte[] data) throws UnsupportedEncodingException
66 {
67 this.deserialize(data, 0, data.length);
68 }
69
70
71
72
73
74 public byte[] serialize() throws IOException
75 {
76
77
78
79 if (this.format == Format.SEVEN_BIT_SAFE)
80 {
81 ByteArrayOutputStream bos = new ByteArrayOutputStream();
82 this.serialize7bit(bos);
83 return bos.toByteArray();
84 }
85 else
86 return this.serialize8bit();
87
88 }
89
90 protected byte[] serialize8bit() throws UnsupportedEncodingException, IOException
91 {
92
93
94 if (this.data.length >= Math.pow(2, 32))
95 throw new IllegalStateException("Asked to serialize more than 2^32 bytes data into a 8-bit Trap message");
96
97 byte b1 = 0, b2 = 0;
98
99 byte[] mData = this.getCompressedData();
100
101
102 b1 |= this.op.getOp() | 0x80;
103
104
105 b2 = 0;
106 if (this.compressed)
107 b2 |= 0x80;
108
109 int authLen = (this.authString != null ? this.authString.length() : 0);
110
111 ByteBuffer buf = ByteBuffer.allocate(HEADER_SIZE + authLen + mData.length);
112 buf.put(b1);
113 buf.put(b2);
114
115
116 buf.putShort((short) authLen);
117
118
119 buf.putInt(this.getMessageId());
120
121
122 buf.put((byte) 0);
123
124
125 buf.put((byte) this.channelId);
126
127
128 buf.putShort((short) 0);
129
130 buf.putInt(mData.length);
131
132 if (authLen > 0)
133 buf.put(this.authString.getBytes("UTF-8"));
134
135 buf.put(mData);
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153 return buf.array();
154 }
155
156 protected void serialize7bit(ByteArrayOutputStream bos) throws IOException
157 {
158
159
160 if (this.data.length >= Math.pow(2, 28))
161 throw new IllegalStateException("Asked to serialize more than 2^28 bytes data into a 7-bit Trap message");
162
163 byte b = 0;
164
165
166 b |= this.op.getOp();
167 bos.write(b);
168
169 int authLen = (this.authString != null ? this.authString.length() : 0);
170
171
172
173 bos.write(this.getBits(authLen, 17, 18));
174
175
176 bos.write(this.getBits(authLen, 19, 25));
177
178
179 bos.write(this.getBits(authLen, 26, 32));
180
181
182 this.writeInt7(this.getMessageId(), bos, true);
183 this.writeInt7(0, bos, true);
184 this.writeInt7(this.data.length, bos, false);
185
186 if (authLen > 0)
187 bos.write(this.authString.getBytes("UTF-8"));
188
189 bos.write(this.data);
190 }
191
192 private void writeInt7(int src, ByteArrayOutputStream bos, boolean signed)
193 {
194 bos.write(this.getBits(src, 5, 11));
195 bos.write(this.getBits(src, 12, 18));
196 bos.write(this.getBits(src, 19, 25));
197 bos.write(this.getBits(src, 26, 32));
198 }
199
200
201
202
203
204 public int deserialize(byte[] rawData, int offset, int length) throws UnsupportedEncodingException
205 {
206
207 if ((offset + length) > rawData.length)
208 throw new IllegalArgumentException("Offset and length specified exceed the buffer");
209
210 if (length < 16)
211 return -1;
212
213 int authLen;
214 int contentLen;
215
216 if ((rawData[offset + 0] & 0x80) != 0)
217 {
218
219 this.format = Format.REGULAR;
220 this.op = Operation.getType(rawData[offset + 0] & 0x3F);
221 this.compressed = (rawData[offset + 1] & 0x80) != 0;
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246 ByteBuffer hs = ByteBuffer.wrap(rawData, offset + 2, TrapMessageImpl.HEADER_SIZE - 2);
247 authLen = hs.getShort() & 0xFFFF;
248 this.messageId = hs.getInt();
249
250
251 hs.get();
252 this.channelId = hs.get();
253
254
255 hs.getShort();
256
257 contentLen = hs.getInt();
258
259
260 }
261 else
262 {
263
264 this.format = Format.SEVEN_BIT_SAFE;
265 this.op = Operation.getType(rawData[offset + 0] & 0x3F);
266
267 authLen = ((rawData[offset + 1] & 0x03) << 14) | ((rawData[offset + 2] & 0x7F) << 7) | ((rawData[offset + 3] & 0x7F) << 0);
268 this.messageId = ((rawData[offset + 4] & 0x7F) << 21) | ((rawData[offset + 5] & 0x7F) << 14) | ((rawData[offset + 6] & 0x7F) << 7) | ((rawData[offset + 7] & 0x7F) << 0);
269
270
271 contentLen = ((rawData[offset + 12] & 0x7F) << 21) | ((rawData[offset + 13] & 0x7F) << 14) | ((rawData[offset + 14] & 0x7F) << 7) | ((rawData[offset + 15] & 0x7F) << 0);
272
273 this.compressed = false;
274 this.channelId = 0;
275 }
276
277
278 int messageSize = 16 + authLen + contentLen;
279
280 if (length < messageSize)
281 return -1;
282
283
284 int startByte = offset + 16;
285
286
287 if (authLen > 0)
288 {
289
290
291 this.authString = new String(rawData, startByte, authLen, Charset.forName("UTF-8"));
292 startByte += authLen;
293 }
294 else
295 {
296 this.authString = null;
297 }
298
299 if (!this.compressed)
300 {
301
302
303 this.data = new byte[contentLen];
304 System.arraycopy(rawData, startByte, this.data, 0, contentLen);
305 }
306 else
307 {
308 ByteArrayOutputStream bos = new ByteArrayOutputStream();
309 InflaterOutputStream ios = new InflaterOutputStream(bos);
310
311 try
312 {
313 ios.write(rawData, startByte, contentLen);
314 ios.flush();
315 this.data = bos.toByteArray();
316
317 ios.close();
318 bos.close();
319 }
320 catch (IOException e)
321 {
322 throw new RuntimeException(e);
323 }
324 }
325
326
327 return messageSize;
328 }
329
330 private int getBits(int src, int startBit, int endBit)
331 {
332 int mask = (int) (Math.pow(2, (endBit - startBit) + 1) - 1);
333 mask = mask << (32 - endBit);
334 int rv = (src & mask) >> (32 - endBit);
335 return rv;
336 }
337
338
339
340
341
342 public byte[] getData()
343 {
344 return this.data;
345 }
346
347
348
349
350
351 public TrapMessage setData(byte[] data)
352 {
353 this.data = data;
354 return this;
355 }
356
357
358
359
360
361 public String getAuthData()
362 {
363 return this.authString;
364 }
365
366
367
368
369
370 public TrapMessage setAuthData(String authData)
371 {
372 if ((authData != null) && (authData.length() > 65535))
373 throw new IllegalArgumentException("Cannot have an AuthString more than 65535 bytes");
374
375 if (authData == null || authData.length() == 0)
376 this.authString = null;
377 else
378 this.authString = authData;
379
380 return this;
381 }
382
383
384
385
386
387 public Format getFormat()
388 {
389 return this.format;
390 }
391
392
393
394
395
396 public TrapMessage setFormat(Format format)
397 {
398 this.format = format;
399 return this;
400 }
401
402
403
404
405
406 public Operation getOp()
407 {
408 return this.op;
409 }
410
411
412
413
414
415 public TrapMessage setOp(Operation op)
416 {
417 this.op = op;
418 return this;
419 }
420
421 public int getMessageId()
422 {
423 return this.messageId;
424 }
425
426 public TrapMessage setMessageId(int messageId)
427 {
428 this.messageId = messageId;
429 return this;
430 }
431
432 public long length()
433 {
434 int l = HEADER_SIZE;
435 if (this.authString != null)
436 l += StringUtil.toUtfBytes(this.authString).length;
437
438 if (this.getCompressedData() != null)
439 l += this.getCompressedData().length;
440 return l;
441 }
442
443 public String toString()
444 {
445 return this.getOp() + "/C" + this.getChannel() + "/" + this.getMessageId() + (this.data != null ? "/" + this.data.length : "");
446 }
447
448 public TrapMessage setCompressed(boolean isCompressed)
449 {
450 this.compressed = isCompressed;
451 return this;
452 }
453
454 public boolean isCompressed()
455 {
456 return this.compressed;
457 }
458
459 public TrapMessage setChannel(int channelID)
460 {
461 if (channelID < 0 || channelID > 63)
462 throw new IllegalArgumentException("Channel ID " + channelID + " outside the allowed range.");
463
464 this.channelId = channelID;
465 return this;
466 }
467
468 public int getChannel()
469 {
470 return this.getFormat() == Format.REGULAR ? this.channelId : 0;
471 }
472
473 public byte[] getCompressedData()
474 {
475
476 if (!this.isCompressed())
477 return this.getData();
478
479 try
480 {
481 ByteArrayOutputStream tos = new ByteArrayOutputStream();
482 DeflaterOutputStream dos = new DeflaterOutputStream(tos);
483
484 dos.write(this.getData());
485 dos.finish();
486 dos.flush();
487
488 byte[] mData = tos.toByteArray();
489
490 dos.close();
491 tos.close();
492 dos = null;
493 tos = null;
494
495 return mData;
496 }
497 catch (IOException e)
498 {
499 return this.getData();
500 }
501 }
502 }