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    }