001 /*
002 GRANITE DATA SERVICES
003 Copyright (C) 2011 GRANITE DATA SERVICES S.A.S.
004
005 This file is part of Granite Data Services.
006
007 Granite Data Services is free software; you can redistribute it and/or modify
008 it under the terms of the GNU Library General Public License as published by
009 the Free Software Foundation; either version 2 of the License, or (at your
010 option) any later version.
011
012 Granite Data Services is distributed in the hope that it will be useful, but
013 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
014 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
015 for more details.
016
017 You should have received a copy of the GNU Library General Public License
018 along with this library; if not, see <http://www.gnu.org/licenses/>.
019 */
020
021 package org.granite.messaging.amf.io;
022
023 import java.io.DataInputStream;
024 import java.io.EOFException;
025 import java.io.Externalizable;
026 import java.io.IOException;
027 import java.io.InputStream;
028 import java.io.ObjectInput;
029 import java.io.UTFDataFormatException;
030 import java.util.ArrayList;
031 import java.util.Date;
032 import java.util.HashMap;
033 import java.util.List;
034 import java.util.Map;
035
036 import org.granite.context.GraniteContext;
037 import org.granite.logging.Logger;
038 import org.granite.messaging.amf.AMF3Constants;
039 import org.granite.messaging.amf.io.util.ActionScriptClassDescriptor;
040 import org.granite.messaging.amf.io.util.DefaultActionScriptClassDescriptor;
041 import org.granite.messaging.amf.io.util.externalizer.Externalizer;
042 import org.granite.messaging.amf.io.util.instantiator.AbstractInstantiator;
043 import org.granite.util.ClassUtil;
044 import org.granite.util.XMLUtil;
045 import org.w3c.dom.Document;
046
047 /**
048 * @author Franck WOLFF
049 */
050 public class AMF3Deserializer extends DataInputStream implements ObjectInput, AMF3Constants {
051
052 ///////////////////////////////////////////////////////////////////////////
053 // Fields.
054
055 protected static final Logger log = Logger.getLogger(AMF3Deserializer.class);
056 protected static final Logger logMore = Logger.getLogger(AMF3Deserializer.class.getName() + "_MORE");
057
058 protected final List<String> storedStrings = new ArrayList<String>();
059 protected final List<Object> storedObjects = new ArrayList<Object>();
060 protected final List<ActionScriptClassDescriptor> storedClassDescriptors = new ArrayList<ActionScriptClassDescriptor>();
061
062 protected final GraniteContext context = GraniteContext.getCurrentInstance();
063
064 protected final AMF3DeserializerSecurizer securizer = context.getGraniteConfig().getAmf3DeserializerSecurizer();
065
066 protected final XMLUtil xmlUtil = new XMLUtil();
067
068 protected final boolean debug;
069 protected final boolean debugMore;
070
071 ///////////////////////////////////////////////////////////////////////////
072 // Constructor.
073
074 public AMF3Deserializer(InputStream in) {
075 super(in);
076
077 debug = log.isDebugEnabled();
078 debugMore = logMore.isDebugEnabled();
079
080 if (debugMore) logMore.debug("new AMF3Deserializer(in=%s)", in);
081 }
082
083 ///////////////////////////////////////////////////////////////////////////
084 // ObjectInput implementation.
085
086 public Object readObject() throws IOException {
087 if (debugMore) logMore.debug("readObject()...");
088
089 try {
090 int type = readAMF3Integer();
091 return readObject(type);
092 }
093 catch (IOException e) {
094 throw e;
095 }
096 catch (Exception e) {
097 throw new AMF3SerializationException(e);
098 }
099 }
100
101 ///////////////////////////////////////////////////////////////////////////
102 // AMF3 deserialization.
103
104 protected Object readObject(int type) throws IOException {
105
106 if (debugMore) logMore.debug("readObject(type=0x%02X)", type);
107
108 switch (type) {
109 case AMF3_UNDEFINED: // 0x00;
110 case AMF3_NULL: // 0x01;
111 return null;
112 case AMF3_BOOLEAN_FALSE: // 0x02;
113 return Boolean.FALSE;
114 case AMF3_BOOLEAN_TRUE: // 0x03;
115 return Boolean.TRUE;
116 case AMF3_INTEGER: // 0x04;
117 return Integer.valueOf(readAMF3Integer());
118 case AMF3_NUMBER: // 0x05;
119 return readAMF3Double();
120 case AMF3_STRING: // 0x06;
121 return readAMF3String();
122 case AMF3_XML: // 0x07;
123 return readAMF3Xml();
124 case AMF3_DATE: // 0x08;
125 return readAMF3Date();
126 case AMF3_ARRAY: // 0x09;
127 return readAMF3Array();
128 case AMF3_OBJECT: // 0x0A;
129 return readAMF3Object();
130 case AMF3_XMLSTRING: // 0x0B;
131 return readAMF3XmlString();
132 case AMF3_BYTEARRAY: // 0x0C;
133 return readAMF3ByteArray();
134
135 case AMF3_VECTOR_INT: // 0x0D;
136 return readAMF3VectorInt();
137 case AMF3_VECTOR_UINT: // 0x0E;
138 return readAMF3VectorUInt();
139 case AMF3_VECTOR_NUMBER: // 0x0F;
140 return readAMF3VectorNumber();
141 case AMF3_VECTOR_OBJECT: // 0x10;
142 return readAMF3VectorObject();
143
144 default:
145 throw new IllegalArgumentException("Unknown type: " + type);
146 }
147 }
148
149 protected int readAMF3Integer() throws IOException {
150 int result = 0;
151
152 int n = 0;
153 int b = readUnsignedByte();
154 while ((b & 0x80) != 0 && n < 3) {
155 result <<= 7;
156 result |= (b & 0x7f);
157 b = readUnsignedByte();
158 n++;
159 }
160 if (n < 3) {
161 result <<= 7;
162 result |= b;
163 } else {
164 result <<= 8;
165 result |= b;
166 if ((result & 0x10000000) != 0)
167 result |= 0xe0000000;
168 }
169
170 if (debugMore) logMore.debug("readAMF3Integer() -> %d", result);
171
172 return result;
173 }
174
175 protected Double readAMF3Double() throws IOException {
176 double d = readDouble();
177 Double result = (Double.isNaN(d) ? null : Double.valueOf(d));
178
179 if (debugMore) logMore.debug("readAMF3Double() -> %f", result);
180
181 return result;
182 }
183
184 protected String readAMF3String() throws IOException {
185 String result = null;
186
187 if (debugMore) logMore.debug("readAMF3String()...");
188
189 int type = readAMF3Integer();
190 if ((type & 0x01) == 0) // stored string
191 result = getFromStoredStrings(type >> 1);
192 else {
193 int length = type >> 1;
194 if (debugMore) logMore.debug("readAMF3String() - length=%d", length);
195
196 if (length > 0) {
197
198 byte[] utfBytes = new byte[length];
199 char[] utfChars = new char[length];
200
201 readFully(utfBytes);
202
203 int c, c2, c3, iBytes = 0, iChars = 0;
204 while (iBytes < length) {
205 c = utfBytes[iBytes++] & 0xFF;
206 if (c <= 0x7F)
207 utfChars[iChars++] = (char)c;
208 else {
209 switch (c >> 4) {
210 case 12: case 13:
211 c2 = utfBytes[iBytes++];
212 if ((c2 & 0xC0) != 0x80)
213 throw new UTFDataFormatException("Malformed input around byte " + (iBytes-2));
214 utfChars[iChars++] = (char)(((c & 0x1F) << 6) | (c2 & 0x3F));
215 break;
216 case 14:
217 c2 = utfBytes[iBytes++];
218 c3 = utfBytes[iBytes++];
219 if (((c2 & 0xC0) != 0x80) || ((c3 & 0xC0) != 0x80))
220 throw new UTFDataFormatException("Malformed input around byte " + (iBytes-3));
221 utfChars[iChars++] = (char)(((c & 0x0F) << 12) | ((c2 & 0x3F) << 6) | ((c3 & 0x3F) << 0));
222 break;
223 default:
224 throw new UTFDataFormatException("Malformed input around byte " + (iBytes-1));
225 }
226 }
227 }
228 result = new String(utfChars, 0, iChars);
229
230 if (debugMore) logMore.debug("readAMF3String() - result=%s", result);
231
232 addToStoredStrings(result);
233 } else
234 result = "";
235 }
236
237 if (debugMore) logMore.debug("readAMF3String() -> %s", result);
238
239 return result;
240 }
241
242
243 protected Date readAMF3Date() throws IOException {
244 Date result = null;
245
246 int type = readAMF3Integer();
247 if ((type & 0x01) == 0) // stored Date
248 result = (Date)getFromStoredObjects(type >> 1);
249 else {
250 result = new Date((long)readDouble());
251 addToStoredObjects(result);
252 }
253
254 if (debugMore) logMore.debug("readAMF3Date() -> %s", result);
255
256 return result;
257 }
258
259 protected Object readAMF3Array() throws IOException {
260 Object result = null;
261
262 int type = readAMF3Integer();
263 if ((type & 0x01) == 0) // stored array.
264 result = getFromStoredObjects(type >> 1);
265 else {
266 final int size = type >> 1;
267
268 String key = readAMF3String();
269 if (key.length() == 0) {
270 Object[] objects = new Object[size];
271 addToStoredObjects(objects);
272
273 for (int i = 0; i < size; i++)
274 objects[i] = readObject();
275
276 result = objects;
277 }
278 else {
279 Map<Object, Object> map = new HashMap<Object, Object>();
280 addToStoredObjects(map);
281
282 while(key.length() > 0) {
283 map.put(key, readObject());
284 key = readAMF3String();
285 }
286 for (int i = 0; i < size; i++)
287 map.put(Integer.valueOf(i), readObject());
288
289 result = map;
290 }
291 }
292
293 if (debugMore) logMore.debug("readAMF3Array() -> %s", result);
294
295 return result;
296 }
297
298 protected Object readAMF3VectorInt() throws IOException {
299 Object result = null;
300
301 int type = readAMF3Integer();
302 if ((type & 0x01) == 0) // stored vector.
303 result = getFromStoredObjects(type >> 1);
304 else {
305 final int length = type >> 1;
306 List<Integer> vector = new ArrayList<Integer>(length);
307
308 addToStoredObjects(result);
309
310 readAMF3Integer(); // always 0x00?
311
312 for (int i = 0; i < length; i++)
313 vector.add(readInt());
314
315 result = vector;
316 }
317
318 if (debugMore) logMore.debug("readAMF3VectorInt() -> %s", result);
319
320 return result;
321 }
322
323 protected Object readAMF3VectorUInt() throws IOException {
324 Object result = null;
325
326 int type = readAMF3Integer();
327 if ((type & 0x01) == 0) // stored vector.
328 result = getFromStoredObjects(type >> 1);
329 else {
330 final int length = type >> 1;
331 List<Long> vector = new ArrayList<Long>(length);
332
333 addToStoredObjects(result);
334
335 readAMF3Integer(); // always 0x00?
336
337 for (int i = 0; i < length; i++)
338 vector.add(readInt() & 0xffffffffL);
339
340 result = vector;
341 }
342
343 if (debugMore) logMore.debug("readAMF3VectorUInt() -> %s", result);
344
345 return result;
346 }
347
348 protected Object readAMF3VectorNumber() throws IOException {
349 Object result = null;
350
351 int type = readAMF3Integer();
352 if ((type & 0x01) == 0) // stored vector.
353 result = getFromStoredObjects(type >> 1);
354 else {
355 final int length = type >> 1;
356 List<Double> vector = new ArrayList<Double>(length);
357
358 addToStoredObjects(result);
359
360 readAMF3Integer(); // always 0x00?
361
362 for (int i = 0; i < length; i++)
363 vector.add(readDouble());
364
365 result = vector;
366 }
367
368 if (debugMore) logMore.debug("readAMF3VectorDouble() -> %s", result);
369
370 return result;
371 }
372
373 protected Object readAMF3VectorObject() throws IOException {
374 Object result = null;
375
376 int type = readAMF3Integer();
377 if ((type & 0x01) == 0) // stored vector.
378 result = getFromStoredObjects(type >> 1);
379 else {
380 final int length = type >> 1;
381 List<Object> vector = new ArrayList<Object>(length);
382
383 addToStoredObjects(result);
384
385 readAMF3Integer(); // always 0x00?
386 readAMF3Integer(); // always 0x01?
387
388 for (int i = 0; i < length; i++)
389 vector.add(readObject());
390
391 result = vector;
392 }
393
394 if (debugMore) logMore.debug("readAMF3VectorObject() -> %s", result);
395
396 return result;
397 }
398
399 protected Object readAMF3Object() throws IOException {
400 if (debug) log.debug("readAMF3Object()...");
401
402 Object result = null;
403
404 int type = readAMF3Integer();
405 if (debug) log.debug("readAMF3Object() - type=0x%02X", type);
406
407 if ((type & 0x01) == 0) // stored object.
408 result = getFromStoredObjects(type >> 1);
409 else {
410 boolean inlineClassDef = (((type >> 1) & 0x01) != 0);
411 if (debug) log.debug("readAMF3Object() - inlineClassDef=%b", inlineClassDef);
412
413 // read class decriptor.
414 ActionScriptClassDescriptor desc = null;
415 if (inlineClassDef) {
416 int propertiesCount = type >> 4;
417 if (debug) log.debug("readAMF3Object() - propertiesCount=%d", propertiesCount);
418
419 byte encoding = (byte)((type >> 2) & 0x03);
420 if (debug) log.debug("readAMF3Object() - encoding=%d", encoding);
421
422 String className = readAMF3String();
423 if (debug) log.debug("readAMF3Object() - className=%s", className);
424
425 // Check if the class is allowed to be instantiated.
426 if (securizer != null && !securizer.allowInstantiation(className))
427 throw new SecurityException("Illegal attempt to instantiate class: " + className + ", securizer: " + securizer.getClass());
428
429 // try to find out custom AS3 class descriptor
430 Class<? extends ActionScriptClassDescriptor> descriptorType = null;
431 if (!"".equals(className))
432 descriptorType = context.getGraniteConfig().getActionScriptDescriptor(className);
433 if (debug) log.debug("readAMF3Object() - descriptorType=%s", descriptorType);
434
435 if (descriptorType != null) {
436 // instantiate descriptor
437 Class<?>[] argsDef = new Class[]{String.class, byte.class};
438 Object[] argsVal = new Object[]{className, Byte.valueOf(encoding)};
439 try {
440 desc = ClassUtil.newInstance(descriptorType, argsDef, argsVal);
441 } catch (Exception e) {
442 throw new RuntimeException("Could not instantiate AS descriptor: " + descriptorType, e);
443 }
444 }
445 if (desc == null)
446 desc = new DefaultActionScriptClassDescriptor(className, encoding);
447 addToStoredClassDescriptors(desc);
448
449 if (debug) log.debug("readAMF3Object() - defining %d properties...", propertiesCount);
450 for (int i = 0; i < propertiesCount; i++) {
451 String name = readAMF3String();
452 if (debug) log.debug("readAMF3Object() - defining property name=%s", name);
453 desc.defineProperty(name);
454 }
455 } else
456 desc = getFromStoredClassDescriptors(type >> 2);
457
458 if (debug) log.debug("readAMF3Object() - actionScriptClassDescriptor=%s", desc);
459
460 int objectEncoding = desc.getEncoding();
461
462 // Find externalizer and create Java instance.
463 Externalizer externalizer = desc.getExternalizer();
464 if (externalizer != null) {
465 try {
466 result = externalizer.newInstance(desc.getType(), this);
467 } catch (Exception e) {
468 throw new RuntimeException("Could not instantiate type: " + desc.getType(), e);
469 }
470 } else
471 result = desc.newJavaInstance();
472
473 int index = addToStoredObjects(result);
474
475 // Entity externalizers (eg. OpenJPA) may return null values for non-null AS3 objects (ie. proxies).
476 if (result == null) {
477 if (debug) log.debug("readAMF3Object() - Added null object to stored objects for actionScriptClassDescriptor=%s", desc);
478 return null;
479 }
480
481 // read object content...
482 if ((objectEncoding & 0x01) != 0) {
483 // externalizer.
484 if (externalizer != null) {
485 if (debug) log.debug("readAMF3Object() - using externalizer=%s", externalizer);
486 try {
487 externalizer.readExternal(result, this);
488 } catch (IOException e) {
489 throw e;
490 } catch (Exception e) {
491 throw new RuntimeException("Could not read externalized object: " + result, e);
492 }
493 }
494 // legacy externalizable.
495 else {
496 if (debug) log.debug("readAMF3Object() - legacy Externalizable=%s", result.getClass());
497 if (!(result instanceof Externalizable)) {
498 throw new RuntimeException(
499 "The ActionScript3 class bound to " + result.getClass().getName() +
500 " (ie: [RemoteClass(alias=\"" + result.getClass().getName() + "\")])" +
501 " implements flash.utils.IExternalizable but this Java class neither" +
502 " implements java.io.Externalizable nor is in the scope of a configured" +
503 " externalizer (please fix your granite-config.xml)"
504 );
505 }
506 try {
507 ((Externalizable)result).readExternal(this);
508 } catch (IOException e) {
509 throw e;
510 } catch (Exception e) {
511 throw new RuntimeException("Could not read externalizable object: " + result, e);
512 }
513 }
514 }
515 else {
516 // defined values...
517 if (desc.getPropertiesCount() > 0) {
518 if (debug) log.debug("readAMF3Object() - reading defined properties...");
519 for (int i = 0; i < desc.getPropertiesCount(); i++) {
520 byte vType = readByte();
521 Object value = readObject(vType);
522 if (debug) log.debug("readAMF3Object() - setting defined property: %s=%s", desc.getPropertyName(i), value);
523 desc.setPropertyValue(i, result, value);
524 }
525 }
526
527 // dynamic values...
528 if (objectEncoding == 0x02) {
529 if (debug) log.debug("readAMF3Object() - reading dynamic properties...");
530 while (true) {
531 String name = readAMF3String();
532 if (name.length() == 0)
533 break;
534 byte vType = readByte();
535 Object value = readObject(vType);
536 if (debug) log.debug("readAMF3Object() - setting dynamic property: %s=%s", name, value);
537 desc.setPropertyValue(name, result, value);
538 }
539 }
540 }
541
542 if (result instanceof AbstractInstantiator<?>) {
543 if (debug) log.debug("readAMF3Object() - resolving instantiator...");
544 try {
545 result = ((AbstractInstantiator<?>)result).resolve();
546 } catch (Exception e) {
547 throw new RuntimeException("Could not instantiate object: " + result, e);
548 }
549 setStoredObject(index, result);
550 }
551 }
552
553 if (debug) log.debug("readAMF3Object() -> %s", result);
554
555 return result;
556 }
557
558 protected Document readAMF3Xml() throws IOException {
559 String xml = readAMF3XmlString();
560 Document result = xmlUtil.buildDocument(xml);
561
562 if (debugMore) logMore.debug("readAMF3Xml() -> %s", result);
563
564 return result;
565 }
566
567 protected String readAMF3XmlString() throws IOException {
568 String result = null;
569
570 int type = readAMF3Integer();
571 if ((type & 0x01) == 0) // stored String
572 result = getFromStoredStrings(type >> 1);
573 else {
574 byte[] bytes = readBytes(type >> 1);
575 result = new String(bytes, "UTF-8");
576 addToStoredStrings(result);
577 }
578
579 if (debugMore) logMore.debug("readAMF3XmlString() -> %s", result);
580
581 return result;
582 }
583
584 protected byte[] readAMF3ByteArray() throws IOException {
585 byte[] result = null;
586
587 int type = readAMF3Integer();
588 if ((type & 0x01) == 0) // stored object.
589 result = (byte[])getFromStoredObjects(type >> 1);
590 else {
591 result = readBytes(type >> 1);
592 addToStoredObjects(result);
593 }
594
595 if (debugMore) logMore.debug("readAMF3ByteArray() -> %s", result);
596
597 return result;
598 }
599
600 ///////////////////////////////////////////////////////////////////////////
601 // Cached objects methods.
602
603 protected void addToStoredStrings(String s) {
604 if (debug) log.debug("addToStoredStrings(s=%s) at index=%d", s, storedStrings.size());
605 storedStrings.add(s);
606 }
607
608 protected String getFromStoredStrings(int index) {
609 if (debug) log.debug("getFromStoredStrings(index=%d)", index);
610 String s = storedStrings.get(index);
611 if (debug) log.debug("getFromStoredStrings() -> %s", s);
612 return s;
613 }
614
615 protected int addToStoredObjects(Object o) {
616 int index = storedObjects.size();
617 if (debug) log.debug("addToStoredObjects(o=%s) at index=%d", o, index);
618 storedObjects.add(o);
619 return index;
620 }
621
622 protected void setStoredObject(int index, Object o) {
623 if (debug) log.debug("setStoredObject(index=%d, o=%s)", index, o);
624 storedObjects.set(index, o);
625 }
626
627 protected Object getFromStoredObjects(int index) {
628 if (debug) log.debug("getFromStoredObjects(index=%d)", index);
629 Object o = storedObjects.get(index);
630 if (debug) log.debug("getFromStoredObjects() -> %s", o);
631 return o;
632 }
633
634 protected void addToStoredClassDescriptors(ActionScriptClassDescriptor desc) {
635 if (debug) log.debug("addToStoredClassDescriptors(desc=%s) at index=%d", desc, storedClassDescriptors.size());
636 storedClassDescriptors.add(desc);
637 }
638
639 protected ActionScriptClassDescriptor getFromStoredClassDescriptors(int index) {
640 if (debug) log.debug("getFromStoredClassDescriptors(index=%d)", index);
641 ActionScriptClassDescriptor desc = storedClassDescriptors.get(index);
642 if (debug) log.debug("getFromStoredClassDescriptors() -> %s", desc);
643 return desc;
644 }
645
646 ///////////////////////////////////////////////////////////////////////////
647 // Utilities.
648
649 protected byte[] readBytes(int count) throws IOException {
650 byte[] bytes = new byte[count];
651 //readFully(bytes);
652
653 int b = -1;
654 for (int i = 0; i < count; i++) {
655 b = in.read();
656 if (b == -1)
657 throw new EOFException();
658 bytes[i] = (byte)b;
659 }
660 return bytes;
661 }
662 }