io.vertx.servicediscovery.impl.DiscoveryImpl Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 2011-2016 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.servicediscovery.impl;
import io.vertx.codegen.annotations.Nullable;
import io.vertx.core.*;
import io.vertx.core.internal.VertxInternal;
import io.vertx.core.internal.logging.Logger;
import io.vertx.core.internal.logging.LoggerFactory;
import io.vertx.core.json.JsonObject;
import io.vertx.servicediscovery.*;
import io.vertx.servicediscovery.Record;
import io.vertx.servicediscovery.spi.ServiceDiscoveryBackend;
import io.vertx.servicediscovery.spi.ServiceExporter;
import io.vertx.servicediscovery.spi.ServiceImporter;
import io.vertx.servicediscovery.spi.ServicePublisher;
import java.util.*;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* Default implementation of the service discovery.
*
* @author Clement Escoffier
*/
public class DiscoveryImpl implements ServiceDiscovery, ServicePublisher {
private final VertxInternal vertx;
private final String announce;
private final String usage;
private final ServiceDiscoveryBackend backend;
private final Set importers = new CopyOnWriteArraySet<>();
private final Set exporters = new CopyOnWriteArraySet<>();
private final Set bindings = new CopyOnWriteArraySet<>();
private final static Logger LOGGER = LoggerFactory.getLogger(DiscoveryImpl.class.getName());
private final String id;
private final ServiceDiscoveryOptions options;
public DiscoveryImpl(Vertx vertx, ServiceDiscoveryOptions options) {
this(vertx, options, getBackend(options.getBackendConfiguration().getString("backend-name", null)));
}
/**
* Creates a new instance of {@link DiscoveryImpl}
*
* @param vertx the vert.x instance
* @param options the options
* @param backend the backend service
*/
DiscoveryImpl(Vertx vertx, ServiceDiscoveryOptions options, ServiceDiscoveryBackend backend) {
this.vertx = (VertxInternal) vertx;
this.announce = options.getAnnounceAddress();
this.usage = options.getUsageAddress();
this.backend = backend;
this.backend.init(vertx, options.getBackendConfiguration());
this.id = options.getName() != null ? options.getName() : getNodeId(vertx);
this.options = options;
}
private String getNodeId(Vertx vertx) {
if (vertx.isClustered()) {
return ((VertxInternal) vertx).getClusterManager().getNodeId();
} else {
return "localhost";
}
}
private static ServiceDiscoveryBackend getBackend(String maybeName) {
ServiceLoader backends = ServiceLoader.load(ServiceDiscoveryBackend.class);
Iterator iterator = backends.iterator();
if (maybeName == null) {
if (!iterator.hasNext()) {
return new DefaultServiceDiscoveryBackend();
} else {
return iterator.next();
}
}
if (maybeName.equals(DefaultServiceDiscoveryBackend.class.getName())) {
return new DefaultServiceDiscoveryBackend();
}
// We have a name
while (iterator.hasNext()) {
ServiceDiscoveryBackend backend = iterator.next();
if (backend.name().equals(maybeName)) {
return backend;
}
}
throw new IllegalStateException("Cannot find the discovery backend implementation with name " + maybeName + " in " +
"the classpath");
}
private Collection getServiceImporterFromSPI() {
ServiceLoader importers = ServiceLoader.load(ServiceImporter.class);
Iterator iterator = importers.iterator();
List list = new ArrayList<>();
// We have a name
while (iterator.hasNext()) {
ServiceImporter importer = iterator.next();
list.add(importer);
}
return list;
}
@Override
public ServiceReference getReference(Record record) {
return getReferenceWithConfiguration(record, new JsonObject());
}
@Override
public ServiceReference getReferenceWithConfiguration(Record record, JsonObject configuration) {
ServiceReference reference = ServiceTypes.get(record).get(vertx, this, record, configuration);
bindings.add(reference);
sendBindEvent(reference);
return reference;
}
private void sendBindEvent(ServiceReference reference) {
if (usage == null) {
return;
}
vertx.eventBus().publish(usage, new JsonObject()
.put(ServiceDiscovery.EVENT_TYPE, ServiceDiscovery.EVENT_TYPE_BIND)
.put(ServiceDiscovery.EVENT_RECORD, reference.record().toJson())
.put(ServiceDiscovery.EVENT_ID, id));
}
@Override
public boolean release(ServiceReference reference) {
boolean removed = bindings.remove(reference);
reference.release();
sendUnbindEvent(reference);
return removed;
}
private void sendUnbindEvent(ServiceReference reference) {
if (usage == null) {
return;
}
vertx.eventBus().publish(usage, new JsonObject()
.put(ServiceDiscovery.EVENT_TYPE, ServiceDiscovery.EVENT_TYPE_RELEASE)
.put(ServiceDiscovery.EVENT_RECORD, reference.record().toJson())
.put(ServiceDiscovery.EVENT_ID, id));
}
public ServiceDiscovery registerServiceImporter(ServiceImporter importer, JsonObject configuration,
Completable completionHandler) {
JsonObject conf;
if (configuration == null) {
conf = new JsonObject();
} else {
conf = configuration;
}
Promise completed = vertx.promise();
completed.future().onComplete(
ar -> {
if (ar.failed()) {
LOGGER.error("Cannot start the service importer " + importer, ar.cause());
if (completionHandler != null) {
completionHandler.fail(ar.cause());
}
} else {
importers.add(importer);
LOGGER.info("Service importer " + importer + " started");
if (completionHandler != null) {
completionHandler.succeed();
}
}
}
);
importer.start(vertx, this, conf, completed);
return this;
}
@Override
public Future registerServiceImporter(ServiceImporter importer, JsonObject configuration) {
Promise promise = vertx.promise();
registerServiceImporter(importer, configuration, promise);
return promise.future();
}
@Override
public Future registerServiceExporter(ServiceExporter exporter, JsonObject configuration) {
Promise promise = vertx.promise();
registerServiceExporter(exporter, configuration, promise);
return promise.future();
}
public ServiceDiscovery registerServiceExporter(ServiceExporter exporter, JsonObject configuration,
Completable completionHandler) {
JsonObject conf;
if (configuration == null) {
conf = new JsonObject();
} else {
conf = configuration;
}
Promise completed = vertx.promise();
completed.future().onComplete(
ar -> {
if (ar.failed()) {
LOGGER.error("Cannot start the service importer " + exporter, ar.cause());
if (completionHandler != null) {
completionHandler.fail(ar.cause());
}
} else {
exporters.add(exporter);
LOGGER.info("Service exporter " + exporter + " started");
if (completionHandler != null) {
completionHandler.succeed();
}
}
}
);
exporter.init(vertx, this, conf, completed);
return this;
}
@Override
public void close() {
LOGGER.info("Stopping service discovery");
List> futures = new ArrayList<>();
for (ServiceImporter importer : importers) {
Promise promise = vertx.promise();
importer.close(v -> promise.complete());
futures.add(promise.future());
}
for (ServiceExporter exporter : exporters) {
Promise promise = vertx.promise();
exporter.close(promise::complete);
futures.add(promise.future());
}
bindings.forEach(ServiceReference::release);
bindings.clear();
Future.all(futures).onComplete(ar -> {
if (ar.succeeded()) {
LOGGER.info("Discovery bridges stopped");
} else {
LOGGER.warn("Some discovery bridges did not stopped smoothly", ar.cause());
}
});
}
public void publish(Record record, Completable resultHandler) {
Status status = record.getStatus() == null || record.getStatus() == Status.UNKNOWN
? Status.UP : record.getStatus();
backend.store(record.setStatus(status), ar -> {
if (ar.failed()) {
resultHandler.fail(ar.cause());
return;
}
for (ServiceExporter exporter : exporters) {
exporter.onPublish(new Record(ar.result()));
}
Record announcedRecord = new Record(ar.result());
announcedRecord
.setRegistration(null)
.setStatus(status);
vertx.eventBus().publish(announce, announcedRecord.toJson());
resultHandler.succeed(ar.result());
});
}
@Override
public Future publish(Record record) {
Promise promise = vertx.promise();
publish(record, promise);
return promise.future();
}
public void unpublish(String id, Completable resultHandler) {
backend.remove(id, record -> {
if (record.failed()) {
resultHandler.fail(record.cause());
return;
}
for (ServiceExporter exporter : exporters) {
exporter.onUnpublish(id);
}
Record announcedRecord = new Record(record.result());
announcedRecord
.setRegistration(null)
.setStatus(Status.DOWN);
vertx.eventBus().publish(announce, announcedRecord.toJson());
resultHandler.succeed();
});
}
@Override
public Future unpublish(String id) {
Promise promise = vertx.promise();
unpublish(id, promise);
return promise.future();
}
public void getRecord(JsonObject filter,
Completable resultHandler) {
boolean includeOutOfService = false;
Function accept;
if (filter == null) {
accept = r -> true;
} else {
includeOutOfService = filter.getString("status") != null;
accept = r -> r.match(filter);
}
getRecord(accept, includeOutOfService, resultHandler);
}
@Override
public Future<@Nullable Record> getRecord(JsonObject filter) {
Promise promise = vertx.promise();
getRecord(filter, promise);
return promise.future();
}
public void getRecord(String id, Completable<@Nullable Record> resultHandler) {
backend.getRecord(id, ar -> {
if (ar.succeeded()) {
resultHandler.succeed(ar.result());
} else {
resultHandler.fail(ar.cause());
}
});
}
@Override
public Future<@Nullable Record> getRecord(String id) {
Promise promise = vertx.promise();
getRecord(id, promise);
return promise.future();
}
public void getRecord(Function filter, Completable resultHandler) {
getRecord(filter, false, resultHandler);
}
@Override
public Future<@Nullable Record> getRecord(Function filter) {
Promise promise = vertx.promise();
getRecord(filter, promise);
return promise.future();
}
public void getRecord(Function filter, boolean includeOutOfService, Completable
resultHandler) {
Objects.requireNonNull(filter);
backend.getRecords(list -> {
if (list.failed()) {
resultHandler.fail(list.cause());
} else {
Optional any = list.result().stream()
.filter(filter::apply)
.filter(record -> includeOutOfService || record.getStatus() == Status.UP)
.findAny();
if (any.isPresent()) {
resultHandler.succeed(any.get());
} else {
resultHandler.succeed();
}
}
});
}
@Override
public Future<@Nullable Record> getRecord(Function filter, boolean includeOutOfService) {
Promise promise = vertx.promise();
getRecord(filter, includeOutOfService, promise);
return promise.future();
}
public void getRecords(JsonObject filter, Completable> resultHandler) {
boolean includeOutOfService = false;
Function accept;
if (filter == null) {
accept = r -> true;
} else {
includeOutOfService = filter.getString("status") != null;
accept = r -> r.match(filter);
}
getRecords(accept, includeOutOfService, resultHandler);
}
@Override
public Future> getRecords(JsonObject filter) {
Promise> promise = vertx.promise();
getRecords(filter, promise);
return promise.future();
}
public void getRecords(Function filter, Completable> resultHandler) {
getRecords(filter, false, resultHandler);
}
@Override
public Future> getRecords(Function filter) {
Promise> promise = vertx.promise();
getRecords(filter, promise);
return promise.future();
}
public void getRecords(Function filter, boolean includeOutOfService, Completable> resultHandler) {
Objects.requireNonNull(filter);
backend.getRecords(list -> {
if (list.failed()) {
resultHandler.fail(list.cause());
} else {
resultHandler.succeed(
list.result().stream()
.filter(filter::apply)
.filter(record -> includeOutOfService || record.getStatus() == Status.UP)
.collect(Collectors.toList())
);
}
});
}
@Override
public Future> getRecords(Function filter, boolean includeOutOfService) {
Promise> promise = vertx.promise();
getRecords(filter, includeOutOfService, promise);
return promise.future();
}
public void update(Record record, Completable resultHandler) {
backend.update(record, ar -> {
if (ar.failed()) {
resultHandler.fail(ar.cause());
} else {
for (ServiceExporter exporter : exporters) {
exporter.onUpdate(record);
}
Record announcedRecord = new Record(record);
vertx.eventBus().publish(announce, announcedRecord.toJson());
resultHandler.succeed(record);
}
});
}
@Override
public Future update(Record record) {
Promise promise = vertx.promise();
update(record, promise);
return promise.future();
}
@Override
public Set bindings() {
return new HashSet<>(bindings);
}
@Override
public ServiceDiscoveryOptions options() {
return options;
}
/**
* Checks whether the reference is hold by this service discovery. If so, remove it from the list of bindings and
* fire the "release" event.
*
* @param reference the reference
*/
public void unbind(ServiceReference reference) {
if (bindings.remove(reference)) {
sendUnbindEvent(reference);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy