// TODO need to fix public MetaData<?, ?> readMetaData(IoBuffer buffer) { MetaData<?, ?> retMeta = new MetaData<String, Object>(); Input input = new Input(buffer); String metaType = Deserializer.deserialize(input, String.class); log.debug("Metadata type: {}", metaType); Map<String, ?> m = Deserializer.deserialize(input, Map.class); retMeta.putAll(m); return retMeta; }
/** * Read remoting headers. * * @param in Input data as byte buffer * @return header map */ @SuppressWarnings("unchecked") protected Map<String, Object> readHeaders(IoBuffer in) { int version = in.getUnsignedShort(); // skip the version int count = in.getUnsignedShort(); log.debug("Read headers - version: {} count: {}", version, count); if (count == 0) { // No headers present return Collections.EMPTY_MAP; } Input input; if (version == 3) { input = new org.red5.io.amf3.Input(in); } else { input = new org.red5.io.amf.Input(in); } Map<String, Object> result = new HashMap<String, Object>(); for (int i = 0; i < count; i++) { String name = input.getString(); boolean required = in.get() == 0x01; int size = in.getInt(); Object value = Deserializer.deserialize(input, Object.class); log.debug( "Header: {} Required: {} Size: {} Value: {}", new Object[] {name, required, size, value}); result.put(name, value); } return result; }
/** * Creates recordset page from Input object * * @param input Input object to use as source for data that has to be deserialized */ @SuppressWarnings({"unchecked", "rawtypes"}) public RecordSetPage(Input input) { Deserializer deserizalizer = new Deserializer(); Map mapResult = deserizalizer.deserialize(input, Map.class); cursor = (Integer) mapResult.get("Cursor"); data = (List<List<Object>>) mapResult.get("Page"); }
/** * Decode calls. * * @param in Input data as byte buffer * @return List of pending calls */ protected List<RemotingCall> decodeCalls(IoBuffer in) { log.debug("Decode calls"); // in.getInt(); List<RemotingCall> calls = new LinkedList<RemotingCall>(); org.red5.io.amf.Input input; int count = in.getUnsignedShort(); log.debug("Calls: {}", count); int limit = in.limit(); // Loop over all the body elements for (int i = 0; i < count; i++) { in.limit(limit); String serviceString = org.red5.io.amf.Input.getString(in); String clientCallback = org.red5.io.amf.Input.getString(in); log.debug("callback: {}", clientCallback); Object[] args = null; boolean isAMF3 = false; @SuppressWarnings("unused") int length = in.getInt(); // Set the limit and deserialize // NOTE: disabled because the FP sends wrong values here /* * if (length != -1) in.limit(in.position()+length); */ byte type = in.get(); if (type == AMF.TYPE_ARRAY) { int elements = in.getInt(); List<Object> values = new ArrayList<Object>(); RefStorage refStorage = null; for (int j = 0; j < elements; j++) { byte amf3Check = in.get(); in.position(in.position() - 1); isAMF3 = (amf3Check == AMF.TYPE_AMF3_OBJECT); if (isAMF3) { if (refStorage == null) { input = new org.red5.io.amf3.Input(in); } else { input = new org.red5.io.amf3.Input(in, refStorage); } } else { input = new org.red5.io.amf.Input(in); } // prepare remoting mode input.reset(); // add deserialized object to the value list values.add(Deserializer.deserialize(input, Object.class)); if (isAMF3) { refStorage = ((org.red5.io.amf3.Input) input).getRefStorage(); } } args = values.toArray(new Object[values.size()]); if (log.isDebugEnabled()) { for (Object element : args) { log.debug("> " + element); } } } else if (type == AMF.TYPE_NULL) { log.debug("Got null amf type"); } else if (type != AMF.TYPE_ARRAY) { throw new RuntimeException("AMF0 array type expected but found " + type); } String serviceName; String serviceMethod; int dotPos = serviceString.lastIndexOf('.'); if (dotPos != -1) { serviceName = serviceString.substring(0, dotPos); serviceMethod = serviceString.substring(dotPos + 1, serviceString.length()); } else { serviceName = ""; serviceMethod = serviceString; } boolean isMessaging = false; if ("".equals(serviceName) && "null".equals(serviceMethod)) { // Use fixed service and method name for Flex messaging requests, // this probably will change in the future. serviceName = FlexMessagingService.SERVICE_NAME; serviceMethod = "handleRequest"; isMessaging = true; } log.debug("Service: {} Method: {}", serviceName, serviceMethod); // Add the call to the list calls.add( new RemotingCall(serviceName, serviceMethod, args, clientCallback, isAMF3, isMessaging)); } return calls; }
@SuppressWarnings({"unchecked", "rawtypes", "serial"}) public Object readObject(Deserializer deserializer, Type target) { int type = readAMF3Integer(); log.debug("Type: {} and {} ref {}", new Object[] {type, (type & 1), (type >> 1)}); if ((type & 1) == 0) { // Reference Object ref = getReference(type >> 1); if (ref != null) { return ref; } byte b = buf.get(); if (b == 7) { log.debug("BEL: {}", b); // 7 } else { log.debug("Non-BEL byte: {}", b); log.debug("Extra byte: {}", buf.get()); } } type >>= 1; List<String> attributes = null; String className; Object result = null; boolean inlineClass = (type & 1) == 1; log.debug("Class is in-line? {}", inlineClass); if (!inlineClass) { ClassReference info = refStorage.classReferences.get(type >> 1); className = info.className; attributes = info.attributeNames; type = info.type; if (attributes != null) { type |= attributes.size() << 2; } } else { type >>= 1; className = readString(String.class); log.debug("Type: {} classname: {}", type, className); // check for flex class alias since these wont be detected as externalizable if (classAliases.containsKey(className)) { // make sure type is externalizable type = 1; } else if (className.startsWith("flex")) { // set the attributes for messaging classes if (className.endsWith("CommandMessage")) { attributes = new LinkedList<String>() { { add("timestamp"); add("headers"); add("operation"); add("body"); add("correlationId"); add("messageId"); add("timeToLive"); add("clientId"); add("destination"); } }; } else { log.debug("Attributes for {} were not set", className); } } } amf3_mode += 1; Object instance = newInstance(className); Map<String, Object> properties = null; PendingObject pending = new PendingObject(); int tempRefId = storeReference(pending); log.debug("Object type: {}", (type & 0x03)); switch (type & 0x03) { case AMF3.TYPE_OBJECT_PROPERTY: log.debug("Detected: Object property type"); // Load object properties into map int count = type >> 2; log.debug("Count: {}", count); if (attributes == null) { attributes = new ArrayList<String>(count); for (int i = 0; i < count; i++) { attributes.add(readString(String.class)); } refStorage.classReferences.add( new ClassReference(className, AMF3.TYPE_OBJECT_PROPERTY, attributes)); } properties = new ObjectMap<String, Object>(); for (int i = 0; i < count; i++) { String name = attributes.get(i); properties.put(name, deserializer.deserialize(this, getPropertyType(instance, name))); } break; case AMF3.TYPE_OBJECT_EXTERNALIZABLE: log.debug("Detected: Externalizable type"); // Use custom class to deserialize the object if ("".equals(className)) { throw new RuntimeException("Classname is required to load an Externalizable object"); } log.debug("Externalizable class: {}", className); if (className.length() == 3) { // check for special DS class aliases className = classAliases.get(className); } result = newInstance(className); if (result == null) { throw new RuntimeException(String.format("Could not instantiate class: %s", className)); } if (!(result instanceof IExternalizable)) { throw new RuntimeException( String.format("Class must implement the IExternalizable interface: %s", className)); } refStorage.classReferences.add( new ClassReference(className, AMF3.TYPE_OBJECT_EXTERNALIZABLE, null)); storeReference(tempRefId, result); ((IExternalizable) result).readExternal(new DataInput(this, deserializer)); break; case AMF3.TYPE_OBJECT_VALUE: log.debug("Detected: Object value type"); // First, we should read typed (non-dynamic) properties ("sealed traits" according to AMF3 // specification). // Property names are stored in the beginning, then values are stored. count = type >> 2; log.debug("Count: {}", count); if (attributes == null) { attributes = new ArrayList<String>(count); for (int i = 0; i < count; i++) { attributes.add(readString(String.class)); } refStorage.classReferences.add( new ClassReference(className, AMF3.TYPE_OBJECT_VALUE, attributes)); } // use the size of the attributes if we have no count if (count == 0 && attributes != null) { count = attributes.size(); log.debug("Using class attribute size for property count: {}", count); // read the attributes from the stream and log if count doesnt match List<String> tmpAttributes = new ArrayList<String>(count); for (int i = 0; i < count; i++) { tmpAttributes.add(readString(String.class)); } if (count != tmpAttributes.size()) { log.debug("Count and attributes length does not match!"); } refStorage.classReferences.add( new ClassReference(className, AMF3.TYPE_OBJECT_VALUE, attributes)); } properties = new ObjectMap<String, Object>(); for (String key : attributes) { log.debug("Looking for property: {}", key); Object value = deserializer.deserialize(this, getPropertyType(instance, key)); log.debug("Key: {} Value: {}", key, value); properties.put(key, value); } log.trace("Buffer - position: {} limit: {}", buf.position(), buf.limit()); // no more items to read if we are at the end of the buffer if (buf.position() < buf.limit()) { // Now we should read dynamic properties which are stored as name-value pairs. // Dynamic properties are NOT remembered in 'classReferences'. String key = readString(String.class); while (!"".equals(key)) { Object value = deserializer.deserialize(this, getPropertyType(instance, key)); properties.put(key, value); key = readString(String.class); } } break; default: case AMF3.TYPE_OBJECT_PROXY: log.debug("Detected: Object proxy type"); if ("".equals(className)) { throw new RuntimeException("Classname is required to load an Externalizable object"); } log.debug("Externalizable class: {}", className); result = newInstance(className); if (result == null) { throw new RuntimeException(String.format("Could not instantiate class: %s", className)); } if (!(result instanceof IExternalizable)) { throw new RuntimeException( String.format("Class must implement the IExternalizable interface: %s", className)); } refStorage.classReferences.add(new ClassReference(className, AMF3.TYPE_OBJECT_PROXY, null)); storeReference(tempRefId, result); ((IExternalizable) result).readExternal(new DataInput(this, deserializer)); } amf3_mode -= 1; if (result == null) { // Create result object based on classname if ("".equals(className)) { // "anonymous" object, load as Map // Resolve circular references for (Map.Entry<String, Object> entry : properties.entrySet()) { if (entry.getValue() == pending) { entry.setValue(properties); } } storeReference(tempRefId, properties); result = properties; } else if ("RecordSet".equals(className)) { // TODO: how are RecordSet objects encoded? throw new RuntimeException("Objects of type RecordSet not supported yet."); } else if ("RecordSetPage".equals(className)) { // TODO: how are RecordSetPage objects encoded? throw new RuntimeException("Objects of type RecordSetPage not supported yet."); } else { // Apply properties to object result = newInstance(className); if (result != null) { storeReference(tempRefId, result); Class resultClass = result.getClass(); pending.resolveProperties(result); for (Map.Entry<String, Object> entry : properties.entrySet()) { // Resolve circular references final String key = entry.getKey(); Object value = entry.getValue(); if (value == pending) { value = result; } if (value instanceof PendingObject) { // Defer setting of value until real object is created ((PendingObject) value).addPendingProperty(result, resultClass, key); continue; } if (value != null) { try { final Field field = resultClass.getField(key); final Class fieldType = field.getType(); if (!fieldType.isAssignableFrom(value.getClass())) { value = ConversionUtils.convert(value, fieldType); } else if (value instanceof Enum) { value = Enum.valueOf(fieldType, value.toString()); } field.set(result, value); } catch (Exception e) { try { BeanUtils.setProperty(result, key, value); } catch (IllegalAccessException ex) { log.warn("Error mapping key: {} value: {}", key, value); } catch (InvocationTargetException ex) { log.warn("Error mapping key: {} value: {}", key, value); } } } else { log.debug("Skipping null property: {}", key); } } } // else fall through } } return result; }
/** * Returns an array * * @return int Length of array */ @SuppressWarnings({"unchecked", "rawtypes"}) public Object readArray(Deserializer deserializer, Type target) { int count = readAMF3Integer(); log.debug("Count: {} and {} ref {}", new Object[] {count, (count & 1), (count >> 1)}); if ((count & 1) == 0) { // Reference Object ref = getReference(count >> 1); if (ref != null) { return ref; } } count = (count >> 1); String key = readString(String.class); amf3_mode += 1; Object result; if (key.equals("")) { Class<?> nested = Object.class; Class<?> collection = Collection.class; Collection resultCollection; if (target instanceof ParameterizedType) { ParameterizedType t = (ParameterizedType) target; Type[] actualTypeArguments = t.getActualTypeArguments(); if (actualTypeArguments.length == 1) { nested = (Class<?>) actualTypeArguments[0]; } target = t.getRawType(); } if (target instanceof Class) { collection = (Class) target; } if (collection.isArray()) { nested = ArrayUtils.getGenericType(collection.getComponentType()); result = Array.newInstance(nested, count); storeReference(result); for (int i = 0; i < count; i++) { final Object value = deserializer.deserialize(this, nested); Array.set(result, i, value); } } else { if (SortedSet.class.isAssignableFrom(collection)) { resultCollection = new TreeSet(); } else if (Set.class.isAssignableFrom(collection)) { resultCollection = new HashSet(count); } else { resultCollection = new ArrayList(count); } result = resultCollection; storeReference(result); for (int i = 0; i < count; i++) { final Object value = deserializer.deserialize(this, nested); resultCollection.add(value); } } } else { Class<?> k = Object.class; Class<?> v = Object.class; Class<?> collection = Collection.class; if (target instanceof ParameterizedType) { ParameterizedType t = (ParameterizedType) target; Type[] actualTypeArguments = t.getActualTypeArguments(); if (actualTypeArguments.length == 2) { k = (Class<?>) actualTypeArguments[0]; v = (Class<?>) actualTypeArguments[1]; } target = t.getRawType(); } if (target instanceof Class) { collection = (Class) target; } if (SortedMap.class.isAssignableFrom(collection)) { collection = TreeMap.class; } else { collection = HashMap.class; } Map resultMap; try { resultMap = (Map) collection.newInstance(); } catch (Exception e) { resultMap = new HashMap(count); } // associative array storeReference(resultMap); while (!key.equals("")) { final Object value = deserializer.deserialize(this, v); resultMap.put(key, value); key = readString(k); } for (int i = 0; i < count; i++) { final Object value = deserializer.deserialize(this, v); resultMap.put(i, value); } result = resultMap; } amf3_mode -= 1; return result; }