All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.pipservices4.grpc.controllers.GrpcEndpoint Maven / Gradle / Ivy

package org.pipservices4.grpc.controllers;

import io.grpc.Server;
import io.grpc.ServerBuilder;
import io.grpc.ServerServiceDefinition;
import io.grpc.netty.GrpcSslContexts;
import io.grpc.netty.NettyServerBuilder;
import io.netty.handler.ssl.SslContext;
import org.pipservices4.components.config.ConfigParams;
import org.pipservices4.components.config.IConfigurable;
import org.pipservices4.commons.errors.*;
import org.pipservices4.components.context.ContextResolver;
import org.pipservices4.components.context.IContext;
import org.pipservices4.components.refer.IReferenceable;
import org.pipservices4.components.refer.IReferences;
import org.pipservices4.components.refer.ReferenceException;
import org.pipservices4.components.run.IOpenable;
import org.pipservices4.observability.count.CompositeCounters;
import org.pipservices4.observability.log.CompositeLogger;
import org.pipservices4.grpc.commandable.CommandableGrpc;
import org.pipservices4.grpc.commandable.InvokeReply;
import org.pipservices4.config.connect.HttpConnectionResolver;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * Used for creating GRPC endpoints. An endpoint is a URL, at which a given service can be accessed by a client.
 * 

* ### Configuration parameters ### *

* Parameters to pass to the {@link #configure} method for component configuration: *

 * - connection(s) - the connection resolver's connections:
 *     - "connection.discovery_key" - the key to use for connection resolving in a discovery service;
 *     - "connection.protocol" - the connection's protocol;
 *     - "connection.host" - the target host;
 *     - "connection.port" - the target port;
 *     - "connection.uri" - the target URI.
 * - credential - the HTTPS credentials:
 *     - "credential.ssl_key_file" - the SSL private key in PEM
 *     - "credential.ssl_crt_file" - the SSL certificate in PEM
 *     - "credential.ssl_ca_file" - the certificate authorities (root cerfiticates) in PEM
 * 
*

* ### References ### *

* A logger, counters, and a connection resolver can be referenced by passing the * following references to the object's {@link #setReferences} method: *

* - logger: *:logger:*:*:1.0 * - counters: *:counters:*:*:1.0" * - discovery: *:discovery:*:*:1.0" (for the connection resolver). *

* ### Example ### *

 * {@code
 * public MyMethod(ConfigParams config, IReferences references) {
 *     let endpoint = new HttpEndpoint();
 *     if (this._config)
 *         endpoint.configure(this._config);
 *     if (this._references)
 *         endpoint.setReferences(this._references);
 *     ...
 *     this._endpoint.open(context);
 * }
 * }
 */
public class GrpcEndpoint implements IOpenable, IConfigurable, IReferenceable {

    private static final ConfigParams _defaultConfig = ConfigParams.fromTuples(
            "connection.protocol", "http",
            "connection.host", "0.0.0.0",
            "connection.port", 3000,

            "credential.ssl_key_file", null,
            "credential.ssl_crt_file", null,
            "credential.ssl_ca_file", null,

            "options.maintenance_enabled", false,
            "options.request_max_size", 1024 * 1024,
            "options.file_max_size", 200 * 1024 * 1024,
            "options.connect_timeout", 60000,
            "options.debug", true
    );

    protected final List _interceptors = new ArrayList<>();
    private ServerBuilder> _builder;
    private Server _server;
    private final HttpConnectionResolver _connectionResolver = new HttpConnectionResolver();
    private final CompositeLogger _logger = new CompositeLogger();
    private final CompositeCounters _counters = new CompositeCounters();
    private boolean _maintenanceEnabled = false;
    private long _fileMaxSize = 200 * 1024 * 1024;
    private String _uri;
    private List _registrations = new ArrayList<>();

    /**
     * Configures this HttpEndpoint using the given configuration parameters.
     *
     * 
     * __Configuration parameters:__
     * - __connection(s)__ - the connection resolver's connections;
     *     - "connection.discovery_key" - the key to use for connection resolving in a discovery service;
     *     - "connection.protocol" - the connection's protocol;
     *     - "connection.host" - the target host;
     *     - "connection.port" - the target port;
     *     - "connection.uri" - the target URI.
     *     - "credential.ssl_key_file" - SSL private key in PEM
     *     - "credential.ssl_crt_file" - SSL certificate in PEM
     *     - "credential.ssl_ca_file" - Certificate authority (root certificate) in PEM
     * 
* * @param config configuration parameters, containing a "connection(s)" section. * @see ConfigParams (in the PipServices "Commons" package) */ @Override public void configure(ConfigParams config) throws ConfigException { config = config.setDefaults(GrpcEndpoint._defaultConfig); this._connectionResolver.configure(config); this._maintenanceEnabled = config.getAsBooleanWithDefault("options.maintenance_enabled", this._maintenanceEnabled); this._fileMaxSize = config.getAsLongWithDefault("options.file_max_size", this._fileMaxSize); } /** * Sets references to this endpoint's logger, counters, and connection resolver. * *
     * References:
     * - logger: *:logger:\*:\*:1.0
     * - counters: *:counters:\*:\*:1.0
     * - discovery: *:discovery:\*:\*:1.0 (for the connection resolver)
     * 
* * @param references an IReferences object, containing references to a logger, counters, * and a connection resolver. * @see IReferences (in the PipServices "Commons" package) */ @Override public void setReferences(IReferences references) throws ReferenceException, ConfigException { this._logger.setReferences(references); this._counters.setReferences(references); this._connectionResolver.setReferences(references); } /** * @return whether or not this endpoint is open with an actively listening GRPC server. */ @Override public boolean isOpen() { return this._server != null; } /** * Opens a connection using the parameters resolved by the referenced connection * resolver and creates a GRPC server (service) using the set options and parameters. * * @param context (optional) a context to trace execution through call chain. */ @Override public void open(IContext context) throws ApplicationException { if (this.isOpen()) { return; } var connection = this._connectionResolver.resolve(context); this._uri = connection.getAsString("uri"); var port = connection.getAsInteger("port"); try { // TODO add credentials if (Objects.equals(connection.getAsStringWithDefault("protocol", "http"), "https")) { var sslKeyPath = connection.getAsNullableString("ssl_key_file"); var sslCrtPath = connection.getAsNullableString("ssl_crt_file"); var sslCaPath = connection.getAsNullableString("ssl_ca_file"); // var ca = new ArrayList<>(); // // if (sslCaPath != null) { // try(var caFile = new FileInputStream(sslCaPath)) { // var caText = new String(caFile.readAllBytes(), StandardCharsets.UTF_8); // var crtIndex = caText.lastIndexOf("-----BEGIN CERTIFICATE-----"); // if (crtIndex > -1) { // ca.add(caText.substring(crtIndex)); // caText = caText.substring(0, crtIndex); // } // } // } SslContext sslContext = GrpcSslContexts.forClient() // if server's cert doesn't chain to a standard root .trustManager(new File(sslCaPath)) .keyManager(new File(sslCrtPath), new File(sslKeyPath)) // client cert .build(); _builder = NettyServerBuilder.forPort(port) .addService(new CommandableImpl()) .sslContext(sslContext); } else { // Create instance of express application _builder = ServerBuilder.forPort(port) .addService(new CommandableImpl()); } // Start operations performRegistrations(); _server = _builder.build(); _server.start(); Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { // Use stderr here since the logger may have been reset by its JVM shutdown hook. GrpcEndpoint.this.close(context); // System.err.println("*** grpc server shut down"); } }); } catch (Exception ex) { this._server = null; throw new ConnectionException( ContextResolver.getTraceId(context), "CANNOT_CONNECT", "Opening GRPC service failed" ).wrap(ex).withDetails("url", this._uri); } } /** * Closes this endpoint and the GRPC server (service) that was opened earlier. * * @param context (optional) a context to trace execution through call chain. */ @Override public void close(IContext context) { if (_server != null) { this._uri = null; try { _server.shutdown().awaitTermination(30, TimeUnit.SECONDS); this._logger.debug(context, "Closed GRPC service at %s", this._uri); this._server = null; } catch (InterruptedException ex) { this._logger.warn(context, "Failed while closing GRPC service: %s", ex); throw new RuntimeException(ex); } } } /** * Registers a registerable object for dynamic endpoint discovery. * * @param registration the registration to add. * @see IRegisterable */ public void register(IRegisterable registration) { this._registrations.add(registration); } /** * Unregisters a registerable object, so that it is no longer used in dynamic * endpoint discovery. * * @param registration the registration to remove. * @see IRegisterable */ public void unregister(IRegisterable registration) { this._registrations = this._registrations.stream().filter(r -> r != registration).toList(); } private void performRegistrations() throws ReferenceException { for (var registration : this._registrations) { registration.register(); } // register interceptors in service _interceptors.forEach((interceptor) -> _builder.intercept(interceptor)); } class CommandableImpl extends CommandableGrpc.CommandableImplBase { @Override public void invoke(org.pipservices4.grpc.commandable.InvokeRequest request, io.grpc.stub.StreamObserver responseObserver) { InvokeReply reply; reply = InvokeReply.newBuilder().setResultJsonBytes(request.getArgsJsonBytes()).build(); responseObserver.onNext(reply); responseObserver.onCompleted(); } } /** * Registers a service with related implementation * * @param service a GRPC service object. */ public void registerService(ServerServiceDefinition service) { this._builder.addService(service); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy