/*
* 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.artemis.tests.integration.client;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.activemq.artemis.api.core.ActiveMQDisconnectedException;
import org.apache.activemq.artemis.api.core.ActiveMQException;
import org.apache.activemq.artemis.api.core.ActiveMQExceptionType;
import org.apache.activemq.artemis.api.core.ActiveMQIOErrorException;
import org.apache.activemq.artemis.api.core.ActiveMQInternalErrorException;
import org.apache.activemq.artemis.api.core.ActiveMQNonExistentQueueException;
import org.apache.activemq.artemis.api.core.Interceptor;
import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.api.core.client.ClientConsumer;
import org.apache.activemq.artemis.api.core.client.ClientMessage;
import org.apache.activemq.artemis.api.core.client.ClientProducer;
import org.apache.activemq.artemis.api.core.client.ClientSession;
import org.apache.activemq.artemis.api.core.client.ClientSessionFactory;
import org.apache.activemq.artemis.api.core.client.MessageHandler;
import org.apache.activemq.artemis.api.core.client.ServerLocator;
import org.apache.activemq.artemis.core.client.impl.ClientProducerImpl;
import org.apache.activemq.artemis.core.client.impl.ClientSessionInternal;
import org.apache.activemq.artemis.core.protocol.core.Packet;
import org.apache.activemq.artemis.core.protocol.core.impl.PacketImpl;
import org.apache.activemq.artemis.core.protocol.core.impl.RemotingConnectionImpl;
import org.apache.activemq.artemis.core.remoting.CloseListener;
import org.apache.activemq.artemis.api.core.RoutingType;
import org.apache.activemq.artemis.core.server.ServerSession;
import org.apache.activemq.artemis.core.server.impl.ServerSessionImpl;
import org.apache.activemq.artemis.core.settings.impl.AddressFullMessagePolicy;
import org.apache.activemq.artemis.core.settings.impl.AddressSettings;
import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection;
import org.apache.activemq.artemis.tests.integration.IntegrationTestLogger;
import org.apache.activemq.artemis.tests.util.ActiveMQTestBase;
import org.apache.activemq.artemis.tests.util.SingleServerTestBase;
import org.apache.activemq.artemis.utils.RandomUtil;
import org.junit.Test;
public class TemporaryQueueTest extends SingleServerTestBase {
// Constants -----------------------------------------------------
private static final IntegrationTestLogger log = IntegrationTestLogger.LOGGER;
private static final long CONNECTION_TTL = 2000;
// Attributes ----------------------------------------------------
// Static --------------------------------------------------------
// Constructors --------------------------------------------------
// Public --------------------------------------------------------
@Test
public void testConsumeFromTemporaryQueue() throws Exception {
SimpleString queue = RandomUtil.randomSimpleString();
SimpleString address = RandomUtil.randomSimpleString();
session.createTemporaryQueue(address, queue);
ClientProducer producer = session.createProducer(address);
ClientMessage msg = session.createMessage(false);
producer.send(msg);
session.start();
ClientConsumer consumer = session.createConsumer(queue);
ClientMessage message = consumer.receive(500);
assertNotNull(message);
message.acknowledge();
consumer.close();
session.deleteQueue(queue);
session.close();
}
@Test
public void testMemoryLeakOnAddressSettingForTemporaryQueue() throws Exception {
for (int i = 0; i < 1000; i++) {
SimpleString queue = RandomUtil.randomSimpleString();
SimpleString address = RandomUtil.randomSimpleString();
session.createTemporaryQueue(address, queue);
session.close();
session = sf.createSession();
}
session.close();
sf.close();
System.out.println("size = " + server.getAddressSettingsRepository().getCacheSize());
assertTrue(server.getAddressSettingsRepository().getCacheSize() < 10);
}
@Test
public void testPaginStoreIsRemovedWhenQueueIsDeleted() throws Exception {
SimpleString queue = RandomUtil.randomSimpleString();
SimpleString address = RandomUtil.randomSimpleString();
session.createTemporaryQueue(address, queue);
ClientProducer producer = session.createProducer(address);
ClientMessage msg = session.createMessage(false);
producer.send(msg);
session.start();
ClientConsumer consumer = session.createConsumer(queue);
ClientMessage message = consumer.receive(500);
assertNotNull(message);
message.acknowledge();
SimpleString[] storeNames = server.getPagingManager().getStoreNames();
assertTrue(Arrays.asList(storeNames).contains(address));
consumer.close();
session.deleteQueue(queue);
session.close();
storeNames = server.getPagingManager().getStoreNames();
assertFalse(Arrays.asList(storeNames).contains(address));
}
@Test
public void testConsumeFromTemporaryQueueCreatedByOtherSession() throws Exception {
SimpleString queue = RandomUtil.randomSimpleString();
SimpleString address = RandomUtil.randomSimpleString();
session.createTemporaryQueue(address, queue);
ClientProducer producer = session.createProducer(address);
producer.send(session.createMessage(false));
ClientSession session2 = sf.createSession(false, true, true);
session2.start();
ClientConsumer consumer = session2.createConsumer(queue);
ClientMessage message = consumer.receive(500);
assertNotNull(message);
session2.close();
session.close();
}
@Test
public void testDeleteTemporaryQueueAfterConnectionIsClosed() throws Exception {
SimpleString queue = RandomUtil.randomSimpleString();
SimpleString address = RandomUtil.randomSimpleString();
session.createTemporaryQueue(address, queue);
RemotingConnectionImpl conn = (RemotingConnectionImpl) server.getRemotingService().getConnections().iterator().next();
final CountDownLatch latch = new CountDownLatch(1);
conn.addCloseListener(new CloseListener() {
@Override
public void connectionClosed() {
latch.countDown();
}
});
session.close();
sf.close();
// wait for the closing listeners to be fired
assertTrue("connection close listeners not fired", latch.await(2 * TemporaryQueueTest.CONNECTION_TTL, TimeUnit.MILLISECONDS));
sf = addSessionFactory(createSessionFactory(locator));
session = sf.createSession(false, true, true);
session.start();
try {
session.createConsumer(queue);
fail("temp queue must not exist after the remoting connection is closed");
} catch (ActiveMQNonExistentQueueException neqe) {
//ol
} catch (ActiveMQException e) {
fail("Invalid Exception type:" + e.getType());
}
session.close();
}
@Test
public void testQueueWithWildcard() throws Exception {
session.createQueue("a.b", RoutingType.MULTICAST, "queue1");
session.createTemporaryQueue("a.#", RoutingType.MULTICAST, "queue2");
session.createTemporaryQueue("a.#", RoutingType.MULTICAST, "queue3");
ClientProducer producer = session.createProducer("a.b");
producer.send(session.createMessage(false));
ClientConsumer cons = session.createConsumer("queue2");
session.start();
ClientMessage msg = cons.receive(5000);
assertNotNull(msg);
msg.acknowledge();
cons.close();
cons = session.createConsumer("queue3");
session.start();
msg = cons.receive(5000);
assertNotNull(msg);
msg.acknowledge();
cons.close();
session.deleteQueue("queue2");
session.deleteQueue("queue3");
session.close();
}
@Test
public void testQueueWithWildcard2() throws Exception {
session.createQueue("a.b", RoutingType.MULTICAST, "queue1");
session.createTemporaryQueue("a.#", RoutingType.MULTICAST, "queue2");
session.createTemporaryQueue("a.#", RoutingType.MULTICAST, "queue3");
ClientProducer producer = session.createProducer("a.b");
producer.send(session.createMessage(false));
ClientConsumer cons = session.createConsumer("queue2");
session.start();
ClientMessage msg = cons.receive(5000);
assertNotNull(msg);
msg.acknowledge();
cons.close();
cons = session.createConsumer("queue3");
session.start();
msg = cons.receive(5000);
assertNotNull(msg);
msg.acknowledge();
cons.close();
session.deleteQueue("queue2");
session.deleteQueue("queue3");
session.close();
}
@Test
public void testQueueWithWildcard3() throws Exception {
session.createQueue("a.b", RoutingType.MULTICAST, "queue1");
session.createTemporaryQueue("a.#", RoutingType.MULTICAST, "queue2");
session.createTemporaryQueue("a.#", RoutingType.MULTICAST, "queue2.1");
session.deleteQueue("queue2");
}
@Test
public void testDeleteTemporaryQueueAfterConnectionIsClosed_2() throws Exception {
SimpleString queue = RandomUtil.randomSimpleString();
SimpleString address = RandomUtil.randomSimpleString();
session.createTemporaryQueue(address, queue);
assertEquals(1, server.getConnectionCount());
// we create a second session. the temp queue must be present
// even after we closed the session which created it
ClientSession session2 = sf.createSession(false, true, true);
session.close();
// let some time for the server to clean the connections
// Thread.sleep(1000);
session2.start();
session2.createConsumer(queue);
session2.close();
}
@Test
public void testRecreateConsumerOverServerFailure() throws Exception {
ServerLocator serverWithReattach = createInVMNonHALocator().setReconnectAttempts(-1).setRetryInterval(1000).setConfirmationWindowSize(-1).setConnectionTTL(TemporaryQueueTest.CONNECTION_TTL).setClientFailureCheckPeriod(TemporaryQueueTest.CONNECTION_TTL / 3);
ClientSessionFactory reattachSF = createSessionFactory(serverWithReattach);
ClientSession session = reattachSF.createSession(false, false);
session.createTemporaryQueue("tmpAd", "tmpQ");
ClientConsumer consumer = session.createConsumer("tmpQ");
ClientProducer prod = session.createProducer("tmpAd");
session.start();
RemotingConnectionImpl conn = (RemotingConnectionImpl) ((ClientSessionInternal) session).getConnection();
conn.fail(new ActiveMQIOErrorException());
prod.send(session.createMessage(false));
session.commit();
assertNotNull(consumer.receive(1000));
session.close();
reattachSF.close();
serverWithReattach.close();
}
@Test
public void testTemporaryQueuesWithFilter() throws Exception {
int countTmpQueue = 0;
final AtomicInteger errors = new AtomicInteger(0);
class MyHandler implements MessageHandler {
final String color;
final CountDownLatch latch;
final ClientSession sess;
MyHandler(ClientSession sess, String color, int expectedMessages) {
this.sess = sess;
latch = new CountDownLatch(expectedMessages);
this.color = color;
}
public boolean waitCompletion() throws Exception {
return latch.await(10, TimeUnit.SECONDS);
}
@Override
public void onMessage(ClientMessage message) {
try {
message.acknowledge();
sess.commit();
latch.countDown();
if (!message.getStringProperty("color").equals(color)) {
log.warn("Unexpected color " + message.getStringProperty("color") + " when we were expecting " + color);
errors.incrementAndGet();
}
} catch (Exception e) {
log.warn(e.getMessage(), e);
errors.incrementAndGet();
}
}
}
String address = "AD_test";
int iterations = 100;
int msgs = 100;
for (int i = 0; i < iterations; i++) {
ClientSessionFactory clientsConnecton = addSessionFactory(createSessionFactory(locator));
ClientSession localSession = clientsConnecton.createSession();
ClientProducer prod = localSession.createProducer(address);
localSession.start();
log.info("Iteration " + i);
String queueRed = address + "_red_" + (countTmpQueue++);
String queueBlue = address + "_blue_" + (countTmpQueue++);
ClientSession sessConsumerRed = clientsConnecton.createSession();
sessConsumerRed.createTemporaryQueue(address, queueRed, "color='red'");
MyHandler redHandler = new MyHandler(sessConsumerRed, "red", msgs);
ClientConsumer redClientConsumer = sessConsumerRed.createConsumer(queueRed);
redClientConsumer.setMessageHandler(redHandler);
sessConsumerRed.start();
ClientSession sessConsumerBlue = clientsConnecton.createSession();
sessConsumerBlue.createTemporaryQueue(address, queueBlue, "color='blue'");
MyHandler blueHandler = new MyHandler(sessConsumerBlue, "blue", msgs);
ClientConsumer blueClientConsumer = sessConsumerBlue.createConsumer(queueBlue);
blueClientConsumer.setMessageHandler(blueHandler);
sessConsumerBlue.start();
try {
ClientMessage msgBlue = session.createMessage(false);
msgBlue.putStringProperty("color", "blue");
ClientMessage msgRed = session.createMessage(false);
msgRed.putStringProperty("color", "red");
for (int nmsg = 0; nmsg < msgs; nmsg++) {
prod.send(msgBlue);
prod.send(msgRed);
session.commit();
}
blueHandler.waitCompletion();
redHandler.waitCompletion();
assertEquals(0, errors.get());
} finally {
localSession.close();
clientsConnecton.close();
}
}
}
@Test
public void testDeleteTemporaryQueueWhenClientCrash() throws Exception {
session.close();
sf.close();
final SimpleString queue = RandomUtil.randomSimpleString();
SimpleString address = RandomUtil.randomSimpleString();
// server must received at least one ping from the client to pass
// so that the server connection TTL is configured with the client value
final CountDownLatch pingOnServerLatch = new CountDownLatch(1);
server.getRemotingService().addIncomingInterceptor(new Interceptor() {
@Override
public boolean intercept(final Packet packet, final RemotingConnection connection) throws ActiveMQException {
if (packet.getType() == PacketImpl.PING) {
pingOnServerLatch.countDown();
}
return true;
}
});
ServerLocator locator = createInVMNonHALocator();
locator.setConnectionTTL(TemporaryQueueTest.CONNECTION_TTL);
sf = addSessionFactory(createSessionFactory(locator));
session = sf.createSession(false, true, true);
session.createTemporaryQueue(address, queue);
assertTrue("server has not received any ping from the client", pingOnServerLatch.await(2 * server.getConfiguration().getConnectionTtlCheckInterval(), TimeUnit.MILLISECONDS));
assertEquals(1, server.getConnectionCount());
RemotingConnection remotingConnection = server.getRemotingService().getConnections().iterator().next();
final CountDownLatch serverCloseLatch = new CountDownLatch(1);
remotingConnection.addCloseListener(new CloseListener() {
@Override
public void connectionClosed() {
serverCloseLatch.countDown();
}
});
((ClientSessionInternal) session).getConnection().fail(new ActiveMQInternalErrorException("simulate a client failure"));
// let some time for the server to clean the connections
assertTrue("server has not closed the connection", serverCloseLatch.await(2 * server.getConfiguration().getConnectionTtlCheckInterval() + 2 * TemporaryQueueTest.CONNECTION_TTL, TimeUnit.MILLISECONDS));
// The next getCount will be asynchronously done at the end of failure. We will wait some time until it has reached there.
for (long timeout = System.currentTimeMillis() + 5000; timeout > System.currentTimeMillis() && server.getConnectionCount() > 0; ) {
Thread.sleep(1);
}
assertEquals(0, server.getConnectionCount());
session.close();
sf.close();
ServerLocator locator2 = createInVMNonHALocator();
sf = addSessionFactory(createSessionFactory(locator2));
session = sf.createSession(false, true, true);
session.start();
ActiveMQAction activeMQAction = new ActiveMQAction() {
@Override
public void run() throws ActiveMQException {
session.createConsumer(queue);
}
};
ActiveMQTestBase.expectActiveMQException("temp queue must not exist after the server detected the client crash", ActiveMQExceptionType.QUEUE_DOES_NOT_EXIST, activeMQAction);
session.close();
locator2.close();
}
@Test
public void testBlockingWithTemporaryQueue() throws Exception {
AddressSettings setting = new AddressSettings().setAddressFullMessagePolicy(AddressFullMessagePolicy.BLOCK).setMaxSizeBytes(1024 * 1024);
server.getAddressSettingsRepository().addMatch("TestAD", setting);
ClientSessionFactory consumerCF = createSessionFactory(locator);
ClientSession consumerSession = consumerCF.createSession(true, true);
consumerSession.addMetaData("consumer", "consumer");
consumerSession.createTemporaryQueue("TestAD", "Q1");
consumerSession.createConsumer("Q1");
consumerSession.start();
final ClientProducerImpl prod = (ClientProducerImpl) session.createProducer("TestAD");
final AtomicInteger errors = new AtomicInteger(0);
final AtomicInteger msgs = new AtomicInteger(0);
final int TOTAL_MSG = 1000;
Thread t = new Thread() {
@Override
public void run() {
try {
for (int i = 0; i < TOTAL_MSG; i++) {
ClientMessage msg = session.createMessage(false);
msg.getBodyBuffer().writeBytes(new byte[1024]);
prod.send(msg);
msgs.incrementAndGet();
}
} catch (Throwable e) {
e.printStackTrace();
errors.incrementAndGet();
}
System.out.println("done");
}
};
t.start();
while (msgs.get() == 0) {
Thread.sleep(100);
}
int blockedTime = 0;
// https://issues.apache.org/jira/browse/ARTEMIS-368
while (t.isAlive() && errors.get() == 0 && (!prod.getProducerCredits().isBlocked() || blockedTime < 60)) {
if (prod.getProducerCredits().isBlocked()) {
blockedTime++;
} else {
blockedTime = 0;
}
Thread.sleep(100);
}
assertEquals(0, errors.get());
ClientSessionFactory newConsumerCF = createSessionFactory(locator);
ClientSession newConsumerSession = newConsumerCF.createSession(true, true);
newConsumerSession.createTemporaryQueue("TestAD", "Q2");
ClientConsumer newConsumer = newConsumerSession.createConsumer("Q2");
newConsumerSession.start();
int toReceive = TOTAL_MSG - msgs.get();
for (ServerSession sessionIterator : server.getSessions()) {
if (sessionIterator.getMetaData("consumer") != null) {
System.out.println("Failing session");
ServerSessionImpl impl = (ServerSessionImpl) sessionIterator;
impl.getRemotingConnection().fail(new ActiveMQDisconnectedException("failure e"));
}
}
int secondReceive = 0;
ClientMessage msg = null;
while (secondReceive < toReceive && (msg = newConsumer.receive(5000)) != null) {
msg.acknowledge();
secondReceive++;
}
assertNull(newConsumer.receiveImmediate());
assertEquals(toReceive, secondReceive);
t.join();
}
// Package protected ---------------------------------------------
// Protected -----------------------------------------------------
}