/*
* Copyright 2014 Red Hat, Inc.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*/
package io.vertx.test.core;
import io.vertx.core.AsyncResult;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
import io.vertx.core.VertxOptions;
import io.vertx.core.eventbus.*;
import io.vertx.core.impl.VertxInternal;
import io.vertx.core.spi.cluster.ClusterManager;
import io.vertx.test.fakecluster.FakeClusterManager;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Map;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
/**
* @author <a href="http://tfox.org">Tim Fox</a>
*/
public class ClusteredEventBusTest extends ClusteredEventBusTestBase {
@Test
public void testLocalHandlerNotReceive() throws Exception {
startNodes(2);
vertices[1].eventBus().localConsumer(ADDRESS1).handler(msg -> {
fail("Should not receive message");
});
vertices[0].eventBus().send(ADDRESS1, "foo");
vertices[0].setTimer(1000, id -> testComplete());
await();
}
@Test
public void testDecoderSendAsymmetric() throws Exception {
startNodes(2);
MessageCodec codec = new MyPOJOEncoder1();
vertices[0].eventBus().registerCodec(codec);
vertices[1].eventBus().registerCodec(codec);
String str = TestUtils.randomAlphaString(100);
testSend(new MyPOJO(str), str, null, new DeliveryOptions().setCodecName(codec.name()));
}
@Test
public void testDecoderReplyAsymmetric() throws Exception {
startNodes(2);
MessageCodec codec = new MyPOJOEncoder1();
vertices[0].eventBus().registerCodec(codec);
vertices[1].eventBus().registerCodec(codec);
String str = TestUtils.randomAlphaString(100);
testReply(new MyPOJO(str), str, null, new DeliveryOptions().setCodecName(codec.name()));
}
@Test
public void testDecoderSendSymmetric() throws Exception {
startNodes(2);
MessageCodec codec = new MyPOJOEncoder2();
vertices[0].eventBus().registerCodec(codec);
vertices[1].eventBus().registerCodec(codec);
String str = TestUtils.randomAlphaString(100);
MyPOJO pojo = new MyPOJO(str);
testSend(pojo, pojo, null, new DeliveryOptions().setCodecName(codec.name()));
}
@Test
public void testDecoderReplySymmetric() throws Exception {
startNodes(2);
MessageCodec codec = new MyPOJOEncoder2();
vertices[0].eventBus().registerCodec(codec);
vertices[1].eventBus().registerCodec(codec);
String str = TestUtils.randomAlphaString(100);
MyPOJO pojo = new MyPOJO(str);
testReply(pojo, pojo, null, new DeliveryOptions().setCodecName(codec.name()));
}
@Test
public void testDefaultDecoderSendAsymmetric() throws Exception {
startNodes(2);
MessageCodec codec = new MyPOJOEncoder1();
vertices[0].eventBus().registerDefaultCodec(MyPOJO.class, codec);
vertices[1].eventBus().registerDefaultCodec(MyPOJO.class, codec);
String str = TestUtils.randomAlphaString(100);
testSend(new MyPOJO(str), str, null, null);
}
@Test
public void testDefaultDecoderReplyAsymmetric() throws Exception {
startNodes(2);
MessageCodec codec = new MyPOJOEncoder1();
vertices[0].eventBus().registerDefaultCodec(MyPOJO.class, codec);
vertices[1].eventBus().registerDefaultCodec(MyPOJO.class, codec);
String str = TestUtils.randomAlphaString(100);
testReply(new MyPOJO(str), str, null, null);
}
@Test
public void testDefaultDecoderSendSymetric() throws Exception {
startNodes(2);
MessageCodec codec = new MyPOJOEncoder2();
vertices[0].eventBus().registerDefaultCodec(MyPOJO.class, codec);
vertices[1].eventBus().registerDefaultCodec(MyPOJO.class, codec);
String str = TestUtils.randomAlphaString(100);
MyPOJO pojo = new MyPOJO(str);
testSend(pojo, pojo, null, null);
}
@Test
public void testDefaultDecoderReplySymetric() throws Exception {
startNodes(2);
MessageCodec codec = new MyPOJOEncoder2();
vertices[0].eventBus().registerDefaultCodec(MyPOJO.class, codec);
vertices[1].eventBus().registerDefaultCodec(MyPOJO.class, codec);
String str = TestUtils.randomAlphaString(100);
MyPOJO pojo = new MyPOJO(str);
testReply(pojo, pojo, null, null);
}
@Test
public void testDefaultCodecReplyExceptionSubclass() throws Exception {
startNodes(2);
MyReplyException myReplyException = new MyReplyException(23, "my exception");
MyReplyExceptionMessageCodec codec = new MyReplyExceptionMessageCodec();
vertices[0].eventBus().registerDefaultCodec(MyReplyException.class, codec);
vertices[1].eventBus().registerDefaultCodec(MyReplyException.class, codec);
MessageConsumer<ReplyException> reg = vertices[0].eventBus().<ReplyException>consumer(ADDRESS1, msg -> {
assertTrue(msg.body() instanceof MyReplyException);
testComplete();
});
reg.completionHandler(ar -> {
vertices[1].eventBus().send(ADDRESS1, myReplyException);
});
await();
}
// Make sure ping/pong works ok
@Test
public void testClusteredPong() throws Exception {
startNodes(2, new VertxOptions().setClusterPingInterval(500).setClusterPingReplyInterval(500));
AtomicBoolean sending = new AtomicBoolean();
MessageConsumer<String> consumer = vertices[0].eventBus().<String>consumer("foobar").handler(msg -> {
if (!sending.get()) {
sending.set(true);
vertx.setTimer(4000, id -> {
vertices[1].eventBus().send("foobar", "whatever2");
});
} else {
testComplete();
}
});
consumer.completionHandler(ar -> {
assertTrue(ar.succeeded());
vertices[1].eventBus().send("foobar", "whatever");
});
await();
}
@Test
public void testConsumerHandlesCompletionAsynchronously1() {
startNodes(2);
MessageConsumer<Object> consumer = vertices[0].eventBus().consumer(ADDRESS1);
ThreadLocal<Object> stack = new ThreadLocal<>();
stack.set(true);
consumer.completionHandler(v -> {
assertTrue(Vertx.currentContext().isEventLoopContext());
assertNull(stack.get());
testComplete();
});
consumer.handler(msg -> {});
await();
}
@Test
public void testConsumerHandlesCompletionAsynchronously2() {
startNodes(2);
MessageConsumer<Object> consumer = vertices[0].eventBus().consumer(ADDRESS1);
consumer.handler(msg -> {
});
ThreadLocal<Object> stack = new ThreadLocal<>();
stack.set(true);
consumer.completionHandler(v -> {
assertTrue(Vertx.currentContext().isEventLoopContext());
assertNull(stack.get());
testComplete();
});
await();
}
@Test
public void testSubsRemovedForClosedNode() throws Exception {
testSubsRemoved(latch -> {
vertices[1].close(onSuccess(v -> {
latch.countDown();
}));
});
}
@Test
public void testSubsRemovedForKilledNode() throws Exception {
testSubsRemoved(latch -> {
VertxInternal vi = (VertxInternal)vertices[1];
vi.getClusterManager().leave(onSuccess(v -> {
latch.countDown();
}));
});
}
private void testSubsRemoved(Consumer<CountDownLatch> action) throws Exception {
startNodes(3);
CountDownLatch regLatch = new CountDownLatch(1);
AtomicInteger cnt = new AtomicInteger();
vertices[0].eventBus().consumer(ADDRESS1, msg -> {
int c = cnt.getAndIncrement();
assertEquals(msg.body(), "foo" + c);
if (c == 9) {
testComplete();
}
if (c > 9) {
fail("too many messages");
}
}).completionHandler(onSuccess(v -> {
vertices[1].eventBus().consumer(ADDRESS1, msg -> {
fail("shouldn't get message");
}).completionHandler(onSuccess(v2 -> {
regLatch.countDown();
}));
}));
awaitLatch(regLatch);
CountDownLatch closeLatch = new CountDownLatch(1);
action.accept(closeLatch);
awaitLatch(closeLatch);
// Allow time for kill to be propagate
Thread.sleep(2000);
vertices[2].runOnContext(v -> {
// Now send some messages from node 2 - they should ALL go to node 0
EventBus ebSender = vertices[2].eventBus();
for (int i = 0; i < 10; i++) {
ebSender.send(ADDRESS1, "foo" + i);
}
});
await();
}
@Test
public void sendNoContext() throws Exception {
int size = 1000;
ConcurrentLinkedDeque<Integer> expected = new ConcurrentLinkedDeque<>();
ConcurrentLinkedDeque<Integer> obtained = new ConcurrentLinkedDeque<>();
startNodes(2);
CountDownLatch latch = new CountDownLatch(1);
vertices[1].eventBus().<Integer>consumer(ADDRESS1, msg -> {
obtained.add(msg.body());
if (obtained.size() == expected.size()) {
assertEquals(new ArrayList<>(expected), new ArrayList<>(obtained));
testComplete();
}
}).completionHandler(ar -> {
assertTrue(ar.succeeded());
latch.countDown();
});
latch.await();
EventBus bus = vertices[0].eventBus();
for (int i = 0;i < size;i++) {
expected.add(i);
bus.send(ADDRESS1, i);
}
await();
}
}