diff options
23 files changed, 1102 insertions, 734 deletions
diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java index 586d6b8796..ccb2211a55 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java @@ -25,6 +25,7 @@ import org.apache.qpid.AMQException; import org.apache.qpid.framing.BasicContentHeaderProperties; import org.apache.qpid.framing.ContentHeaderBody; import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.FieldTableFactory; import org.apache.qpid.server.management.MBeanConstructor; import org.apache.qpid.server.management.MBeanDescription; import org.apache.qpid.server.queue.AMQMessage; @@ -154,7 +155,7 @@ public class HeadersExchange extends AbstractExchange } String[] bindings = binding.split(","); - FieldTable fieldTable = new FieldTable(); + FieldTable fieldTable = FieldTableFactory.newFieldTable(); for (int i = 0; i < bindings.length; i++) { String[] keyAndValue = bindings[i].split("="); diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/auth/amqplain/AmqPlainSaslServer.java b/java/broker/src/main/java/org/apache/qpid/server/security/auth/amqplain/AmqPlainSaslServer.java index 3ad74ce180..c364ca1d8d 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/security/auth/amqplain/AmqPlainSaslServer.java +++ b/java/broker/src/main/java/org/apache/qpid/server/security/auth/amqplain/AmqPlainSaslServer.java @@ -22,6 +22,7 @@ package org.apache.qpid.server.security.auth.amqplain; import org.apache.qpid.framing.FieldTable; import org.apache.qpid.framing.AMQFrameDecodingException; +import org.apache.qpid.framing.FieldTableFactory; import org.apache.mina.common.ByteBuffer; import javax.security.sasl.SaslServer; @@ -54,7 +55,7 @@ public class AmqPlainSaslServer implements SaslServer { try { - final FieldTable ft = new FieldTable(ByteBuffer.wrap(response), response.length); + final FieldTable ft = FieldTableFactory.newFieldTable(ByteBuffer.wrap(response), response.length); String username = (String) ft.get("LOGIN"); // we do not care about the prompt but it throws if null NameCallback nameCb = new NameCallback("prompt", username); diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQSession.java b/java/client/src/main/java/org/apache/qpid/client/AMQSession.java index 57b941a060..bfd294f09e 100644 --- a/java/client/src/main/java/org/apache/qpid/client/AMQSession.java +++ b/java/client/src/main/java/org/apache/qpid/client/AMQSession.java @@ -830,7 +830,7 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi final AMQProtocolHandler protocolHandler = _connection.getProtocolHandler(); // TODO: construct the rawSelector from the selector string if rawSelector == null - final FieldTable ft = new FieldTable(); + final FieldTable ft = FieldTableFactory.newFieldTable(); //if (rawSelector != null) // ft.put("headers", rawSelector.getDataAsBytes()); if (rawSelector != null) @@ -992,7 +992,7 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi public QueueSender createSender(Queue queue) throws JMSException { //return (QueueSender) createProducer(queue); - return new QueueSenderAdapter(createProducer(queue),queue); + return new QueueSenderAdapter(createProducer(queue), queue); } public Topic createTopic(String topicName) throws JMSException @@ -1072,7 +1072,7 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi public TopicPublisher createPublisher(Topic topic) throws JMSException { //return (TopicPublisher) createProducer(topic); - return new TopicPublisherAdapter(createProducer(topic), topic); + return new TopicPublisherAdapter(createProducer(topic), topic); } public QueueBrowser createBrowser(Queue queue) throws JMSException diff --git a/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionStartMethodHandler.java b/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionStartMethodHandler.java index caef9a3f44..9333df3fe4 100644 --- a/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionStartMethodHandler.java +++ b/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionStartMethodHandler.java @@ -32,6 +32,7 @@ import org.apache.qpid.client.state.StateAwareMethodListener; import org.apache.qpid.framing.ConnectionStartBody; import org.apache.qpid.framing.ConnectionStartOkBody; import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.FieldTableFactory; import javax.security.sasl.Sasl; import javax.security.sasl.SaslClient; @@ -117,7 +118,7 @@ public class ConnectionStartMethodHandler implements StateAwareMethodListener } stateManager.changeState(AMQState.CONNECTION_NOT_TUNED); - FieldTable clientProperties = new FieldTable(); + FieldTable clientProperties = FieldTableFactory.newFieldTable(); clientProperties.put("instance", ps.getClientID()); clientProperties.put("product", "Qpid"); clientProperties.put("version", "1.0"); diff --git a/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessage.java b/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessage.java index 30031326d5..fbb55ae289 100644 --- a/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessage.java +++ b/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessage.java @@ -28,9 +28,8 @@ import org.apache.qpid.client.AMQQueue; import org.apache.qpid.client.AMQTopic; import org.apache.qpid.client.JmsNotImplementedException; import org.apache.qpid.framing.BasicContentHeaderProperties; -import org.apache.qpid.framing.FieldTableKeyEnumeration; import org.apache.qpid.framing.FieldTable; -import org.apache.qpid.framing.PropertyFieldTable; +import org.apache.qpid.framing.FieldTableFactory; import javax.jms.Destination; import javax.jms.JMSException; @@ -45,20 +44,6 @@ public abstract class AbstractJMSMessage extends AMQMessage implements javax.jms { private static final Map _destinationCache = Collections.synchronizedMap(new ReferenceMap()); - - //todo Remove these and Change _headers to use a subclass of PropertyFieldTable that limits - // the properties that can be added... or suitably handles the values that cannot be added to the - // AMQP header field table. - public static final char BOOLEAN_PROPERTY_PREFIX = PropertyFieldTable.BOOLEAN_PROPERTY_PREFIX; - public static final char BYTE_PROPERTY_PREFIX = PropertyFieldTable.BYTE_PROPERTY_PREFIX; - public static final char SHORT_PROPERTY_PREFIX = PropertyFieldTable.SHORT_PROPERTY_PREFIX; - public static final char INT_PROPERTY_PREFIX = PropertyFieldTable.INT_PROPERTY_PREFIX; - public static final char LONG_PROPERTY_PREFIX = PropertyFieldTable.LONG_PROPERTY_PREFIX; - public static final char FLOAT_PROPERTY_PREFIX = PropertyFieldTable.FLOAT_PROPERTY_PREFIX; - public static final char DOUBLE_PROPERTY_PREFIX = PropertyFieldTable.DOUBLE_PROPERTY_PREFIX; - public static final char STRING_PROPERTY_PREFIX = PropertyFieldTable.STRING_PROPERTY_PREFIX; - - protected boolean _redelivered; protected ByteBuffer _data; @@ -268,213 +253,136 @@ public abstract class AbstractJMSMessage extends AMQMessage implements javax.jms public boolean propertyExists(String propertyName) throws JMSException { checkPropertyName(propertyName); - - Iterator keys = getJmsContentHeaderProperties().getHeaders().keySet().iterator(); - - while (keys.hasNext()) - { - String key = (String) keys.next(); - - if (key.endsWith(propertyName)) - { - return true; - } - } - return false; - + return getJmsContentHeaderProperties().getHeaders().propertyExists(propertyName); } public boolean getBooleanProperty(String propertyName) throws JMSException { checkPropertyName(propertyName); - // store as integer as temporary workaround - //Boolean b = (Boolean) getJmsContentHeaderProperties().headers.get(BOOLEAN_PROPERTY_PREFIX + propertyName); - Long b = (Long) getJmsContentHeaderProperties().getHeaders().get(BOOLEAN_PROPERTY_PREFIX + propertyName); - if (b == null) + if (getJmsContentHeaderProperties() == null) { - return Boolean.valueOf(null).booleanValue(); - } - else - { - return b.longValue() != 0; + System.out.println("HEADERS ARE NULL"); } + + return getJmsContentHeaderProperties().getHeaders().getBoolean(propertyName); } public byte getByteProperty(String propertyName) throws JMSException { checkPropertyName(propertyName); - Byte b = (Byte) getJmsContentHeaderProperties().getHeaders().get(BYTE_PROPERTY_PREFIX + propertyName); - if (b == null) - { - return Byte.valueOf(null).byteValue(); - } - else - { - return b.byteValue(); - } + return getJmsContentHeaderProperties().getHeaders().getByte(propertyName); } public short getShortProperty(String propertyName) throws JMSException { checkPropertyName(propertyName); - Short s = (Short) getJmsContentHeaderProperties().getHeaders().get(SHORT_PROPERTY_PREFIX + propertyName); - if (s == null) - { - return Short.valueOf(null).shortValue(); - } - else - { - return s.shortValue(); - } + return getJmsContentHeaderProperties().getHeaders().getShort(propertyName); } public int getIntProperty(String propertyName) throws JMSException { checkPropertyName(propertyName); - Integer i = (Integer) getJmsContentHeaderProperties().getHeaders().get(INT_PROPERTY_PREFIX + propertyName); - if (i == null) - { - return Integer.valueOf(null).intValue(); - } - else - { - return i.intValue(); - } + return getJmsContentHeaderProperties().getHeaders().getInteger(propertyName); } public long getLongProperty(String propertyName) throws JMSException { checkPropertyName(propertyName); - Long l = (Long) getJmsContentHeaderProperties().getHeaders().get(LONG_PROPERTY_PREFIX + propertyName); - if (l == null) - { - // temp - the spec says do this but this throws a NumberFormatException - //return Long.valueOf(null).longValue(); - return 0; - } - else - { - return l.longValue(); - } + return getJmsContentHeaderProperties().getHeaders().getLong(propertyName); } public float getFloatProperty(String propertyName) throws JMSException { checkPropertyName(propertyName); - final Float f = (Float) getJmsContentHeaderProperties().getHeaders().get(FLOAT_PROPERTY_PREFIX + propertyName); - if (f == null) - { - return Float.valueOf(null).floatValue(); - } - else - { - return f.floatValue(); - } + return getJmsContentHeaderProperties().getHeaders().getFloat(propertyName); } public double getDoubleProperty(String propertyName) throws JMSException { checkPropertyName(propertyName); - final Double d = (Double) getJmsContentHeaderProperties().getHeaders().get(DOUBLE_PROPERTY_PREFIX + propertyName); - if (d == null) - { - return Double.valueOf(null).doubleValue(); - } - else - { - return d.shortValue(); - } + return getJmsContentHeaderProperties().getHeaders().getDouble(propertyName); } public String getStringProperty(String propertyName) throws JMSException { checkPropertyName(propertyName); - return (String) getJmsContentHeaderProperties().getHeaders().get(STRING_PROPERTY_PREFIX + propertyName); + return getJmsContentHeaderProperties().getHeaders().getString(propertyName); } public Object getObjectProperty(String propertyName) throws JMSException { checkPropertyName(propertyName); - throw new JmsNotImplementedException(); + return getJmsContentHeaderProperties().getHeaders().getObject(propertyName); } public Enumeration getPropertyNames() throws JMSException { - return new FieldTableKeyEnumeration(getJmsContentHeaderProperties().getHeaders()) - { - public Object nextElement() - { - String propName = (String) _iterator.next(); - - //The propertyName has a single Char prefix. Skip this. - return propName.substring(1); - } - }; + return getJmsContentHeaderProperties().getHeaders().getPropertyNames(); } public void setBooleanProperty(String propertyName, boolean b) throws JMSException { checkWritableProperties(); checkPropertyName(propertyName); - //getJmsContentHeaderProperties().headers.put(BOOLEAN_PROPERTY_PREFIX + propertyName, Boolean.valueOf(b)); - getJmsContentHeaderProperties().getHeaders().put(BOOLEAN_PROPERTY_PREFIX + propertyName, b ? new Long(1) : new Long(0)); + getJmsContentHeaderProperties().getHeaders().setBoolean(propertyName, b); } public void setByteProperty(String propertyName, byte b) throws JMSException { checkWritableProperties(); checkPropertyName(propertyName); - getJmsContentHeaderProperties().getHeaders().put(BYTE_PROPERTY_PREFIX + propertyName, new Byte(b)); + getJmsContentHeaderProperties().getHeaders().setByte(propertyName, new Byte(b)); } public void setShortProperty(String propertyName, short i) throws JMSException { checkWritableProperties(); checkPropertyName(propertyName); - getJmsContentHeaderProperties().getHeaders().put(SHORT_PROPERTY_PREFIX + propertyName, new Short(i)); + getJmsContentHeaderProperties().getHeaders().setShort(propertyName, new Short(i)); } public void setIntProperty(String propertyName, int i) throws JMSException { checkWritableProperties(); checkPropertyName(propertyName); - getJmsContentHeaderProperties().getHeaders().put(INT_PROPERTY_PREFIX + propertyName, new Integer(i)); + getJmsContentHeaderProperties().getHeaders().setInteger(propertyName, new Integer(i)); } public void setLongProperty(String propertyName, long l) throws JMSException { checkWritableProperties(); checkPropertyName(propertyName); - getJmsContentHeaderProperties().getHeaders().put(LONG_PROPERTY_PREFIX + propertyName, new Long(l)); + getJmsContentHeaderProperties().getHeaders().setLong(propertyName, new Long(l)); } public void setFloatProperty(String propertyName, float f) throws JMSException { checkWritableProperties(); checkPropertyName(propertyName); - getJmsContentHeaderProperties().getHeaders().put(FLOAT_PROPERTY_PREFIX + propertyName, new Float(f)); + getJmsContentHeaderProperties().getHeaders().setFloat(propertyName, new Float(f)); } public void setDoubleProperty(String propertyName, double v) throws JMSException { checkWritableProperties(); checkPropertyName(propertyName); - getJmsContentHeaderProperties().getHeaders().put(DOUBLE_PROPERTY_PREFIX + propertyName, new Double(v)); + getJmsContentHeaderProperties().getHeaders().setDouble(propertyName, new Double(v)); } public void setStringProperty(String propertyName, String value) throws JMSException { checkWritableProperties(); checkPropertyName(propertyName); - getJmsContentHeaderProperties().getHeaders().put(STRING_PROPERTY_PREFIX + propertyName, value); + getJmsContentHeaderProperties().getHeaders().setString(propertyName, value); } - public void setObjectProperty(String string, Object object) throws JMSException + public void setObjectProperty(String propertyName, Object object) throws JMSException { - //todo this should be changed to something else.. the Header doesn't support objects. - throw new RuntimeException("Not Implemented"); + checkWritableProperties(); + checkPropertyName(propertyName); + getJmsContentHeaderProperties().getHeaders().setObject(propertyName, object); } public void acknowledge() throws JMSException @@ -518,56 +426,13 @@ public abstract class AbstractJMSMessage extends AMQMessage implements javax.jms buf.append("\nJMS reply to: ").append(String.valueOf(getJMSReplyTo())); buf.append("\nAMQ message number: ").append(_deliveryTag); buf.append("\nProperties:"); - final Iterator it = getJmsContentHeaderProperties().getHeaders().entrySet().iterator(); - if (!it.hasNext()) + if (getJmsContentHeaderProperties().getHeaders().isEmpty()) { buf.append("<NONE>"); } - while (it.hasNext()) + else { - final Map.Entry entry = (Map.Entry) it.next(); - final String propertyName = (String) entry.getKey(); - if (propertyName == null) - { - buf.append("\nInternal error: Property with NULL key defined"); - } - else - { - buf.append('\n').append(propertyName.substring(1)); - - char typeIdentifier = propertyName.charAt(0); - switch (typeIdentifier) - { - case org.apache.qpid.client.message.AbstractJMSMessage.BOOLEAN_PROPERTY_PREFIX: - buf.append("<boolean> "); - break; - case org.apache.qpid.client.message.AbstractJMSMessage.BYTE_PROPERTY_PREFIX: - buf.append("<byte> "); - break; - case org.apache.qpid.client.message.AbstractJMSMessage.SHORT_PROPERTY_PREFIX: - buf.append("<short> "); - break; - case org.apache.qpid.client.message.AbstractJMSMessage.INT_PROPERTY_PREFIX: - buf.append("<int> "); - break; - case org.apache.qpid.client.message.AbstractJMSMessage.LONG_PROPERTY_PREFIX: - buf.append("<long> "); - break; - case org.apache.qpid.client.message.AbstractJMSMessage.FLOAT_PROPERTY_PREFIX: - buf.append("<float> "); - break; - case org.apache.qpid.client.message.AbstractJMSMessage.DOUBLE_PROPERTY_PREFIX: - buf.append("<double> "); - break; - case org.apache.qpid.client.message.AbstractJMSMessage.STRING_PROPERTY_PREFIX: - buf.append("<string> "); - break; - default: - buf.append("<unknown type (identifier " + - typeIdentifier + ") "); - } - buf.append(String.valueOf(entry.getValue())); - } + buf.append('\n').append(getJmsContentHeaderProperties().getHeaders()); } return buf.toString(); } @@ -589,7 +454,6 @@ public abstract class AbstractJMSMessage extends AMQMessage implements javax.jms private void checkPropertyName(String propertyName) { - if (propertyName == null) { throw new IllegalArgumentException("Property name must not be null"); @@ -609,7 +473,7 @@ public abstract class AbstractJMSMessage extends AMQMessage implements javax.jms // We need to convert every property into a String representation // Note that type information is preserved in the property name // - final FieldTable table = new FieldTable(); + final FieldTable table = FieldTableFactory.newFieldTable(); final Iterator entries = getJmsContentHeaderProperties().getHeaders().entrySet().iterator(); while (entries.hasNext()) { diff --git a/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessage.java b/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessage.java index 233f05244f..5282dce4c9 100644 --- a/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessage.java +++ b/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessage.java @@ -23,10 +23,10 @@ package org.apache.qpid.client.message; import org.apache.mina.common.ByteBuffer; import org.apache.qpid.framing.BasicContentHeaderProperties; import org.apache.qpid.framing.PropertyFieldTable; +import org.apache.qpid.framing.FieldTableFactory; import org.apache.qpid.AMQException; import javax.jms.JMSException; -import javax.jms.MessageFormatException; import java.util.Enumeration; public class JMSMapMessage extends JMSTextMessage implements javax.jms.MapMessage @@ -58,7 +58,7 @@ public class JMSMapMessage extends JMSTextMessage implements javax.jms.MapMessag try { - _map = new PropertyFieldTable(getText()); + _map = FieldTableFactory.newFieldTable(getText()); } catch (JMSException e) { @@ -74,7 +74,7 @@ public class JMSMapMessage extends JMSTextMessage implements javax.jms.MapMessag { _data.release(); } - _data = null; + _data = null; } public String toBodyString() throws JMSException diff --git a/java/client/src/main/java/org/apache/qpid/client/security/amqplain/AmqPlainSaslClient.java b/java/client/src/main/java/org/apache/qpid/client/security/amqplain/AmqPlainSaslClient.java index 81d3fb76d5..4291cb3259 100644 --- a/java/client/src/main/java/org/apache/qpid/client/security/amqplain/AmqPlainSaslClient.java +++ b/java/client/src/main/java/org/apache/qpid/client/security/amqplain/AmqPlainSaslClient.java @@ -21,6 +21,7 @@ package org.apache.qpid.client.security.amqplain; import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.FieldTableFactory; import javax.security.sasl.SaslClient; import javax.security.sasl.SaslException; @@ -71,7 +72,7 @@ public class AmqPlainSaslClient implements SaslClient { throw new SaslException("Error handling SASL callbacks: " + e, e); } - FieldTable table = new FieldTable(); + FieldTable table = FieldTableFactory.newFieldTable(); table.put("LOGIN", nameCallback.getName()); table.put("PASSWORD", pwdCallback.getPassword()); return table.getDataAsBytes(); diff --git a/java/client/src/test/java/org/apache/qpid/framing/FieldTableTest.java b/java/client/src/test/java/org/apache/qpid/framing/FieldTableTest.java index 49e1630f15..2a7cb8be30 100644 --- a/java/client/src/test/java/org/apache/qpid/framing/FieldTableTest.java +++ b/java/client/src/test/java/org/apache/qpid/framing/FieldTableTest.java @@ -33,6 +33,33 @@ import junit.framework.TestCase; public class FieldTableTest extends TestCase { + + public void testEncoding() + { + FieldTable table = FieldTableFactory.newFieldTable(); + + String key = "String"; + String value = "Hello"; + table.put(key, value); + + //Add one for the type encoding + int size = EncodingUtils.encodedShortStringLength(key) + 1 + + EncodingUtils.encodedLongStringLength(value); + + assertEquals(table.getEncodedSize(), size); + + key = "Integer"; + Integer number = new Integer(60); + table.put(key, number); + + //Add one for the type encoding + size += EncodingUtils.encodedShortStringLength(key) + 1 + 4; + + + assertEquals(table.getEncodedSize(), size); + } + + public void testDataDump() throws IOException, AMQFrameDecodingException { byte[] data = readBase64("content.txt"); @@ -46,7 +73,7 @@ public class FieldTableTest extends TestCase ByteBuffer buffer = ByteBuffer.allocate(data.length); buffer.put(data); buffer.flip(); - FieldTable table = new FieldTable(buffer, size); + FieldTable table = FieldTableFactory.newFieldTable(buffer, size); } /* @@ -107,7 +134,7 @@ public class FieldTableTest extends TestCase FieldTable load(String name) throws IOException { - return populate(new FieldTable(), read(name)); + return populate(FieldTableFactory.newFieldTable(), read(name)); } Properties read(String name) throws IOException @@ -123,11 +150,12 @@ public class FieldTableTest extends TestCase { String key = (String) i.nextElement(); String value = properties.getProperty(key); - try{ + try + { int ival = Integer.parseInt(value); table.put(key, (long) ival); } - catch(NumberFormatException e) + catch (NumberFormatException e) { table.put(key, value); } @@ -144,7 +172,8 @@ public class FieldTableTest extends TestCase { StringBuffer buffer = new StringBuffer(); String line = in.readLine(); - while (line != null){ + while (line != null) + { buffer.append(line).append(" "); line = in.readLine(); } diff --git a/java/client/src/test/java/org/apache/qpid/headers/MessageFactory.java b/java/client/src/test/java/org/apache/qpid/headers/MessageFactory.java index f1f310c6e5..6f538d068c 100644 --- a/java/client/src/test/java/org/apache/qpid/headers/MessageFactory.java +++ b/java/client/src/test/java/org/apache/qpid/headers/MessageFactory.java @@ -22,6 +22,7 @@ package org.apache.qpid.headers; import org.apache.qpid.client.AMQSession; import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.FieldTableFactory; import javax.jms.BytesMessage; import javax.jms.Destination; @@ -127,14 +128,14 @@ class MessageFactory FieldTable getConsumerBinding() { - FieldTable binding = new FieldTable(); + FieldTable binding = FieldTableFactory.newFieldTable(); binding.put("SF0000", "value"); return binding; } FieldTable getControllerBinding() { - FieldTable binding = new FieldTable(); + FieldTable binding = FieldTableFactory.newFieldTable(); binding.put("SCONTROL", "value"); return binding; } diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/basic/FieldTableKeyEnumeratorTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/basic/FieldTableKeyEnumeratorTest.java index 2e740aa3eb..ad180e3a89 100644 --- a/java/client/src/test/java/org/apache/qpid/test/unit/basic/FieldTableKeyEnumeratorTest.java +++ b/java/client/src/test/java/org/apache/qpid/test/unit/basic/FieldTableKeyEnumeratorTest.java @@ -21,6 +21,7 @@ package org.apache.qpid.test.unit.basic; import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.FieldTableFactory; import org.apache.qpid.client.message.JMSTextMessage; import org.apache.qpid.client.message.TestMessageHelper; @@ -36,7 +37,7 @@ public class FieldTableKeyEnumeratorTest extends TestCase { public void testKeyEnumeration() { - FieldTable result = new FieldTable(); + FieldTable result = FieldTableFactory.newFieldTable(); result.put("one", 1L); result.put("two", 2L); result.put("three", 3L); diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/basic/FieldTableMessageTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/basic/FieldTableMessageTest.java index 67b7f49565..c1ecef6b57 100644 --- a/java/client/src/test/java/org/apache/qpid/test/unit/basic/FieldTableMessageTest.java +++ b/java/client/src/test/java/org/apache/qpid/test/unit/basic/FieldTableMessageTest.java @@ -26,10 +26,12 @@ import org.apache.qpid.client.AMQQueue; import org.apache.qpid.client.AMQSession; import org.apache.qpid.client.message.JMSBytesMessage; import org.apache.qpid.framing.AMQFrameDecodingException; -import org.apache.qpid.framing.FieldTable; import org.apache.qpid.framing.FieldTableTest; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.FieldTableFactory; import org.apache.qpid.test.VMBrokerSetup; import org.apache.mina.common.ByteBuffer; +import org.apache.log4j.Logger; import java.io.IOException; import java.util.ArrayList; @@ -39,6 +41,9 @@ import junit.framework.TestCase; public class FieldTableMessageTest extends TestCase implements MessageListener { + + private static final Logger _logger = Logger.getLogger(FieldTableMessageTest.class); + private AMQConnection _connection; private AMQDestination _destination; private AMQSession _session; @@ -50,7 +55,7 @@ public class FieldTableMessageTest extends TestCase implements MessageListener protected void setUp() throws Exception { super.setUp(); - init(new AMQConnection(_connectionString, "guest", "guest", randomize("Client"), "/test_path")); + init(new AMQConnection(_connectionString, "guest", "guest", randomize("Client"), "/test_path")); } protected void tearDown() throws Exception @@ -80,7 +85,7 @@ public class FieldTableMessageTest extends TestCase implements MessageListener private FieldTable load() throws IOException { - FieldTable result = new FieldTable(); + FieldTable result = FieldTableFactory.newFieldTable(); result.put("one", 1L); result.put("two", 2L); result.put("three", 3L); @@ -128,7 +133,7 @@ public class FieldTableMessageTest extends TestCase implements MessageListener for (Object m : received) { ByteBuffer buffer = ((JMSBytesMessage) m).getData(); - FieldTable actual = new FieldTable(buffer, buffer.remaining()); + FieldTable actual = FieldTableFactory.newFieldTable(buffer, buffer.remaining()); new FieldTableTest().assertEquivalent(_expected, actual); } } diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/basic/FieldTablePropertyTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/basic/FieldTablePropertyTest.java new file mode 100644 index 0000000000..92b4831d93 --- /dev/null +++ b/java/client/src/test/java/org/apache/qpid/test/unit/basic/FieldTablePropertyTest.java @@ -0,0 +1,65 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.unit.basic; + +import org.apache.qpid.framing.PropertyFieldTable; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.client.message.JMSTextMessage; +import org.apache.qpid.client.message.TestMessageHelper; + +import java.util.Enumeration; +import java.util.NoSuchElementException; + +import javax.jms.JMSException; + +import junit.framework.TestCase; + +public class FieldTablePropertyTest extends TestCase +{ + public void testPropertyNames() + { + try + { + JMSTextMessage text = TestMessageHelper.newJMSTextMessage(); + + text.setBooleanProperty("Boolean1", true); + text.setBooleanProperty("Boolean2", true); + text.setIntProperty("Int", 2); + text.setLongProperty("Long", 2); + + Enumeration e = text.getPropertyNames(); + + assertEquals("Boolean1", e.nextElement()); + assertTrue("Boolean2".equals(e.nextElement())); + assertTrue("Int".equals(e.nextElement())); + assertTrue("Long".equals(e.nextElement())); + } + catch (JMSException e) + { + + } + } + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(FieldTablePropertyTest.class); + } +} diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/basic/MapMessageTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/basic/MapMessageTest.java index a685e75a29..5353a19d13 100644 --- a/java/client/src/test/java/org/apache/qpid/test/unit/basic/MapMessageTest.java +++ b/java/client/src/test/java/org/apache/qpid/test/unit/basic/MapMessageTest.java @@ -47,6 +47,7 @@ public class MapMessageTest extends TestCase implements MessageListener private final List<String> messages = new ArrayList<String>(); private int _count = 100; public String _connectionString = "vm://:1"; + private byte[] _bytes = {99, 98, 97, 96, 95}; protected void setUp() throws Exception { @@ -104,9 +105,31 @@ public class MapMessageTest extends TestCase implements MessageListener MapMessage message = _session.createMapMessage(); message.setBoolean("odd", i / 2 == 0); + message.setByte("byte", (byte) Byte.MAX_VALUE); + + message.setBytes("bytes", _bytes); + message.setChar("char", (char) 'c'); + message.setDouble("double", (double) Double.MAX_VALUE); + message.setFloat("float", (float) Float.MAX_VALUE); + message.setInt("messageNumber", i); + message.setInt("int", (int) Integer.MAX_VALUE); + + message.setLong("long", (long) Long.MAX_VALUE); + message.setShort("short", (short) Short.MAX_VALUE); message.setString("message", text); + + message.setObject("object-bool", true); + message.setObject("object-byte", Byte.MAX_VALUE); + message.setObject("object-bytes", _bytes); + message.setObject("object-char", 'c'); + message.setObject("object-double", Double.MAX_VALUE); + message.setObject("object-float", Float.MAX_VALUE); + message.setObject("object-int", Integer.MAX_VALUE); + message.setObject("object-long", Long.MAX_VALUE); + message.setObject("object-short", Short.MAX_VALUE); + producer.send(message); } } @@ -130,7 +153,31 @@ public class MapMessageTest extends TestCase implements MessageListener { actual.add(m.getString("message")); assertEqual(m.getInt("messageNumber"), count); - assertEqual(m.getBoolean("odd"), count / 2 == 0); + + + assertEqual(count / 2 == 0, m.getBoolean("odd")); + assertEqual((byte) Byte.MAX_VALUE, m.getByte("byte")); + + assertBytesEqual(_bytes, m.getBytes("bytes")); + assertEqual((char) 'c', m.getChar("char")); + assertEqual((double) Double.MAX_VALUE, m.getDouble("double")); + assertEqual((float) Float.MAX_VALUE, m.getFloat("float")); + + assertEqual(count, m.getInt("messageNumber")); + assertEqual((int) Integer.MAX_VALUE, m.getInt("int")); + assertEqual((long) Long.MAX_VALUE, m.getLong("long")); + assertEqual((short) Short.MAX_VALUE, m.getShort("short")); + + assertEqual(true, m.getObject("object-bool")); + assertEqual(Byte.MAX_VALUE, m.getObject("object-byte")); + assertBytesEqual(_bytes, (byte[]) m.getObject("object-bytes")); + assertEqual('c', m.getObject("object-char")); + assertEqual(Double.MAX_VALUE, m.getObject("object-double")); + assertEqual(Float.MAX_VALUE, m.getObject("object-float")); + assertEqual(Integer.MAX_VALUE, m.getObject("object-int")); + assertEqual(Long.MAX_VALUE, m.getObject("object-long")); + assertEqual(Short.MAX_VALUE, m.getObject("object-short")); + try { @@ -153,7 +200,7 @@ public class MapMessageTest extends TestCase implements MessageListener Assert.fail("Message should be writeable"); } - //Check property write status + //Check property write status try { m.setStringProperty("test", "test"); @@ -181,6 +228,17 @@ public class MapMessageTest extends TestCase implements MessageListener assertEqual(messages.iterator(), actual.iterator()); } + private void assertBytesEqual(byte[] expected, byte[] actual) + { + Assert.assertEquals(expected.length, actual.length); + + for (int index = 0; index < expected.length; index++) + { + Assert.assertEquals(expected[index], actual[index]); + } + } + + private static void assertEqual(Iterator expected, Iterator actual) { List<String> errors = new ArrayList<String>(); diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/basic/PropertyValueTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/basic/PropertyValueTest.java index de7e12ac61..02f371e81b 100644 --- a/java/client/src/test/java/org/apache/qpid/test/unit/basic/PropertyValueTest.java +++ b/java/client/src/test/java/org/apache/qpid/test/unit/basic/PropertyValueTest.java @@ -20,32 +20,37 @@ */ package org.apache.qpid.test.unit.basic; +import junit.framework.Assert; +import junit.framework.TestCase; +import org.apache.log4j.Logger; import org.apache.qpid.client.AMQConnection; import org.apache.qpid.client.AMQQueue; import org.apache.qpid.client.AMQSession; import org.apache.qpid.client.message.JMSTextMessage; import org.apache.qpid.test.VMBrokerSetup; -import org.apache.log4j.Logger; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Queue; import java.util.ArrayList; import java.util.Iterator; import java.util.List; -import javax.jms.*; - -import junit.framework.TestCase; -import junit.framework.Assert; public class PropertyValueTest extends TestCase implements MessageListener { private static final Logger _logger = Logger.getLogger(PropertyValueTest.class); + private int count = 0; private AMQConnection _connection; private Destination _destination; private AMQSession _session; private final List<JMSTextMessage> received = new ArrayList<JMSTextMessage>(); private final List<String> messages = new ArrayList<String>(); - private int _count = 1;//100; + private int _count = 100; public String _connectionString = "vm://:1"; protected void setUp() throws Exception @@ -104,25 +109,31 @@ public class PropertyValueTest extends TestCase implements MessageListener Message m = _session.createTextMessage(text); m.setBooleanProperty("Bool", true); -// m.setByteProperty("Byte", (byte) Byte.MAX_VALUE); -// m.setDoubleProperty("Double", (double) Double.MAX_VALUE); -// m.setFloatProperty("Float", (float) Float.MAX_VALUE); -// m.setIntProperty("Int", (int) Integer.MAX_VALUE); + + m.setByteProperty("Byte", (byte) Byte.MAX_VALUE); + m.setDoubleProperty("Double", (double) Double.MAX_VALUE); + m.setFloatProperty("Float", (float) Float.MAX_VALUE); + m.setIntProperty("Int", (int) Integer.MAX_VALUE); + m.setJMSCorrelationID("Correlation"); m.setJMSPriority(100); - // Set Q - //m.setJMSReplyTo(_session.Queue("TestReply")); - - // Temporary Queue - Queue q = _session.createTemporaryQueue(); - m.setJMSReplyTo(q);//Queue("TestReply")); + // Queue + Queue q = //_session.createTemporaryQueue(); + q = new AMQQueue("TestReply"); + m.setJMSReplyTo(q); m.setStringProperty("TempQueue", q.toString()); -// m.setJMSType("Test"); + + _logger.info("Message:" + m); + + Assert.assertEquals("Check temp queue has been set correctly", + m.getJMSReplyTo().toString(), m.getStringProperty("TempQueue")); + + m.setJMSType("Test"); m.setLongProperty("UnsignedInt", (long) 4294967295L); -// m.setLongProperty("Long", (long) Long.MAX_VALUE); + m.setLongProperty("Long", (long) Long.MAX_VALUE); -// m.setShortProperty("Short", (short) Short.MAX_VALUE); + m.setShortProperty("Short", (short) Short.MAX_VALUE); m.setStringProperty("String", "Test"); _logger.info("Sending Msg:" + m); @@ -152,35 +163,31 @@ public class PropertyValueTest extends TestCase implements MessageListener Assert.assertEquals("Check Boolean properties are correctly transported", true, m.getBooleanProperty("Bool")); -// Assert.assertEquals("Check Byte properties are correctly transported", -// (byte) Byte.MAX_VALUE, m.getByteProperty("Byte")); -// Assert.assertEquals("Check Double properties are correctly transported", -// (double) Double.MAX_VALUE, m.getDoubleProperty("Double")); -// Assert.assertEquals("Check Float properties are correctly transported", -// (float) Float.MAX_VALUE, m.getFloatProperty("Float")); -// Assert.assertEquals("Check Int properties are correctly transported", -// (int) Integer.MAX_VALUE, m.getIntProperty("Int")); + Assert.assertEquals("Check Byte properties are correctly transported", + (byte) Byte.MAX_VALUE, m.getByteProperty("Byte")); + Assert.assertEquals("Check Double properties are correctly transported", + (double) Double.MAX_VALUE, m.getDoubleProperty("Double")); + Assert.assertEquals("Check Float properties are correctly transported", + (float) Float.MAX_VALUE, m.getFloatProperty("Float")); + Assert.assertEquals("Check Int properties are correctly transported", + (int) Integer.MAX_VALUE, m.getIntProperty("Int")); Assert.assertEquals("Check CorrelationID properties are correctly transported", "Correlation", m.getJMSCorrelationID()); // Assert.assertEquals("Check Priority properties are correctly transported", // 100, m.getJMSPriority()); - //Set Queue -// Assert.assertEquals("Check ReplyTo properties are correctly transported", -// _session.createQueue("TestReply"), m.getJMSReplyTo()); - - //Temporary Queue + // Queue Assert.assertEquals("Check ReplyTo properties are correctly transported", m.getStringProperty("TempQueue"), m.getJMSReplyTo().toString()); // Assert.assertEquals("Check Type properties are correctly transported", // "Test", m.getJMSType()); -// Assert.assertEquals("Check Short properties are correctly transported", -// (short) Short.MAX_VALUE, m.getShortProperty("Short")); + Assert.assertEquals("Check Short properties are correctly transported", + (short) Short.MAX_VALUE, m.getShortProperty("Short")); Assert.assertEquals("Check UnsignedInt properties are correctly transported", (long) 4294967295L, m.getLongProperty("UnsignedInt")); -// Assert.assertEquals("Check Long properties are correctly transported", -// (long)Long.MAX_VALUE, m.getLongProperty("Long")); + Assert.assertEquals("Check Long properties are correctly transported", + (long) Long.MAX_VALUE, m.getLongProperty("Long")); Assert.assertEquals("Check String properties are correctly transported", "Test", m.getStringProperty("String")); } diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/basic/TextMessageTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/basic/TextMessageTest.java index dfbec81549..cd3954fbcb 100644 --- a/java/client/src/test/java/org/apache/qpid/test/unit/basic/TextMessageTest.java +++ b/java/client/src/test/java/org/apache/qpid/test/unit/basic/TextMessageTest.java @@ -28,6 +28,7 @@ import org.apache.qpid.client.vmbroker.AMQVMBrokerCreationException; import org.apache.qpid.client.transport.TransportConnection; import org.apache.qpid.client.message.JMSTextMessage; import org.apache.qpid.test.VMBrokerSetup; +import org.apache.log4j.Logger; import java.util.ArrayList; import java.util.Iterator; @@ -39,6 +40,8 @@ import junit.framework.Assert; public class TextMessageTest extends TestCase implements MessageListener { + private static final Logger _logger = Logger.getLogger(TextMessageTest.class); + private AMQConnection _connection; private Destination _destination; private AMQSession _session; @@ -100,7 +103,11 @@ public class TextMessageTest extends TestCase implements MessageListener { String text = "Message " + i; messages.add(text); - producer.send(_session.createTextMessage(text)); + Message m = _session.createTextMessage(text); + m.setStringProperty("String", "hello"); + + _logger.info("Sending Msg:" + m); + producer.send(m); } } diff --git a/java/common/src/main/java/org/apache/qpid/framing/BasicContentHeaderProperties.java b/java/common/src/main/java/org/apache/qpid/framing/BasicContentHeaderProperties.java index 6a4c385033..a908c76286 100644 --- a/java/common/src/main/java/org/apache/qpid/framing/BasicContentHeaderProperties.java +++ b/java/common/src/main/java/org/apache/qpid/framing/BasicContentHeaderProperties.java @@ -435,7 +435,7 @@ public class BasicContentHeaderProperties implements ContentHeaderProperties if (_headers == null) { - setHeaders(new FieldTable()); + setHeaders(FieldTableFactory.newFieldTable()); } return _headers; diff --git a/java/common/src/main/java/org/apache/qpid/framing/EncodingUtils.java b/java/common/src/main/java/org/apache/qpid/framing/EncodingUtils.java index 3a683b8e90..97fb434e1c 100644 --- a/java/common/src/main/java/org/apache/qpid/framing/EncodingUtils.java +++ b/java/common/src/main/java/org/apache/qpid/framing/EncodingUtils.java @@ -286,7 +286,7 @@ public class EncodingUtils } else { - return new FieldTable(buffer, length); + return FieldTableFactory.newFieldTable(buffer, length); } } @@ -330,9 +330,9 @@ public class EncodingUtils // than constructing one from a char array. // this approach here is valid since we know that all the chars are // ASCII (0-127) - byte[] stringBytes = new byte[(int)length]; - buffer.get(stringBytes, 0, (int)length); - char[] stringChars = new char[(int)length]; + byte[] stringBytes = new byte[(int) length]; + buffer.get(stringBytes, 0, (int) length); + char[] stringChars = new char[(int) length]; for (int i = 0; i < stringChars.length; i++) { stringChars[i] = (char) stringBytes[i]; @@ -350,7 +350,7 @@ public class EncodingUtils } else { - byte[] result = new byte[(int)length]; + byte[] result = new byte[(int) length]; buffer.get(result); return result; } @@ -388,7 +388,7 @@ public class EncodingUtils // TODO: Doesn't support embedded quotes properly. String[] expressions = selector.split(" +"); - FieldTable result = new FieldTable(); + FieldTable result = FieldTableFactory.newFieldTable(); for (int i = 0; i < expressions.length; i++) { @@ -481,7 +481,7 @@ public class EncodingUtils public static char[] convertToHexCharArray(byte[] from) { int length = from.length; - char[] result_buff = new char[length * 2 + 2]; + char[] result_buff = new char[length * 2 + 2]; result_buff[0] = '0'; result_buff[1] = 'x'; diff --git a/java/common/src/main/java/org/apache/qpid/framing/FieldTable.java b/java/common/src/main/java/org/apache/qpid/framing/FieldTable.java index 796e1843af..44d0268561 100644 --- a/java/common/src/main/java/org/apache/qpid/framing/FieldTable.java +++ b/java/common/src/main/java/org/apache/qpid/framing/FieldTable.java @@ -1,317 +1,96 @@ /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. * + * */ package org.apache.qpid.framing; -import org.apache.log4j.Logger; import org.apache.mina.common.ByteBuffer; -import java.util.*; - -/** - * From the protocol document: - * field-table = short-integer *field-value-pair - * field-value-pair = field-name field-value - * field-name = short-string - * field-value = 'S' long-string - * / 'I' long-integer - * / 'D' decimal-value - * / 'T' long-integer - * decimal-value = decimals long-integer - * decimals = OCTET - */ -public class FieldTable extends LinkedHashMap +import java.util.Map; +import java.util.Enumeration; + +public interface FieldTable extends Map { - private static final Logger _logger = Logger.getLogger(FieldTable.class); - private long _encodedSize = 0; - - public FieldTable() - { - super(); - } - - /** - * Construct a new field table. - * - * @param buffer the buffer from which to read data. The length byte must be read already - * @param length the length of the field table. Must be > 0. - * @throws AMQFrameDecodingException if there is an error decoding the table - */ - public FieldTable(ByteBuffer buffer, long length) throws AMQFrameDecodingException - { - super(); - final boolean debug = _logger.isDebugEnabled(); - assert length > 0; - _encodedSize = length; - int sizeRead = 0; - while (sizeRead < _encodedSize) - { - int sizeRemaining = buffer.remaining(); - final String key = EncodingUtils.readShortString(buffer); - // TODO: use proper charset decoder - byte iType = buffer.get(); - final char type = (char) iType; - Object value; - switch (type) - { - case'S': - value = EncodingUtils.readLongString(buffer); - break; - case'I': - value = new Long(buffer.getUnsignedInt()); - break; - default: - String msg = "Field '" + key + "' - unsupported field table type: " + type; - //some extra debug information... - msg += " (" + iType + "), length=" + length + ", sizeRead=" + sizeRead + ", sizeRemaining=" + sizeRemaining; - throw new AMQFrameDecodingException(msg); - } - sizeRead += (sizeRemaining - buffer.remaining()); - - if (debug) - { - _logger.debug("FieldTable::FieldTable(buffer," + length + "): Read type '" + type + "', key '" + key + "', value '" + value + "' (now read " + sizeRead + " of " + length + " encoded bytes)..."); - } - - // we deliberately want to call put in the parent class since we do - // not need to do the size calculations - super.put(key, value); - } - - if (debug) - { - _logger.debug("FieldTable::FieldTable(buffer," + length + "): Done."); - } - } - - public void writeToBuffer(ByteBuffer buffer) - { - final boolean debug = _logger.isDebugEnabled(); - - if (debug) - { - _logger.debug("FieldTable::writeToBuffer: Writing encoded size of " + _encodedSize + "..."); - } - - // write out the total length, which we have kept up to date as data is added - EncodingUtils.writeUnsignedInteger(buffer, _encodedSize); - final Iterator it = this.entrySet().iterator(); - while (it.hasNext()) - { - Map.Entry me = (Map.Entry) it.next(); - String key = (String) me.getKey(); - - EncodingUtils.writeShortStringBytes(buffer, key); - Object value = me.getValue(); - - if (debug) - { - _logger.debug("FieldTable::writeToBuffer: Writing key '" + key + "' of type " + value.getClass() + ", value '" + value + "'..."); - } - - if (value instanceof byte[]) - { - buffer.put((byte) 'S'); - EncodingUtils.writeLongstr(buffer, (byte[]) value); - } - else if (value instanceof String) - { - // TODO: look at using proper charset encoder - buffer.put((byte) 'S'); - EncodingUtils.writeLongStringBytes(buffer, (String) value); - } - else if (value instanceof Long) - { - // TODO: look at using proper charset encoder - buffer.put((byte) 'I'); - EncodingUtils.writeUnsignedInteger(buffer, ((Long) value).longValue()); - } - else - { - // Should never get here - throw new IllegalArgumentException("Key '" + key + "': Unsupported type in field table, type: " + ((value == null) ? "null-object" : value.getClass())); - } - } - - if (debug) - { - _logger.debug("FieldTable::writeToBuffer: Done."); - } - } - - public byte[] getDataAsBytes() - { - final ByteBuffer buffer = ByteBuffer.allocate((int) _encodedSize); // XXX: Is cast a problem? - final Iterator it = this.entrySet().iterator(); - while (it.hasNext()) - { - Map.Entry me = (Map.Entry) it.next(); - String key = (String) me.getKey(); - EncodingUtils.writeShortStringBytes(buffer, key); - Object value = me.getValue(); - if (value instanceof byte[]) - { - buffer.put((byte) 'S'); - EncodingUtils.writeLongstr(buffer, (byte[]) value); - } - else if (value instanceof String) - { - // TODO: look at using proper charset encoder - buffer.put((byte) 'S'); - EncodingUtils.writeLongStringBytes(buffer, (String) value); - } - else if (value instanceof char[]) - { - // TODO: look at using proper charset encoder - buffer.put((byte) 'S'); - EncodingUtils.writeLongStringBytes(buffer, (char[]) value); - } - else if (value instanceof Long || value instanceof Integer) - { - // TODO: look at using proper charset encoder - buffer.put((byte) 'I'); - EncodingUtils.writeUnsignedInteger(buffer, ((Long) value).longValue()); - } - else - { - // Should never get here - assert false; - } - } - final byte[] result = new byte[(int) _encodedSize]; - buffer.flip(); - buffer.get(result); - buffer.release(); - return result; - } - - public Object put(Object key, Object value) - { - final boolean debug = _logger.isDebugEnabled(); - - if (key == null) - { - throw new IllegalArgumentException("All keys must be Strings - was passed: null"); - } - else if (!(key instanceof String)) - { - throw new IllegalArgumentException("All keys must be Strings - was passed: " + key.getClass()); - } - - Object existing; - - if ((existing = super.remove(key)) != null) - { - if (debug) - { - _logger.debug("Found duplicate of key '" + key + "', previous value '" + existing + "' (" + existing.getClass() + "), to be replaced by '" + value + "', (" + value.getClass() + ") - stack trace of source of duplicate follows...", new Throwable().fillInStackTrace()); - } - - // If we are in effect deleting the value (see comment on null values being deleted - // below) then we also need to remove the name from the encoding length. - if (value == null) - { - _encodedSize -= EncodingUtils.encodedShortStringLength((String) key); - } - - // FIXME: Should be able to short-cut this process if the old and new values are - // the same object and/or type and size... - _encodedSize -= getEncodingSize(existing); - } - else - { - if (value != null) - { - _encodedSize += EncodingUtils.encodedShortStringLength((String) key); - } - } - - // For now: Setting a null value is the equivalent of deleting it. - // This is ambiguous in the JMS spec and needs thrashing out and potentially - // testing against other implementations. - if (value != null) - { - _encodedSize += getEncodingSize(value); - } - - return super.put(key, value); - } - - public Object remove(Object key) - { - if (super.containsKey(key)) - { - final Object value = super.remove(key); - _encodedSize -= EncodingUtils.encodedShortStringLength((String) key); - - // This check is, for now, unnecessary (we don't store null values). - if (value != null) - { - _encodedSize -= getEncodingSize(value); - } - - return value; - } - else - { - return null; - } - } - - /** - * @return unsigned integer - */ - public long getEncodedSize() - { - return _encodedSize; - } - - /** - * @return integer - */ - private static int getEncodingSize(Object value) - { - int encodingSize; - - // the extra byte if for the type indicator that is written out - if (value instanceof String) - { - encodingSize = 1 + EncodingUtils.encodedLongStringLength((String) value); - } - else if (value instanceof char[]) - { - encodingSize = 1 + EncodingUtils.encodedLongStringLength((char[]) value); - } - else if (value instanceof Integer) - { - encodingSize = 1 + 4; - } - else if (value instanceof Long) - { - encodingSize = 1 + 4; - } - else - { - throw new IllegalArgumentException("Unsupported type in field table: " + value.getClass()); - } - - return encodingSize; - } + void writeToBuffer(ByteBuffer buffer); + + void setFromBuffer(ByteBuffer buffer, long length) throws AMQFrameDecodingException; + + byte[] getDataAsBytes(); + + public long getEncodedSize(); + + Object put(Object key, Object value); + + Object remove(Object key); + + + public Enumeration getPropertyNames(); + + public boolean propertyExists(String propertyName); + + //Getters + + public Boolean getBoolean(String string); + + public Byte getByte(String string); + + public Short getShort(String string); + + public Integer getInteger(String string); + + public Long getLong(String string); + + public Float getFloat(String string); + + public Double getDouble(String string); + + public String getString(String string); + + public Character getCharacter(String string); + + public byte[] getBytes(String string); + + public Object getObject(String string); + + // Setters + public Object setBoolean(String string, boolean b); + + public Object setByte(String string, byte b); + + public Object setShort(String string, short i); + + public Object setInteger(String string, int i); + + public Object setLong(String string, long l); + + public Object setFloat(String string, float v); + + public Object setDouble(String string, double v); + + public Object setString(String string, String string1); + + public Object setChar(String string, char c); + + public Object setBytes(String string, byte[] bytes); + + public Object setBytes(String string, byte[] bytes, int start, int length); + + public Object setObject(String string, Object object); + } diff --git a/java/common/src/main/java/org/apache/qpid/framing/FieldTableFactory.java b/java/common/src/main/java/org/apache/qpid/framing/FieldTableFactory.java new file mode 100644 index 0000000000..1ec57da35b --- /dev/null +++ b/java/common/src/main/java/org/apache/qpid/framing/FieldTableFactory.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.framing; + +import org.apache.mina.common.ByteBuffer; + +public class FieldTableFactory +{ + public static FieldTable newFieldTable() + { + return new PropertyFieldTable(); + } + + public static FieldTable newFieldTable(ByteBuffer byteBuffer, long length) throws AMQFrameDecodingException + { + return new PropertyFieldTable(byteBuffer, length); + } + + public static PropertyFieldTable newFieldTable(String text) + { + return new PropertyFieldTable(text); + } +} diff --git a/java/common/src/main/java/org/apache/qpid/framing/FieldTableKeyEnumeration.java b/java/common/src/main/java/org/apache/qpid/framing/FieldTableKeyEnumeration.java deleted file mode 100644 index e3ba9080c7..0000000000 --- a/java/common/src/main/java/org/apache/qpid/framing/FieldTableKeyEnumeration.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.framing; - -import java.util.Enumeration; -import java.util.Iterator; - - -public class FieldTableKeyEnumeration implements Enumeration -{ - protected FieldTable _table; - protected Iterator _iterator; - - public FieldTableKeyEnumeration(FieldTable ft) - { - _table = ft; - _iterator = ft.keySet().iterator(); - } - - public boolean hasMoreElements() - { - return _iterator.hasNext(); - } - - public Object nextElement() - { - return _iterator.next(); - } -} diff --git a/java/common/src/main/java/org/apache/qpid/framing/PropertyFieldTable.java b/java/common/src/main/java/org/apache/qpid/framing/PropertyFieldTable.java index 96360e4aaa..36558011ac 100644 --- a/java/common/src/main/java/org/apache/qpid/framing/PropertyFieldTable.java +++ b/java/common/src/main/java/org/apache/qpid/framing/PropertyFieldTable.java @@ -21,20 +21,28 @@ package org.apache.qpid.framing; import org.apache.log4j.Logger; +import org.apache.mina.common.ByteBuffer; +import java.util.Collection; import java.util.Enumeration; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; +import java.util.Set; import java.util.StringTokenizer; import java.util.Vector; //extends FieldTable -public class PropertyFieldTable +public class PropertyFieldTable implements FieldTable, Map { - private static final Logger _logger = Logger.getLogger(PropertyFieldTable.class); + + public static final char AMQP_DECIMAL_PROPERTY_PREFIX = 'D'; + public static final char AMQP_UNSIGNEDINT_PROPERTY_PREFIX = 'I'; + public static final char AMQP_TIMESTAMP_PROPERTY_PREFIX = 'T'; + public static final char AMQP_STRING_PROPERTY_PREFIX = 'S'; + public static final char BOOLEAN_PROPERTY_PREFIX = 'B'; public static final char BYTE_PROPERTY_PREFIX = 'b'; public static final char SHORT_PROPERTY_PREFIX = 's'; @@ -42,10 +50,12 @@ public class PropertyFieldTable public static final char LONG_PROPERTY_PREFIX = 'l'; public static final char FLOAT_PROPERTY_PREFIX = 'f'; public static final char DOUBLE_PROPERTY_PREFIX = 'd'; - public static final char STRING_PROPERTY_PREFIX = 'S'; + public static final char STRING_PROPERTY_PREFIX = AMQP_STRING_PROPERTY_PREFIX; public static final char CHAR_PROPERTY_PREFIX = 'c'; public static final char BYTES_PROPERTY_PREFIX = 'y'; + //Our custom prefix for encoding across the wire + private static final char XML_PROPERTY_PREFIX = 'X'; private static final String BOOLEAN = "boolean"; private static final String BYTE = "byte"; @@ -66,7 +76,7 @@ public class PropertyFieldTable private LinkedHashMap<String, Object> _properties; private LinkedHashMap<String, String> _propertyNamesTypeMap; - + private long _encodedSize = 0; public PropertyFieldTable() { @@ -84,73 +94,167 @@ public class PropertyFieldTable } catch (Exception e) { - System.out.println(textFormat); - e.printStackTrace(); + _logger.error("Unable to decode PropertyFieldTable format:" + textFormat, e); } + } + /** + * Construct a new field table. + * + * @param buffer the buffer from which to read data. The length byte must be read already + * @param length the length of the field table. Must be > 0. + * @throws AMQFrameDecodingException if there is an error decoding the table + */ + public PropertyFieldTable(ByteBuffer buffer, long length) throws AMQFrameDecodingException + { + this(); + setFromBuffer(buffer, length); } // ************ Getters + private Object get(String propertyName, char prefix) + { + String type = _propertyNamesTypeMap.get(propertyName); + + if (type == null) + { + return null; + } + + if (type.equals("" + prefix)) + { + return _properties.get(propertyName); + } + else + { + return null; + } + } + public Boolean getBoolean(String string) { - return (Boolean) _properties.get(BOOLEAN_PROPERTY_PREFIX + string); + Object o = get(string, BOOLEAN_PROPERTY_PREFIX); + if (o != null) + { + return (Boolean) o; + } + else + { + return null; + } } public Byte getByte(String string) { - return (Byte) _properties.get(BYTE_PROPERTY_PREFIX + string); + Object o = get(string, BYTE_PROPERTY_PREFIX); + if (o != null) + { + return (Byte) o; + } + else + { + return null; + } } public Short getShort(String string) { - return (Short) _properties.get(SHORT_PROPERTY_PREFIX + string); + Object o = get(string, SHORT_PROPERTY_PREFIX); + if (o != null) + { + return (Short) o; + } + else + { + return null; + } } public Integer getInteger(String string) { - return (Integer) _properties.get(INT_PROPERTY_PREFIX + string); + Object o = get(string, INT_PROPERTY_PREFIX); + if (o != null) + { + return (Integer) o; + } + else + { + return null; + } } public Long getLong(String string) { - return (Long) _properties.get(LONG_PROPERTY_PREFIX + string); + Object o = get(string, LONG_PROPERTY_PREFIX); + if (o != null) + { + return (Long) o; + } + else + { + return null; + } } public Float getFloat(String string) { - return (Float) _properties.get(FLOAT_PROPERTY_PREFIX + string); + Object o = get(string, FLOAT_PROPERTY_PREFIX); + if (o != null) + { + return (Float) o; + } + else + { + return null; + } } public Double getDouble(String string) { - return (Double) _properties.get(DOUBLE_PROPERTY_PREFIX + string); + Object o = get(string, DOUBLE_PROPERTY_PREFIX); + if (o != null) + { + return (Double) o; + } + else + { + return null; + } } public String getString(String string) { - return (String) _properties.get(STRING_PROPERTY_PREFIX + string); + Object o = get(string, STRING_PROPERTY_PREFIX); + if (o != null) + { + return (String) o; + } + else + { + return null; + } } public Character getCharacter(String string) { - return (Character) _properties.get(CHAR_PROPERTY_PREFIX + string); + Object o = get(string, CHAR_PROPERTY_PREFIX); + if (o != null) + { + return (Character) o; + } + else + { + return null; + } } public byte[] getBytes(String string) { - return (byte[]) _properties.get(BYTES_PROPERTY_PREFIX + string); - } - - public Object getObject(String string) - { - String typestring = _propertyNamesTypeMap.get(string); - - if (typestring != null && !typestring.equals("")) + Object o = get(string, BYTES_PROPERTY_PREFIX); + if (o != null) { - char type = typestring.charAt(0); - - return _properties.get(type + string); + return (byte[]) o; } else { @@ -158,91 +262,66 @@ public class PropertyFieldTable } } - // ************ Setters - - - public void setBoolean(String string, boolean b) + public Object getObject(String string) { - checkPropertyName(string, BOOLEAN_PROPERTY_PREFIX); + return _properties.get(string); + } + // ************ Setters - _propertyNamesTypeMap.put(string, "" + BOOLEAN_PROPERTY_PREFIX); - _properties.put(BOOLEAN_PROPERTY_PREFIX + string, b);// ? new Long(1) : new Long(0)); + public Object setBoolean(String string, boolean b) + { + return put(BOOLEAN_PROPERTY_PREFIX + string, b); } - public void setByte(String string, byte b) + public Object setByte(String string, byte b) { - checkPropertyName(string, BYTE_PROPERTY_PREFIX); - - - _properties.put(BYTE_PROPERTY_PREFIX + string, b); + return put(BYTE_PROPERTY_PREFIX + string, b); } - public void setShort(String string, short i) + public Object setShort(String string, short i) { - checkPropertyName(string, SHORT_PROPERTY_PREFIX); - - - _properties.put(SHORT_PROPERTY_PREFIX + string, i); + return put(SHORT_PROPERTY_PREFIX + string, i); } - public void setInteger(String string, int i) + public Object setInteger(String string, int i) { - checkPropertyName(string, INT_PROPERTY_PREFIX); - - - _properties.put(INT_PROPERTY_PREFIX + string, i); + return put(INT_PROPERTY_PREFIX + string, i); } - public void setLong(String string, long l) + public Object setLong(String string, long l) { - checkPropertyName(string, LONG_PROPERTY_PREFIX); - - - _properties.put(LONG_PROPERTY_PREFIX + string, l); + return put(LONG_PROPERTY_PREFIX + string, l); } - public void setFloat(String string, float v) + public Object setFloat(String string, float v) { - checkPropertyName(string, FLOAT_PROPERTY_PREFIX); - - - _properties.put(FLOAT_PROPERTY_PREFIX + string, v); + return put(FLOAT_PROPERTY_PREFIX + string, v); } - public void setDouble(String string, double v) + public Object setDouble(String string, double v) { - checkPropertyName(string, DOUBLE_PROPERTY_PREFIX); - - - _properties.put(DOUBLE_PROPERTY_PREFIX + string, v); + return put(DOUBLE_PROPERTY_PREFIX + string, v); } - public void setString(String string, String string1) + public Object setString(String string, String string1) { - checkPropertyName(string, STRING_PROPERTY_PREFIX); - - - _properties.put(STRING_PROPERTY_PREFIX + string, string1); + return put(STRING_PROPERTY_PREFIX + string, string1); } - public void setChar(String string, char c) + public Object setChar(String string, char c) { - checkPropertyName(string, CHAR_PROPERTY_PREFIX); - - _properties.put(CHAR_PROPERTY_PREFIX + string, c); + return put(CHAR_PROPERTY_PREFIX + string, c); } - public void setBytes(String string, byte[] bytes) + public Object setBytes(String string, byte[] bytes) { - setBytes(string, bytes, 0, bytes.length); + return setBytes(string, bytes, 0, bytes.length); } - public void setBytes(String string, byte[] bytes, int start, int length) + public Object setBytes(String string, byte[] bytes, int start, int length) { - checkPropertyName(string, BYTES_PROPERTY_PREFIX); - - _properties.put(BYTES_PROPERTY_PREFIX + string, sizeByteArray(bytes, start, length)); + return put(BYTES_PROPERTY_PREFIX + string, sizeByteArray(bytes, start, length)); } private byte[] sizeByteArray(byte[] bytes, int start, int length) @@ -259,65 +338,65 @@ public class PropertyFieldTable } - public void setObject(String string, Object object) + public Object setObject(String string, Object object) { if (object instanceof Boolean) { - setBoolean(string, (Boolean) object); + return setBoolean(string, (Boolean) object); } else { if (object instanceof Byte) { - setByte(string, (Byte) object); + return setByte(string, (Byte) object); } else { if (object instanceof Short) { - setShort(string, (Short) object); + return setShort(string, (Short) object); } else { if (object instanceof Integer) { - setInteger(string, (Integer) object); + return setInteger(string, (Integer) object); } else { if (object instanceof Long) { - setLong(string, (Long) object); + return setLong(string, (Long) object); } else { if (object instanceof Float) { - setFloat(string, (Float) object); + return setFloat(string, (Float) object); } else { if (object instanceof Double) { - setDouble(string, (Double) object); + return setDouble(string, (Double) object); } else { if (object instanceof String) { - setString(string, (String) object); + return setString(string, (String) object); } else { if (object instanceof Character) { - setChar(string, (Character) object); + return setChar(string, (Character) object); } else { if (object instanceof byte[]) { - setBytes(string, (byte[]) object); + return setBytes(string, (byte[]) object); } } } @@ -328,8 +407,7 @@ public class PropertyFieldTable } } } - - + return null; } // ***** Methods @@ -344,12 +422,16 @@ public class PropertyFieldTable { String key = (String) keys.next(); - names.add(key.substring(1)); + names.add(key); } return names.elements(); } + public boolean propertyExists(String propertyName) + { + return _propertyNamesTypeMap.containsKey(propertyName); + } public boolean itemExists(String string) { @@ -367,7 +449,6 @@ public class PropertyFieldTable return false; } - public String toString() { return valueOf(this); @@ -390,35 +471,55 @@ public class PropertyFieldTable else { buf.append('\n'); - buf.append(propertyXML(propertyName, true)); - if (propertyName.charAt(0) == BYTES_PROPERTY_PREFIX) - { - //remove '>' - buf.deleteCharAt(buf.length() - 1); + buf.append(valueAsXML(table._propertyNamesTypeMap.get(propertyName) + propertyName, entry.getValue())); + } + } + buf.append("\n"); + buf.append(PROPERTY_FIELD_TABLE_CLOSE_XML); - byte[] bytes = (byte[]) entry.getValue(); - buf.append(" length='").append(bytes.length).append("'>"); + return buf.toString(); + } - buf.append(byteArrayToXML(propertyName.substring(1), bytes)); - } - else - { + private static String valueAsXML(String name, Object value) + { + char propertyPrefix = name.charAt(0); + String propertyName = name.substring(1); - buf.append(String.valueOf(entry.getValue())); - } - buf.append(propertyXML(propertyName, false)); - } + StringBuffer buf = new StringBuffer(); + // Start Tag + buf.append(propertyXML(name, true)); + + // Value + if (propertyPrefix == BYTES_PROPERTY_PREFIX) + { + //remove '>' + buf.deleteCharAt(buf.length() - 1); + + byte[] bytes = (byte[]) value; + buf.append(" length='").append(bytes.length).append("'>"); + + buf.append(byteArrayToXML(propertyName, bytes)); + } + else + { + buf.append(String.valueOf(value)); } - buf.append("\n"); - buf.append(PROPERTY_FIELD_TABLE_CLOSE_XML); + + //End Tag + buf.append(propertyXML(name, false)); return buf.toString(); } - private void checkPropertyName(String propertyName, char propertyPrefix) + private Object checkPropertyName(String name) { + String propertyName = name.substring(1); + char propertyPrefix = name.charAt(0); + + Object previous = null; + if (propertyName == null) { throw new IllegalArgumentException("Property name must not be null"); @@ -432,15 +533,29 @@ public class PropertyFieldTable if (currentValue != null) { - _properties.remove(currentValue + propertyName); + previous = _properties.remove(currentValue + propertyName); + + // If we are in effect deleting the value (see comment on null values being deleted + // below) then we also need to remove the name from the encoding length. + if (previous == null) + { + _encodedSize -= EncodingUtils.encodedShortStringLength(propertyName); + } + + // FIXME: Should be able to short-cut this process if the old and new values are + // the same object and/or type and size... + _encodedSize -= getEncodingSize(currentValue + propertyName, previous); } _propertyNamesTypeMap.put(propertyName, "" + propertyPrefix); + + return previous; } - private static String propertyXML(String propertyName, boolean start) + private static String propertyXML(String name, boolean start) { - char typeIdentifier = propertyName.charAt(0); + char propertyPrefix = name.charAt(0); + String propertyName = name.substring(1); StringBuffer buf = new StringBuffer(); @@ -453,8 +568,7 @@ public class PropertyFieldTable buf.append("</"); } - - switch (typeIdentifier) + switch (propertyPrefix) { case BOOLEAN_PROPERTY_PREFIX: buf.append(BOOLEAN); @@ -487,14 +601,13 @@ public class PropertyFieldTable buf.append(CHAR); break; default: - buf.append(UNKNOWN + " (identifier ").append(typeIdentifier).append(")"); + buf.append(UNKNOWN + " (identifier ").append(propertyPrefix).append(")"); break; } - if (start) { - buf.append(" name='").append(propertyName.substring(1)).append("'"); + buf.append(" name='").append(propertyName).append("'"); } buf.append(">"); @@ -519,8 +632,6 @@ public class PropertyFieldTable private void processBytesXMLLine(String xmlline) { - String type = xmlline.substring(1, xmlline.indexOf(" ")); - String propertyName = xmlline.substring(xmlline.indexOf('\'') + 1, xmlline.indexOf('\'', xmlline.indexOf('\'') + 1)); String value = xmlline.substring(xmlline.indexOf(">") + 1, @@ -545,7 +656,6 @@ public class PropertyFieldTable { String token = tokenizer.nextToken(); - if (token.equals(PROPERTY_FIELD_TABLE_CLOSE_XML) || token.equals(BYTES_CLOSE_XML)) { @@ -555,7 +665,6 @@ public class PropertyFieldTable if (token.equals(BYTES_CLOSE_XML)) { processing_bytes = false; - } if (processing) @@ -578,11 +687,9 @@ public class PropertyFieldTable { processing = true; } - } } - private void processXMLLine(String xmlline) { // <<type> name='<property>'><value></<type>> @@ -611,11 +718,39 @@ public class PropertyFieldTable } if (type.equals(BYTES)) { - Integer length = Integer.parseInt(xmlline.substring( - xmlline.lastIndexOf("=") + 2 - , xmlline.lastIndexOf("'"))); + int headerEnd = xmlline.indexOf('>'); + String bytesHeader = xmlline.substring(0, headerEnd); + + //Extract length value + Integer length = Integer.parseInt(bytesHeader.substring( + bytesHeader.lastIndexOf("=") + 2 + , bytesHeader.lastIndexOf("'"))); + + byte[] bytes = new byte[length]; setBytes(propertyName, bytes); + + //Check if the line contains all the byte values + // This is needed as the XMLLine sent across the wire is the bytes value + + int byteStart = xmlline.indexOf('<', headerEnd); + + if (byteStart > 0) + { + while (!xmlline.startsWith(BYTES_CLOSE_XML, byteStart)) + { + //This should be the next byte line + int bytePrefixEnd = xmlline.indexOf('>', byteStart) + 1; + int byteEnd = xmlline.indexOf('>', bytePrefixEnd) + 1; + + String byteline = xmlline.substring(byteStart, byteEnd); + + processBytesXMLLine(byteline); + + byteStart = xmlline.indexOf('<', byteEnd); + } + } + } if (type.equals(SHORT)) { @@ -651,6 +786,311 @@ public class PropertyFieldTable } } + // ************************* Byte Buffer Processing -} + public void writeToBuffer(ByteBuffer buffer) + { + final boolean debug = _logger.isDebugEnabled(); + + if (debug) + { + _logger.debug("FieldTable::writeToBuffer: Writing encoded size of " + _encodedSize + "..."); + } + + EncodingUtils.writeUnsignedInteger(buffer, _encodedSize); + + putDataInBuffer(buffer); + } + + public byte[] getDataAsBytes() + { + final ByteBuffer buffer = ByteBuffer.allocate((int) _encodedSize); // FIXME XXX: Is cast a problem? + + putDataInBuffer(buffer); + + final byte[] result = new byte[(int) _encodedSize]; + buffer.flip(); + buffer.get(result); + buffer.release(); + return result; + } + + public int size() + { + return _properties.size(); + } + + public boolean isEmpty() + { + return _properties.isEmpty(); + } + + public boolean containsKey(Object key) + { + return _properties.containsKey(key); + } + + public boolean containsValue(Object value) + { + return _properties.containsValue(value); + } + + public Object get(Object key) + { + return _properties.get(key); + } + + + public Object put(Object key, Object value) + { + return setObject(key.toString(), value); + } + + protected Object put(String key, Object value) + { + Object previous = checkPropertyName(key); + + + String propertyName = key.substring(1); + char propertyPrefix = _propertyNamesTypeMap.get(propertyName).charAt(0); + + if (value != null) + { + //Add the size of the propertyName + _encodedSize += EncodingUtils.encodedShortStringLength(propertyName); + + // For now: Setting a null value is the equivalent of deleting it. + // This is ambiguous in the JMS spec and needs thrashing out and potentially + // testing against other implementations. + + //Add the size of the content + _encodedSize += getEncodingSize(key, value); + } + + _properties.put((String) propertyName, value); + + return previous; + } + + public Object remove(Object key) + { + if (key instanceof String) + { + throw new IllegalArgumentException("Property key be a string"); + } + + char propertyPrefix = ((String) key).charAt(0); + + if (_properties.containsKey(key)) + { + final Object value = _properties.remove(key); + // plus one for the type + _encodedSize -= EncodingUtils.encodedShortStringLength(((String) key)); + + // This check is, for now, unnecessary (we don't store null values). + if (value != null) + { + _encodedSize -= getEncodingSize(propertyPrefix + (String) key, value); + } + + return value; + } + else + { + return null; + } + } + + public void putAll(Map t) + { + Iterator it = t.keySet().iterator(); + + while (it.hasNext()) + { + Object key = it.next(); + put(key, t.get(key)); + } + } + + public void clear() + { + _properties.clear(); + _propertyNamesTypeMap.clear(); + } + + public Set keySet() + { + return _properties.keySet(); + } + + public Collection values() + { + return _properties.values(); + } + + public Set entrySet() + { + return _properties.entrySet(); + } + + public long getEncodedSize() + { + return _encodedSize; + } + + + private void putDataInBuffer(ByteBuffer buffer) + { + final Iterator it = _properties.entrySet().iterator(); + + //If there are values then write out the encoded Size... could check _encodedSize != 0 + // write out the total length, which we have kept up to date as data is added + + + while (it.hasNext()) + { + Map.Entry me = (Map.Entry) it.next(); + String propertyName = (String) me.getKey(); + + //The type value + char propertyPrefix = _propertyNamesTypeMap.get(propertyName).charAt(0); + //The actual param name skipping type + + EncodingUtils.writeShortStringBytes(buffer, propertyName); + Object value = me.getValue(); + + switch (propertyPrefix) + { + + case STRING_PROPERTY_PREFIX: + // TODO: look at using proper charset encoder + buffer.put((byte) STRING_PROPERTY_PREFIX); + EncodingUtils.writeLongStringBytes(buffer, (String) value); + break; + + case AMQP_UNSIGNEDINT_PROPERTY_PREFIX: + case LONG_PROPERTY_PREFIX: + case INT_PROPERTY_PREFIX: + case BOOLEAN_PROPERTY_PREFIX: + case BYTE_PROPERTY_PREFIX: + case SHORT_PROPERTY_PREFIX: + case FLOAT_PROPERTY_PREFIX: + case DOUBLE_PROPERTY_PREFIX: + case CHAR_PROPERTY_PREFIX: + case BYTES_PROPERTY_PREFIX: + case XML_PROPERTY_PREFIX: + // Encode as XML + buffer.put((byte) XML_PROPERTY_PREFIX); + EncodingUtils.writeLongStringBytes(buffer, valueAsXML(propertyPrefix + propertyName, value)); + break; + default: + { + + // Should never get here + throw new IllegalArgumentException("Key '" + propertyName + "': Unsupported type in field table, type: " + ((value == null) ? "null-object" : value.getClass())); + } + } + } + } + + + public void setFromBuffer(ByteBuffer buffer, long length) throws AMQFrameDecodingException + { + final boolean debug = _logger.isDebugEnabled(); + + int sizeRead = 0; + while (sizeRead < length) + { + int sizeRemaining = buffer.remaining(); + final String key = EncodingUtils.readShortString(buffer); + // TODO: use proper charset decoder + byte iType = buffer.get(); + final char type = (char) iType; + Object value = null; + + switch (type) + { + case STRING_PROPERTY_PREFIX: + value = EncodingUtils.readLongString(buffer); + break; + case LONG_PROPERTY_PREFIX: + case INT_PROPERTY_PREFIX: + case BOOLEAN_PROPERTY_PREFIX: + case BYTE_PROPERTY_PREFIX: + case SHORT_PROPERTY_PREFIX: + case FLOAT_PROPERTY_PREFIX: + case DOUBLE_PROPERTY_PREFIX: + case CHAR_PROPERTY_PREFIX: + case BYTES_PROPERTY_PREFIX: + case XML_PROPERTY_PREFIX: + processXMLLine(EncodingUtils.readLongString(buffer)); + break; + default: + String msg = "Field '" + key + "' - unsupported field table type: " + type + "."; + //some extra debug information... + msg += " (" + iType + "), length=" + length + ", sizeRead=" + sizeRead + ", sizeRemaining=" + sizeRemaining; + throw new AMQFrameDecodingException(msg); + } + + sizeRead += (sizeRemaining - buffer.remaining()); + + if (debug) + { + _logger.debug("FieldTable::PropFieldTable(buffer," + length + "): Read type '" + type + "', key '" + key + "', value '" + value + "' (now read " + sizeRead + " of " + length + " encoded bytes)..."); + } + + if (type != XML_PROPERTY_PREFIX) + { + setObject(key, value); + } + } + + if (debug) + { + _logger.debug("FieldTable::FieldTable(buffer," + length + "): Done."); + } + } + + + /** + * @param name the property name with type prefix + * @param value the property value + * @return integer + */ + private static int getEncodingSize(String name, Object value) + { + int encodingSize; + + char propertyPrefix = name.charAt(0); + + switch (propertyPrefix) + { + // the extra byte if for the type indicator that is written out + case STRING_PROPERTY_PREFIX: + encodingSize = 1 + EncodingUtils.encodedLongStringLength((String) value); + break; + case LONG_PROPERTY_PREFIX: + case INT_PROPERTY_PREFIX: + case BOOLEAN_PROPERTY_PREFIX: + case BYTE_PROPERTY_PREFIX: + case SHORT_PROPERTY_PREFIX: + case FLOAT_PROPERTY_PREFIX: + case DOUBLE_PROPERTY_PREFIX: + case CHAR_PROPERTY_PREFIX: + case BYTES_PROPERTY_PREFIX: + case XML_PROPERTY_PREFIX: + encodingSize = 1 + EncodingUtils.encodedLongStringLength(valueAsXML(name, value)); + break; + default: + //encodingSize = 1 + EncodingUtils.encodedLongStringLength(String.valueOf(value)); + // We are using XML String encoding + throw new IllegalArgumentException("Unsupported type in field table: " + value.getClass()); + } + +// the extra byte for the type indicator is calculated in the name + return encodingSize; + } + + +} diff --git a/java/common/src/test/java/org/apache/qpid/framing/PropertyFieldTableTest.java b/java/common/src/test/java/org/apache/qpid/framing/PropertyFieldTableTest.java index 5070b6ecb1..0b6820b8a9 100644 --- a/java/common/src/test/java/org/apache/qpid/framing/PropertyFieldTableTest.java +++ b/java/common/src/test/java/org/apache/qpid/framing/PropertyFieldTableTest.java @@ -25,6 +25,10 @@ import junit.framework.TestCase; import java.util.Enumeration; +import org.apache.mina.common.ByteBuffer; +import org.apache.mina.common.ByteBufferProxy; +import org.apache.mina.common.support.BaseByteBuffer; + public class PropertyFieldTableTest extends TestCase { @@ -206,7 +210,7 @@ public class PropertyFieldTableTest extends TestCase PropertyFieldTable table2 = new PropertyFieldTable(table1XML); - Assert.assertEquals(table1XML, table2.toString()); + Assert.assertEquals(table1XML, table2.toString()); } public void testKeyEnumeration() @@ -273,6 +277,117 @@ public class PropertyFieldTableTest extends TestCase Assert.assertEquals(Short.MAX_VALUE, table.getObject("object-short")); } + + public void testwriteBuffer() + { + byte[] bytes = {99, 98, 97, 96, 95}; + + PropertyFieldTable table = new PropertyFieldTable(); + table.setBoolean("bool", true); + table.setByte("byte", Byte.MAX_VALUE); + + table.setBytes("bytes", bytes); + table.setChar("char", 'c'); + table.setDouble("double", Double.MAX_VALUE); + table.setFloat("float", Float.MAX_VALUE); + table.setInteger("int", Integer.MAX_VALUE); + table.setLong("long", Long.MAX_VALUE); + table.setShort("short", Short.MAX_VALUE); + + + final ByteBuffer buffer = ByteBuffer.allocate((int) table.getEncodedSize()); // FIXME XXX: Is cast a problem? + + table.writeToBuffer(buffer); + + buffer.flip(); + + long length = buffer.getUnsignedInt(); + + try + { + PropertyFieldTable table2 = new PropertyFieldTable(buffer, length); + + Assert.assertEquals((Boolean) true, table2.getBoolean("bool")); + Assert.assertEquals((Byte) Byte.MAX_VALUE, table2.getByte("byte")); + assertBytesEqual(bytes, table2.getBytes("bytes")); + Assert.assertEquals((Character) 'c', table2.getCharacter("char")); + Assert.assertEquals(Double.MAX_VALUE, table2.getDouble("double")); + Assert.assertEquals(Float.MAX_VALUE, table2.getFloat("float")); + Assert.assertEquals((Integer) Integer.MAX_VALUE, table2.getInteger("int")); + Assert.assertEquals((Long) Long.MAX_VALUE, table2.getLong("long")); + Assert.assertEquals((Short) Short.MAX_VALUE, table2.getShort("short")); + } + catch (AMQFrameDecodingException e) + { + e.printStackTrace(); + fail("PFT should be instantiated from bytes." + e.getCause()); + } + } + + public void testEncodingSize() + { + FieldTable result = FieldTableFactory.newFieldTable(); + int size = 0; + result.put("one", 1L); + // size is 1(size) + bytes for short string + size = 1 + 3; // 1 + key length + // or size is 1(the type) + number of bytes (4bytes worth) + bytes + size += 1 + 4; // 1 + 4 + value length + size += "<long name='one'>1</long>".length(); // this is the xml encoding for a long. + assertEquals(size, result.getEncodedSize()); + + result.put("two", 2L); + size += 1 + 3; // 1 + key length + size += 1 + 4; // 1 + 4 + value length + size += "<long name='two'>2</long>".length(); // this is the xml encoding for a long. + assertEquals(size, result.getEncodedSize()); + + result.put("three", 3L); + size += 1 + 5; // 1 + key length + size += 1 + 4; // 1 + 4 + value length + size += "<long name='three'>3</long>".length(); // this is the xml encoding for a long. + assertEquals(size, result.getEncodedSize()); + + result.put("four", 4L); + size += 1 + 4; // 1 + key length + size += 1 + 4; // 1 + 4 + value length + size += "<long name='four'>4</long>".length(); // this is the xml encoding for a long. + assertEquals(size, result.getEncodedSize()); + + result.put("five", 5L); + size += 1 + 4; // 1 + key length + size += 1 + 4; // 1 + 4 + value length + size += "<long name='five'>5</long>".length(); // this is the xml encoding for a long. + assertEquals(size, result.getEncodedSize()); + + //fixme should perhaps be expanded to incorporate all types. + + final ByteBuffer buffer = ByteBuffer.allocate((int) result.getEncodedSize()); // FIXME XXX: Is cast a problem? + + result.writeToBuffer(buffer); + + buffer.flip(); + + long length = buffer.getUnsignedInt(); + + try + { + PropertyFieldTable table2 = new PropertyFieldTable(buffer, length); + + Assert.assertEquals((Long) 1L, table2.getLong("one")); + Assert.assertEquals((Long) 2L, table2.getLong("two")); + Assert.assertEquals((Long) 3L, table2.getLong("three")); + Assert.assertEquals((Long) 4L, table2.getLong("four")); + Assert.assertEquals((Long) 5L, table2.getLong("five")); + } + catch (AMQFrameDecodingException e) + { + e.printStackTrace(); + fail("PFT should be instantiated from bytes." + e.getCause()); + } + + } + private void assertBytesEqual(byte[] expected, byte[] actual) { Assert.assertEquals(expected.length, actual.length); diff --git a/java/systests/src/test/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTest.java b/java/systests/src/test/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTest.java index 53ae097ea6..a7611df55d 100644 --- a/java/systests/src/test/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTest.java +++ b/java/systests/src/test/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTest.java @@ -20,26 +20,25 @@ */ package org.apache.qpid.server.exchange; -import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.queue.NoConsumersException; -import org.apache.qpid.server.queue.AMQMessage; -import org.apache.qpid.server.store.MessageStore; -import org.apache.qpid.server.store.SkeletonMessageStore; -import org.apache.qpid.server.registry.ApplicationRegistry; +import junit.framework.TestCase; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.BasicContentHeaderProperties; import org.apache.qpid.framing.BasicPublishBody; -import org.apache.qpid.framing.ContentHeaderBody; import org.apache.qpid.framing.ContentBody; +import org.apache.qpid.framing.ContentHeaderBody; import org.apache.qpid.framing.FieldTable; -import org.apache.qpid.framing.BasicContentHeaderProperties; -import org.apache.qpid.AMQException; +import org.apache.qpid.framing.FieldTableFactory; +import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.store.SkeletonMessageStore; -import java.util.List; import java.util.ArrayList; import java.util.Arrays; -import java.util.Set; import java.util.HashSet; - -import junit.framework.TestCase; +import java.util.List; +import java.util.Set; public class AbstractHeadersExchangeTest extends TestCase { @@ -105,7 +104,7 @@ public class AbstractHeadersExchangeTest extends TestCase static FieldTable getHeaders(String... entries) { - FieldTable headers = new FieldTable(); + FieldTable headers = FieldTableFactory.newFieldTable(); for (String s : entries) { String[] parts = s.split("=", 2); |