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 021package org.granite.messaging.amf.io; 022 023import java.io.DataInputStream; 024import java.io.EOFException; 025import java.io.Externalizable; 026import java.io.IOException; 027import java.io.InputStream; 028import java.io.ObjectInput; 029import java.io.UTFDataFormatException; 030import java.util.ArrayList; 031import java.util.Date; 032import java.util.HashMap; 033import java.util.List; 034import java.util.Map; 035 036import org.granite.context.GraniteContext; 037import org.granite.logging.Logger; 038import org.granite.messaging.amf.AMF3Constants; 039import org.granite.messaging.amf.io.util.ActionScriptClassDescriptor; 040import org.granite.messaging.amf.io.util.DefaultActionScriptClassDescriptor; 041import org.granite.messaging.amf.io.util.externalizer.Externalizer; 042import org.granite.messaging.amf.io.util.instantiator.AbstractInstantiator; 043import org.granite.util.TypeUtil; 044import org.granite.util.XMLUtil; 045import org.granite.util.XMLUtilFactory; 046import org.w3c.dom.Document; 047 048/** 049 * @author Franck WOLFF 050 */ 051public 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().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 String 574 result = getFromStoredStrings(type >> 1); 575 else { 576 byte[] bytes = readBytes(type >> 1); 577 result = new String(bytes, "UTF-8"); 578 addToStoredStrings(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}