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