/** * 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.activemq.transport.amqp.interop; import static org.apache.activemq.transport.amqp.AmqpSupport.CONNECTION_OPEN_FAILED; import static org.apache.activemq.transport.amqp.AmqpSupport.PLATFORM; import static org.apache.activemq.transport.amqp.AmqpSupport.PRODUCT; import static org.apache.activemq.transport.amqp.AmqpSupport.VERSION; import static org.apache.activemq.transport.amqp.AmqpSupport.contains; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; import java.util.Arrays; import java.util.Collection; import java.util.Map; import java.util.concurrent.TimeUnit; import org.apache.activemq.transport.amqp.AmqpSupport; import org.apache.activemq.transport.amqp.client.AmqpClient; import org.apache.activemq.transport.amqp.client.AmqpClientTestSupport; import org.apache.activemq.transport.amqp.client.AmqpConnection; import org.apache.activemq.transport.amqp.client.AmqpMessage; import org.apache.activemq.transport.amqp.client.AmqpReceiver; import org.apache.activemq.transport.amqp.client.AmqpSender; import org.apache.activemq.transport.amqp.client.AmqpSession; import org.apache.activemq.transport.amqp.client.AmqpValidator; import org.apache.qpid.proton.amqp.Symbol; import org.apache.qpid.proton.amqp.transport.AmqpError; import org.apache.qpid.proton.amqp.transport.ErrorCondition; import org.apache.qpid.proton.engine.Connection; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; /** * Test broker handling of AMQP connections with various configurations. */ @RunWith(Parameterized.class) public class AmqpConnectionsTest extends AmqpClientTestSupport { private static final Symbol QUEUE_PREFIX = Symbol.valueOf("queue-prefix"); private static final Symbol TOPIC_PREFIX = Symbol.valueOf("topic-prefix"); private static final Symbol ANONYMOUS_RELAY = Symbol.valueOf("ANONYMOUS-RELAY"); private static final Symbol DELAYED_DELIVERY = Symbol.valueOf("DELAYED_DELIVERY"); @Parameters(name="{0}") public static Collection<Object[]> data() { return Arrays.asList(new Object[][] { {"amqp", false}, {"amqp+ws", false}, {"amqp+ssl", true}, {"amqp+wss", true} }); } public AmqpConnectionsTest(String connectorScheme, boolean secure) { super(connectorScheme, secure); } @Override protected String getAdditionalConfig() { return "&wireFormat.maxAmqpFrameSize=1048576"; } @Test(timeout = 60000) public void testCanConnect() throws Exception { AmqpClient client = createAmqpClient(); assertNotNull(client); AmqpConnection connection = trackConnection(client.connect()); assertNotNull(connection); assertEquals(1, getProxyToBroker().getCurrentConnectionsCount()); Connection protonConnection = connection.getConnection(); org.apache.qpid.proton.engine.Transport protonTransport = protonConnection.getTransport(); protonTransport.getChannelMax(); assertNull(protonTransport.head()); assertNull(protonTransport.tail()); assertNull(protonTransport.getInputBuffer()); assertNull(protonTransport.getOutputBuffer()); try { protonTransport.bind(protonConnection); fail("Should not be able to mutate"); } catch (UnsupportedOperationException e) {} try { protonTransport.close(); fail("Should not be able to mutate"); } catch (UnsupportedOperationException e) {} try { protonTransport.setChannelMax(1); fail("Should not be able to mutate"); } catch (UnsupportedOperationException e) {} connection.close(); assertEquals(0, getProxyToBroker().getCurrentConnectionsCount()); } @Test(timeout = 60000) public void testConnectionCarriesExpectedCapabilities() throws Exception { AmqpClient client = createAmqpClient(); assertNotNull(client); client.setValidator(new AmqpValidator() { @Override public void inspectOpenedResource(Connection connection) { Symbol[] offered = connection.getRemoteOfferedCapabilities(); if (!contains(offered, ANONYMOUS_RELAY)) { markAsInvalid("Broker did not indicate it support anonymous relay"); } if (!contains(offered, DELAYED_DELIVERY)) { markAsInvalid("Broker did not indicate it support delayed message delivery"); } Map<Symbol, Object> properties = connection.getRemoteProperties(); if (!properties.containsKey(QUEUE_PREFIX)) { markAsInvalid("Broker did not send a queue prefix value"); } if (!properties.containsKey(TOPIC_PREFIX)) { markAsInvalid("Broker did not send a queue prefix value"); } if (!properties.containsKey(PRODUCT)) { markAsInvalid("Broker did not send a queue product name value"); } if (!properties.containsKey(VERSION)) { markAsInvalid("Broker did not send a queue version value"); } if (!properties.containsKey(PLATFORM)) { markAsInvalid("Broker did not send a queue platform name value"); } else { LOG.info("Broker platform = {}", properties.get(PLATFORM)); } } }); AmqpConnection connection = trackConnection(client.connect()); assertNotNull(connection); assertEquals(1, getProxyToBroker().getCurrentConnectionsCount()); connection.getStateInspector().assertValid(); connection.close(); assertEquals(0, getProxyToBroker().getCurrentConnectionsCount()); } @Test(timeout = 60000) public void testConnectionCarriesContainerId() throws Exception { AmqpClient client = createAmqpClient(); assertNotNull(client); client.setValidator(new AmqpValidator() { @Override public void inspectOpenedResource(Connection connection) { String remoteContainer = connection.getRemoteContainer(); if (remoteContainer == null || !remoteContainer.equals(brokerService.getBrokerName())) { markAsInvalid("Broker did not send a valid container ID"); } else { LOG.info("Broker container ID = {}", remoteContainer); } } }); AmqpConnection connection = trackConnection(client.connect()); assertNotNull(connection); assertEquals(1, getProxyToBroker().getCurrentConnectionsCount()); connection.getStateInspector().assertValid(); connection.close(); assertEquals(0, getProxyToBroker().getCurrentConnectionsCount()); } @Test(timeout = 60000) public void testCanConnectWithDifferentContainerIds() throws Exception { AmqpClient client = createAmqpClient(); assertNotNull(client); AmqpConnection connection1 = trackConnection(client.createConnection()); AmqpConnection connection2 = trackConnection(client.createConnection()); connection1.setContainerId(getTestName() + "-Client:1"); connection2.setContainerId(getTestName() + "-Client:2"); connection1.connect(); assertEquals(1, getProxyToBroker().getCurrentConnectionsCount()); connection2.connect(); assertEquals(2, getProxyToBroker().getCurrentConnectionsCount()); connection1.close(); assertEquals(1, getProxyToBroker().getCurrentConnectionsCount()); connection2.close(); assertEquals(0, getProxyToBroker().getCurrentConnectionsCount()); } @Test(timeout = 60000) public void testCannotConnectWithSameContainerId() throws Exception { AmqpClient client = createAmqpClient(); assertNotNull(client); AmqpConnection connection1 = trackConnection(client.createConnection()); AmqpConnection connection2 = trackConnection(client.createConnection()); connection1.setContainerId(getTestName()); connection2.setContainerId(getTestName()); connection1.connect(); assertEquals(1, getProxyToBroker().getCurrentConnectionsCount()); connection2.setStateInspector(new AmqpValidator() { @Override public void inspectOpenedResource(Connection connection) { if (!connection.getRemoteProperties().containsKey(CONNECTION_OPEN_FAILED)) { markAsInvalid("Broker did not set connection establishment failed property"); } } @Override public void inspectClosedResource(Connection connection) { ErrorCondition remoteError = connection.getRemoteCondition(); if (remoteError == null || remoteError.getCondition() == null) { markAsInvalid("Broker did not add error condition for duplicate client ID"); } else { if (!remoteError.getCondition().equals(AmqpError.INVALID_FIELD)) { markAsInvalid("Broker did not set condition to " + AmqpError.INVALID_FIELD); } if (!remoteError.getCondition().equals(AmqpError.INVALID_FIELD)) { markAsInvalid("Broker did not set condition to " + AmqpError.INVALID_FIELD); } } // Validate the info map contains a hint that the container/client id was the problem Map<?, ?> infoMap = remoteError.getInfo(); if (infoMap == null) { markAsInvalid("Broker did not set an info map on condition"); } else if (!infoMap.containsKey(AmqpSupport.INVALID_FIELD)) { markAsInvalid("Info map does not contain expected key"); } else { Object value = infoMap.get(AmqpSupport.INVALID_FIELD); if(!AmqpSupport.CONTAINER_ID.equals(value)) { markAsInvalid("Info map does not contain expected value: " + value); } } } }); try { connection2.connect(); fail("Should not be able to connect with same container Id."); } catch (Exception ex) { LOG.info("Second connection with same container Id failed as expected."); } connection2.getStateInspector().assertValid(); assertEquals(1, getProxyToBroker().getCurrentConnectionsCount()); connection1.close(); assertEquals(0, getProxyToBroker().getCurrentConnectionsCount()); } @Test(timeout = 60000) public void testSimpleSendOneReceive() throws Exception { AmqpClient client = createAmqpClient(); AmqpConnection connection = trackConnection(client.connect()); AmqpSession session = connection.createSession(); AmqpSender sender = session.createSender("queue://" + getTestName()); AmqpReceiver receiver = session.createReceiver("queue://" + getTestName()); AmqpMessage message = new AmqpMessage(); final int PAYLOAD_SIZE = 1024 * 1024; byte[] payload = new byte[PAYLOAD_SIZE]; for (int i = 0; i < PAYLOAD_SIZE; i++) { payload[i] = (byte) (i % PAYLOAD_SIZE); } message.setMessageId("msg" + 1); message.setMessageAnnotation("serialNo", 1); message.setBytes(payload); sender.send(message); sender.close(); LOG.info("Attempting to read message with receiver"); receiver.flow(2); AmqpMessage received = receiver.receive(10, TimeUnit.SECONDS); assertNotNull("Should have read message", received); assertEquals("msg1", received.getMessageId()); received.accept(); receiver.close(); connection.close(); } }