Repository: eclipse-vertx/vert.x

Problem statement:
Vertx.clusteredVertx() handler not called if cannot bind to address
When starting clustered vetx instance:

``` java
Vertx.clusteredVertx(
        new VertxOptions()
            .setClustered(true)
            .setClusterHost("127.0.0.1")
            .setClusterPort(9999),
        result ->
        {
            System.out.println("Cluster started");
        }
    );
```

if it cannot bind to the address it prints the exception:

```
io.vertx.core.impl.VertxImpl SEVERE: Failed to start event bus
java.net.BindException: Address already in use
        at sun.nio.ch.Net.bind0(Native Method)
        at sun.nio.ch.Net.bind(Net.java:433)
        at sun.nio.ch.Net.bind(Net.java:425)
        at sun.nio.ch.ServerSocketChannelImpl.bind(ServerSocketChannelImpl.java:223)
        at sun.nio.ch.ServerSocketAdaptor.bind(ServerSocketAdaptor.java:74)
        at io.netty.channel.socket.nio.NioServerSocketChannel.doBind(NioServerSocketChannel.java:125)
        at io.netty.channel.AbstractChannel$AbstractUnsafe.bind(AbstractChannel.java:485)
        at io.netty.channel.DefaultChannelPipeline$HeadContext.bind(DefaultChannelPipeline.java:1089)
        at io.netty.channel.AbstractChannelHandlerContext.invokeBind(AbstractChannelHandlerContext.java:440)
        at io.netty.channel.AbstractChannelHandlerContext.bind(AbstractChannelHandlerContext.java:425)
        at io.netty.channel.DefaultChannelPipeline.bind(DefaultChannelPipeline.java:903)
        at io.netty.channel.AbstractChannel.bind(AbstractChannel.java:198)
        at io.netty.bootstrap.AbstractBootstrap$2.run(AbstractBootstrap.java:348)
        at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:358)
        at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:357)
        at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:112)
        at java.lang.Thread.run(Thread.java:745)
```

and the result handler is never called.

The problem seems to be in `io.vertx.core.impl.VertxImpl.createAndStartEventBus()` where only error is logged and the `resultHandler` is not called:

``` java
} else {
  log.error("Failed to start event bus", ar2.cause());
}
```



Hints:
would you mind to contribute a test for this and possibly a fix ?


Patch:
diff --git a/src/main/java/io/vertx/core/impl/VertxImpl.java b/src/main/java/io/vertx/core/impl/VertxImpl.java
--- a/src/main/java/io/vertx/core/impl/VertxImpl.java
+++ b/src/main/java/io/vertx/core/impl/VertxImpl.java
@@ -204,19 +204,17 @@ private void createAndStartEventBus(VertxOptions options, Handler<AsyncResult<Ve
     } else {
       eventBus = new EventBusImpl(this);
     }
-    eventBus.start(ar2 -> {
-      if (ar2.succeeded()) {
+    eventBus.start(ar -> {
+      if (ar.succeeded()) {
         if (metrics != null) {
           // If the metric provider wants to use the event bus, it cannot use it in its constructor as the event bus
           // may not be initialized yet. We invokes the eventBusInitialized so it can starts using the event bus.
           metrics.eventBusInitialized(eventBus);
         }
-
-        if (resultHandler != null) {
-          resultHandler.handle(Future.succeededFuture(this));
-        }
+        if (resultHandler != null) resultHandler.handle(Future.succeededFuture(this));
       } else {
-        log.error("Failed to start event bus", ar2.cause());
+        log.error("Failed to start event bus", ar.cause());
+        if (resultHandler != null) resultHandler.handle(Future.failedFuture(ar.cause()));
       }
     });
   }


Test patch:
diff --git a/src/test/java/io/vertx/test/core/ClusteredEventBusStartFailureTest.java b/src/test/java/io/vertx/test/core/ClusteredEventBusStartFailureTest.java
new file mode 100644
--- /dev/null
+++ b/src/test/java/io/vertx/test/core/ClusteredEventBusStartFailureTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2011-2017 The original author or authors
+ * ------------------------------------------------------
+ * 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.Vertx;
+import io.vertx.core.VertxOptions;
+import io.vertx.test.fakecluster.FakeClusterManager;
+import org.junit.Test;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author Thomas Segismont
+ */
+public class ClusteredEventBusStartFailureTest {
+
+  @Test
+  public void testCallbackInvokedOnFailure() throws Exception {
+    VertxOptions options = new VertxOptions()
+      .setClusterManager(new FakeClusterManager())
+      .setClusterHost(getClass().getSimpleName());
+
+    AtomicReference<AsyncResult<Vertx>> resultRef = new AtomicReference<>();
+
+    CountDownLatch latch = new CountDownLatch(1);
+    Vertx.clusteredVertx(options, ar -> {
+      resultRef.set(ar);
+      latch.countDown();
+    });
+    latch.await(5, TimeUnit.SECONDS);
+
+    assertFalse(resultRef.get() == null);
+    assertTrue(resultRef.get().failed());
+  }
+}