package com.grendelscan.commons.flex.messages; import java.io.DataOutputStream; import java.io.Externalizable; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.grendelscan.commons.flex.AbstractAmfData; import com.grendelscan.commons.flex.AbstractAmfDataContainer; import com.grendelscan.commons.flex.AbstractAmfNamedDataContainer; import com.grendelscan.commons.flex.AmfOutputStream; import com.grendelscan.commons.flex.AmfPrimitiveData; import com.grendelscan.commons.flex.NamedAmfDataContainer; import com.grendelscan.commons.flex.arrays.AmfAssociativeArrayData; import com.grendelscan.commons.flex.arrays.AmfByteArray; import com.grendelscan.commons.flex.dataTypeDefinitions.AmfDataType; import com.grendelscan.commons.flex.interfaces.AmfGenericObject; import com.grendelscan.commons.flex.output.AmfOutputStreamRegistry; import com.grendelscan.commons.http.dataHandling.data.Data; import com.grendelscan.commons.http.dataHandling.data.DataUtils; import com.grendelscan.commons.http.dataHandling.references.DataReference; import com.grendelscan.commons.http.dataHandling.references.NameOrValueReference; import com.grendelscan.commons.http.dataHandling.references.NamedDataContainerDataReference; import com.grendelscan.commons.flex.AmfUtils; import flex.messaging.messages.AbstractMessage; import flex.messaging.util.UUIDUtils; public abstract class AmfAbstractMessage extends AbstractAmfNamedDataContainer implements AmfGenericObject { private static final Logger LOGGER = LoggerFactory.getLogger(AmfAbstractMessage.class); /** * */ private static final long serialVersionUID = 1L; // Serialization constants private static final short HAS_NEXT_FLAG = 128; private static final short BODY_FLAG = 1; private static final short CLIENT_ID_FLAG = 2; private static final short DESTINATION_FLAG = 4; private static final short HEADERS_FLAG = 8; private static final short MESSAGE_ID_FLAG = 16; private static final short TIMESTAMP_FLAG = 32; private static final short TIME_TO_LIVE_FLAG = 64; private static final short CLIENT_ID_BYTES_FLAG = 1; private static final short MESSAGE_ID_BYTES_FLAG = 2; private static final String BODY = "body"; private static final String HEADERS = "headers"; private static final String CLIENT_ID = "ClientId"; private static final String DESTINATION = "destination"; private static final String MESSAGE_ID = "messageId"; private static final String TIMESTAMP = "timestamp"; private static final String TIME_TO_LIVE = "timeToLive"; // The className property is not a AmfPrimitive data. It is the "value" of // the object protected String className; /** * Externalizable basically means that property names will not be printed because * the reciever knows how the object will be formated. */ private boolean externalizable; protected AmfAbstractMessage(String name, AbstractMessage message, AmfDataType type, String className, AbstractAmfDataContainer<?> parent, int transactionId) { super(name, type, parent, false, transactionId); this.className = className; createHeaders(message.getHeaders()); addFixedField(BODY, AmfUtils.parseAmfData(message.getBody(), this, transactionId, true), false); addFixedField(CLIENT_ID, AmfUtils.parseAmfData(message.getClientId(), this, transactionId, true), false); addFixedField(DESTINATION, AmfUtils.parseAmfData(message.getDestination(), this, transactionId, true), true); addFixedField(MESSAGE_ID, AmfUtils.parseAmfData(message.getMessageId(), this, transactionId, true), true); addFixedField(TIMESTAMP, AmfUtils.parseAmfData(message.getTimestamp(), this, transactionId, true), true); addFixedField(TIME_TO_LIVE, AmfUtils.parseAmfData(message.getTimeToLive(), this, transactionId, true), true); externalizable = message instanceof Externalizable; } protected AmfAbstractMessage(String name, AmfDataType type, boolean externalizable, AbstractAmfDataContainer<?> parent, int transactionId) { super(name, type, parent, false, transactionId); createHeaders(null); addFixedField(BODY, AmfUtils.parseAmfData(null, this, transactionId, true), false); addFixedField(CLIENT_ID, AmfUtils.parseAmfData("", this, transactionId, true), false); addFixedField(DESTINATION, AmfUtils.parseAmfData("", this, transactionId, true), true); addFixedField(MESSAGE_ID, AmfUtils.parseAmfData("", this, transactionId, true), true); addFixedField(TIMESTAMP, AmfUtils.parseAmfData(0, this, transactionId, true), true); addFixedField(TIME_TO_LIVE, AmfUtils.parseAmfData(0, this, transactionId, true), true); this.externalizable = externalizable; } protected String getBodyDisplayName() { return "body"; } private void createHeaders(Map<?, ?> map) { addFixedField(HEADERS, new AmfAssociativeArrayData(HEADERS, map, this, getTransactionId()), true); } public AbstractAmfData getBody() { return properties.get(getBodyDisplayName()).getValueData(); } @Override public String getClassName() { return className; } @Override public void setClassName(String className) { this.className = className; } @Override public void writeBytes(OutputStream out) { AmfOutputStream outputStream = AmfOutputStreamRegistry.getStream(out); try { writeCodeToStream(outputStream); writeAttributes(outputStream); if (externalizable) { writeExternal(outputStream); } else { for (Data datum: properties.getSortedValues()) { datum.writeBytes(out); } } } catch (IOException e) { LOGGER.error("Problem writing AMF: " + e.toString(), e); } } private void writeAttributes(DataOutputStream outputStream) throws IOException { boolean dynamic = false; int count = 0; if (! externalizable) { count = properties.size(); } AmfUtils.writeUInt29(outputStream, 3 | (externalizable ? 4 : 0) | (dynamic ? 8 : 0) | (count << 4)); AmfUtils.writeAMFUTF(outputStream, false, className, useAmf3Code); if (! externalizable) { for (NamedAmfDataContainer container: properties.getSortedValues()) { AmfUtils.writeAMFUTF(outputStream, false, new String(DataUtils.getBytes(container.getNameData())), useAmf3Code); } } } protected void writeExternal(AmfOutputStream outputStream) throws IOException { short flags = 0; byte[] clientIdBytes = null; byte[] messageIdBytes = null; // clientIdBytes = UUIDUtils.toByteArray(((AmfPrimitiveData) properties.get(CLIENT_ID)).getValue()); // messageIdBytes = UUIDUtils.toByteArray(((AmfPrimitiveData) properties.get(MESSAGE_ID)).getValue()); clientIdBytes = UUIDUtils.toByteArray(new String(DataUtils.getBytes(properties.get(CLIENT_ID)))); messageIdBytes = UUIDUtils.toByteArray(new String(DataUtils.getBytes(properties.get(MESSAGE_ID)))); if (getBody() != null) { flags |= BODY_FLAG; } if (properties.get(CLIENT_ID) != null && clientIdBytes == null) { flags |= CLIENT_ID_FLAG; } if (properties.get(DESTINATION) != null) { flags |= DESTINATION_FLAG; } if (((AmfAssociativeArrayData)properties.get(HEADERS).getValueData()).getSize() > 0) { flags |= HEADERS_FLAG; } if ((properties.get(MESSAGE_ID) != null) && (messageIdBytes == null)) { flags |= MESSAGE_ID_FLAG; } if (!((AmfPrimitiveData) properties.get(TIMESTAMP).getValueData()).getValue().equals("0")) { flags |= TIMESTAMP_FLAG; } if (!((AmfPrimitiveData) properties.get(TIME_TO_LIVE).getValueData()).getValue().equals("0")) { flags |= TIME_TO_LIVE_FLAG; } if ((clientIdBytes != null) || (messageIdBytes != null)) { flags |= HAS_NEXT_FLAG; } outputStream.writeByte(flags); flags = 0; if (clientIdBytes != null) { flags |= CLIENT_ID_BYTES_FLAG; } if (messageIdBytes != null) { flags |= MESSAGE_ID_BYTES_FLAG; } if (flags != 0) { outputStream.writeByte(flags); } if (getBody() != null) { getBody().writeBytes(outputStream); } if ((properties.get(CLIENT_ID) != null) && (clientIdBytes == null)) { properties.get(CLIENT_ID).writeBytes(outputStream); } if (properties.get(DESTINATION) != null) { properties.get(DESTINATION).writeBytes(outputStream); } if ((properties.get(HEADERS)) != null) { properties.get(HEADERS).writeBytes(outputStream); } if ((properties.get(MESSAGE_ID) != null) && (messageIdBytes == null)) { properties.get(MESSAGE_ID).writeBytes(outputStream); } if (!((AmfPrimitiveData) properties.get(TIMESTAMP).getValueData()).getValue().equals("0")) { properties.get(TIMESTAMP).writeBytes(outputStream); } if (!((AmfPrimitiveData) properties.get(TIME_TO_LIVE).getValueData()).getValue().equals("0")) { properties.get(TIME_TO_LIVE).writeBytes(outputStream); } if (clientIdBytes != null) { AmfByteArray aba = new AmfByteArray("Client ID Bytes", clientIdBytes, this, -1); aba.writeBytes(outputStream); } if (messageIdBytes != null) { AmfByteArray aba = new AmfByteArray("Message ID Bytes", messageIdBytes, this, -1); aba.writeBytes(outputStream); } } protected void addFixedField(String name, AbstractAmfData value, boolean lockType) { value.setDeletable(false); value.setNameLocked(true); value.setTypeLocked(lockType); value.setName(name); putChild(name, value); } }