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 */ 022package org.granite.messaging.amf.io; 023 024import java.io.DataInputStream; 025import java.io.EOFException; 026import java.io.Externalizable; 027import java.io.IOException; 028import java.io.InputStream; 029import java.io.ObjectInput; 030import java.io.UTFDataFormatException; 031import java.util.ArrayList; 032import java.util.Date; 033import java.util.HashMap; 034import java.util.List; 035import java.util.Map; 036 037import org.granite.context.GraniteContext; 038import org.granite.logging.Logger; 039import org.granite.messaging.amf.AMF3Constants; 040import org.granite.messaging.amf.io.util.ActionScriptClassDescriptor; 041import org.granite.messaging.amf.io.util.DefaultActionScriptClassDescriptor; 042import org.granite.messaging.amf.io.util.externalizer.Externalizer; 043import org.granite.messaging.amf.io.util.instantiator.AbstractInstantiator; 044import org.granite.util.TypeUtil; 045import org.granite.util.XMLUtil; 046import org.granite.util.XMLUtilFactory; 047import org.w3c.dom.Document; 048 049/** 050 * @author Franck WOLFF 051 */ 052public 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}