/* * #%L * Wisdom-Framework * %% * Copyright (C) 2013 - 2016 Wisdom Framework * %% * Licensed 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. * #L% */ package org.wisdom.framework.vertx; import io.vertx.core.Vertx; import io.vertx.core.VertxOptions; import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runners.model.Statement; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Supplier; /** * @author <a href="http://escoffier.me">Clement Escoffier</a> */ public class RunOnVertxContext implements TestRule { private volatile Vertx vertx; private final Supplier<Vertx> createVertx; private final BiConsumer<Vertx, CountDownLatch> closeVertx; /** * Create a new rule managing a Vertx instance created with default options. The Vert.x instance * is created and closed for each test. */ public RunOnVertxContext() { this(new VertxOptions()); } /** * Create a new rule managing a Vertx instance created with specified options. The Vert.x instance * is created and closed for each test. * * @param options the vertx options */ public RunOnVertxContext(VertxOptions options) { this(() -> Vertx.vertx(options)); } /** * Create a new rule with supplier/consumer for creating/closing a Vert.x instance. The lambda are invoked for each * test. The {@code closeVertx} lambda should invoke the consumer with null when the {@code vertx} instance is closed. * * @param createVertx the create Vert.x supplier * @param closeVertx the close Vert.x consumer */ public RunOnVertxContext(Supplier<Vertx> createVertx, BiConsumer<Vertx, Consumer<Void>> closeVertx) { this.createVertx = createVertx; this.closeVertx = (vertx, latch) -> closeVertx.accept(vertx, v -> latch.countDown()); } /** * Create a new rule with supplier for creating a Vert.x instance. The lambda are invoked for each * test. * * @param createVertx the create Vert.x supplier */ public RunOnVertxContext(Supplier<Vertx> createVertx) { this(createVertx, (vertx, latch) -> vertx.close(ar -> latch.accept(null))); } /** * Retrieves the current Vert.x instance, this value varies according to the test life cycle. * * @return the vertx object */ public Vertx vertx() { return vertx; } @Override public Statement apply(Statement base, Description description) { return new Statement() { @Override public void evaluate() throws Throwable { vertx = createVertx.get(); CountDownLatch lock = new CountDownLatch(1); AtomicReference<Throwable> error = new AtomicReference<>(); vertx.runOnContext((v) -> { try { base.evaluate(); } catch (Throwable throwable) { error.set(throwable); } finally { lock.countDown(); } } ); lock.await(); CountDownLatch latch = new CountDownLatch(1); closeVertx.accept(vertx, latch); try { if (!latch.await(30 * 1000, TimeUnit.MILLISECONDS)) { System.err.println("Could not close Vert.x in tme"); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } if (error.get() != null) { throw error.get(); } } }; } }