/*
* 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.netty.util.CharsetUtil;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Context;
import io.vertx.core.DeploymentOptions;
import io.vertx.core.Future;
import io.vertx.core.Verticle;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.eventbus.DeliveryOptions;
import io.vertx.core.eventbus.EventBus;
import io.vertx.core.eventbus.MessageCodec;
import io.vertx.core.eventbus.MessageConsumer;
import io.vertx.core.eventbus.ReplyException;
import io.vertx.core.eventbus.ReplyFailure;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import org.junit.Test;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import static org.hamcrest.CoreMatchers.*;
/**
* @author <a href="http://tfox.org">Tim Fox</a>
*/
public abstract class EventBusTestBase extends VertxTestBase {
protected static final String ADDRESS1 = "some-address1";
protected static final String ADDRESS2 = "some-address2";
@Test
public void testSendNull() {
testSend(null);
}
@Test
public void testReplyNull() {
testReply(null);
}
@Test
public void testPublishNull() {
testPublish(null);
}
@Test
public void testSendString() {
String str = TestUtils.randomUnicodeString(100);
testSend(str);
}
@Test
public void testReplyString() {
String str = TestUtils.randomUnicodeString(100);
testReply(str);
}
@Test
public void testPublishString() {
String str = TestUtils.randomUnicodeString(100);
testPublish(str);
}
@Test
public void testSendBooleanTrue() {
testSend(true);
}
@Test
public void testSendBooleanFalse() {
testSend(false);
}
@Test
public void testReplyBooleanTrue() {
testReply(true);
}
@Test
public void testReplyBooleanFalse() {
testReply(false);
}
@Test
public void testPublishBooleanTrue() {
testPublish(true);
}
@Test
public void testPublishBooleanFalse() {
testPublish(false);
}
@Test
public void testSendBuffer() {
Buffer sent = TestUtils.randomBuffer(100);
testSend(sent, (buffer) -> {
assertEquals(sent, buffer);
assertFalse(sent == buffer); // Make sure it's copied
});
}
@Test
public void testReplyBuffer() {
Buffer sent = TestUtils.randomBuffer(100);
testReply(sent, (bytes) -> {
assertEquals(sent, bytes);
assertFalse(sent == bytes); // Make sure it's copied
});
}
@Test
public void testPublishBuffer() {
Buffer sent = TestUtils.randomBuffer(100);
testPublish(sent, (buffer) -> {
assertEquals(sent, buffer);
assertFalse(sent == buffer); // Make sure it's copied
});
}
@Test
public void testSendByte() {
testSend(TestUtils.randomByte());
}
@Test
public void testReplyByte() {
testReply(TestUtils.randomByte());
}
@Test
public void testPublishByte() {
testPublish(TestUtils.randomByte());
}
@Test
public void testSendByteArray() {
byte[] sent = TestUtils.randomByteArray(100);
testSend(sent, (bytes) -> {
TestUtils.byteArraysEqual(sent, bytes);
assertFalse(sent == bytes); // Make sure it's copied
});
}
@Test
public void testReplyByteArray() {
byte[] sent = TestUtils.randomByteArray(100);
testReply(sent, (bytes) -> {
TestUtils.byteArraysEqual(sent, bytes);
assertFalse(sent == bytes); // Make sure it's copied
});
}
@Test
public void testPublishByteArray() {
byte[] sent = TestUtils.randomByteArray(100);
testPublish(sent, (bytes) -> {
TestUtils.byteArraysEqual(sent, bytes);
assertFalse(sent == bytes); // Make sure it's copied
});
}
@Test
public void testSendCharacter() {
testSend(TestUtils.randomChar());
}
@Test
public void testReplyCharacter() {
testReply(TestUtils.randomChar());
}
@Test
public void testPublishCharacter() {
testPublish(TestUtils.randomChar());
}
@Test
public void testSendDouble() {
testSend(TestUtils.randomDouble());
}
@Test
public void testReplyDouble() {
testReply(TestUtils.randomDouble());
}
@Test
public void testPublishDouble() {
testPublish(TestUtils.randomDouble());
}
@Test
public void testSendFloat() {
testSend(TestUtils.randomFloat());
}
@Test
public void testReplyFloat() {
testReply(TestUtils.randomFloat());
}
@Test
public void testPublishFloat() {
testPublish(TestUtils.randomFloat());
}
@Test
public void testSendInteger() {
testSend(TestUtils.randomInt());
}
@Test
public void testReplyInteger() {
testReply(TestUtils.randomInt());
}
@Test
public void testPublishInteger() {
testPublish(TestUtils.randomInt());
}
@Test
public void testSendLong() {
testSend(TestUtils.randomLong());
}
@Test
public void testReplyLong() {
testReply(TestUtils.randomLong());
}
@Test
public void testPublishLong() {
testPublish(TestUtils.randomLong());
}
@Test
public void testSendShort() {
testSend(TestUtils.randomShort());
}
@Test
public void testReplyShort() {
testReply(TestUtils.randomShort());
}
@Test
public void testPublishShort() {
testPublish(TestUtils.randomShort());
}
@Test
public void testSendJsonArray() {
JsonArray arr = new JsonArray();
arr.add(TestUtils.randomUnicodeString(100)).add(TestUtils.randomInt()).add(TestUtils.randomBoolean());
testSend(arr, (received) -> {
assertEquals(arr, received);
assertFalse(arr == received); // Make sure it's copied
});
}
@Test
public void testReplyJsonArray() {
JsonArray arr = new JsonArray();
arr.add(TestUtils.randomUnicodeString(100)).add(TestUtils.randomInt()).add(TestUtils.randomBoolean());
testReply(arr, (received) -> {
assertEquals(arr, received);
assertFalse(arr == received); // Make sure it's copied
});
}
@Test
public void testPublishJsonArray() {
JsonArray arr = new JsonArray();
arr.add(TestUtils.randomUnicodeString(100)).add(TestUtils.randomInt()).add(TestUtils.randomBoolean());
testPublish(arr, (received) -> {
assertEquals(arr, received);
assertFalse(arr == received); // Make sure it's copied
});
}
@Test
public void testSendJsonObject() {
JsonObject obj = new JsonObject();
obj.put(TestUtils.randomUnicodeString(100), TestUtils.randomUnicodeString(100)).put(TestUtils.randomUnicodeString(100), TestUtils.randomInt());
testSend(obj, (received) -> {
assertEquals(obj, received);
assertFalse(obj == received); // Make sure it's copied
});
}
@Test
public void testReplyJsonObject() {
JsonObject obj = new JsonObject();
obj.put(TestUtils.randomUnicodeString(100), TestUtils.randomUnicodeString(100)).put(TestUtils.randomUnicodeString(100), TestUtils.randomInt());
testReply(obj, (received) -> {
assertEquals(obj, received);
assertFalse(obj == received); // Make sure it's copied
});
}
@Test
public void testPublishJsonObject() {
JsonObject obj = new JsonObject();
obj.put(TestUtils.randomUnicodeString(100), TestUtils.randomUnicodeString(100)).put(TestUtils.randomUnicodeString(100), TestUtils.randomInt());
testPublish(obj, (received) -> {
assertEquals(obj, received);
assertFalse(obj == received); // Make sure it's copied
});
}
@Test
public void testSendWithHeaders() {
testSend("foo", "foo", null, new DeliveryOptions().addHeader("uhqwduh", "qijwdqiuwd").addHeader("iojdijef", "iqjwddh"));
}
@Test
public void testSendWithDeliveryOptionsButNoHeaders() {
testSend("foo", "foo", null, new DeliveryOptions());
}
@Test
public void testReplyWithHeaders() {
testReply("foo", "foo", null, new DeliveryOptions().addHeader("uhqwduh", "qijwdqiuwd").addHeader("iojdijef", "iqjwddh"));
}
@Test
public void testReplyFromWorker() throws Exception {
String expectedBody = TestUtils.randomAlphaString(20);
startNodes(2);
CountDownLatch latch = new CountDownLatch(1);
vertices[0].deployVerticle(new AbstractVerticle() {
@Override
public void start() throws Exception {
vertices[1].eventBus().<String>consumer(ADDRESS1, msg -> {
msg.reply(expectedBody);
}).completionHandler(ar -> {
assertTrue(ar.succeeded());
latch.countDown();
});
}
}, new DeploymentOptions().setWorker(true));
awaitLatch(latch);
vertices[0].eventBus().send(ADDRESS1, "whatever", reply -> {
assertTrue(reply.succeeded());
assertEquals(expectedBody, reply.result().body());
testComplete();
});
await();
}
@Test
public void testSendFromExecuteBlocking() throws Exception {
String expectedBody = TestUtils.randomAlphaString(20);
CountDownLatch receivedLatch = new CountDownLatch(1);
startNodes(2);
vertices[1].eventBus().<String>consumer(ADDRESS1, msg -> {
assertEquals(expectedBody, msg.body());
receivedLatch.countDown();
}).completionHandler(ar -> {
assertTrue(ar.succeeded());
vertices[0].executeBlocking(fut -> {
vertices[0].eventBus().send(ADDRESS1, expectedBody);
try {
awaitLatch(receivedLatch); // Make sure message is sent even if we're busy
} catch (InterruptedException e) {
Thread.interrupted();
fut.fail(e);
}
fut.complete();
}, ar2 -> {
if (ar2.succeeded()) {
testComplete();
} else {
fail(ar2.cause());
}
});
});
await();
}
@Test
public void testNoHandlersCallbackContext() {
startNodes(2);
waitFor(4);
// On an "external" thread
vertices[0].eventBus().send("blah", "blah", ar -> {
assertTrue(ar.failed());
if (ar.cause() instanceof ReplyException) {
ReplyException cause = (ReplyException) ar.cause();
assertSame(ReplyFailure.NO_HANDLERS, cause.failureType());
} else {
fail(ar.cause());
}
assertTrue("Not an EL thread", Context.isOnEventLoopThread());
complete();
});
// On a EL context
vertices[0].runOnContext(v -> {
Context ctx = vertices[0].getOrCreateContext();
vertices[0].eventBus().send("blah", "blah", ar -> {
assertTrue(ar.failed());
if (ar.cause() instanceof ReplyException) {
ReplyException cause = (ReplyException) ar.cause();
assertSame(ReplyFailure.NO_HANDLERS, cause.failureType());
} else {
fail(ar.cause());
}
assertSame(ctx, vertices[0].getOrCreateContext());
complete();
});
});
// On a Worker context
vertices[0].deployVerticle(new AbstractVerticle() {
@Override
public void start() throws Exception {
Context ctx = getVertx().getOrCreateContext();
vertices[0].eventBus().send("blah", "blah", ar -> {
assertTrue(ar.failed());
if (ar.cause() instanceof ReplyException) {
ReplyException cause = (ReplyException) ar.cause();
assertSame(ReplyFailure.NO_HANDLERS, cause.failureType());
} else {
fail(ar.cause());
}
assertSame(ctx, getVertx().getOrCreateContext());
complete();
});
}
}, new DeploymentOptions().setWorker(true));
// Inside executeBlocking
vertices[0].executeBlocking(fut -> {
vertices[0].eventBus().send("blah", "blah", ar -> {
assertTrue(ar.failed());
if (ar.cause() instanceof ReplyException) {
ReplyException cause = (ReplyException) ar.cause();
assertSame(ReplyFailure.NO_HANDLERS, cause.failureType());
} else {
fail(ar.cause());
}
assertTrue("Not an EL thread", Context.isOnEventLoopThread());
complete();
});
fut.complete();
}, false, null);
await();
}
private long burnCpu() {
long res = 1;
for (int i = 2; i <= 2000; i++) {
res = res * i;
}
return res;
}
@Test
public void testSendWhileUnsubscribing() throws Exception {
startNodes(2);
AtomicBoolean unregistered = new AtomicBoolean();
Verticle sender = new AbstractVerticle() {
@Override
public void start() throws Exception {
getVertx().runOnContext(v -> sendMsg());
}
private void sendMsg() {
if (!unregistered.get()) {
getVertx().eventBus().send("whatever", "marseille");
burnCpu();
getVertx().runOnContext(v -> sendMsg());
} else {
getVertx().eventBus().send("whatever", "marseille", ar -> {
Throwable cause = ar.cause();
assertThat(cause, instanceOf(ReplyException.class));
ReplyException replyException = (ReplyException) cause;
assertEquals(ReplyFailure.NO_HANDLERS, replyException.failureType());
testComplete();
});
}
}
};
Verticle receiver = new AbstractVerticle() {
boolean unregisterCalled;
@Override
public void start(Future<Void> startFuture) throws Exception {
EventBus eventBus = getVertx().eventBus();
MessageConsumer<String> consumer = eventBus.consumer("whatever");
consumer.handler(m -> {
if (!unregisterCalled) {
consumer.unregister(v -> unregistered.set(true));
unregisterCalled = true;
}
m.reply("ok");
}).completionHandler(startFuture);
}
};
CountDownLatch deployLatch = new CountDownLatch(1);
vertices[0].exceptionHandler(this::fail).deployVerticle(receiver, onSuccess(receiverId -> {
vertices[1].exceptionHandler(this::fail).deployVerticle(sender, onSuccess(senderId -> {
deployLatch.countDown();
}));
}));
awaitLatch(deployLatch);
await();
CountDownLatch closeLatch = new CountDownLatch(2);
vertices[0].close(v -> closeLatch.countDown());
vertices[1].close(v -> closeLatch.countDown());
awaitLatch(closeLatch);
}
protected <T> void testSend(T val) {
testSend(val, null);
}
protected abstract <T, R> void testSend(T val, R received, Consumer<T> consumer, DeliveryOptions options);
protected abstract <T> void testSend(T val, Consumer<T> consumer);
protected <T> void testReply(T val) {
testReply(val, null);
}
protected abstract <T> void testReply(T val, Consumer<T> consumer);
protected abstract <T, R> void testReply(T val, R received, Consumer<R> consumer, DeliveryOptions options);
protected <T> void testPublish(T val) {
testPublish(val, null);
}
protected abstract <T> void testPublish(T val, Consumer<T> consumer);
public static class MySystemDecoder implements MessageCodec<MyPOJO, String> {
@Override
public void encodeToWire(Buffer buffer, MyPOJO s) {
}
@Override
public String decodeFromWire(int pos, Buffer buffer) {
return null;
}
@Override
public String transform(MyPOJO s) {
return null;
}
@Override
public String name() {
return "mysystemdecoder";
}
@Override
public byte systemCodecID() {
return 0;
}
}
public static class NullNameCodec implements MessageCodec<String, String> {
@Override
public void encodeToWire(Buffer buffer, String s) {
}
@Override
public String decodeFromWire(int pos, Buffer buffer) {
return null;
}
@Override
public String transform(String s) {
return null;
}
@Override
public String name() {
return null;
}
@Override
public byte systemCodecID() {
return 0;
}
}
public static class MyPOJOEncoder1 implements MessageCodec<MyPOJO, String> {
@Override
public void encodeToWire(Buffer buffer, MyPOJO myPOJO) {
byte[] bytes = myPOJO.getStr().getBytes(CharsetUtil.UTF_8);
buffer.appendInt(bytes.length);
buffer.appendBytes(bytes);
}
@Override
public String decodeFromWire(int pos, Buffer buffer) {
int length = buffer.getInt(pos);
pos += 4;
byte[] bytes = buffer.getBytes(pos, pos + length);
return new String(bytes, CharsetUtil.UTF_8);
}
@Override
public String transform(MyPOJO myPOJO) {
return myPOJO.getStr();
}
@Override
public String name() {
return "mypojoencoder1";
}
@Override
public byte systemCodecID() {
return -1;
}
}
public static class MyPOJOEncoder2 implements MessageCodec<MyPOJO, MyPOJO> {
@Override
public void encodeToWire(Buffer buffer, MyPOJO myPOJO) {
byte[] bytes = myPOJO.getStr().getBytes(CharsetUtil.UTF_8);
buffer.appendInt(bytes.length);
buffer.appendBytes(bytes);
}
@Override
public MyPOJO decodeFromWire(int pos, Buffer buffer) {
int length = buffer.getInt(pos);
pos += 4;
byte[] bytes = buffer.getBytes(pos, pos + length);
String str = new String(bytes, CharsetUtil.UTF_8);
return new MyPOJO(str);
}
@Override
public MyPOJO transform(MyPOJO myPOJO) {
return new MyPOJO(myPOJO.getStr());
}
@Override
public String name() {
return "mypojoencoder2";
}
@Override
public byte systemCodecID() {
return -1;
}
}
public static class MyPOJO {
private String str;
public MyPOJO(String str) {
this.str = str;
}
public String getStr() {
return str;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MyPOJO myPOJO = (MyPOJO) o;
if (str != null ? !str.equals(myPOJO.str) : myPOJO.str != null) return false;
return true;
}
@Override
public int hashCode() {
return str != null ? str.hashCode() : 0;
}
}
public static class MyReplyException extends ReplyException {
public MyReplyException(int failureCode, String message) {
super(ReplyFailure.RECIPIENT_FAILURE, failureCode, message);
}
}
public static class MyReplyExceptionMessageCodec implements
MessageCodec<MyReplyException, MyReplyException> {
@Override
public void encodeToWire(Buffer buffer, MyReplyException body) {
buffer.appendInt(body.failureCode());
if (body.getMessage() == null) {
buffer.appendByte((byte)0);
} else {
buffer.appendByte((byte)1);
byte[] encoded = body.getMessage().getBytes(CharsetUtil.UTF_8);
buffer.appendInt(encoded.length);
buffer.appendBytes(encoded);
}
}
@Override
public MyReplyException decodeFromWire(int pos, Buffer buffer) {
int failureCode = buffer.getInt(pos);
pos += 4;
boolean isNull = buffer.getByte(pos) == (byte)0;
String message;
if (!isNull) {
pos++;
int strLength = buffer.getInt(pos);
pos += 4;
byte[] bytes = buffer.getBytes(pos, pos + strLength);
message = new String(bytes, CharsetUtil.UTF_8);
} else {
message = null;
}
return new MyReplyException(failureCode, message);
}
@Override
public MyReplyException transform(MyReplyException obj) {
return obj;
}
@Override
public String name() {
return "myReplyException";
}
@Override
public byte systemCodecID() {
return -1;
}
}
}