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.DataOutputStream;
025import java.io.Externalizable;
026import java.io.IOException;
027import java.io.ObjectOutput;
028import java.io.OutputStream;
029import java.lang.reflect.Array;
030import java.math.BigDecimal;
031import java.math.BigInteger;
032import java.util.Calendar;
033import java.util.Collection;
034import java.util.Date;
035import java.util.HashMap;
036import java.util.IdentityHashMap;
037import java.util.Map;
038
039import org.granite.config.flex.Channel;
040import org.granite.context.GraniteContext;
041import org.granite.logging.Logger;
042import org.granite.messaging.amf.AMF3Constants;
043import org.granite.messaging.amf.io.convert.Converters;
044import org.granite.messaging.amf.io.util.ClassGetter;
045import org.granite.messaging.amf.io.util.DefaultJavaClassDescriptor;
046import org.granite.messaging.amf.io.util.IndexedJavaClassDescriptor;
047import org.granite.messaging.amf.io.util.JavaClassDescriptor;
048import org.granite.messaging.amf.io.util.externalizer.Externalizer;
049import org.granite.messaging.amf.types.AMFDictionaryValue;
050import org.granite.messaging.amf.types.AMFSpecialValue;
051import org.granite.messaging.amf.types.AMFSpecialValueFactory;
052import org.granite.messaging.amf.types.AMFVectorIntValue;
053import org.granite.messaging.amf.types.AMFVectorNumberValue;
054import org.granite.messaging.amf.types.AMFVectorObjectValue;
055import org.granite.messaging.amf.types.AMFVectorUintValue;
056import org.granite.util.TypeUtil;
057import org.granite.util.XMLUtil;
058import org.granite.util.XMLUtilFactory;
059import org.w3c.dom.Document;
060
061import flex.messaging.io.ArrayCollection;
062
063/**
064 * @author Franck WOLFF
065 */
066public class AMF3Serializer extends DataOutputStream implements ObjectOutput, AMF3Constants {
067
068    ///////////////////////////////////////////////////////////////////////////
069    // Fields.
070
071    protected static final Logger log = Logger.getLogger(AMF3Serializer.class);
072    protected static final Logger logMore = Logger.getLogger(AMF3Serializer.class.getName() + "_MORE");
073
074    protected final Map<String, Integer> storedStrings = new HashMap<String, Integer>();
075    protected final Map<Object, Integer> storedObjects = new IdentityHashMap<Object, Integer>();
076    protected final Map<String, IndexedJavaClassDescriptor> storedClassDescriptors
077        = new HashMap<String, IndexedJavaClassDescriptor>();
078
079    protected final GraniteContext context = GraniteContext.getCurrentInstance();
080    protected final Converters converters = context.getGraniteConfig().getConverters();
081
082    protected final boolean externalizeLong
083                = (context.getGraniteConfig().getExternalizer(Long.class.getName()) != null);
084    protected final boolean externalizeBigInteger
085                = (context.getGraniteConfig().getExternalizer(BigInteger.class.getName()) != null);
086    protected final boolean externalizeBigDecimal
087        = (context.getGraniteConfig().getExternalizer(BigDecimal.class.getName()) != null);
088
089    protected final XMLUtil xmlUtil = XMLUtilFactory.getXMLUtil();
090    
091    // TODO: allow aliaser configuration in granite-config.xml
092    protected final AMFSpecialValueFactory specialValueFactory = new AMFSpecialValueFactory();
093    
094    protected final boolean debug = log.isDebugEnabled();
095    protected final boolean debugMore = logMore.isDebugEnabled();
096
097    protected Channel channel = null;
098
099    ///////////////////////////////////////////////////////////////////////////
100    // Constructor.
101
102    public AMF3Serializer(OutputStream out) {
103        super(out);
104
105        if (debugMore) logMore.debug("new AMF3Serializer(out=%s)", out);
106    }
107
108    ///////////////////////////////////////////////////////////////////////////
109    // ObjectOutput implementation.
110
111    public void writeObject(Object o) throws IOException {
112        if (debugMore) logMore.debug("writeObject(o=%s)", o);
113
114        try {
115                if (o == null)
116                    write(AMF3_NULL);
117                else if (o instanceof AMFSpecialValue)
118                        writeAMF3SpecialValue((AMFSpecialValue<?>)o);
119                else if (!(o instanceof Externalizable)) {
120        
121                    if (converters.hasReverters())
122                        o = converters.revert(o);
123        
124                        if (o == null)
125                            write(AMF3_NULL);
126                        else if (o instanceof String || o instanceof Character)
127                        writeAMF3String(o.toString());
128                    else if (o instanceof Boolean)
129                        write(((Boolean)o).booleanValue() ? AMF3_BOOLEAN_TRUE : AMF3_BOOLEAN_FALSE);
130                    else if (o instanceof Number) {
131                        if (o instanceof Integer || o instanceof Short || o instanceof Byte)
132                            writeAMF3Integer(((Number)o).intValue());
133                        else if (externalizeLong && o instanceof Long)
134                                writeAMF3Object(o);
135                        else if (externalizeBigInteger && o instanceof BigInteger)
136                                writeAMF3Object(o);
137                        else if (externalizeBigDecimal && o instanceof BigDecimal)
138                                writeAMF3Object(o);
139                        else
140                            writeAMF3Number(((Number)o).doubleValue());
141                    }
142                    else if (o instanceof Date)
143                        writeAMF3Date((Date)o);
144                    else if (o instanceof Calendar)
145                        writeAMF3Date(((Calendar)o).getTime());
146                    else if (o instanceof Document)
147                        writeAMF3Xml((Document)o);
148                    else if (o instanceof Collection<?>)
149                        writeAMF3Collection((Collection<?>)o);
150                    else if (o.getClass().isArray()) {
151                        if (o.getClass().getComponentType() == Byte.TYPE)
152                            writeAMF3ByteArray((byte[])o);
153                        else
154                            writeAMF3Array(o);
155                    }
156                    else
157                        writeAMF3Object(o);
158                }
159                else
160                    writeAMF3Object(o);
161        }
162        catch (IOException e) {
163                throw e;
164        }
165        catch (Exception e) {
166                throw new AMF3SerializationException(e);
167        }
168    }
169
170    ///////////////////////////////////////////////////////////////////////////
171    // AMF3 serialization.
172
173    protected void writeAMF3SpecialValue(AMFSpecialValue<?> value) throws IOException {
174        if (debugMore) logMore.debug("writeAMF3SpecialValue(value=%s)", value);
175
176        switch (value.type) {
177        case AMF3_DICTIONARY:
178                        writeAMF3Dictionary((AMFDictionaryValue)value);
179                        break;
180        case AMF3_VECTOR_INT:
181                        writeAMF3VectorInt((AMFVectorIntValue)value);
182                break;
183        case AMF3_VECTOR_NUMBER:
184                        writeAMF3VectorNumber((AMFVectorNumberValue)value);
185                break;
186        case AMF3_VECTOR_UINT:
187                        writeAMF3VectorUint((AMFVectorUintValue)value);
188                break;
189        case AMF3_VECTOR_OBJECT:
190                        writeAMF3VectorObject((AMFVectorObjectValue)value);
191                break;
192        default:
193                        throw new RuntimeException("Unsupported AMF special value: " + value);
194        }
195    }
196    
197    protected void writeAMF3VectorObject(AMFVectorObjectValue value) throws IOException {
198        if (debugMore) logMore.debug("writeAMF3VectorObject(value=%s)", value);
199
200        write(AMF3_VECTOR_OBJECT);
201
202        Object o = value.value;
203        
204        int index = indexOfStoredObjects(o);
205        if (index >= 0)
206            writeAMF3IntegerData(index << 1);
207        else {
208            addToStoredObjects(o);
209
210            int length = getArrayOrCollectionLength(o);
211            writeAMF3IntegerData(length << 1 | 0x01);
212            write(value.fixed ? 0x01 : 0x00);
213            writeAMF3StringData(value.type);
214
215            if (o.getClass().isArray()) {
216                for (int i = 0; i < length; i++)
217                    writeObject(Array.get(o, i));
218            }
219            else {
220                for (Object item : (Collection<?>)o)
221                        writeObject(item);
222            }
223        }
224    }
225    
226    protected void writeAMF3VectorInt(AMFVectorIntValue value) throws IOException {
227        if (debugMore) logMore.debug("writeAMF3VectorInt(value=%s)", value);
228
229        write(AMF3_VECTOR_INT);
230
231        Object o = value.value;
232
233        int index = indexOfStoredObjects(o);
234        if (index >= 0)
235            writeAMF3IntegerData(index << 1);
236        else {
237            addToStoredObjects(o);
238
239            int length = getArrayOrCollectionLength(o);
240            writeAMF3IntegerData(length << 1 | 0x01);
241            write(value.fixed ? 0x01 : 0x00);
242
243            if (o.getClass().isArray()) {
244                for (int i = 0; i < length; i++)
245                        writeInt(((Number)Array.get(o, i)).intValue());
246            }
247            else {
248                for (Object item : (Collection<?>)o)
249                        writeInt(((Number)item).intValue());
250            }
251        }
252    }
253    
254    protected void writeAMF3VectorNumber(AMFVectorNumberValue value) throws IOException {
255        if (debugMore) logMore.debug("writeAMF3VectorNumber(value=%s)", value);
256
257        write(AMF3_VECTOR_NUMBER);
258
259        Object o = value.value;
260
261        int index = indexOfStoredObjects(o);
262        if (index >= 0)
263            writeAMF3IntegerData(index << 1);
264        else {
265            addToStoredObjects(o);
266
267            int length = getArrayOrCollectionLength(o);
268            writeAMF3IntegerData(length << 1 | 0x01);
269            write(value.fixed ? 0x01 : 0x00);
270
271            if (o.getClass().isArray()) {
272                for (int i = 0; i < length; i++)
273                        writeDouble(((Number)Array.get(o, i)).doubleValue());
274            }
275            else {
276                for (Object item : (Collection<?>)o)
277                        writeDouble(((Number)item).doubleValue());
278            }
279        }
280    }
281    
282    protected void writeAMF3VectorUint(AMFVectorUintValue value) throws IOException {
283        if (debugMore) logMore.debug("writeAMF3VectorUint(value=%s)", value);
284
285        write(AMF3_VECTOR_UINT);
286
287        Object o = value.value;
288
289        int index = indexOfStoredObjects(o);
290        if (index >= 0)
291            writeAMF3IntegerData(index << 1);
292        else {
293            addToStoredObjects(o);
294
295            int length = getArrayOrCollectionLength(o);
296            writeAMF3IntegerData(length << 1 | 0x01);
297            write(value.fixed ? 0x01 : 0x00);
298
299            if (o.getClass().isArray()) {
300                for (int i = 0; i < length; i++)
301                        writeInt(((Number)Array.get(o, i)).intValue());
302            }
303            else {
304                for (Object item : (Collection<?>)o)
305                        writeInt(((Number)item).intValue());
306            }
307        }
308    }
309
310    protected void writeAMF3Dictionary(AMFDictionaryValue value) throws IOException {
311        if (debugMore) logMore.debug("writeAMFDictionary(value=%s)", value);
312
313        write(AMF3_DICTIONARY);
314
315        Map<?, ?> o = value.value;
316
317        int index = indexOfStoredObjects(o);
318        if (index >= 0)
319            writeAMF3IntegerData(index << 1);
320        else {
321            addToStoredObjects(o);
322
323            int length = o.size();
324            writeAMF3IntegerData(length << 1 | 0x01);
325            write(value.weakKeys ? 0x01 : 0x00);
326
327            for (Map.Entry<?, ?> entry : o.entrySet()) {
328                writeObject(entry.getKey());
329                writeObject(entry.getValue());
330            }
331        }
332    }
333    
334    protected int getArrayOrCollectionLength(Object o) {
335        if (o.getClass().isArray())
336                return Array.getLength(o);
337        return ((Collection<?>)o).size();
338    }
339
340    protected void writeAMF3Integer(int i) throws IOException {
341        if (debugMore) logMore.debug("writeAMF3Integer(i=%d)", i);
342
343        if (i < AMF3_INTEGER_MIN || i > AMF3_INTEGER_MAX) {
344            if (debugMore) logMore.debug("writeAMF3Integer() - %d is out of AMF3 int range, writing it as a Number", i);
345            writeAMF3Number(i);
346        }
347        else {
348            write(AMF3_INTEGER);
349            writeAMF3IntegerData(i);
350        }
351    }
352
353    protected void writeAMF3IntegerData(int i) throws IOException {
354        if (debugMore) logMore.debug("writeAMF3IntegerData(i=%d)", i);
355
356        if (i < AMF3_INTEGER_MIN || i > AMF3_INTEGER_MAX)
357            throw new IllegalArgumentException("Integer out of range: " + i);
358
359        if (i < 0 || i >= 0x200000) {
360            write(((i >> 22) & 0x7F) | 0x80);
361            write(((i >> 15) & 0x7F) | 0x80);
362            write(((i >> 8) & 0x7F) | 0x80);
363            write(i & 0xFF);
364        }
365        else {
366            if (i >= 0x4000)
367                write(((i >> 14) & 0x7F) | 0x80);
368            if (i >= 0x80)
369                write(((i >> 7) & 0x7F) | 0x80);
370            write(i & 0x7F);
371        }
372    }
373
374    protected void writeAMF3Number(double d) throws IOException {
375        if (debugMore) logMore.debug("writeAMF3Number(d=%f)", d);
376
377        write(AMF3_NUMBER);
378        writeDouble(d);
379    }
380
381    protected void writeAMF3String(String s) throws IOException {
382        if (debugMore) logMore.debug("writeAMF3String(s=%s)", s);
383
384        write(AMF3_STRING);
385        writeAMF3StringData(s);
386    }
387
388    protected void writeAMF3StringData(String s) throws IOException {
389        if (debugMore) logMore.debug("writeAMF3StringData(s=%s)", s);
390
391        if (s.length() == 0) {
392            write(0x01);
393            return;
394        }
395
396        int index = indexOfStoredStrings(s);
397
398        if (index >= 0)
399            writeAMF3IntegerData(index << 1);
400        else {
401            addToStoredStrings(s);
402
403            final int sLength = s.length();
404
405            // Compute and write modified UTF-8 string length.
406            int uLength = 0;
407            for (int i = 0; i < sLength; i++) {
408                int c = s.charAt(i);
409                if ((c >= 0x0001) && (c <= 0x007F))
410                    uLength++;
411                else if (c > 0x07FF)
412                    uLength += 3;
413                else
414                    uLength += 2;
415            }
416            writeAMF3IntegerData((uLength << 1) | 0x01);
417
418            // Write modified UTF-8 bytes.
419            for (int i = 0; i < sLength; i++) {
420                int c = s.charAt(i);
421                if ((c >= 0x0001) && (c <= 0x007F)) {
422                    write(c);
423                }
424                else if (c > 0x07FF) {
425                    write(0xE0 | ((c >> 12) & 0x0F));
426                    write(0x80 | ((c >>  6) & 0x3F));
427                    write(0x80 | ((c >>  0) & 0x3F));
428                }
429                else {
430                    write(0xC0 | ((c >>  6) & 0x1F));
431                    write(0x80 | ((c >>  0) & 0x3F));
432                }
433            }
434        }
435    }
436
437    protected void writeAMF3Xml(Document doc) throws IOException {
438        if (debugMore) logMore.debug("writeAMF3Xml(doc=%s)", doc);
439
440        byte xmlType = AMF3_XMLSTRING;
441        Channel channel = getChannel();
442        if (channel != null && channel.isLegacyXmlSerialization())
443            xmlType = AMF3_XML;
444        write(xmlType);
445
446        int index = indexOfStoredObjects(doc);
447        if (index >= 0)
448            writeAMF3IntegerData(index << 1);
449        else {
450            addToStoredObjects(doc);
451
452            byte[] bytes = xmlUtil.toString(doc).getBytes("UTF-8");
453            writeAMF3IntegerData((bytes.length << 1) | 0x01);
454            write(bytes);
455        }
456    }
457
458    protected void writeAMF3Date(Date date) throws IOException {
459        if (debugMore) logMore.debug("writeAMF3Date(date=%s)", date);
460
461        write(AMF3_DATE);
462
463        int index = indexOfStoredObjects(date);
464        if (index >= 0)
465            writeAMF3IntegerData(index << 1);
466        else {
467            addToStoredObjects(date);
468            writeAMF3IntegerData(0x01);
469            writeDouble(date.getTime());
470        }
471    }
472
473    protected void writeAMF3Array(Object array) throws IOException {
474        if (debugMore) logMore.debug("writeAMF3Array(array=%s)", array);
475
476        write(AMF3_ARRAY);
477
478        int index = indexOfStoredObjects(array);
479        if (index >= 0)
480            writeAMF3IntegerData(index << 1);
481        else {
482            addToStoredObjects(array);
483
484            int length = Array.getLength(array);
485            writeAMF3IntegerData(length << 1 | 0x01);
486            write(0x01);
487            for (int i = 0; i < length; i++)
488                writeObject(Array.get(array, i));
489        }
490    }
491
492    protected void writeAMF3ByteArray(byte[] bytes) throws IOException {
493        if (debugMore) logMore.debug("writeAMF3ByteArray(bytes=%s)", bytes);
494
495        write(AMF3_BYTEARRAY);
496
497        int index = indexOfStoredObjects(bytes);
498        if (index >= 0)
499            writeAMF3IntegerData(index << 1);
500        else {
501            addToStoredObjects(bytes);
502
503            writeAMF3IntegerData(bytes.length << 1 | 0x01);
504            //write(bytes);
505
506            for (int i = 0; i < bytes.length; i++)
507                out.write(bytes[i]);
508        }
509    }
510
511    protected void writeAMF3Collection(Collection<?> c) throws IOException {
512        if (debugMore) logMore.debug("writeAMF3Collection(c=%s)", c);
513
514        Channel channel = getChannel();
515        if (channel != null && channel.isLegacyCollectionSerialization())
516            writeAMF3Array(c.toArray());
517        else {
518            ArrayCollection ac = (c instanceof ArrayCollection ? (ArrayCollection)c : new ArrayCollection(c));
519            writeAMF3Object(ac);
520        }
521    }
522
523    protected void writeAMF3Object(Object o) throws IOException {
524        if (debug) log.debug("writeAMF3Object(o=%s)...", o);
525
526        write(AMF3_OBJECT);
527
528        int index = indexOfStoredObjects(o);
529        if (index >= 0)
530            writeAMF3IntegerData(index << 1);
531        else {
532            addToStoredObjects(o);
533
534            ClassGetter classGetter = context.getGraniteConfig().getClassGetter();
535            if (debug) log.debug("writeAMF3Object() - classGetter=%s", classGetter);
536
537            Class<?> oClass = classGetter.getClass(o);
538            if (debug) log.debug("writeAMF3Object() - oClass=%s", oClass);
539
540            JavaClassDescriptor desc = null;
541
542            // write class description.
543            IndexedJavaClassDescriptor iDesc = getFromStoredClassDescriptors(oClass);
544            if (iDesc != null) {
545                desc = iDesc.getDescriptor();
546                writeAMF3IntegerData(iDesc.getIndex() << 2 | 0x01);
547            }
548            else {
549                iDesc = addToStoredClassDescriptors(oClass);
550                desc = iDesc.getDescriptor();
551
552                writeAMF3IntegerData((desc.getPropertiesCount() << 4) | (desc.getEncoding() << 2) | 0x03);
553                writeAMF3StringData(desc.getName());
554
555                for (int i = 0; i < desc.getPropertiesCount(); i++)
556                    writeAMF3StringData(desc.getPropertyName(i));
557            }
558            if (debug) log.debug("writeAMF3Object() - desc=%s", desc);
559
560            // write object content.
561            if (desc.isExternalizable()) {
562                Externalizer externalizer = desc.getExternalizer();
563
564                if (externalizer != null) {
565                    if (debug) log.debug("writeAMF3Object() - using externalizer=%s", externalizer);
566                    try {
567                        externalizer.writeExternal(o, this);
568                    }
569                    catch (IOException e) {
570                        throw e;
571                    }
572                    catch (Exception e) {
573                        throw new RuntimeException("Could not externalize object: " + o, e);
574                    }
575                }
576                else {
577                    if (debug) log.debug("writeAMF3Object() - legacy Externalizable=%s", o);
578                    ((Externalizable)o).writeExternal(this);
579                }
580            }
581            else {
582                if (debug) log.debug("writeAMF3Object() - writing defined properties...");
583                for (int i = 0; i < desc.getPropertiesCount(); i++) {
584                    Object obj = desc.getPropertyValue(i, o);
585                    if (debug) log.debug("writeAMF3Object() - writing defined property: %s=%s", desc.getPropertyName(i), obj);
586                    writeObject(specialValueFactory.createSpecialValue(desc.getProperty(i), obj));
587                }
588
589                if (desc.isDynamic()) {
590                    if (debug) log.debug("writeAMF3Object() - writing dynamic properties...");
591                    Map<?, ?> oMap = (Map<?, ?>)o;
592                    for (Map.Entry<?, ?> entry : oMap.entrySet()) {
593                        Object key = entry.getKey();
594                        if (key != null) {
595                            String propertyName = key.toString();
596                            if (propertyName.length() > 0) {
597                                if (debug) log.debug(
598                                    "writeAMF3Object() - writing dynamic property: %s=%s",
599                                    propertyName, entry.getValue()
600                                );
601                                writeAMF3StringData(propertyName);
602                                writeObject(entry.getValue());
603                            }
604                        }
605                    }
606                    writeAMF3StringData("");
607                }
608            }
609        }
610
611        if (debug) log.debug("writeAMF3Object(o=%s) - Done", o);
612    }
613
614    ///////////////////////////////////////////////////////////////////////////
615    // Cached objects methods.
616
617    protected void addToStoredStrings(String s) {
618        if (!storedStrings.containsKey(s)) {
619            Integer index = Integer.valueOf(storedStrings.size());
620            if (debug) log.debug("addToStoredStrings(s=%s) at index=%d", s, index);
621            storedStrings.put(s, index);
622        }
623    }
624
625    protected int indexOfStoredStrings(String s) {
626        Integer index = storedStrings.get(s);
627        if (debug) log.debug("indexOfStoredStrings(s=%s) -> %d", s, (index != null ? index : -1));
628        return (index != null ? index : -1);
629    }
630
631    protected void addToStoredObjects(Object o) {
632        if (o != null && !storedObjects.containsKey(o)) {
633            Integer index = Integer.valueOf(storedObjects.size());
634            if (debug) log.debug("addToStoredObjects(o=%s) at index=%d", o, index);
635            storedObjects.put(o, index);
636        }
637    }
638
639    protected int indexOfStoredObjects(Object o) {
640        Integer index = storedObjects.get(o);
641        if (debug) log.debug("indexOfStoredObjects(o=%s) -> %d", o, (index != null ? index : -1));
642        return (index != null ? index : -1);
643    }
644
645    protected IndexedJavaClassDescriptor addToStoredClassDescriptors(Class<?> clazz) {
646        final String name = JavaClassDescriptor.getClassName(clazz);
647
648        if (debug) log.debug("addToStoredClassDescriptors(clazz=%s)", clazz);
649
650        if (storedClassDescriptors.containsKey(name))
651            throw new RuntimeException(
652                "Descriptor of \"" + name + "\" is already stored at index: " +
653                getFromStoredClassDescriptors(clazz).getIndex()
654            );
655
656        // find custom class descriptor and instantiate if any
657        JavaClassDescriptor desc = null;
658
659        Class<? extends JavaClassDescriptor> descriptorType
660            = context.getGraniteConfig().getJavaDescriptor(clazz.getName());
661        if (descriptorType != null) {
662            Class<?>[] argsDef = new Class[]{Class.class};
663            Object[] argsVal = new Object[]{clazz};
664            try {
665                desc = TypeUtil.newInstance(descriptorType, argsDef, argsVal);
666            }
667            catch (Exception e) {
668                throw new RuntimeException("Could not instantiate Java descriptor: " + descriptorType);
669            }
670        }
671
672        if (desc == null)
673            desc = new DefaultJavaClassDescriptor(clazz);
674
675        IndexedJavaClassDescriptor iDesc = new IndexedJavaClassDescriptor(storedClassDescriptors.size(), desc);
676
677        if (debug) log.debug("addToStoredClassDescriptors() - putting: name=%s, iDesc=%s", name, iDesc);
678
679        storedClassDescriptors.put(name, iDesc);
680
681        return iDesc;
682    }
683
684    protected IndexedJavaClassDescriptor getFromStoredClassDescriptors(Class<?> clazz) {
685        if (debug) log.debug("getFromStoredClassDescriptors(clazz=%s)", clazz);
686
687        String name = JavaClassDescriptor.getClassName(clazz);
688        IndexedJavaClassDescriptor iDesc = storedClassDescriptors.get(name);
689
690        if (debug) log.debug("getFromStoredClassDescriptors() -> %s", iDesc);
691
692        return iDesc;
693    }
694
695    ///////////////////////////////////////////////////////////////////////////
696    // Utilities.
697
698    protected Channel getChannel() {
699        if (channel == null) {
700            String channelId = context.getAMFContext().getChannelId();
701            if (channelId != null)
702                channel = context.getServicesConfig().findChannelById(channelId);
703            if (channel == null)
704                log.debug("Could not get channel for channel id: %s", channelId);
705        }
706        return channel;
707    }
708}