VertX JUnit 5 MongoDB test does not complete (TimeoutException) or completes too early (testContext.awaitCompletion not working) - mongodb

Background:
During migration from JUnit4 to JUnit5 using VertX I read the migration guides which explain:
how to use the changed Promise and Future Vertx interfaces
how to VertxTestContext, Vertx auto-injection in Vertx Tests
how to use testContext.awaitCondition(), textContext.completing(), testContext.completeNow() etc.
Having this information in mind I wrote the following test:
Test Code:
import io.vertx.core.Promise;
import io.vertx.core.Future;
#ExtendWith(VertxExtension.class)
class RestApiTest {
#BeforeAll
static void setUpMongoDatabase() throws IOException {
(...)
}
#BeforeEach
void setUp(Vertx vertx, VertxTestContext ctx) {
vertx.deployVerticle(ApiVerticle.class.getName(), options, ctx.completing());
return WebClient.create(vertx);
}
#AfterEach
void tearDown(Vertx vertx, VertxTestContext testContext) {
assertThat(vertx.deploymentIDs().size(), is(equalTo(2)));
}
#AfterAll
static void stopMongoDatabase() {
(...)
}
#Test
void test(Vertx vertx, VertxTestContext testContext) {
Future<Void> insertFuture = insertTestData();
future.setHandler(testContext.completing());
// This ether throws a TimeoutException or does not block until the insert completed
testContext.awaitCompletion(5, TimeUnit.SECONDS);
// assert
mongoClient.findOne(COLLECTION, QUERY, result -> {
if (result.succeeded()) testContext.completeNow();
else testContext.failNow();
});
}
Future<Void> insertTestData() {
Promise<Void> promise = Promise.promise();
Future<Void> future = promise.future();
mongoClient.insert(COLLECTION, QUERY, result -> {
if (result.succeeded()) {
promise.complete();
} else {
promise.fail();
}
});
return future;
}
}
Problem:
testContext.awaitCompletion() ether throws a TimeoutException
or does not block until the async insert completed so that my assert returns successfully
Question:
How can I wait for the async mongo query to complete before I continue with my test?

The problem was that I am using the VertX Promise and Future classes:
those classes only work on a VertX Verticle
my insertTestData() method is not executed on a Verticle
One solution is to use the java.util.concurrent.ReentrantLock and Condition classes instead:
#Test
void test() {
insertTestData(); // This is now synchronous as required
// assert
mongoClient.findOne(COLLECTION, QUERY, result -> {
if (result.succeeded()) testContext.completeNow();
else testContext.failNow();
});
}
void insertTestData() {
ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
mongoClient.insert(COLLECTION, QUERY, result -> {
if (result.succeeded()) {
lock.lock();
try {
condition.signal();
} finally {
lock.unlock();
}
} else {
fail();
}
});
lock.lock();
try {
condition.await(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
throw new IllegalStateException(e);
} finally {
lock.unlock();
}
}
}

Related

Asynchronous GET on REST with okhttp3

I am trying to perform an asynchronous GET-request on my openHAB-project. I have done it before and reused parts of my code to create a new Android app, but it is not working.
In theory I want the state of the "GastSwitch"-item to be written into a String (gastSwitchState) to then be used as a trigger for opening a different activity. If the result of the request is "OFF" the app is supposed to keep running, but stay in the MainActivity.
When debugging it seems like the getGastSwitchState-method is jumped entirely after the enqeue-Method is called. Can someone explain to me, why my code seems to leave out half of the method?
I know that this way of doing it should work, but I can't find where I went wrong.
//connect with REST-API in openHAB :
// GET Status GastSwitch: if Switch = "ON" go to MeetingActivity
//Timer to GET the GastSwitch-status every 30 seconds:
TimerTask gastSwitchTimerTask = new TimerTask() {
public void run() {
try {
getGastSwitchState("myURLforOpenHABGastSwitchState", new Callback() {
#Override
public void getParameter(String string) {
if (gastSwitch.equals("ON")) {
Intent activityIntent = new Intent(getApplicationContext(), MeetingActivity.class);
startActivity(activityIntent);
}
}
});
} catch (IOException e) {
e.printStackTrace();
tvLog.setText(e.toString());
}
}
};
// Timer for GETting the GastSwitch-state every 30 seconds
long emergencyDelay = 1000 * 30 * 1;
Timer gastSwitchTimer = new Timer();
gastSwitchTimer.schedule(gastSwitchTimerTask, 0, emergencyDelay);
}
//Method for GETting the GastSwitch-state from REST-API:
void getGastSwitchState(String url, final Callback callback) throws IOException {
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
okhttp3.Request request = new okhttp3.Request.Builder()
.url(url)
.method("GET", null)
.addHeader("AuthToken", "")
.build();
client.newCall(request)
.enqueue(new okhttp3.Callback() {
#Override
public void onResponse(#NotNull okhttp3.Call call, #NotNull Response response) throws IOException {
if (response.isSuccessful()) {
final String res = response.body().string();
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
gastSwitch = res;
tvLog.setText(gastSwitch);
callback.getParameter(gastSwitch);
}
});
}
}
#Override
public void onFailure(#NotNull okhttp3.Call call, #NotNull IOException e) {
runOnUiThread(new Runnable() {
#Override
public void run() {
}
});
}
});

RxJava Problem with reading a file with Observable and take operator

My working environment is JDK 1.6 and RxJava 2
I want to make an Observable which emits an item that is a file line string read via BufferedReader as follows:
...
Observable<String> fileLineObservable = Observable.defer(new Callable<String>(){
return new ObservableSource<String> call() throws Exception {
return new ObservableSource<String>() {
public void subscribe(Observer<String> observer) {
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(filePath));
String line = null;
while ((line = reader.readLine()) != null) {
observer.onNext(line);
}
observer.onComplete();
... catching exception and close reader
}
}
}
}
});
I also want to make an Observer that observes the above Observable with one take(count) operator as follows:
fileLineObservable.take(2)
.subscribe(new Consumer<String>() {
public void onNext(String line) {
... do something with the file line string
}
});
I meet NullPointerException when executing the above code and I know why. The NPE is caused by that the second call of onNext leads to execute onComplete on the TakeObserver instance and inside the onComplete method, upstream.dispose that is not set(null) is called. The upstream variable of TakeObserver is supposed to be set with onSubscribe(Disposable disposable) when it subscribes an Observable.
How can I solve this problem? Should I implement my own Disposable class to set the upstream of TakeObserver?
What about this solution?
Observable<String> observableFile2(Path path) {
return Observable.using(
() -> Files.newBufferedReader(path),
reader -> {
return Observable.fromIterable(() -> {
return new Iterator<>() {
private String nextLine = null;
#Override
public boolean hasNext() {
try {
nextLine = reader.readLine();
return nextLine != null;
} catch (Exception ex) {
return false;
}
}
#Override
public String next() {
if (nextLine != null) {
return nextLine;
}
throw new IllegalStateException("nextLine can not be null.");
}
};
});
},
BufferedReader::close
);
}
Observable#using makes sure, that the BufferedReader is closed properly on disposable / onError
Observable#fromIterable wraps the readLine calls and handles onComplete for us.
Testing
testImplementation("org.junit.jupiter:junit-jupiter-api:5.6.2")
testRuntimeOnly("org.junit.platform:junit-platform-launcher:1.6.2")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.6.2")
testRuntimeOnly("org.junit.vintage:junit-vintage-engine:5.6.2")
testImplementation("com.google.jimfs:jimfs:1.1")
Tests
#Test
void name() {
observableFile2(hello).take(2)
.test()
.assertValues("line0", "line1")
.assertComplete();
}
#Test
void name2() {
observableFile2(hello).take(10)
.test()
.assertValues("line0", "line1", "line2", "line3")
.assertComplete();
}
#Test
void name3() {
observableFile2(hello2)
.test()
.assertComplete();
}

Unity How to StartCoroutine OnApplicationQuit?

I want to fire StartCoroutine(LogOutUser(url, () => { Debug.Log("logOut req done"); }));, which sends data to server ,on OnApplicationQuit function . however it gives this error *
NullReferenceException: Object reference not set to an instance of an object
RegisterLogIn+d__16.MoveNext () (at Assets/Scripts/RegisterLogIn.cs:71)*
RegisterLogIn is class of code below
void LogOut()
{
string url = String.Format("http://localhost:7989/RegisterApi/logout?myTempID={0}", myTempID);
StartCoroutine(LogOutUser(url, () => { Debug.Log("logOut req done"); }));
}
private async void OnApplicationQuit()
{
LogOut();
await websocket.Close();
}
IEnumerator LogOutUser(string url, Action onSuccess)
{
UnityWebRequest req = UnityWebRequest.Get(url);
yield return req.SendWebRequest();
while (!req.isDone)
yield return null;
string result = req.downloadHandler.text;
onSuccess();
}
how can i do this ?
I understand OnApplicationQuit not wait for coroutines because is like another thread and continue with their job.
You can wrapper your own application quit to handle your logout.
public class MyGame : MonoBehaviour
{
public void GameLogic()
{
MyApplication.QuitWithLogOut();
}
}
public class MyApplication : Application
{
public static void QuitWithLogOut()
{
Quiter quiter = new GameObject().AddComponent<Quiter>();
quiter.Quit();
}
}
public class Quiter : MonoBehaviour
{
private bool isLogOutDone;
public void Quit()
{
string url = String.Format("http://localhost:7989/RegisterApi/logout?myTempID={0}", myTempID);
StartCoroutine(LogOutUser(url, () => { Debug.Log("logOut req done"); }));
}
LogOutUser(string url, Action onSuccess)
{
UnityWebRequest req = UnityWebRequest.Get(url);
yield return req.SendWebRequest();
while (!req.isDone)
yield return null;
string result = req.downloadHandler.text;
isLogOutDone = true;
onSuccess();
}
}
If you start a coroutine in OnApplicationQuit.. the application will quit before the coroutine has a chance to finish, and all objects are being destroyed.
According to this thread a simple Get SendWebRequest is threaded with no dependency on the main thread, so it should be safe to block the main thread until complete.
https://forum.unity.com/threads/http-requests-without-coroutines.495418/
You can try blocking the main thread from returning and wait for your network request, but I think you only have a few seconds before unity will hard terminate in that case.
Try this:
void LogOut()
{
string url = String.Format("http://localhost:7989/RegisterApi/logout?myTempID={0}", myTempID);
LogOutUser(url, () => { Debug.Log("logOut req done"); });
}
private async void OnApplicationQuit()
{
LogOut();
await websocket.Close();
}
void LogOutUser(string url, Action onSuccess)
{
UnityWebRequest req = UnityWebRequest.Get(url);
req.SendWebRequest();
while (!req.isDone) {
System.Threading.Thread.Sleep(100);
}
string result = req.downloadHandler.text;
onSuccess();
}

Vertx instance variable is null when trying to access it from it's method

Below is verticle
package com.api.redis.gateway.verticle;
import java.util.UUID;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RoutingContext;
import io.vertx.redis.RedisClient;
import io.vertx.redis.RedisOptions;
public class SimpleRestChild extends SimpleRestServer{
RedisClient client;
#Override
public void start() {
// TODO Auto-generated method stub
super.start();
client = RedisClient.create(vertx, new RedisOptions().setHost("127.0.0.1").setPort(6379));
client.subscribe("channelForServiceToPublish", handler -> {
if(handler.succeeded())
System.out.println("SimpleRestServer subscibed to the channel successfully");
});
}
public void handleSubscription(RoutingContext routingContext) {
JsonObject requestAsJson = routingContext.getBodyAsJson();
requestAsJson.put("uuid", getUUID());
// this client object is null.
client.set("request", requestAsJson.toString(), handler ->{
System.out.println("Simple server is setting value to redis client");
if(handler.succeeded()) {
System.out.println("Key and value is stored in Redis Server");
}else if(handler.failed()) {
System.out.println("Key and value is failed to be stored on Redis Server with cause : "+ handler.cause().getMessage());
}
});
client.publish("channelForServerToPublish", "ServiceOne", handler -> {
if(handler.succeeded()) {
System.out.println("Simple Server published message successfully");
}else if(handler.failed()) {
System.out.println("Simple Server failed to published message");
}
});
routingContext.vertx().eventBus().consumer("io.vertx.redis.channelForServiceToPublish", handler -> {
client.get("response", res ->{
if(res.succeeded()) {
JsonObject responseAsJson = new JsonObject(res.result());
if(responseAsJson.getString("uuid").equalsIgnoreCase(requestAsJson.getString("uuid"))) {
routingContext.response().setStatusCode(200).end(res.result());
}
}else if(res.failed()) {
System.out.println("Failed to get message from Redis Server");
routingContext.response().setStatusCode(500).end("Server Error ");
}
});
});
}
private String getUUID() {
UUID uid = UUID.randomUUID();
return uid.toString();
}
}
And below is the main verticle from where the above verticle is getting deployed and on any request to httpserver it's hanlder method is getting called.
package com.api.redis.gateway.verticle;
import io.vertx.core.AbstractVerticle;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.handler.BodyHandler;
import io.vertx.redis.RedisClient;
import io.vertx.redis.RedisOptions;
public class SimpleRestServer extends AbstractVerticle{
#Override
public void start(){
int http_port = 9001;
vertx.deployVerticle("com.api.redis.gateway.verticle.SimpleRestChild", handler -> {
if(handler.succeeded()) {
System.out.println(" SimpleRestChild deployed successfully");
}
});
Router router = Router.router(vertx);
router.route().handler(BodyHandler.create());
SimpleRestChild child = null;
try {
child = (SimpleRestChild) Class.forName("com.api.redis.gateway.verticle.SimpleRestChild").newInstance();
} catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
e.printStackTrace();
}
router.route("/subscription").handler(child::handleSubscription);
vertx.createHttpServer().requestHandler(router::accept).listen(http_port);
System.out.println("Server started at port : " + http_port);
}
}
When handleSubscription is getting called for any "/subscription" request. client object is coming as null.
As per my understanding two objects are getting created here. One with start() and other not having start().
I want to initialize Redisclient once.And use this object when handleSubscription() will get called for any request to "/subscription".
How to achieve this ?
How to fix this problem.
the requests may be coming in before the client initialization is actually complete.
AbstractVerticle has two variations of start():
start(), and
start(Future<Void> startFuture)
the overloaded version with the Future parameter should be used to perform potentially long-running initializations that are necessary to do before the Verticle can be considered deployed and ready. (there's a section dedicated to this topic in the docs).
so you might try changing your code as follows:
public class SimpleRestChild extends SimpleRestServer {
RedisClient client;
#Override
public void start(Future<Void> startFuture) {
client = ...
// important point below is that this Verticle's
// deployment status depends on whether or not
// the client initialization succeeds
client.subscribe("...", handler -> {
if(handler.succeeded()) {
startFuture.complete();
} else {
startFuture.fail(handler.cause());
}
);
}
}
and:
public class SimpleRestServer extends AbstractVerticle {
#Override
public void start(Future<Void> startFuture) {
int http_port = 9001;
vertx.deployVerticle("...", handler -> {
// if the child Verticle is successfully deployed
// then move on to completing this Verticle's
// initialization
if(handler.succeeded()) {
Router router = ...
...
// if the server is successfully installed then
// invoke the Future to signal this Verticle
// is deployed
vertx.createHttpServer()
.requestHandler(router::accept)
.listen(http_port, handler -> {
if(handler.succeeded()) {
startFuture.complete();
} else {
startFuture.fail(handler.cause());
}
});
} else {
startFuture.fail(handler.cause());
}
}
using this type of approach, your Verticles will only service requests when all their dependent resources are fully initialized.

rxjava: queue scheduler with default idle job

I have a client server application and I'm using rxjava to do server requests from the client. The client should only do one request at a time so I intent to use a thread queue scheduler similar to the trampoline scheduler.
Now I try to implement a mechanism to watch changes on the server. Therefore I send a long living request that blocks until the server has some changes and sends back the result (long pull).
This long pull request should only run when the job queue is idle. I'm looking for a way to automatically stop the watch request when a regular request is scheduled and start it again when the queue becomes empty. I thought about modifying the trampoline scheduler to get this behavior but I have the feeling that this is a common problem and there might be an easier solution?
You can hold onto the Subscription returned by scheduling the long poll task, unsubscribe it if the queue becomes non-empty and re-schedule if the queue becomes empty.
Edit: here is an example with the basic ExecutorScheduler:
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
public class IdleScheduling {
static final class TaskQueue {
final ExecutorService executor;
final AtomicReference<Future<?>> idleFuture;
final Runnable idleRunnable;
final AtomicInteger wip;
public TaskQueue(Runnable idleRunnable) {
this.executor = Executors.newFixedThreadPool(1);
this.idleRunnable = idleRunnable;
this.idleFuture = new AtomicReference<>();
this.wip = new AtomicInteger();
this.idleFuture.set(executor.submit(idleRunnable));
}
public void shutdownNow() {
executor.shutdownNow();
}
public Future<?> enqueue(Runnable task) {
if (wip.getAndIncrement() == 0) {
idleFuture.get().cancel(true);
}
return executor.submit(() -> {
task.run();
if (wip.decrementAndGet() == 0) {
startIdle();
}
});
}
void startIdle() {
idleFuture.set(executor.submit(idleRunnable));
}
}
public static void main(String[] args) throws Exception {
TaskQueue tq = new TaskQueue(() -> {
while (!Thread.currentThread().isInterrupted()) {
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
System.out.println("Idle interrupted...");
return;
}
System.out.println("Idle...");
}
});
try {
Thread.sleep(1500);
tq.enqueue(() -> System.out.println("Work 1"));
Thread.sleep(500);
tq.enqueue(() -> {
System.out.println("Work 2");
try {
Thread.sleep(500);
} catch (InterruptedException ex) {
}
});
tq.enqueue(() -> System.out.println("Work 3"));
Thread.sleep(1500);
} finally {
tq.shutdownNow();
}
}
}