blob: 32c97bac9dd7fee6ba3814c1e57b2aad1e751f8a [file] [log] [blame]
/*
*
* 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.client;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import javax.jms.Destination;
import javax.jms.Queue;
import javax.jms.Topic;
import org.apache.qpid.test.utils.QpidTestCase;
public class AMQDestinationTest extends QpidTestCase
{
public void testEqualsAndHashCodeForAddressBasedDestinations() throws Exception
{
AMQDestination dest = new AMQQueue("ADDR:Foo; {node :{type:queue}}");
AMQDestination dest1 = new AMQTopic("ADDR:Foo; {node :{type:topic}}");
AMQDestination dest10 = new AMQTopic("ADDR:Foo; {node :{type:topic}, link:{name:my-topic}}");
AMQDestination dest2 = new AMQQueue("ADDR:Foo; {create:always,node :{type:queue}}");
String bUrl = "BURL:direct://amq.direct/test-route/Foo?routingkey='Foo'";
AMQDestination dest3 = new AMQQueue(bUrl);
assertTrue(dest.equals(dest));
assertFalse(dest.equals(dest1));
assertTrue(dest.equals(dest2));
assertFalse(dest.equals(dest3));
assertTrue(dest1.equals(dest10));
assertTrue(dest.hashCode() == dest.hashCode());
assertTrue(dest.hashCode() != dest1.hashCode());
assertTrue(dest.hashCode() == dest2.hashCode());
assertTrue(dest.hashCode() != dest3.hashCode());
assertTrue(dest1.hashCode() == dest10.hashCode());
AMQDestination dest4 = new AMQQueue("ADDR:Foo/Bar; {node :{type:queue}}");
AMQDestination dest5 = new AMQQueue("ADDR:Foo/Bar2; {node :{type:queue}}");
assertFalse(dest4.equals(dest5));
assertTrue(dest4.hashCode() != dest5.hashCode());
AMQDestination dest6 = new AMQAnyDestination("ADDR:Foo; {node :{type:queue}}");
AMQDestination dest7 = new AMQAnyDestination("ADDR:Foo; {create: always, node :{type:queue}, link:{capacity: 10}}");
AMQDestination dest8 = new AMQAnyDestination("ADDR:Foo; {create: always, link:{capacity: 10}}");
AMQDestination dest9 = new AMQAnyDestination("ADDR:Foo/bar");
assertTrue(dest6.equals(dest7));
assertFalse(dest6.equals(dest8)); //dest8 type unknown, could be a topic
assertFalse(dest7.equals(dest8)); //dest8 type unknown, could be a topic
assertFalse(dest6.equals(dest9));
assertTrue(dest6.hashCode() == dest7.hashCode());
assertTrue(dest6.hashCode() != dest8.hashCode());
assertTrue(dest7.hashCode() != dest8.hashCode());
assertTrue(dest6.hashCode() != dest9.hashCode());
}
/**
* Tests that destinations created with the same options string will share the same address options map.
*/
public void testCacheAddressOptionsMaps() throws Exception
{
// Create destinations 1 and 3 with the same options string, and destinations 2 and 4 with a different one
String optionsStringA = "{create: always, node: {type: topic}}";
String optionsStringB = "{}"; // empty options
AMQDestination dest1 = createDestinationWithOptions("testDest1", optionsStringA);
AMQDestination dest2 = createDestinationWithOptions("testDest2", optionsStringB);
AMQDestination dest3 = createDestinationWithOptions("testDest3", optionsStringA);
AMQDestination dest4 = createDestinationWithOptions("testDest4", optionsStringB);
// Destinations 1 and 3 should refer to the same address options map
assertSame("Destinations 1 and 3 were created with the same options and should refer to the same options map.",
dest1.getAddress().getOptions(), dest3.getAddress().getOptions());
// Destinations 2 and 4 should refer to the same address options map
assertSame("Destinations 2 and 4 were created with the same options and should refer to the same options map.",
dest2.getAddress().getOptions(), dest4.getAddress().getOptions());
// Destinations 1 and 2 should have a different options map
assertNotSame("Destinations 1 and 2 should not have the same options map.",
dest1.getAddress().getOptions(), dest2.getAddress().getOptions());
// Verify the contents of the shared map are as expected
Map<String, Object> optionsA = new HashMap<String, Object>();
optionsA.put("create", "always");
optionsA.put("node", Collections.singletonMap("type", "topic"));
assertEquals("Contents of the shared address options map are not as expected.",
optionsA, dest1.getAddress().getOptions());
assertEquals("Contents of the empty shared address options map are not as expected.",
Collections.emptyMap(), dest2.getAddress().getOptions());
// Verify that address options map is immutable
try
{
dest1.getAddress().getOptions().put("testKey", "testValue");
fail("Should not be able able to modify an address's options map.");
}
catch (UnsupportedOperationException e)
{
// expected
}
}
private AMQDestination createDestinationWithOptions(String destName, String optionsString) throws Exception
{
String addr = "ADDR:" + destName + "; " + optionsString;
return new AMQAnyDestination(addr);
}
/**
* Tests that when a queue has no link subscription arguments and no link bindings, its Subscription
* arguments and its bindings list refer to constant empty collections.
*/
public void testEmptyLinkBindingsAndSubscriptionArgs() throws Exception
{
// no link properties
assertEmptyLinkBindingsAndSubscriptionArgs(new AMQAnyDestination("ADDR:testQueue"));
// has link properties but no x-bindings; has link x-subscribes but no arguments
String xSubscribeAddr = "ADDR:testQueueWithXSubscribes; {link: {x-subscribes: {exclusive: true}}}";
assertEmptyLinkBindingsAndSubscriptionArgs(new AMQAnyDestination(xSubscribeAddr));
}
private void assertEmptyLinkBindingsAndSubscriptionArgs(AMQDestination dest)
{
assertEquals("Default link subscription arguments should be the constant Collections empty map.",
Collections.emptyMap(), dest.getLink().getSubscription().getArgs());
assertSame("Defaultl link bindings should be the constant Collections empty list.",
Collections.emptyList(), dest.getLink().getBindings());
}
/**
* Tests that when a node has no bindings specified, its bindings list refers to a constant empty list,
* so that we are not consuming extra memory unnecessarily.
*/
public void testEmptyNodeBindings() throws Exception
{
// no node properties
assertEmptyNodeBindings(new AMQAnyDestination("ADDR:testDest1"));
// has node properties but no x-bindings
assertEmptyNodeBindings(new AMQAnyDestination("ADDR:testDest2; {node: {type: queue}}"));
assertEmptyNodeBindings(new AMQAnyDestination("ADDR:testDest3; {node: {type: topic}}"));
}
public void testSimple_BURL() throws Exception
{
AMQQueue queue = new AMQQueue("BURL:direct://amq.direct/test-route/Foo");
assertEquals("direct", queue.getExchangeClass());
assertEquals("amq.direct", queue.getExchangeName());
assertEquals("Foo", queue.getQueueName());
assertEquals(queue.getQueueName(), queue.getRoutingKey());
assertFalse(queue.isDurable());
}
public void testAnonymousExchange_BURL() throws Exception
{
AMQQueue queue = new AMQQueue("BURL:direct:///Foo?durable='true'");
assertEquals("direct", queue.getExchangeClass());
assertEquals("", queue.getExchangeName());
assertEquals("Foo", queue.getQueueName());
assertEquals(queue.getQueueName(), queue.getRoutingKey());
assertTrue(queue.isDurable());
}
public void testNonDurableExchange_BURL() throws Exception
{
AMQQueue queue = new AMQQueue("BURL:direct://mynondurableexch//Foo?exchangeautodelete='true'");
assertEquals("direct", queue.getExchangeClass());
assertEquals("mynondurableexch", queue.getExchangeName());
assertEquals("Foo", queue.getQueueName());
assertEquals("Foo", queue.getRoutingKey());
assertTrue(queue.isExchangeAutoDelete());
}
public void testSerializeAMQQueue_BURL() throws Exception
{
Queue queue = new AMQQueue("BURL:direct://amq.direct/test-route/Foo?routingkey='Foo'");
assertTrue(queue instanceof Serializable);
Queue deserialisedQueue = (Queue) serialiseDeserialiseDestination(queue);
assertEquals(queue, deserialisedQueue);
assertEquals(queue.hashCode(), deserialisedQueue.hashCode());
}
public void testSerializeAMQQueue_ADDR() throws Exception
{
Queue queue = new AMQQueue("ADDR:testDest2; {node: {type: queue}}");
assertTrue(queue instanceof Serializable);
Queue deserialisedQueue = (Queue) serialiseDeserialiseDestination(queue);
assertEquals(queue, deserialisedQueue);
assertEquals(queue.hashCode(), deserialisedQueue.hashCode());
}
public void testSerializeAMQTopic_BURL() throws Exception
{
Topic topic = new AMQTopic("BURL:topic://amq.topic/mytopic/?routingkey='mytopic'");
assertTrue(topic instanceof Serializable);
Topic deserialisedTopic = (Topic) serialiseDeserialiseDestination(topic);
assertEquals(topic, deserialisedTopic);
assertEquals(topic.hashCode(), deserialisedTopic.hashCode());
}
public void testSerializeAMQTopic_ADDR() throws Exception
{
Topic topic = new AMQTopic("ADDR:my-topic; {assert: always, node:{ type: topic }}");
assertTrue(topic instanceof Serializable);
Topic deserialisedTopic = (Topic) serialiseDeserialiseDestination(topic);
assertEquals(topic, deserialisedTopic);
assertEquals(topic.hashCode(), deserialisedTopic.hashCode());
}
private void assertEmptyNodeBindings(AMQDestination dest)
{
assertSame("Empty node bindings should refer to the constant Collections empty list.",
Collections.emptyList(), dest.getNode().getBindings());
}
private Destination serialiseDeserialiseDestination(final Destination dest) throws Exception
{
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(dest);
oos.close();
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
Object deserializedObject = ois.readObject();
ois.close();
return (Destination)deserializedObject;
}
}