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