Java tutorial
/* * Copyright (c) 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.engagingspaces.graphql.servicediscovery.publisher; import io.engagingspaces.graphql.discovery.impl.AbstractRegistrar; import io.engagingspaces.graphql.events.SchemaAnnounceHandler; import io.engagingspaces.graphql.events.SchemaPublishedHandler; import io.engagingspaces.graphql.events.SchemaUnpublishedHandler; import io.engagingspaces.graphql.events.impl.SchemaMessageConsumers; import io.engagingspaces.graphql.schema.SchemaDefinition; import io.engagingspaces.graphql.servicediscovery.service.GraphQLService; import io.vertx.core.*; import io.vertx.core.eventbus.MessageConsumer; import io.vertx.core.json.JsonObject; import io.vertx.servicediscovery.Record; import io.vertx.servicediscovery.ServiceDiscovery; import io.vertx.servicediscovery.ServiceDiscoveryOptions; import io.vertx.servicediscovery.Status; import java.util.*; import java.util.function.BiConsumer; /** * Manages {@link ServiceDiscovery} creation, schema registration and schema events. * * @author <a href="https://github.com/aschrijver/">Arnold Schrijver</a> */ public class SchemaRegistrar extends AbstractRegistrar<SchemaRegistration> { private final String publisherId; private final SchemaMessageConsumers consumerManager; protected SchemaRegistrar(Vertx vertx, String publisherId) { super(vertx); this.publisherId = publisherId == null ? UUID.randomUUID().toString() : publisherId; this.consumerManager = new SchemaMessageConsumers(vertx); } /** * Creates a new schema registrar instance for managing service discoveries, tracking published schema's and * trigger schema publication events for the {@link SchemaPublisher} it is associated with. * * @param vertx the vert.x instance * @return the schema registrar */ public static SchemaRegistrar create(Vertx vertx) { return new SchemaRegistrar(vertx, null); } /** * Creates a new schema registrar instance for managing service discoveries, tracking published schema's and * trigger schema publication events for the {@link SchemaPublisher} it is associated with. * <p> * The {@code publisherId} is used in schema registrations to determine target publisher for event handling. * * @param vertx the vert.x instance * @param publisherId the name of the publisher * @return the schema registrar */ public static SchemaRegistrar create(Vertx vertx, String publisherId) { return new SchemaRegistrar(vertx, publisherId); } /** * @return the name of the publisher associated with this registrar. */ protected String getPublisherId() { return publisherId; } /** * @param options the service discovery options * @return the existing or created service discovery instance */ protected ServiceDiscovery getOrCreateDiscovery(ServiceDiscoveryOptions options) { return super.getOrCreateDiscovery(options, () -> null); } /** * @return the graphql schema's that are published by the associated publisher. */ @Override protected List<SchemaRegistration> registrations() { return super.registrations(); } /** * Registers a schema definition created by the * {@link GraphQLService}. * <p> * The provided registration is cloned, completed with publisher-related information, registered and then returned. * * @param partialRegistration the partially completed schema registration * @param options the service discovery options to add * @param publishedHandler the event handler to invoke on schema published events * @param unpublishedHandler the event handler to invoke on schema unpublished events * @return the completed schema registration */ protected SchemaRegistration register(SchemaRegistration partialRegistration, ServiceDiscoveryOptions options, SchemaPublishedHandler<SchemaRegistration> publishedHandler, SchemaUnpublishedHandler<SchemaRegistration> unpublishedHandler) { // First start listening to schema events. registerSchemaEventConsumers(options, publishedHandler, unpublishedHandler); // Then register service consumer created from schema definition, if it was not registered yet. MessageConsumer<JsonObject> serviceConsumer = registerSchemaServiceConsumer(partialRegistration.getRecord(), partialRegistration.getSchemaDefinition()); // Complete the schema registration SchemaRegistration fullRegistration = SchemaRegistration.create(partialRegistration.getDiscovery(), options, partialRegistration.getRecord(), partialRegistration.getSchemaDefinition(), serviceConsumer); return super.register(options.getName(), fullRegistration); } /** * Unregisters the published schema indicated by the provided registration. * * @param registration the schema registration */ @Override protected void unregister(SchemaRegistration registration) { consumerManager.unregisterConsumer(registration.getRecord().getLocation().getString(Record.ENDPOINT)); super.unregister(registration); } /** * Finds the schema registration that is managed by this registrar and is published to the specified discovery. * * @param discoveryName the service discovery name to search * @param schemaName the name of the published schema (graphql service name) * @return optional that holds the schema registration, or empty if not found */ protected Optional<SchemaRegistration> findRegistration(String discoveryName, String schemaName) { return registrations().stream() .filter(registration -> discoveryName.equals(registration.getDiscoveryOptions().getName())) .filter(registration -> registration.getSchemaName().equals(schemaName)).findFirst(); } /** * Closes the registrar and releases all its resources. * * @param closeAction the action to perform for closing registered schema's * @param closeHandler the close handler */ protected void close(BiConsumer<SchemaRegistration, Handler<AsyncResult<Void>>> closeAction, Handler<AsyncResult<Void>> closeHandler) { Objects.requireNonNull(closeHandler, "Schema registrar close handler cannot be null"); if (!registrations().isEmpty()) { // Execute the close action against each of the published schema's (e.g. un-publishing) List<Future> futures = new ArrayList<>(); registrations().forEach(registration -> closeAction.accept(registration, rh -> futures .add(rh.succeeded() ? Future.succeededFuture() : Future.failedFuture(rh.cause())))); handleCloseCompletion(closeHandler, futures); } else { doClose(closeHandler); } } private void registerSchemaEventConsumers(ServiceDiscoveryOptions options, SchemaPublishedHandler<SchemaRegistration> publishedHandler, SchemaUnpublishedHandler<SchemaRegistration> unpublishedHandler) { SchemaAnnounceHandler announceHandler = record -> findRegistration(options.getName(), record.getName()) .ifPresent(reg -> { if (Status.UP.equals(record.getStatus())) { publishedHandler.schemaPublished(reg); } else { unpublishedHandler.schemaUnpublished(reg); } }); consumerManager.registerConsumer(options.getAnnounceAddress(), announceHandler); } private MessageConsumer<JsonObject> registerSchemaServiceConsumer(Record record, SchemaDefinition definition) { String address = record.getLocation().getString(Record.ENDPOINT); return consumerManager.registerServiceConsumer(address, definition); } private void handleCloseCompletion(Handler<AsyncResult<Void>> closeHandler, List<Future> futures) { CompositeFuture.all(futures).setHandler(rh -> { if (rh.succeeded()) { CompositeFuture composite = rh.result(); for (int index = 0; index < composite.size(); index++) { if (composite.succeeded(index) && composite.resultAt(index) != null) { composite.<SchemaRegistration>resultAt(index).unregisterServiceProxy(); } } doClose(closeHandler); } else { closeHandler.handle(Future.failedFuture(rh.cause())); } }); } private void doClose(Handler<AsyncResult<Void>> closeHandler) { super.close(); consumerManager.close(); closeHandler.handle(Future.succeededFuture()); } }