/*
* 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.paging;
import java.util.concurrent.atomic.AtomicInteger;
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.ServerLocator;
import org.apache.activemq.artemis.core.server.ActiveMQServer;
import org.apache.activemq.artemis.core.server.Queue;
import org.apache.activemq.artemis.api.core.RoutingType;
import org.apache.activemq.artemis.tests.util.ActiveMQTestBase;
import org.junit.After;
import org.junit.Test;
public class PagingWithFailoverAndCountersTest extends ActiveMQTestBase {
Process liveProcess;
Process backupProcess;
PagingWithFailoverServer inProcessBackup;
private static final int PORT1 = 5000;
private static final int PORT2 = 5001;
private void startLive() throws Exception {
assertNull(liveProcess);
liveProcess = PagingWithFailoverServer.spawnVM(getTestDir(), PORT1, PORT2);
}
private void startBackupInProcess() throws Exception {
assertNull(backupProcess);
assertNull(inProcessBackup);
inProcessBackup = new PagingWithFailoverServer();
inProcessBackup.perform(getTestDir(), PORT2, PORT1, true);
}
@Override
@After
public void tearDown() throws Exception {
killLive();
killBackup();
super.tearDown();
}
private void killBackup() {
try {
if (backupProcess != null) {
backupProcess.destroy();
}
} catch (Throwable ignored) {
}
backupProcess = null;
if (inProcessBackup != null) {
try {
inProcessBackup.getServer().stop(false);
} catch (Throwable ignored) {
ignored.printStackTrace();
}
inProcessBackup = null;
}
}
private void killLive() {
try {
if (liveProcess != null) {
liveProcess.destroy();
}
} catch (Throwable ignored) {
}
liveProcess = null;
}
class TestThread extends Thread {
TestThread() {
}
TestThread(String name) {
super(name);
}
boolean running = true;
final Object waitNotify = new Object();
private boolean failed = false;
public void failed(String message) {
System.err.println(message);
failed = true;
}
public boolean isFailed() {
return failed;
}
public void stopTest() {
synchronized (waitNotify) {
running = false;
waitNotify.notifyAll();
}
while (this.isAlive()) {
try {
this.join(5000);
} catch (Throwable ignored) {
}
if (this.isAlive()) {
this.interrupt();
}
}
assertFalse(failed);
}
public boolean isRunning(long timeWait) {
synchronized (waitNotify) {
try {
if (timeWait > 0) {
long timeout = System.currentTimeMillis() + timeWait;
while (running && timeout > System.currentTimeMillis()) {
waitNotify.wait(timeWait);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
}
return running;
}
}
}
class ConsumerThread extends TestThread {
ClientSessionFactory factory;
String queueName;
final AtomicInteger errors = new AtomicInteger(0);
final int txSize;
ConsumerThread(ClientSessionFactory factory, String queueName, long delayEachMessage, int txSize) {
this.factory = factory;
this.queueName = queueName;
this.txSize = txSize;
}
@Override
public void run() {
try {
ClientSession session;
if (txSize == 0) {
session = factory.createSession(true, true);
} else {
session = factory.createSession(false, false);
}
ClientConsumer cons = session.createConsumer(queueName);
session.start();
long lastCommit = 0;
int msgcount = 0;
long currentMsg = 0;
while (isRunning(0)) {
try {
ClientMessage msg = cons.receive(100);
if (msg != null) {
currentMsg = msg.getLongProperty("count");
if (currentMsg < lastCommit) {
failed("Message received in duplicate out of order, LastCommit = " + lastCommit + ", currentMsg = " + currentMsg);
}
msg.acknowledge();
if (txSize > 0 && msgcount > 0 && (msgcount % txSize == 0)) {
session.commit();
if (currentMsg > lastCommit) {
lastCommit = currentMsg;
} else {
System.out.println("Ignoring setting lastCommit (" + lastCommit + ") <= currentMSG (" + currentMsg + ")");
}
}
msgcount++;
}
if (msgcount % 100 == 0) {
System.out.println("received " + msgcount + " on " + queueName);
}
} catch (Throwable e) {
System.out.println("=====> expected Error at " + currentMsg + " with lastCommit=" + lastCommit);
}
}
session.close();
} catch (Exception e) {
e.printStackTrace();
errors.incrementAndGet();
}
}
}
class MonitorThread extends TestThread {
MonitorThread() {
super("Monitor-thread");
}
@Override
public void run() {
ActiveMQServer server = inProcessBackup.getServer();
try {
waitForServerToStart(server);
// The best to way to validate if the server is ready and operating is to send and consume at least one message
// before we could do valid monitoring
try {
ServerLocator locator = SpawnedServerSupport.createLocator(PORT2).setInitialConnectAttempts(100).setRetryInterval(100);
ClientSessionFactory factory = locator.createSessionFactory();
ClientSession session = factory.createSession();
session.createQueue("new-queue", RoutingType.ANYCAST, "new-queue");
System.out.println("created queue");
session.start();
ClientProducer prod = session.createProducer("new-queue");
prod.send(session.createMessage(true));
ClientConsumer cons = session.createConsumer("new-queue");
cons.receive(500).acknowledge();
cons.close();
session.deleteQueue("new-queue");
locator.close();
} catch (Throwable e) {
e.printStackTrace();
fail(e.getMessage());
}
System.out.println("Started monitoring");
Queue queue2 = inProcessBackup.getServer().locateQueue(SimpleString.toSimpleString("cons2"));
while (isRunning(1)) {
long count = getMessageCount(queue2);
if (count < 0) {
fail("count < 0 .... being " + count);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Test
public void testValidateDeliveryAndCounters() throws Exception {
startLive();
ServerLocator locator = SpawnedServerSupport.createLocator(PORT1).setInitialConnectAttempts(-1).setReconnectAttempts(-1).setRetryInterval(100);
ClientSessionFactory factory = locator.createSessionFactory();
ClientSession session = factory.createSession();
session.createQueue("myAddress", "DeadConsumer", true);
session.createQueue("myAddress", "cons2", true);
startBackupInProcess();
waitForRemoteBackup(factory, 10);
ConsumerThread tConsumer = new ConsumerThread(factory, "cons2", 0, 10);
tConsumer.start();
MonitorThread monitor = new MonitorThread();
ClientProducer prod = session.createProducer("myAddress");
long i = 0;
long timeRun = System.currentTimeMillis() + 5000;
long timeKill = System.currentTimeMillis() + 2000;
while (System.currentTimeMillis() < timeRun) {
i++;
if (System.currentTimeMillis() > timeKill && liveProcess != null) {
killLive();
monitor.start();
}
try {
ClientMessage msg = session.createMessage(true);
msg.putLongProperty("count", i);
prod.send(msg);
} catch (Exception e) {
e.printStackTrace();
}
}
try {
tConsumer.stopTest();
monitor.stopTest();
} finally {
killBackup();
killLive();
}
factory.close();
verifyServer();
}
public void verifyServer() throws Exception {
ServerLocator locator;
ClientSessionFactory factory;
ClientSession session;
ActiveMQServer server = PagingWithFailoverServer.createServer(getTestDir(), PORT1, PORT2, false);
server.start();
waitForServerToStart(server);
Queue queue = server.locateQueue(SimpleString.toSimpleString("cons2"));
int messageCount = getMessageCount(queue);
assertTrue(messageCount >= 0);
locator = SpawnedServerSupport.createLocator(PORT1).setInitialConnectAttempts(100).setReconnectAttempts(-1).setRetryInterval(100);
factory = locator.createSessionFactory();
session = factory.createSession();
session.start();
try {
drainConsumer(session.createConsumer("cons2"), "cons2", messageCount);
} finally {
session.close();
factory.close();
locator.close();
server.stop();
}
}
private void drainConsumer(ClientConsumer consumer, String name, int expectedCount) throws Exception {
for (int i = 0; i < expectedCount; i++) {
ClientMessage msg = consumer.receive(5000);
assertNotNull(msg);
msg.acknowledge();
}
assertNull(consumer.receiveImmediate());
consumer.close();
}
}