Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refs #1: introduce a common class which generates client to connect t…
…o Microservice
- Loading branch information
Showing
7 changed files
with
197 additions
and
44 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
60 changes: 60 additions & 0 deletions
60
common/src/main/java/jp/skypencil/brownie/MicroserviceClientFactory.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package jp.skypencil.brownie; | ||
|
||
import java.util.Objects; | ||
|
||
import javax.annotation.Nonnull; | ||
import javax.annotation.ParametersAreNonnullByDefault; | ||
import javax.inject.Inject; | ||
|
||
import io.vertx.core.json.JsonObject; | ||
import io.vertx.rxjava.core.Future; | ||
import io.vertx.rxjava.core.http.HttpClient; | ||
import io.vertx.rxjava.servicediscovery.ServiceDiscovery; | ||
import lombok.RequiredArgsConstructor; | ||
import rx.Observable; | ||
import rx.Single; | ||
|
||
/** | ||
* A factory class which generates {@code Single<HttpClient>} to connect to | ||
* other microservices. Caller should hold an instance of this factory as | ||
* instance field, and call {@link #createClient(String, Future)} when it tries | ||
* to call other microservices. | ||
*/ | ||
@ParametersAreNonnullByDefault | ||
@RequiredArgsConstructor( | ||
onConstructor = @__(@Inject)) | ||
public class MicroserviceClientFactory { | ||
private final ServiceDiscovery discovery; | ||
|
||
/** | ||
* @param name | ||
* Name of target microservice. | ||
* @param closed | ||
* A {@link Future} which should be completed when caller finished using returned {@link HttpClient}. | ||
* @return A {@link Single} which emits a {@link HttpClient} to invoke other | ||
* microservice. Caller does not have to close this client, but it | ||
* should complete {@link Future} when it finishes to use returned {@link HttpClient}. | ||
*/ | ||
@Nonnull | ||
public Single<HttpClient> createClient(String name, Future<Void> closed) { | ||
Objects.requireNonNull(name, "name"); | ||
Objects.requireNonNull(closed, "closed"); | ||
if (closed.isComplete()) { | ||
return Single.error(new IllegalArgumentException("Given Future is already closed")); | ||
} | ||
|
||
return discovery | ||
.getRecordObservable(new JsonObject().put("name", name)) | ||
.map(discovery::getReference) | ||
.flatMap(reference -> { | ||
HttpClient client = new HttpClient(reference.get()); | ||
closed.setHandler(ar -> { | ||
// this will invoke HttpEndpointReference#close() which closes HttpClient, | ||
// so we do not have to call client.close() explicitly. | ||
reference.release(); | ||
}); | ||
return Observable.just(client); | ||
}) | ||
.toSingle(); | ||
} | ||
} |
99 changes: 99 additions & 0 deletions
99
common/src/test/java/jp/skypencil/brownie/MicroserviceClientFactoryTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
package jp.skypencil.brownie; | ||
|
||
|
||
import static com.google.common.truth.Truth.assertThat; | ||
import static org.mockito.Matchers.any; | ||
import static org.mockito.Matchers.eq; | ||
import static org.mockito.Mockito.doAnswer; | ||
import static org.mockito.Mockito.never; | ||
import static org.mockito.Mockito.spy; | ||
import static org.mockito.Mockito.verify; | ||
|
||
import java.util.concurrent.atomic.AtomicReference; | ||
|
||
import org.junit.Rule; | ||
import org.junit.Test; | ||
import org.junit.runner.RunWith; | ||
|
||
import io.vertx.core.json.JsonObject; | ||
import io.vertx.ext.unit.Async; | ||
import io.vertx.ext.unit.TestContext; | ||
import io.vertx.ext.unit.junit.RunTestOnContext; | ||
import io.vertx.ext.unit.junit.VertxUnitRunner; | ||
import io.vertx.rxjava.core.Future; | ||
import io.vertx.rxjava.core.Vertx; | ||
import io.vertx.rxjava.servicediscovery.ServiceDiscovery; | ||
import io.vertx.rxjava.servicediscovery.ServiceReference; | ||
import io.vertx.rxjava.servicediscovery.types.HttpEndpoint; | ||
import io.vertx.servicediscovery.Record; | ||
|
||
@RunWith(VertxUnitRunner.class) | ||
public class MicroserviceClientFactoryTest { | ||
@Rule | ||
public RunTestOnContext rule = new RunTestOnContext(); | ||
|
||
@Test | ||
public void testSucceededFuture(TestContext context) { | ||
Vertx vertx = Vertx.newInstance(rule.vertx()); | ||
ServiceDiscovery discovery = ServiceDiscovery.create(vertx); | ||
MicroserviceClientFactory factory = new MicroserviceClientFactory(discovery); | ||
|
||
Async async = context.async(); | ||
factory.createClient("name", Future.succeededFuture()) | ||
.subscribe(client -> { | ||
context.fail(); | ||
}, e -> { | ||
assertThat(e).isInstanceOf(IllegalArgumentException.class); | ||
async.complete(); | ||
}); | ||
} | ||
|
||
@Test | ||
public void ensureClientIsCreatedBasedOnServiceDiscovery(TestContext context) { | ||
Vertx vertx = Vertx.newInstance(rule.vertx()); | ||
ServiceDiscovery discovery = spy(ServiceDiscovery.create(vertx)); | ||
Async async = context.async(); | ||
|
||
discovery.publish(HttpEndpoint.createRecord("name", "localhost"), ar -> { | ||
assertThat(ar.succeeded()).isTrue(); | ||
|
||
MicroserviceClientFactory factory = new MicroserviceClientFactory(discovery); | ||
Future<Void> future = spy(Future.future()); | ||
|
||
factory.createClient("name", future).subscribe(client -> { | ||
verify(discovery).getRecordObservable(eq(new JsonObject().put("name", "name"))); | ||
async.complete(); | ||
}, context::fail); | ||
}); | ||
} | ||
|
||
@Test | ||
public void ensureReferenceIsReleased(TestContext context) { | ||
Vertx vertx = Vertx.newInstance(rule.vertx()); | ||
ServiceDiscovery discovery = spy(ServiceDiscovery.create(vertx)); | ||
Async async = context.async(); | ||
|
||
discovery.publish(HttpEndpoint.createRecord("name", "localhost"), ar -> { | ||
assertThat(ar.succeeded()).isTrue(); | ||
|
||
MicroserviceClientFactory factory = new MicroserviceClientFactory(discovery); | ||
Future<Void> future = Future.future(); | ||
AtomicReference<ServiceReference> reference = new AtomicReference<>(); | ||
|
||
doAnswer(invocation -> { | ||
reference.set(spy((ServiceReference) invocation.callRealMethod())); | ||
return reference.get(); | ||
}).when(discovery).getReference(any(Record.class)); | ||
|
||
factory.createClient("name", future).subscribe(client -> { | ||
verify(reference.get(), never()).release(); | ||
|
||
future.complete(); | ||
verify(reference.get()).release(); | ||
|
||
async.complete(); | ||
}, context::fail); | ||
}); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters