diff options
Diffstat (limited to 'java')
7 files changed, 299 insertions, 145 deletions
diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQDestination.java b/java/client/src/main/java/org/apache/qpid/client/AMQDestination.java index eb9682a3cf..3ef32fb008 100644 --- a/java/client/src/main/java/org/apache/qpid/client/AMQDestination.java +++ b/java/client/src/main/java/org/apache/qpid/client/AMQDestination.java @@ -21,8 +21,6 @@ package org.apache.qpid.client; import java.net.URISyntaxException; -import java.util.ArrayList; -import java.util.List; import java.util.Map; import javax.jms.Destination; @@ -34,8 +32,6 @@ import javax.naming.StringRefAddr; import org.apache.qpid.client.messaging.address.AddressHelper; import org.apache.qpid.client.messaging.address.Link; import org.apache.qpid.client.messaging.address.Node; -import org.apache.qpid.client.messaging.address.QpidExchangeOptions; -import org.apache.qpid.client.messaging.address.QpidQueueOptions; import org.apache.qpid.configuration.ClientProperties; import org.apache.qpid.exchange.ExchangeDefaults; import org.apache.qpid.framing.AMQShortString; @@ -78,11 +74,6 @@ public abstract class AMQDestination implements Destination, Referenceable private boolean _exchangeExistsChecked; - private byte[] _byteEncoding; - private static final int IS_DURABLE_MASK = 0x1; - private static final int IS_EXCLUSIVE_MASK = 0x2; - private static final int IS_AUTODELETE_MASK = 0x4; - public static final int QUEUE_TYPE = 1; public static final int TOPIC_TYPE = 2; public static final int UNKNOWN_TYPE = 3; @@ -323,7 +314,11 @@ public abstract class AMQDestination implements Destination, Referenceable { if(_urlAsShortString == null) { - toURL(); + if (_url == null) + { + toURL(); + } + _urlAsShortString = new AMQShortString(_url); } return _urlAsShortString; } @@ -370,7 +365,6 @@ public abstract class AMQDestination implements Destination, Referenceable // calculated URL now out of date _url = null; _urlAsShortString = null; - _byteEncoding = null; } public AMQShortString getRoutingKey() @@ -508,59 +502,10 @@ public abstract class AMQDestination implements Destination, Referenceable sb.deleteCharAt(sb.length() - 1); url = sb.toString(); _url = url; - _urlAsShortString = new AMQShortString(url); } return url; } - public byte[] toByteEncoding() - { - byte[] encoding = _byteEncoding; - if(encoding == null) - { - int size = _exchangeClass.length() + 1 + - _exchangeName.length() + 1 + - 0 + // in place of the destination name - (_queueName == null ? 0 : _queueName.length()) + 1 + - 1; - encoding = new byte[size]; - int pos = 0; - - pos = _exchangeClass.writeToByteArray(encoding, pos); - pos = _exchangeName.writeToByteArray(encoding, pos); - - encoding[pos++] = (byte)0; - - if(_queueName == null) - { - encoding[pos++] = (byte)0; - } - else - { - pos = _queueName.writeToByteArray(encoding,pos); - } - byte options = 0; - if(_isDurable) - { - options |= IS_DURABLE_MASK; - } - if(_isExclusive) - { - options |= IS_EXCLUSIVE_MASK; - } - if(_isAutoDelete) - { - options |= IS_AUTODELETE_MASK; - } - encoding[pos] = options; - - - _byteEncoding = encoding; - - } - return encoding; - } - public boolean equals(Object o) { if (this == o) @@ -614,53 +559,6 @@ public abstract class AMQDestination implements Destination, Referenceable null); // factory location } - - public static Destination createDestination(byte[] byteEncodedDestination) - { - AMQShortString exchangeClass; - AMQShortString exchangeName; - AMQShortString routingKey; - AMQShortString queueName; - boolean isDurable; - boolean isExclusive; - boolean isAutoDelete; - - int pos = 0; - exchangeClass = AMQShortString.readFromByteArray(byteEncodedDestination, pos); - pos+= exchangeClass.length() + 1; - exchangeName = AMQShortString.readFromByteArray(byteEncodedDestination, pos); - pos+= exchangeName.length() + 1; - routingKey = AMQShortString.readFromByteArray(byteEncodedDestination, pos); - pos+= (routingKey == null ? 0 : routingKey.length()) + 1; - queueName = AMQShortString.readFromByteArray(byteEncodedDestination, pos); - pos+= (queueName == null ? 0 : queueName.length()) + 1; - int options = byteEncodedDestination[pos]; - isDurable = (options & IS_DURABLE_MASK) != 0; - isExclusive = (options & IS_EXCLUSIVE_MASK) != 0; - isAutoDelete = (options & IS_AUTODELETE_MASK) != 0; - - if (exchangeClass.equals(ExchangeDefaults.DIRECT_EXCHANGE_CLASS)) - { - return new AMQQueue(exchangeName,routingKey,queueName,isExclusive,isAutoDelete,isDurable); - } - else if (exchangeClass.equals(ExchangeDefaults.TOPIC_EXCHANGE_CLASS)) - { - return new AMQTopic(exchangeName,routingKey,isAutoDelete,queueName,isDurable); - } - else if (exchangeClass.equals(ExchangeDefaults.HEADERS_EXCHANGE_CLASS)) - { - return new AMQHeadersExchange(routingKey); - } - else - { - return new AMQAnyDestination(exchangeName,exchangeClass, - routingKey,isExclusive, - isAutoDelete,queueName, - isDurable, new AMQShortString[0]); - } - - } - public static Destination createDestination(BindingURL binding) { AMQShortString type = binding.getExchangeClass(); diff --git a/java/common/src/main/java/org/apache/qpid/AMQChannelException.java b/java/common/src/main/java/org/apache/qpid/AMQChannelException.java index ef9420ba87..2f6290b55a 100644 --- a/java/common/src/main/java/org/apache/qpid/AMQChannelException.java +++ b/java/common/src/main/java/org/apache/qpid/AMQChannelException.java @@ -54,6 +54,7 @@ public class AMQChannelException extends AMQException public AMQFrame getCloseFrame(int channel) { MethodRegistry reg = MethodRegistry.getMethodRegistry(new ProtocolVersion(major,minor)); - return new AMQFrame(channel, reg.createChannelCloseBody(getErrorCode() == null ? AMQConstant.INTERNAL_ERROR.getCode() : getErrorCode().getCode(), new AMQShortString(getMessage()),_classId,_methodId)); + return new AMQFrame(channel, reg.createChannelCloseBody(getErrorCode() == null ? AMQConstant.INTERNAL_ERROR.getCode() : getErrorCode().getCode(), getMessageAsShortString(),_classId,_methodId)); } + } diff --git a/java/common/src/main/java/org/apache/qpid/AMQConnectionException.java b/java/common/src/main/java/org/apache/qpid/AMQConnectionException.java index 8ef6facef1..ca9c9f9dc4 100644 --- a/java/common/src/main/java/org/apache/qpid/AMQConnectionException.java +++ b/java/common/src/main/java/org/apache/qpid/AMQConnectionException.java @@ -62,9 +62,10 @@ public class AMQConnectionException extends AMQException MethodRegistry reg = MethodRegistry.getMethodRegistry(new ProtocolVersion(major,minor)); return new AMQFrame(0, reg.createConnectionCloseBody(getErrorCode().getCode(), - new AMQShortString(getMessage()), + getMessageAsShortString(), _classId, _methodId)); } + } diff --git a/java/common/src/main/java/org/apache/qpid/AMQException.java b/java/common/src/main/java/org/apache/qpid/AMQException.java index b0c6fccc9e..86d439d269 100644 --- a/java/common/src/main/java/org/apache/qpid/AMQException.java +++ b/java/common/src/main/java/org/apache/qpid/AMQException.java @@ -20,6 +20,7 @@ */ package org.apache.qpid; +import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.protocol.AMQConstant; /** @@ -121,4 +122,19 @@ public class AMQException extends Exception return newAMQE; } + + /** + * Truncates the exception message to 255 characters if its length exceeds 255. + * + * @return exception message + */ + public AMQShortString getMessageAsShortString() + { + String message = getMessage(); + if (message != null && message.length() > AMQShortString.MAX_LENGTH) + { + message = message.substring(0, AMQShortString.MAX_LENGTH - 3) + "..."; + } + return new AMQShortString(message); + } } diff --git a/java/common/src/main/java/org/apache/qpid/framing/AMQShortString.java b/java/common/src/main/java/org/apache/qpid/framing/AMQShortString.java index 39a9beb9e8..2b9e2ffaba 100644 --- a/java/common/src/main/java/org/apache/qpid/framing/AMQShortString.java +++ b/java/common/src/main/java/org/apache/qpid/framing/AMQShortString.java @@ -37,6 +37,10 @@ import java.lang.ref.WeakReference; */ public final class AMQShortString implements CharSequence, Comparable<AMQShortString> { + /** + * The maximum number of octets in AMQ short string as defined in AMQP specification + */ + public static final int MAX_LENGTH = 255; private static final byte MINUS = (byte)'-'; private static final byte ZERO = (byte) '0'; @@ -118,22 +122,19 @@ public final class AMQShortString implements CharSequence, Comparable<AMQShortSt public AMQShortString(byte[] data) { - + if (data == null) + { + throw new NullPointerException("Cannot create AMQShortString with null data[]"); + } + if (data.length > MAX_LENGTH) + { + throw new IllegalArgumentException("Cannot create AMQShortString with number of octets over 255!"); + } _data = data.clone(); _length = data.length; _offset = 0; } - public AMQShortString(byte[] data, int pos) - { - final int size = data[pos++]; - final byte[] dataCopy = new byte[size]; - System.arraycopy(data,pos,dataCopy,0,size); - _length = size; - _data = dataCopy; - _offset = 0; - } - public AMQShortString(String data) { this((data == null) ? EMPTY_CHAR_ARRAY : data.toCharArray()); @@ -146,7 +147,12 @@ public final class AMQShortString implements CharSequence, Comparable<AMQShortSt { throw new NullPointerException("Cannot create AMQShortString with null char[]"); } - + // the current implementation of 0.8/0.9.x short string encoding + // supports only ASCII characters + if (data.length> MAX_LENGTH) + { + throw new IllegalArgumentException("Cannot create AMQShortString with number of octets over 255!"); + } final int length = data.length; final byte[] stringBytes = new byte[length]; int hash = 0; @@ -165,6 +171,17 @@ public final class AMQShortString implements CharSequence, Comparable<AMQShortSt public AMQShortString(CharSequence charSequence) { + if (charSequence == null) + { + // it should be possible to create short string for null data + charSequence = ""; + } + // the current implementation of 0.8/0.9.x short string encoding + // supports only ASCII characters + if (charSequence.length() > MAX_LENGTH) + { + throw new IllegalArgumentException("Cannot create AMQShortString with number of octets over 255!"); + } final int length = charSequence.length(); final byte[] stringBytes = new byte[length]; int hash = 0; @@ -184,6 +201,10 @@ public final class AMQShortString implements CharSequence, Comparable<AMQShortSt private AMQShortString(ByteBuffer data, final int length) { + if (length > MAX_LENGTH) + { + throw new IllegalArgumentException("Cannot create AMQShortString with number of octets over 255!"); + } if(data.isDirect() || data.isReadOnly()) { byte[] dataBytes = new byte[length]; @@ -205,8 +226,17 @@ public final class AMQShortString implements CharSequence, Comparable<AMQShortSt private AMQShortString(final byte[] data, final int from, final int to) { + if (data == null) + { + throw new NullPointerException("Cannot create AMQShortString with null data[]"); + } + int length = to - from; + if (length > MAX_LENGTH) + { + throw new IllegalArgumentException("Cannot create AMQShortString with number of octets over 255!"); + } _offset = from; - _length = to - from; + _length = length; _data = data; } @@ -245,29 +275,6 @@ public final class AMQShortString implements CharSequence, Comparable<AMQShortSt return new CharSubSequence(start, end); } - public int writeToByteArray(byte[] encoding, int pos) - { - final int size = length(); - encoding[pos++] = (byte) size; - System.arraycopy(_data,_offset,encoding,pos,size); - return pos+size; - } - - public static AMQShortString readFromByteArray(byte[] byteEncodedDestination, int pos) - { - - - final AMQShortString shortString = new AMQShortString(byteEncodedDestination, pos); - if(shortString.length() == 0) - { - return null; - } - else - { - return shortString; - } - } - public static AMQShortString readFromBuffer(ByteBuffer buffer) { final short length = buffer.getUnsigned(); @@ -690,6 +697,10 @@ public final class AMQShortString implements CharSequence, Comparable<AMQShortSt size += term.length(); } + if (size > MAX_LENGTH) + { + throw new IllegalArgumentException("Cannot create AMQShortString with number of octets over 255!"); + } byte[] data = new byte[size]; int pos = 0; final byte[] delimData = delim._data; diff --git a/java/common/src/test/java/org/apache/qpid/AMQExceptionTest.java b/java/common/src/test/java/org/apache/qpid/AMQExceptionTest.java index ef6cd41492..f65427e583 100644 --- a/java/common/src/test/java/org/apache/qpid/AMQExceptionTest.java +++ b/java/common/src/test/java/org/apache/qpid/AMQExceptionTest.java @@ -23,6 +23,7 @@ package org.apache.qpid; import junit.framework.TestCase; import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.framing.AMQFrameDecodingException; +import org.apache.qpid.framing.AMQShortString; /** * This test is to ensure that when an AMQException is rethrown that the specified exception is correctly wrapped up. @@ -91,6 +92,18 @@ public class AMQExceptionTest extends TestCase return amqe; } + public void testGetMessageAsString() + { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 25; i++) + { + sb.append("message [" + i + "]"); + } + AMQException e = new AMQException(AMQConstant.INTERNAL_ERROR, sb.toString(), null); + AMQShortString message = e.getMessageAsShortString(); + assertEquals(sb.substring(0, AMQShortString.MAX_LENGTH - 3) + "...", message.toString()); + } + /** * Private class that extends AMQException but does not have a default exception. */ diff --git a/java/common/src/test/java/org/apache/qpid/framing/AMQShortStringTest.java b/java/common/src/test/java/org/apache/qpid/framing/AMQShortStringTest.java index 92e7ce0a80..9a805d87b3 100644 --- a/java/common/src/test/java/org/apache/qpid/framing/AMQShortStringTest.java +++ b/java/common/src/test/java/org/apache/qpid/framing/AMQShortStringTest.java @@ -20,6 +20,10 @@ package org.apache.qpid.framing; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.List; + import junit.framework.TestCase; public class AMQShortStringTest extends TestCase { @@ -105,5 +109,215 @@ public class AMQShortStringTest extends TestCase assertFalse(new AMQShortString("A").equals(new AMQShortString("a"))); } + /** + * Test method for + * {@link org.apache.qpid.framing.AMQShortString#AMQShortString(byte[])}. + */ + public void testCreateAMQShortStringByteArray() + { + byte[] bytes = null; + try + { + bytes = "test".getBytes("UTF-8"); + } + catch (UnsupportedEncodingException e) + { + fail("UTF-8 encoding is not supported anymore by JVM:" + e.getMessage()); + } + AMQShortString string = new AMQShortString(bytes); + assertEquals("constructed amq short string length differs from expected", 4, string.length()); + assertTrue("constructed amq short string differs from expected", string.equals("test")); + } + + /** + * Test method for + * {@link org.apache.qpid.framing.AMQShortString#AMQShortString(java.lang.String)} + * <p> + * Tests short string construction from string with length less than 255. + */ + public void testCreateAMQShortStringString() + { + AMQShortString string = new AMQShortString("test"); + assertEquals("constructed amq short string length differs from expected", 4, string.length()); + assertTrue("constructed amq short string differs from expected", string.equals("test")); + } + + /** + * Test method for + * {@link org.apache.qpid.framing.AMQShortString#AMQShortString(char[])}. + * <p> + * Tests short string construction from char array with length less than 255. + */ + public void testCreateAMQShortStringCharArray() + { + char[] chars = "test".toCharArray(); + AMQShortString string = new AMQShortString(chars); + assertEquals("constructed amq short string length differs from expected", 4, string.length()); + assertTrue("constructed amq short string differs from expected", string.equals("test")); + } + + /** + * Test method for + * {@link org.apache.qpid.framing.AMQShortString#AMQShortString(java.lang.CharSequence)} + * <p> + * Tests short string construction from char sequence with length less than 255. + */ + public void testCreateAMQShortStringCharSequence() + { + AMQShortString string = new AMQShortString((CharSequence) "test"); + assertEquals("constructed amq short string length differs from expected", 4, string.length()); + assertTrue("constructed amq short string differs from expected", string.equals("test")); + } + + /** + * Test method for + * {@link org.apache.qpid.framing.AMQShortString#AMQShortString(byte[])}. + * <p> + * Tests an attempt to create an AMQP short string from byte array with length over 255. + */ + public void testCreateAMQShortStringByteArrayOver255() + { + String test = buildString('a', 256); + byte[] bytes = null; + try + { + bytes = test.getBytes("UTF-8"); + } + catch (UnsupportedEncodingException e) + { + fail("UTF-8 encoding is not supported anymore by JVM:" + e.getMessage()); + } + try + { + new AMQShortString(bytes); + fail("It should not be possible to create AMQShortString with length over 255"); + } + catch (IllegalArgumentException e) + { + assertEquals("Exception message differs from expected", + "Cannot create AMQShortString with number of octets over 255!", e.getMessage()); + } + } + + /** + * Test method for + * {@link org.apache.qpid.framing.AMQShortString#AMQShortString(java.lang.String)} + * <p> + * Tests an attempt to create an AMQP short string from string with length over 255 + */ + public void testCreateAMQShortStringStringOver255() + { + String test = buildString('a', 256); + try + { + new AMQShortString(test); + fail("It should not be possible to create AMQShortString with length over 255"); + } + catch (IllegalArgumentException e) + { + assertEquals("Exception message differs from expected", + "Cannot create AMQShortString with number of octets over 255!", e.getMessage()); + } + } + + /** + * Test method for + * {@link org.apache.qpid.framing.AMQShortString#AMQShortString(char[])}. + * <p> + * Tests an attempt to create an AMQP short string from char array with length over 255. + */ + public void testCreateAMQShortStringCharArrayOver255() + { + String test = buildString('a', 256); + char[] chars = test.toCharArray(); + try + { + new AMQShortString(chars); + fail("It should not be possible to create AMQShortString with length over 255"); + } + catch (IllegalArgumentException e) + { + assertEquals("Exception message differs from expected", + "Cannot create AMQShortString with number of octets over 255!", e.getMessage()); + } + } + + /** + * Test method for + * {@link org.apache.qpid.framing.AMQShortString#AMQShortString(java.lang.CharSequence)} + * <p> + * Tests an attempt to create an AMQP short string from char sequence with length over 255. + */ + public void testCreateAMQShortStringCharSequenceOver255() + { + String test = buildString('a', 256); + try + { + new AMQShortString((CharSequence) test); + fail("It should not be possible to create AMQShortString with length over 255"); + } + catch (IllegalArgumentException e) + { + assertEquals("Exception message differs from expected", + "Cannot create AMQShortString with number of octets over 255!", e.getMessage()); + } + } + + /** + * Tests joining of short strings into a short string with length over 255. + */ + public void testJoinOverflow() + { + List<AMQShortString> data = new ArrayList<AMQShortString>(); + for (int i = 0; i < 25; i++) + { + data.add(new AMQShortString("test data!")); + } + try + { + AMQShortString.join(data, new AMQShortString(" ")); + fail("It should not be possible to create AMQShortString with length over 255"); + } + catch (IllegalArgumentException e) + { + assertEquals("Exception message differs from expected", + "Cannot create AMQShortString with number of octets over 255!", e.getMessage()); + } + } + + /** + * Tests joining of short strings into a short string with length less than 255. + */ + public void testJoin() + { + StringBuilder expected = new StringBuilder(); + List<AMQShortString> data = new ArrayList<AMQShortString>(); + data.add(new AMQShortString("test data 1")); + expected.append("test data 1"); + data.add(new AMQShortString("test data 2")); + expected.append(" test data 2"); + AMQShortString result = AMQShortString.join(data, new AMQShortString(" ")); + assertEquals("join result differs from expected", expected.toString(), result.asString()); + } + + /** + * A helper method to generate a string with given length containing given + * character + * + * @param ch + * char to build string with + * @param length + * target string length + * @return string + */ + private String buildString(char ch, int length) + { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < length; i++) + { + sb.append(ch); + } + return sb.toString(); + } } |