diff --git a/subsystem/src/main/java/org/wildfly/extension/grpc/GrpcServerService.java b/subsystem/src/main/java/org/wildfly/extension/grpc/GrpcServerService.java index 2cf8444..1ea9a09 100644 --- a/subsystem/src/main/java/org/wildfly/extension/grpc/GrpcServerService.java +++ b/subsystem/src/main/java/org/wildfly/extension/grpc/GrpcServerService.java @@ -15,34 +15,25 @@ */ package org.wildfly.extension.grpc; -import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.SocketAddress; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ArrayList; import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.function.Consumer; import java.util.function.Supplier; -import javax.net.ssl.KeyManager; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLException; -import javax.net.ssl.TrustManager; -import org.jboss.as.controller.OperationContext; -import org.jboss.as.controller.OperationFailedException; -import org.jboss.as.controller.capability.CapabilityServiceSupport; -import org.jboss.as.server.Services; -import org.jboss.as.server.deployment.Attachments; import org.jboss.as.server.deployment.DeploymentUnit; -import org.jboss.dmr.ModelNode; import org.jboss.msc.Service; -import org.jboss.msc.service.ServiceBuilder; -import org.jboss.msc.service.ServiceName; -import org.jboss.msc.service.ServiceTarget; import org.jboss.msc.service.StartContext; import org.jboss.msc.service.StartException; import org.jboss.msc.service.StopContext; @@ -50,303 +41,40 @@ import io.grpc.BindableService; import io.grpc.Server; +import io.grpc.ServerServiceDefinition; import io.grpc.netty.GrpcSslContexts; import io.grpc.netty.NettyServerBuilder; +import io.grpc.util.MutableHandlerRegistry; import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslContextBuilder; import io.netty.handler.ssl.SslProvider; -import io.netty.util.internal.logging.InternalLoggerFactory; -import io.netty.util.internal.logging.JdkLoggerFactory; import static java.util.concurrent.TimeUnit.SECONDS; -public class GrpcServerService implements Service { - - public static final ServiceName SERVICE_NAME = ServiceName.of("grpc-server"); - - private static GrpcServerService grpcServerService; - private static KeyManager keyManager; - private static TrustManager trustManager; - private static SSLContext sslContext; - private static Object monitor = new Object(); - private static boolean restart = true; - private static boolean serverRestarting; - private static Set sslUpdates = new HashSet(); - private static Set serverUpdates = new HashSet(); - - private static int FLOW_CONTROL_WINDOW; - private static long HANDSHAKE_TIMEOUT; - private static int INITIAL_FLOW_CONTROL_WINDOW; - private static long KEEP_ALIVE_TIME; - private static long KEEP_ALIVE_TIMEOUT; - private static String KEY_MANAGER_NAME; - private static int MAX_CONCURRENT_CALLS_PER_CONNECTION; - private static long MAX_CONNECTION_AGE; - private static long MAX_CONNECTION_AGE_GRACE; - private static long MAX_CONNECTION_IDLE; - private static int MAX_INBOUND_MESSAGE_SIZE; - private static int MAX_INBOUND_METADATA_SIZE; - private static long PERMIT_KEEP_ALIVE_TIME; - private static boolean PERMIT_KEEP_ALIVE_WITHOUT_CALLS; - private static String PROTOCOL_PROVIDER; - private static String SERVER_HOST; - private static int SERVER_PORT; - private static long SESSION_CACHE_SIZE; - private static long SESSION_TIMEOUT; - private static int SHUTDOWN_TIMEOUT; - private static String SSL_CONTEXT_NAME; - private static boolean START_TLS; - private static String TRUST_MANAGER_NAME; - - enum SSL_ATTRIBUTE { - PROTOCOL_PROVIDER, - SESSION_CACHE_SIZE, - SESSION_TIMEOUT, - START_TLS - }; - - enum SERVER_ATTRIBUTE { - FLOW_CONTROL_WINDOW, - HANDSHAKE_TIMEOUT, - INITIAL_FLOW_CONTROL_WINDOW, - KEEP_ALIVE_TIME, - KEEP_ALIVE_TIMEOUT, - KEY_MANAGER_NAME, - MAX_CONCURRENT_CALLS_PER_CONNECTION, - MAX_CONNECTION_AGE, - MAX_CONNECTION_AGE_GRACE, - MAX_CONNECTION_IDLE, - MAX_INBOUND_MESSAGE_SIZE, - MAX_INBOUND_METADATA_SIZE, - PERMIT_KEEP_ALIVE_TIME, - PERMIT_KEEP_ALIVE_WITHOUT_CALLS, - SERVER_HOST, - SERVER_PORT, - SHUTDOWN_TIMEOUT, - TRUST_MANAGER_NAME - }; - - static void configure(ModelNode configuration, OperationContext context) throws OperationFailedException { - // Initialize the Netty logger factory - InternalLoggerFactory.setDefaultFactory(JdkLoggerFactory.INSTANCE); - - serverUpdates.clear(); - Integer n = GrpcSubsystemDefinition.GRPC_FLOW_CONTROL_WINDOW.resolveModelAttribute(context, configuration) - .asIntOrNull(); - if (n != null && n.intValue() != FLOW_CONTROL_WINDOW) { - FLOW_CONTROL_WINDOW = n; - serverUpdates.add(SERVER_ATTRIBUTE.FLOW_CONTROL_WINDOW); - restart = true; - } - Long l = GrpcSubsystemDefinition.GRPC_HANDSHAKE_TIMEOUT.resolveModelAttribute(context, configuration).asLongOrNull(); - if (l != null && l.longValue() != HANDSHAKE_TIMEOUT) { - HANDSHAKE_TIMEOUT = l; - serverUpdates.add(SERVER_ATTRIBUTE.HANDSHAKE_TIMEOUT); - restart = true; - } - n = GrpcSubsystemDefinition.GRPC_INITIAL_FLOW_CONTROL_WINDOW.resolveModelAttribute(context, configuration) - .asIntOrNull(); - if (n != null && n.intValue() != INITIAL_FLOW_CONTROL_WINDOW) { - INITIAL_FLOW_CONTROL_WINDOW = n; - serverUpdates.add(SERVER_ATTRIBUTE.INITIAL_FLOW_CONTROL_WINDOW); - restart = true; - } - l = GrpcSubsystemDefinition.GRPC_KEEP_ALIVE_TIME.resolveModelAttribute(context, configuration).asLongOrNull(); - if (l != null && l.longValue() != KEEP_ALIVE_TIME) { - KEEP_ALIVE_TIME = l; - serverUpdates.add(SERVER_ATTRIBUTE.KEEP_ALIVE_TIME); - restart = true; - } - n = GrpcSubsystemDefinition.GRPC_KEEP_ALIVE_TIMEOUT.resolveModelAttribute(context, configuration).asIntOrNull(); - if (n != null && n.intValue() != KEEP_ALIVE_TIMEOUT) { - KEEP_ALIVE_TIMEOUT = n; - serverUpdates.add(SERVER_ATTRIBUTE.KEEP_ALIVE_TIMEOUT); - restart = true; - } - String s = GrpcSubsystemDefinition.GRPC_KEY_MANAGER_NAME.resolveModelAttribute(context, configuration).asStringOrNull(); - if ((s != null && !s.equals(KEY_MANAGER_NAME)) || (KEY_MANAGER_NAME != null && !KEY_MANAGER_NAME.equals(s))) { - KEY_MANAGER_NAME = s; - restart = true; - } - n = GrpcSubsystemDefinition.GRPC_MAX_CONCURRENT_CALLS_PER_CONNECTION.resolveModelAttribute(context, configuration) - .asIntOrNull(); - if (n != null && n.intValue() != MAX_CONCURRENT_CALLS_PER_CONNECTION) { - MAX_CONCURRENT_CALLS_PER_CONNECTION = n; - serverUpdates.add(SERVER_ATTRIBUTE.MAX_CONCURRENT_CALLS_PER_CONNECTION); - restart = true; - } - l = GrpcSubsystemDefinition.GRPC_MAX_CONNECTION_AGE.resolveModelAttribute(context, configuration).asLongOrNull(); - if (l != null && l.longValue() != MAX_CONNECTION_AGE) { - MAX_CONNECTION_AGE = l; - serverUpdates.add(SERVER_ATTRIBUTE.MAX_CONNECTION_AGE); - restart = true; - } - l = GrpcSubsystemDefinition.GRPC_MAX_CONNECTION_AGE_GRACE.resolveModelAttribute(context, configuration).asLongOrNull(); - if (l != null && n.longValue() != MAX_CONNECTION_AGE_GRACE) { - MAX_CONNECTION_AGE_GRACE = l; - serverUpdates.add(SERVER_ATTRIBUTE.MAX_CONNECTION_AGE_GRACE); - restart = true; - } - l = GrpcSubsystemDefinition.GRPC_MAX_CONNECTION_IDLE.resolveModelAttribute(context, configuration).asLongOrNull(); - if (l != null && l.longValue() != MAX_CONNECTION_IDLE) { - MAX_CONNECTION_IDLE = l; - serverUpdates.add(SERVER_ATTRIBUTE.MAX_CONNECTION_IDLE); - restart = true; - } - n = GrpcSubsystemDefinition.GRPC_MAX_INBOUND_MESSAGE_SIZE.resolveModelAttribute(context, configuration).asIntOrNull(); - if (n != null && n.intValue() != MAX_INBOUND_MESSAGE_SIZE) { - MAX_INBOUND_MESSAGE_SIZE = n; - serverUpdates.add(SERVER_ATTRIBUTE.MAX_INBOUND_MESSAGE_SIZE); - restart = true; - } - n = GrpcSubsystemDefinition.GRPC_MAX_INBOUND_METADATA_SIZE.resolveModelAttribute(context, configuration).asIntOrNull(); - if (n != null && n.intValue() != MAX_INBOUND_METADATA_SIZE) { - MAX_INBOUND_METADATA_SIZE = n; - serverUpdates.add(SERVER_ATTRIBUTE.MAX_INBOUND_METADATA_SIZE); - restart = true; - } - l = GrpcSubsystemDefinition.GRPC_PERMIT_KEEP_ALIVE_TIME.resolveModelAttribute(context, configuration).asLongOrNull(); - if (l != null && l.longValue() != PERMIT_KEEP_ALIVE_TIME) { - PERMIT_KEEP_ALIVE_TIME = l; - serverUpdates.add(SERVER_ATTRIBUTE.PERMIT_KEEP_ALIVE_TIME); - restart = true; - } - Boolean b = GrpcSubsystemDefinition.GRPC_PERMIT_KEEP_ALIVE_WITHOUT_CALLS.resolveModelAttribute(context, configuration) - .asBooleanOrNull(); - if (b != null && b.booleanValue() != PERMIT_KEEP_ALIVE_WITHOUT_CALLS) { - PERMIT_KEEP_ALIVE_WITHOUT_CALLS = b; - serverUpdates.add(SERVER_ATTRIBUTE.PERMIT_KEEP_ALIVE_WITHOUT_CALLS); - restart = true; - } - s = GrpcSubsystemDefinition.GRPC_PROTOCOL_PROVIDER.resolveModelAttribute(context, configuration).asStringOrNull(); - if ((s != null && !s.equals(PROTOCOL_PROVIDER)) || (PROTOCOL_PROVIDER != null && !PROTOCOL_PROVIDER.equals(s))) { - PROTOCOL_PROVIDER = s; - sslUpdates.add(SSL_ATTRIBUTE.PROTOCOL_PROVIDER); - restart = true; - } - s = GrpcSubsystemDefinition.GRPC_SERVER_HOST.resolveModelAttribute(context, configuration).asStringOrNull(); - if ((s != null && !s.equals(SERVER_HOST)) || (SERVER_HOST != null && !SERVER_HOST.equals(s))) { - SERVER_HOST = s; - restart = true; - } - n = GrpcSubsystemDefinition.GRPC_SERVER_PORT.resolveModelAttribute(context, configuration).asIntOrNull(); - if (n != null && n.intValue() != SERVER_PORT) { - SERVER_PORT = n; - restart = true; - } - l = GrpcSubsystemDefinition.GRPC_SESSION_CACHE_SIZE.resolveModelAttribute(context, configuration).asLongOrNull(); - if (l != null && l.longValue() != SESSION_CACHE_SIZE) { - SESSION_CACHE_SIZE = l; - sslUpdates.add(SSL_ATTRIBUTE.SESSION_CACHE_SIZE); - restart = true; - } - l = GrpcSubsystemDefinition.GRPC_SESSION_TIMEOUT.resolveModelAttribute(context, configuration).asLongOrNull(); - if (l != null && l.longValue() != SESSION_TIMEOUT) { - SESSION_TIMEOUT = l; - sslUpdates.add(SSL_ATTRIBUTE.SESSION_TIMEOUT); - restart = true; - } - n = GrpcSubsystemDefinition.GRPC_SHUTDOWN_TIMEOUT.resolveModelAttribute(context, configuration).asIntOrNull(); - if (n != null && n.intValue() != SHUTDOWN_TIMEOUT) { - SHUTDOWN_TIMEOUT = n; - restart = true; - } - s = GrpcSubsystemDefinition.GRPC_SSL_CONTEXT_NAME.resolveModelAttribute(context, configuration).asStringOrNull(); - if ((s != null && !s.equals(SSL_CONTEXT_NAME)) || (SSL_CONTEXT_NAME != null && !SSL_CONTEXT_NAME.equals(s))) { - SSL_CONTEXT_NAME = s; - restart = true; - } - b = GrpcSubsystemDefinition.GRPC_START_TLS.resolveModelAttribute(context, configuration).asBooleanOrNull(); - if (b != null && b.booleanValue() != START_TLS) { - START_TLS = b; - sslUpdates.add(SSL_ATTRIBUTE.START_TLS); - restart = true; - } - s = GrpcSubsystemDefinition.GRPC_TRUST_MANAGER_NAME.resolveModelAttribute(context, configuration).asStringOrNull(); - if ((s != null && !s.equals(TRUST_MANAGER_NAME)) || (TRUST_MANAGER_NAME != null && !TRUST_MANAGER_NAME.equals(s))) { - TRUST_MANAGER_NAME = s; - restart = true; - } - } - - public static void install(ServiceTarget serviceTarget, DeploymentUnit deploymentUnit, List> serviceClasses) - throws Exception { - if (grpcServerService == null || (restart && !serverRestarting)) { - synchronized (monitor) { - if (grpcServerService == null || (restart && !serverRestarting)) { - serverRestarting = true; - restart = false; - - // setup service - ServiceName serviceName = deploymentUnit.getServiceName().append(SERVICE_NAME); - ServiceBuilder serviceBuilder = serviceTarget.addService(serviceName); - Consumer serviceConsumer = serviceBuilder.provides(serviceName); - - // wire dependencies - Supplier executorSupplier = Services.requireServerExecutor(serviceBuilder); - CapabilityServiceSupport css = deploymentUnit.getAttachment(Attachments.CAPABILITY_SERVICE_SUPPORT); - if (KEY_MANAGER_NAME != null && !"".equals(KEY_MANAGER_NAME)) { - ServiceName keyManagerName = css.getCapabilityServiceName(Capabilities.KEY_MANAGER_CAPABILITY, - KEY_MANAGER_NAME); - Supplier keyManagerSupplier = serviceBuilder.requires(keyManagerName); - if (keyManagerSupplier != null) { - keyManager = keyManagerSupplier.get(); - } - } else { - keyManager = null; - } - if (keyManager != null && SSL_CONTEXT_NAME != null && !"".equals(SSL_CONTEXT_NAME)) { - ServiceName sslContextName = css.getCapabilityServiceName(Capabilities.SSL_CONTEXT_CAPABILITY, - SSL_CONTEXT_NAME); - Supplier sslContextSupplier = serviceBuilder.requires(sslContextName); - if (sslContextSupplier != null) { - sslContext = sslContextSupplier.get(); - } - } else { - sslContext = null; - } - if (keyManager != null && TRUST_MANAGER_NAME != null && !"".equals(TRUST_MANAGER_NAME)) { - ServiceName trustManagerName = css.getCapabilityServiceName(Capabilities.TRUST_MANAGER_CAPABILITY, - TRUST_MANAGER_NAME); - Supplier trustManagerSupplier = serviceBuilder.requires(trustManagerName); - if (trustManagerSupplier != null) { - trustManager = trustManagerSupplier.get(); - } - } else { - trustManager = null; - } - // stop running service - if (grpcServerService != null) { - grpcServerService.stopServer(); - } - // install service - grpcServerService = new GrpcServerService(deploymentUnit.getName(), serviceConsumer, executorSupplier, - serviceClasses); - serviceBuilder.setInstance(grpcServerService); - serviceBuilder.install(); - return; - } - } - } - grpcServerService.addServiceClasses(serviceClasses); - } - - private final String name; +/** + * A gRPC Server service. + * + * @author James R. Perkins + */ +class GrpcServerService implements Service, WildFlyGrpcDeploymentRegistry { private final Consumer serverService; private final Supplier executorService; - private final Set> serviceClasses = new HashSet>(); - private final Set services = new HashSet(); - private Server server; - - private GrpcServerService(String name, Consumer serverService, - Supplier executorService, - List> serviceClasses) throws Exception { - this.name = name; + private final MutableHandlerRegistry registry; + private final NettyServerBuilder serverBuilder; + + private final ServerConfiguration configuration; + private final Map> deploymentServices; + private volatile Server server; + + GrpcServerService(final NettyServerBuilder serverBuilder, final MutableHandlerRegistry registry, + final Consumer serverService, final Supplier executorService, + final ServerConfiguration configuration) { + this.serverBuilder = serverBuilder; + this.registry = registry; this.serverService = serverService; this.executorService = executorService; - for (Class serviceClass : serviceClasses) { - newService(serviceClass, this.serviceClasses, services); - } + this.configuration = configuration; + deploymentServices = new ConcurrentHashMap<>(); } @Override @@ -354,157 +82,96 @@ public void start(StartContext context) { context.asynchronous(); executorService.get().submit(() -> { try { - startServer(); + if (configuration.getKeyManager() != null) { + final SSLContext sslContext = configuration.getSslContext() == null ? null + : configuration.getSslContext() + .get(); + serverBuilder.sslContext(createSslContext(sslContext)); + } + server = serverBuilder.build().start(); + GrpcLogger.LOGGER.serverListening(configuration.getHostName(), server.getPort()); + serverService.accept(this); context.complete(); - serverRestarting = false; } catch (Throwable e) { context.failed(new StartException(e)); } }); - serverService.accept(this); - } - - void addServiceClasses(List> serviceClasses) throws Exception { - for (Class serviceClass : serviceClasses) { - newService(serviceClass, this.serviceClasses, services); - } } - private void startServer() throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException { - GrpcLogger.LOGGER.serverListening(name, SERVER_HOST, SERVER_PORT); - SocketAddress socketAddress = new InetSocketAddress(SERVER_HOST, SERVER_PORT); - NettyServerBuilder serverBuilder = NettyServerBuilder.forAddress(socketAddress); - - for (SERVER_ATTRIBUTE attr : serverUpdates) { - switch (attr) { - case FLOW_CONTROL_WINDOW: - serverBuilder.flowControlWindow(FLOW_CONTROL_WINDOW); - break; - case HANDSHAKE_TIMEOUT: - serverBuilder.handshakeTimeout(HANDSHAKE_TIMEOUT, SECONDS); - break; - case INITIAL_FLOW_CONTROL_WINDOW: - serverBuilder.initialFlowControlWindow(INITIAL_FLOW_CONTROL_WINDOW); - break; - case KEEP_ALIVE_TIME: - serverBuilder.keepAliveTime(KEEP_ALIVE_TIME, SECONDS); - break; - case KEEP_ALIVE_TIMEOUT: - serverBuilder.keepAliveTimeout(KEEP_ALIVE_TIMEOUT, SECONDS); - break; - case KEY_MANAGER_NAME: // Shouldn't be in serverUpdates - break; - case MAX_CONCURRENT_CALLS_PER_CONNECTION: - serverBuilder.maxConcurrentCallsPerConnection(MAX_CONCURRENT_CALLS_PER_CONNECTION); - break; - case MAX_CONNECTION_AGE: - serverBuilder.maxConnectionAge(MAX_CONNECTION_AGE, SECONDS); - break; - case MAX_CONNECTION_AGE_GRACE: - serverBuilder.maxConnectionAgeGrace(MAX_CONNECTION_AGE_GRACE, SECONDS); - break; - case MAX_CONNECTION_IDLE: - serverBuilder.maxConnectionIdle(MAX_CONNECTION_IDLE, SECONDS); - break; - case MAX_INBOUND_MESSAGE_SIZE: - serverBuilder.maxInboundMessageSize(MAX_INBOUND_MESSAGE_SIZE); - break; - case MAX_INBOUND_METADATA_SIZE: - serverBuilder.maxInboundMetadataSize(MAX_INBOUND_METADATA_SIZE); - break; - case PERMIT_KEEP_ALIVE_TIME: - serverBuilder.permitKeepAliveTime(PERMIT_KEEP_ALIVE_TIME, SECONDS); - break; - case PERMIT_KEEP_ALIVE_WITHOUT_CALLS: - serverBuilder.permitKeepAliveWithoutCalls(PERMIT_KEEP_ALIVE_WITHOUT_CALLS); - break; - case SERVER_HOST: // Shouldn't be in serverUpdates - break; - case SERVER_PORT: // Shouldn't be in serverUpdates - break; - case SHUTDOWN_TIMEOUT: // Shouldn't be in serverUpdates - break; - case TRUST_MANAGER_NAME: // Shouldn't be in serverUpdates - break; - - default: - GrpcLogger.LOGGER.unknownAttribute(attr.toString()); - break; + @Override + public void stop(final StopContext context) { + GrpcLogger.LOGGER.grpcStopping(); + final Server server = this.server; + if (server != null) { + try { + server.shutdown().awaitTermination(configuration.getShutdownTimeout(), SECONDS); + } catch (InterruptedException e) { + GrpcLogger.LOGGER.failedToStopGrpcServer(e); } } - - if (keyManager != null && !"".equals(keyManager)) { - serverBuilder.sslContext(getSslContext(sslContext)); - } - - for (BindableService serviceClass : services) { - serverBuilder.addService(serviceClass); - // GrpcLogger.LOGGER.registerService(serviceClass); - } - server = serverBuilder.build().start(); - } - - @SuppressWarnings("deprecation") - private void newService(Class serviceClass, Set> serviceClasses, Set services) - throws ClassNotFoundException, InstantiationException, IllegalAccessException { - if (serviceClasses.contains(serviceClass)) { - return; - } - serviceClasses.add(serviceClass); - Object instance = serviceClass.newInstance(); - services.add((BindableService) instance); + serverService.accept(null); } @Override - public void stop(final StopContext context) { - GrpcLogger.LOGGER.serverStopping(name); - if (server != null) { - stopServer(); + public void addService(final DeploymentUnit deployment, final Class serviceType) { + final String deploymentName = deployment.getName(); + GrpcLogger.LOGGER.registerService(serviceType.getName(), deploymentName); + // We must have a no-arg constructor + final BindableService bindableService; + if (System.getSecurityManager() == null) { + try { + final Constructor constructor = serviceType.getConstructor(); + bindableService = constructor.newInstance(); + } catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) { + throw GrpcLogger.LOGGER.failedToRegisterService(e, serviceType.getName(), deploymentName); + } + } else { + bindableService = AccessController.doPrivileged((PrivilegedAction) () -> { + try { + final Constructor constructor = serviceType.getConstructor(); + return constructor.newInstance(); + } catch (NoSuchMethodException | InvocationTargetException | InstantiationException + | IllegalAccessException e) { + throw GrpcLogger.LOGGER.failedToRegisterService(e, serviceType.getName(), deploymentName); + } + }); } - serverService.accept(null); + final Collection defs = deploymentServices.computeIfAbsent(deploymentName, + (c) -> new ArrayList<>()); + defs.add(registry.addService(bindableService)); } - private void stopServer() { - try { - if (server != null) { - server.shutdown().awaitTermination(SHUTDOWN_TIMEOUT, SECONDS); + @Override + public void removeDeploymentServices(final DeploymentUnit deployment) { + final Collection defs = deploymentServices.remove(deployment.getName()); + if (defs != null) { + for (ServerServiceDefinition def : defs) { + registry.removeService(def); } - } catch (InterruptedException e) { - e.printStackTrace(); } } - private SslContext getSslContext(SSLContext sslContext) throws SSLException { - SslContextBuilder sslContextBuilder = SslContextBuilder.forServer(keyManager); + private SslContext createSslContext(final SSLContext sslContext) throws SSLException { + final SslContextBuilder sslContextBuilder = SslContextBuilder.forServer(configuration.getKeyManager().get()); if (sslContext != null) { sslContextBuilder.sslContextProvider(sslContext.getProvider()); - SSLEngine sslEngine = sslContext.createSSLEngine(); + final SSLEngine sslEngine = sslContext.createSSLEngine(); sslContextBuilder.ciphers(Arrays.asList(sslEngine.getEnabledCipherSuites())); sslContextBuilder.protocols(sslContext.getDefaultSSLParameters().getApplicationProtocols()); - if (trustManager != null) { - sslContextBuilder.trustManager(trustManager); + if (configuration.getTrustManager() != null) { + sslContextBuilder.trustManager(configuration.getTrustManager().get()); } } - for (SSL_ATTRIBUTE attr : sslUpdates) { - switch (attr) { - case PROTOCOL_PROVIDER: - sslContextBuilder.sslProvider(SslProvider.valueOf(PROTOCOL_PROVIDER)); - break; - case SESSION_CACHE_SIZE: - sslContextBuilder.sessionCacheSize(SESSION_CACHE_SIZE); - break; - case SESSION_TIMEOUT: - sslContextBuilder.sessionTimeout(SESSION_TIMEOUT); - break; - case START_TLS: - sslContextBuilder.startTls(START_TLS); - break; - default: - GrpcLogger.LOGGER.unknownAttribute(attr.toString()); - break; - } + if (configuration.getProtocolProvider() != null) { + sslContextBuilder.sslProvider(SslProvider.valueOf(configuration.getProtocolProvider())); + } + if (configuration.getSessionCacheSize() != null) { + sslContextBuilder.sessionCacheSize(configuration.getSessionCacheSize()); + } + if (configuration.getSessionTimeout() != null) { + sslContextBuilder.sessionTimeout(configuration.getSessionTimeout()); } - sslContextBuilder = GrpcSslContexts.configure(sslContextBuilder); - return sslContextBuilder.build(); + sslContextBuilder.startTls(configuration.isStartTls()); + return GrpcSslContexts.configure(sslContextBuilder).build(); } } diff --git a/subsystem/src/main/java/org/wildfly/extension/grpc/GrpcSubsystemAdd.java b/subsystem/src/main/java/org/wildfly/extension/grpc/GrpcSubsystemAdd.java index 181b914..f1268a3 100644 --- a/subsystem/src/main/java/org/wildfly/extension/grpc/GrpcSubsystemAdd.java +++ b/subsystem/src/main/java/org/wildfly/extension/grpc/GrpcSubsystemAdd.java @@ -15,16 +15,33 @@ */ package org.wildfly.extension.grpc; +import java.net.InetSocketAddress; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; + import org.jboss.as.controller.AbstractBoottimeAddStepHandler; +import org.jboss.as.controller.AttributeDefinition; +import org.jboss.as.controller.CapabilityServiceBuilder; +import org.jboss.as.controller.CapabilityServiceTarget; import org.jboss.as.controller.OperationContext; import org.jboss.as.controller.OperationFailedException; import org.jboss.as.server.AbstractDeploymentChainStep; import org.jboss.as.server.DeploymentProcessorTarget; +import org.jboss.as.server.Services; import org.jboss.as.server.deployment.Phase; import org.jboss.dmr.ModelNode; import org.wildfly.extension.grpc.deployment.GrpcDependencyProcessor; import org.wildfly.extension.grpc.deployment.GrpcDeploymentProcessor; +import io.grpc.netty.NettyServerBuilder; +import io.grpc.util.MutableHandlerRegistry; +import io.netty.util.internal.logging.InternalLoggerFactory; +import io.netty.util.internal.logging.JdkLoggerFactory; + class GrpcSubsystemAdd extends AbstractBoottimeAddStepHandler { static GrpcSubsystemAdd INSTANCE = new GrpcSubsystemAdd(); @@ -36,6 +53,133 @@ public GrpcSubsystemAdd() { @Override protected void performBoottime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException { + // Initialize the Netty logger factory + InternalLoggerFactory.setDefaultFactory(JdkLoggerFactory.INSTANCE); + // GrpcServerService.configure(operation, context); + + final String serverHost = GrpcSubsystemDefinition.GRPC_SERVER_HOST.resolveModelAttribute(context, model) + .asString(); + final MutableHandlerRegistry handlerRegistry = new MutableHandlerRegistry(); + final int serverPort = GrpcSubsystemDefinition.GRPC_SERVER_PORT.resolveModelAttribute(context, model).asInt(); + NettyServerBuilder serverBuilder = NettyServerBuilder.forAddress(new InetSocketAddress(serverHost, serverPort)); + serverBuilder.fallbackHandlerRegistry(handlerRegistry); + + if (isDefined(GrpcSubsystemDefinition.GRPC_FLOW_CONTROL_WINDOW, model)) { + serverBuilder + .flowControlWindow(GrpcSubsystemDefinition.GRPC_FLOW_CONTROL_WINDOW.resolveModelAttribute(context, model) + .asInt()); + } + + if (isDefined(GrpcSubsystemDefinition.GRPC_HANDSHAKE_TIMEOUT, model)) { + serverBuilder.handshakeTimeout(GrpcSubsystemDefinition.GRPC_HANDSHAKE_TIMEOUT.resolveModelAttribute(context, model) + .asInt(), TimeUnit.SECONDS); + } + + if (isDefined(GrpcSubsystemDefinition.GRPC_INITIAL_FLOW_CONTROL_WINDOW, model)) { + serverBuilder.initialFlowControlWindow( + GrpcSubsystemDefinition.GRPC_INITIAL_FLOW_CONTROL_WINDOW.resolveModelAttribute(context, model) + .asInt()); + } + + if (isDefined(GrpcSubsystemDefinition.GRPC_KEEP_ALIVE_TIME, model)) { + serverBuilder.keepAliveTime(GrpcSubsystemDefinition.GRPC_KEEP_ALIVE_TIME.resolveModelAttribute(context, model) + .asLong(), TimeUnit.SECONDS); + } + + if (isDefined(GrpcSubsystemDefinition.GRPC_KEEP_ALIVE_TIMEOUT, model)) { + serverBuilder.keepAliveTimeout(GrpcSubsystemDefinition.GRPC_KEEP_ALIVE_TIMEOUT.resolveModelAttribute(context, model) + .asLong(), TimeUnit.SECONDS); + } + + if (isDefined(GrpcSubsystemDefinition.GRPC_MAX_CONCURRENT_CALLS_PER_CONNECTION, model)) { + serverBuilder.maxConcurrentCallsPerConnection( + GrpcSubsystemDefinition.GRPC_MAX_CONCURRENT_CALLS_PER_CONNECTION.resolveModelAttribute(context, model) + .asInt()); + } + + if (isDefined(GrpcSubsystemDefinition.GRPC_MAX_CONNECTION_AGE, model)) { + serverBuilder.maxConnectionAge(GrpcSubsystemDefinition.GRPC_MAX_CONNECTION_AGE.resolveModelAttribute(context, model) + .asLong(), TimeUnit.SECONDS); + } + + if (isDefined(GrpcSubsystemDefinition.GRPC_MAX_CONNECTION_AGE_GRACE, model)) { + serverBuilder.maxConnectionAgeGrace( + GrpcSubsystemDefinition.GRPC_MAX_CONNECTION_AGE_GRACE.resolveModelAttribute(context, model) + .asLong(), + TimeUnit.SECONDS); + } + + if (isDefined(GrpcSubsystemDefinition.GRPC_MAX_CONNECTION_IDLE, model)) { + serverBuilder + .maxConnectionIdle(GrpcSubsystemDefinition.GRPC_MAX_CONNECTION_IDLE.resolveModelAttribute(context, model) + .asLong(), TimeUnit.SECONDS); + } + + if (isDefined(GrpcSubsystemDefinition.GRPC_MAX_INBOUND_MESSAGE_SIZE, model)) { + serverBuilder.maxInboundMessageSize( + GrpcSubsystemDefinition.GRPC_MAX_INBOUND_MESSAGE_SIZE.resolveModelAttribute(context, model) + .asInt()); + } + + if (isDefined(GrpcSubsystemDefinition.GRPC_MAX_INBOUND_METADATA_SIZE, model)) { + serverBuilder.maxInboundMetadataSize( + GrpcSubsystemDefinition.GRPC_MAX_INBOUND_METADATA_SIZE.resolveModelAttribute(context, model) + .asInt()); + } + + if (isDefined(GrpcSubsystemDefinition.GRPC_PERMIT_KEEP_ALIVE_TIME, model)) { + serverBuilder.permitKeepAliveTime( + GrpcSubsystemDefinition.GRPC_PERMIT_KEEP_ALIVE_TIME.resolveModelAttribute(context, model) + .asLong(), + TimeUnit.SECONDS); + } + + serverBuilder.permitKeepAliveWithoutCalls( + GrpcSubsystemDefinition.GRPC_PERMIT_KEEP_ALIVE_WITHOUT_CALLS.resolveModelAttribute(context, model) + .asBoolean()); + + final CapabilityServiceTarget target = context.getCapabilityServiceTarget(); + final CapabilityServiceBuilder builder = target.addCapability(GrpcSubsystemDefinition.SERVER_CAPABILITY); + final ServerConfiguration configuration = new ServerConfiguration(serverHost); + + configuration.setProtocolProvider(GrpcSubsystemDefinition.GRPC_PROTOCOL_PROVIDER.resolveModelAttribute(context, model) + .asStringOrNull()) + .setSessionCacheSize(GrpcSubsystemDefinition.GRPC_SESSION_CACHE_SIZE.resolveModelAttribute(context, model) + .asLongOrNull()) + .setSessionTimeout(GrpcSubsystemDefinition.GRPC_SESSION_TIMEOUT.resolveModelAttribute(context, model) + .asLongOrNull()) + .setShutdownTimeout(GrpcSubsystemDefinition.GRPC_SHUTDOWN_TIMEOUT.resolveModelAttribute(context, model) + .asInt()) + .setStartTls(GrpcSubsystemDefinition.GRPC_START_TLS.resolveModelAttribute(context, model) + .asBoolean()); + + if (isDefined(GrpcSubsystemDefinition.GRPC_TRUST_MANAGER_NAME, model)) { + configuration.setTrustManager(builder.requiresCapability(Capabilities.TRUST_MANAGER_CAPABILITY, TrustManager.class, + GrpcSubsystemDefinition.GRPC_TRUST_MANAGER_NAME.resolveModelAttribute(context, model) + .asString())); + } + + if (isDefined(GrpcSubsystemDefinition.GRPC_KEY_MANAGER_NAME, model)) { + final String name = GrpcSubsystemDefinition.GRPC_KEY_MANAGER_NAME.resolveModelAttribute(context, model) + .asString(); + if (!name.isBlank()) { + configuration.setKeyManager(builder.requiresCapability(Capabilities.KEY_MANAGER_CAPABILITY, KeyManager.class, + name)); + } + } + + if (isDefined(GrpcSubsystemDefinition.GRPC_SSL_CONTEXT_NAME, model)) { + configuration.setSslContext(builder.requiresCapability(Capabilities.SSL_CONTEXT_CAPABILITY, SSLContext.class, + GrpcSubsystemDefinition.GRPC_SSL_CONTEXT_NAME.resolveModelAttribute(context, model).asString())); + } + + final Consumer provides = builder.provides(GrpcSubsystemDefinition.SERVER_CAPABILITY); + + final GrpcServerService service = new GrpcServerService(serverBuilder, handlerRegistry, provides, + Services.requireServerExecutor(builder), configuration); + + builder.setInstance(service) + .install(); context.addStep(new AbstractDeploymentChainStep() { public void execute(DeploymentProcessorTarget processorTarget) { @@ -46,9 +190,13 @@ public void execute(DeploymentProcessorTarget processorTarget) { int DEPLOYMENT_PRIORITY = 6305; processorTarget.addDeploymentProcessor(GrpcExtension.SUBSYSTEM_NAME, Phase.POST_MODULE, - DEPLOYMENT_PRIORITY, new GrpcDeploymentProcessor()); + DEPLOYMENT_PRIORITY, new GrpcDeploymentProcessor(service)); } }, OperationContext.Stage.RUNTIME); - GrpcServerService.configure(operation, context); + + } + + private static boolean isDefined(final AttributeDefinition def, final ModelNode model) { + return model.hasDefined(def.getName()); } } diff --git a/subsystem/src/main/java/org/wildfly/extension/grpc/GrpcSubsystemDefinition.java b/subsystem/src/main/java/org/wildfly/extension/grpc/GrpcSubsystemDefinition.java index 0d25ca8..db9a944 100644 --- a/subsystem/src/main/java/org/wildfly/extension/grpc/GrpcSubsystemDefinition.java +++ b/subsystem/src/main/java/org/wildfly/extension/grpc/GrpcSubsystemDefinition.java @@ -24,6 +24,7 @@ import org.jboss.as.controller.SimpleAttributeDefinition; import org.jboss.as.controller.SimpleAttributeDefinitionBuilder; import org.jboss.as.controller.SimpleResourceDefinition; +import org.jboss.as.controller.capability.RuntimeCapability; import org.jboss.as.controller.operations.validation.IntRangeValidator; import org.jboss.as.controller.operations.validation.LongRangeValidator; import org.jboss.as.controller.operations.validation.ModelTypeValidator; @@ -35,7 +36,6 @@ public class GrpcSubsystemDefinition extends PersistentResourceDefinition { static final SimpleAttributeDefinition GRPC_FLOW_CONTROL_WINDOW = new SimpleAttributeDefinitionBuilder( "flow-control-window", ModelType.INT) .setAllowExpression(true) - .setDefaultValue(null) .setRequired(false) .setRestartAllServices() .setValidator(new IntRangeValidator(0, false, true)) @@ -44,7 +44,6 @@ public class GrpcSubsystemDefinition extends PersistentResourceDefinition { static final SimpleAttributeDefinition GRPC_HANDSHAKE_TIMEOUT = new SimpleAttributeDefinitionBuilder( "handshake-timeout", ModelType.LONG) .setAllowExpression(true) - .setDefaultValue(null) .setRequired(false) .setRestartAllServices() .setValidator(new IntRangeValidator(0, false, true)) @@ -53,7 +52,6 @@ public class GrpcSubsystemDefinition extends PersistentResourceDefinition { static final SimpleAttributeDefinition GRPC_INITIAL_FLOW_CONTROL_WINDOW = new SimpleAttributeDefinitionBuilder( "initial-flow-control-window", ModelType.INT) .setAllowExpression(true) - .setDefaultValue(null) .setRequired(false) .setRestartAllServices() .setValidator(new IntRangeValidator(0, false, true)) @@ -62,7 +60,6 @@ public class GrpcSubsystemDefinition extends PersistentResourceDefinition { static final SimpleAttributeDefinition GRPC_KEEP_ALIVE_TIME = new SimpleAttributeDefinitionBuilder( "keep-alive-time", ModelType.LONG) .setAllowExpression(true) - .setDefaultValue(null) .setRequired(false) .setRestartAllServices() .setValidator(new IntRangeValidator(0, false, true)) @@ -71,7 +68,6 @@ public class GrpcSubsystemDefinition extends PersistentResourceDefinition { static final SimpleAttributeDefinition GRPC_KEEP_ALIVE_TIMEOUT = new SimpleAttributeDefinitionBuilder( "keep-alive-timeout", ModelType.LONG) .setAllowExpression(true) - .setDefaultValue(null) .setRequired(false) .setRestartAllServices() .setValidator(new IntRangeValidator(0, false, true)) @@ -80,7 +76,7 @@ public class GrpcSubsystemDefinition extends PersistentResourceDefinition { static final SimpleAttributeDefinition GRPC_KEY_MANAGER_NAME = new SimpleAttributeDefinitionBuilder( "key-manager-name", ModelType.STRING) .setAllowExpression(true) - .setDefaultValue(null) + .setCapabilityReference(Capabilities.KEY_MANAGER_CAPABILITY) .setRequired(false) .setRestartAllServices() .setValidator(new ModelTypeValidator(ModelType.STRING, false)) @@ -89,7 +85,6 @@ public class GrpcSubsystemDefinition extends PersistentResourceDefinition { static final SimpleAttributeDefinition GRPC_MAX_CONCURRENT_CALLS_PER_CONNECTION = new SimpleAttributeDefinitionBuilder( "max-concurrent-calls-per-connection", ModelType.INT) .setAllowExpression(true) - .setDefaultValue(null) .setRequired(false) .setRestartAllServices() .setValidator(new IntRangeValidator(0, false, true)) @@ -98,7 +93,6 @@ public class GrpcSubsystemDefinition extends PersistentResourceDefinition { static final SimpleAttributeDefinition GRPC_MAX_CONNECTION_AGE = new SimpleAttributeDefinitionBuilder( "max-connection-age", ModelType.LONG) .setAllowExpression(true) - .setDefaultValue(null) .setRequired(false) .setRestartAllServices() .setValidator(new IntRangeValidator(0, false, true)) @@ -107,7 +101,6 @@ public class GrpcSubsystemDefinition extends PersistentResourceDefinition { static final SimpleAttributeDefinition GRPC_MAX_CONNECTION_AGE_GRACE = new SimpleAttributeDefinitionBuilder( "max-connection-age-grace", ModelType.LONG) .setAllowExpression(true) - .setDefaultValue(null) .setRequired(false) .setRestartAllServices() .setValidator(new IntRangeValidator(0, false, true)) @@ -116,7 +109,6 @@ public class GrpcSubsystemDefinition extends PersistentResourceDefinition { static final SimpleAttributeDefinition GRPC_MAX_CONNECTION_IDLE = new SimpleAttributeDefinitionBuilder( "max-connection-idle", ModelType.LONG) .setAllowExpression(true) - .setDefaultValue(null) .setRequired(false) .setRestartAllServices() .setValidator(new IntRangeValidator(0, false, true)) @@ -125,7 +117,6 @@ public class GrpcSubsystemDefinition extends PersistentResourceDefinition { static final SimpleAttributeDefinition GRPC_MAX_INBOUND_MESSAGE_SIZE = new SimpleAttributeDefinitionBuilder( "max-inbound-message-size", ModelType.INT) .setAllowExpression(true) - .setDefaultValue(null) .setRequired(false) .setRestartAllServices() .setValidator(new IntRangeValidator(0, false, true)) @@ -134,7 +125,6 @@ public class GrpcSubsystemDefinition extends PersistentResourceDefinition { static final SimpleAttributeDefinition GRPC_MAX_INBOUND_METADATA_SIZE = new SimpleAttributeDefinitionBuilder( "max-inbound-metadata-size", ModelType.INT) .setAllowExpression(true) - .setDefaultValue(null) .setRequired(false) .setRestartAllServices() .setValidator(new IntRangeValidator(0, false, true)) @@ -143,7 +133,6 @@ public class GrpcSubsystemDefinition extends PersistentResourceDefinition { static final SimpleAttributeDefinition GRPC_PERMIT_KEEP_ALIVE_TIME = new SimpleAttributeDefinitionBuilder( "permit-keep-alive-time", ModelType.LONG) .setAllowExpression(true) - .setDefaultValue(null) .setRequired(false) .setRestartAllServices() .setValidator(new IntRangeValidator(0, false, true)) @@ -152,7 +141,7 @@ public class GrpcSubsystemDefinition extends PersistentResourceDefinition { static final SimpleAttributeDefinition GRPC_PERMIT_KEEP_ALIVE_WITHOUT_CALLS = new SimpleAttributeDefinitionBuilder( "permit-keep-alive-without-calls", ModelType.BOOLEAN) .setAllowExpression(true) - .setDefaultValue(null) + .setDefaultValue(ModelNode.FALSE) .setRequired(false) .setRestartAllServices() .setValidator(new ModelTypeValidator(ModelType.BOOLEAN, false)) @@ -161,10 +150,9 @@ public class GrpcSubsystemDefinition extends PersistentResourceDefinition { static final SimpleAttributeDefinition GRPC_PROTOCOL_PROVIDER = new SimpleAttributeDefinitionBuilder( "protocol-provider", ModelType.STRING) .setAllowExpression(true) - .setDefaultValue(null) .setRequired(false) .setRestartAllServices() - .setValidator(new ModelTypeValidator(ModelType.STRING, false)) + .setValidator(new ModelTypeValidator(ModelType.STRING, true)) .build(); static final SimpleAttributeDefinition GRPC_SERVER_HOST = new SimpleAttributeDefinitionBuilder( @@ -173,7 +161,7 @@ public class GrpcSubsystemDefinition extends PersistentResourceDefinition { .setDefaultValue(new ModelNode("localhost")) .setRequired(false) .setRestartAllServices() - .setValidator(new ModelTypeValidator(ModelType.STRING, false)) + .setValidator(new ModelTypeValidator(ModelType.STRING, true)) .build(); static final SimpleAttributeDefinition GRPC_SERVER_PORT = new SimpleAttributeDefinitionBuilder( @@ -182,61 +170,59 @@ public class GrpcSubsystemDefinition extends PersistentResourceDefinition { .setDefaultValue(new ModelNode(9555)) .setRequired(false) .setRestartAllServices() - .setValidator(new IntRangeValidator(0, 65535, false, true)) + .setValidator(new IntRangeValidator(0, 65535, true, true)) .build(); static final SimpleAttributeDefinition GRPC_SESSION_CACHE_SIZE = new SimpleAttributeDefinitionBuilder( "session-cache-size", ModelType.LONG) .setAllowExpression(true) - .setDefaultValue(null) .setRequired(false) .setRestartAllServices() - .setValidator(new LongRangeValidator(0, Long.MAX_VALUE, false, true)) + .setValidator(new LongRangeValidator(0, Long.MAX_VALUE, true, true)) .build(); static final SimpleAttributeDefinition GRPC_SESSION_TIMEOUT = new SimpleAttributeDefinitionBuilder( "session-timeout", ModelType.LONG) .setAllowExpression(true) - .setDefaultValue(null) .setRequired(false) .setRestartAllServices() - .setValidator(new LongRangeValidator(0, Long.MAX_VALUE, false, true)) + .setValidator(new LongRangeValidator(0, Long.MAX_VALUE, true, true)) .build(); static final SimpleAttributeDefinition GRPC_SHUTDOWN_TIMEOUT = new SimpleAttributeDefinitionBuilder( - "shutdown-timeout", ModelType.INT) + "shutdown-timeout", ModelType.LONG) .setAllowExpression(true) - .setDefaultValue(new ModelNode(3)) + .setDefaultValue(new ModelNode(3L)) .setRequired(false) .setRestartAllServices() - .setValidator(new IntRangeValidator(0, Integer.MAX_VALUE, false, true)) + .setValidator(new LongRangeValidator(0, Integer.MAX_VALUE, true, true)) .build(); static final SimpleAttributeDefinition GRPC_SSL_CONTEXT_NAME = new SimpleAttributeDefinitionBuilder( "ssl-context-name", ModelType.STRING) .setAllowExpression(true) - .setDefaultValue(null) + .setCapabilityReference(Capabilities.SSL_CONTEXT_CAPABILITY) .setRequired(false) .setRestartAllServices() - .setValidator(new ModelTypeValidator(ModelType.STRING, false)) + .setValidator(new ModelTypeValidator(ModelType.STRING, true)) .build(); static final SimpleAttributeDefinition GRPC_START_TLS = new SimpleAttributeDefinitionBuilder( "start-tls", ModelType.BOOLEAN) .setAllowExpression(true) - .setDefaultValue(null) + .setDefaultValue(ModelNode.FALSE) .setRequired(false) .setRestartAllServices() - .setValidator(new ModelTypeValidator(ModelType.BOOLEAN, false)) + .setValidator(new ModelTypeValidator(ModelType.BOOLEAN, true)) .build(); static final SimpleAttributeDefinition GRPC_TRUST_MANAGER_NAME = new SimpleAttributeDefinitionBuilder( "trust-manager-name", ModelType.STRING) .setAllowExpression(true) - .setDefaultValue(null) + .setCapabilityReference(Capabilities.TRUST_MANAGER_CAPABILITY) .setRequired(false) .setRestartAllServices() - .setValidator(new ModelTypeValidator(ModelType.STRING, false)) + .setValidator(new ModelTypeValidator(ModelType.STRING, true)) .build(); static final List ATTRIBUTES = List.of( @@ -264,11 +250,16 @@ public class GrpcSubsystemDefinition extends PersistentResourceDefinition { GRPC_START_TLS, GRPC_TRUST_MANAGER_NAME); + static RuntimeCapability SERVER_CAPABILITY = RuntimeCapability.Builder.of("org.wildfly.grpc.server", false) + .setServiceType(GrpcServerService.class) + .build(); + // This must be initialized last to ensure the other static attributes are created first static final GrpcSubsystemDefinition INSTANCE = new GrpcSubsystemDefinition(); public GrpcSubsystemDefinition() { super(new SimpleResourceDefinition.Parameters(Paths.SUBSYSTEM, GrpcExtension.getResolver()) + .addCapabilities(SERVER_CAPABILITY) .setAddHandler(GrpcSubsystemAdd.INSTANCE) .setRemoveHandler(ReloadRequiredRemoveStepHandler.INSTANCE)); } diff --git a/subsystem/src/main/java/org/wildfly/extension/grpc/ServerConfiguration.java b/subsystem/src/main/java/org/wildfly/extension/grpc/ServerConfiguration.java new file mode 100644 index 0000000..a166234 --- /dev/null +++ b/subsystem/src/main/java/org/wildfly/extension/grpc/ServerConfiguration.java @@ -0,0 +1,121 @@ +/* + * Copyright 2022 Red Hat + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.wildfly.extension.grpc; + +import java.util.function.Supplier; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; + +/** + * A simple configuration for the {@link GrpcServerService}. + * + * @author James R. Perkins + */ +@SuppressWarnings("UnusedReturnValue") +class ServerConfiguration { + + private final String hostName; + private Supplier trustManager; + private Supplier keyManager; + private Supplier sslContext; + private long shutdownTimeout; + private String protocolProvider; + private Long sessionCacheSize; + private Long sessionTimeout; + private boolean startTls; + + ServerConfiguration(final String hostName) { + this.hostName = hostName; + } + + String getHostName() { + return hostName; + } + + Supplier getTrustManager() { + return trustManager; + } + + ServerConfiguration setTrustManager(final Supplier trustManager) { + this.trustManager = trustManager; + return this; + } + + Supplier getKeyManager() { + return keyManager; + } + + ServerConfiguration setKeyManager(final Supplier keyManager) { + this.keyManager = keyManager; + return this; + } + + Supplier getSslContext() { + return sslContext; + } + + ServerConfiguration setSslContext(final Supplier sslContext) { + this.sslContext = sslContext; + return this; + } + + long getShutdownTimeout() { + return shutdownTimeout; + } + + ServerConfiguration setShutdownTimeout(final long shutdownTimeout) { + this.shutdownTimeout = shutdownTimeout; + return this; + } + + String getProtocolProvider() { + return protocolProvider; + } + + ServerConfiguration setProtocolProvider(final String protocolProvider) { + this.protocolProvider = protocolProvider; + return this; + } + + Long getSessionCacheSize() { + return sessionCacheSize; + } + + ServerConfiguration setSessionCacheSize(final Long sessionCacheSize) { + this.sessionCacheSize = sessionCacheSize; + return this; + } + + Long getSessionTimeout() { + return sessionTimeout; + } + + ServerConfiguration setSessionTimeout(final Long sessionTimeout) { + this.sessionTimeout = sessionTimeout; + return this; + } + + boolean isStartTls() { + return startTls; + } + + ServerConfiguration setStartTls(final boolean startTls) { + this.startTls = startTls; + return this; + } +} diff --git a/subsystem/src/main/java/org/wildfly/extension/grpc/WildFlyGrpcDeploymentRegistry.java b/subsystem/src/main/java/org/wildfly/extension/grpc/WildFlyGrpcDeploymentRegistry.java new file mode 100644 index 0000000..200ef1c --- /dev/null +++ b/subsystem/src/main/java/org/wildfly/extension/grpc/WildFlyGrpcDeploymentRegistry.java @@ -0,0 +1,42 @@ +/* + * Copyright 2022 Red Hat + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.wildfly.extension.grpc; + +import org.jboss.as.server.deployment.DeploymentUnit; + +import io.grpc.BindableService; + +/** + * A registry for registering {@linkplain BindableService services} with a deployment. + * + * @author James R. Perkins + */ +public interface WildFlyGrpcDeploymentRegistry { + + /** + * Adds a {@link BindableService} to the gRPC server. + * + * @param serviceType the service to add, + */ + void addService(DeploymentUnit deployment, Class serviceType); + + /** + * Removes all the associated services from the gRPC server. + * + * @param deployment the name of the deployment to remove the services for + */ + void removeDeploymentServices(DeploymentUnit deployment); +} diff --git a/subsystem/src/main/java/org/wildfly/extension/grpc/_private/GrpcLogger.java b/subsystem/src/main/java/org/wildfly/extension/grpc/_private/GrpcLogger.java index 88647a6..8f845bb 100644 --- a/subsystem/src/main/java/org/wildfly/extension/grpc/_private/GrpcLogger.java +++ b/subsystem/src/main/java/org/wildfly/extension/grpc/_private/GrpcLogger.java @@ -17,11 +17,14 @@ import org.jboss.logging.BasicLogger; import org.jboss.logging.Logger; +import org.jboss.logging.annotations.Cause; import org.jboss.logging.annotations.LogMessage; import org.jboss.logging.annotations.Message; import org.jboss.logging.annotations.MessageLogger; -import static org.jboss.logging.Logger.Level.*; +import static org.jboss.logging.Logger.Level.DEBUG; +import static org.jboss.logging.Logger.Level.ERROR; +import static org.jboss.logging.Logger.Level.INFO; @MessageLogger(projectCode = "WFLYGRPC", length = 4) public interface GrpcLogger extends BasicLogger { @@ -29,26 +32,22 @@ public interface GrpcLogger extends BasicLogger { GrpcLogger LOGGER = Logger.getMessageLogger(GrpcLogger.class, "org.wildfly.extension.grpc"); @LogMessage(level = INFO) - @Message(id = 1, value = "gRPC service starting") - void grpcStarting(); + @Message(id = 1, value = "gRPC server listening on %s:%d") + void serverListening(String address, int port); @LogMessage(level = INFO) @Message(id = 2, value = "gRPC service stopping") void grpcStopping(); - @LogMessage(level = INFO) - @Message(id = 3, value = "gRPC server for %s listening on %s:%d") - void serverListening(String name, String address, int port); + @LogMessage(level = ERROR) + @Message(id = 3, value = "Failed to stop gRPC server") + void failedToStopGrpcServer(@Cause Throwable cause); - @LogMessage(level = INFO) - @Message(id = 4, value = "gRPC server for %s stopping") - void serverStopping(String name); + @Message(id = 4, value = "Failed to register %s for deployment %s") + RuntimeException failedToRegisterService(@Cause Throwable cause, String serviceName, String deployment); - @LogMessage(level = INFO) - @Message(id = 5, value = "gRPC service %s registered") - void registerService(String name); + @LogMessage(level = DEBUG) + @Message(id = 5, value = "Registering gRPC service %s for deployment %s.") + void registerService(String serviceName, String deploymentName); - @LogMessage(level = ERROR) - @Message(id = 6, value = "unknown Attribute: %s") - void unknownAttribute(String attribute); } diff --git a/subsystem/src/main/java/org/wildfly/extension/grpc/deployment/GrpcDeploymentProcessor.java b/subsystem/src/main/java/org/wildfly/extension/grpc/deployment/GrpcDeploymentProcessor.java index 5f61a39..b5ff087 100644 --- a/subsystem/src/main/java/org/wildfly/extension/grpc/deployment/GrpcDeploymentProcessor.java +++ b/subsystem/src/main/java/org/wildfly/extension/grpc/deployment/GrpcDeploymentProcessor.java @@ -29,45 +29,54 @@ import org.jboss.dmr.ModelNode; import org.jboss.jandex.DotName; import org.jboss.modules.Module; -import org.jboss.msc.service.ServiceTarget; import org.wildfly.extension.grpc.Constants; import org.wildfly.extension.grpc.GrpcExtension; -import org.wildfly.extension.grpc.GrpcServerService; +import org.wildfly.extension.grpc.WildFlyGrpcDeploymentRegistry; import io.grpc.BindableService; public class GrpcDeploymentProcessor implements DeploymentUnitProcessor { - public static final DotName BINDABLE_CLASS = DotName.createSimple(BindableService.class.getName()); + private static final DotName BINDABLE_CLASS = DotName.createSimple(BindableService.class.getName()); + + private final WildFlyGrpcDeploymentRegistry registry; + + public GrpcDeploymentProcessor(final WildFlyGrpcDeploymentRegistry registry) { + this.registry = registry; + } @Override public void deploy(DeploymentPhaseContext phaseContext) { - ServiceTarget serviceTarget = phaseContext.getServiceTarget(); DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit(); final CompositeIndex index = deploymentUnit.getAttachment(Attachments.COMPOSITE_ANNOTATION_INDEX); - List serviceClasses = index.getAllKnownImplementors(BINDABLE_CLASS).stream().map(ci -> ci.name().toString()) + List serviceClasses = index.getAllKnownImplementors(BINDABLE_CLASS) + .stream() + .map(ci -> ci.name().toString()) .collect(Collectors.toList()); Module module = deploymentUnit.getAttachment(Attachments.MODULE); - List> leaves = getLeaves(serviceClasses, module.getClassLoader()); + List> leaves = getLeaves(serviceClasses, module.getClassLoader()); processManagement(deploymentUnit, leaves); - try { - GrpcServerService.install(serviceTarget, deploymentUnit, getLeaves(serviceClasses, module.getClassLoader())); - } catch (Exception e) { - throw new RuntimeException(e); + for (Class type : getLeaves(serviceClasses, module.getClassLoader())) { + registry.addService(deploymentUnit, type); } } - private List> getLeaves(List classNames, ClassLoader classLoader) { - List> classes = new ArrayList>(); + @Override + public void undeploy(DeploymentUnit context) { + registry.removeDeploymentServices(context); + } + + private List> getLeaves(List classNames, ClassLoader classLoader) { + List> classes = new ArrayList<>(); try { for (String s : classNames) { - classes.add(classLoader.loadClass(s)); + classes.add(classLoader.loadClass(s).asSubclass(BindableService.class)); } } catch (ClassNotFoundException e) { throw new RuntimeException(e); } - List> leaves = new ArrayList>(); - for (Class clazz : classes) { + List> leaves = new ArrayList<>(); + for (Class clazz : classes) { if (isLeaf(clazz, classes)) { leaves.add(clazz); } @@ -75,7 +84,7 @@ private List> getLeaves(List classNames, ClassLoader classLoade return leaves; } - private boolean isLeaf(Class clazz, List> classes) { + private boolean isLeaf(Class clazz, List> classes) { for (Class c : classes) { if (clazz != c && clazz.isAssignableFrom(c)) { return false; @@ -84,7 +93,7 @@ private boolean isLeaf(Class clazz, List> classes) { return true; } - private void processManagement(DeploymentUnit deploymentUnit, List> grpcServiceClasses) { + private void processManagement(DeploymentUnit deploymentUnit, List> grpcServiceClasses) { DeploymentResourceSupport drs = deploymentUnit.getAttachment(Attachments.DEPLOYMENT_RESOURCE_SUPPORT); for (Class clazz : grpcServiceClasses) { @@ -93,8 +102,4 @@ private void processManagement(DeploymentUnit deploymentUnit, List> grp serviceModel.get(Constants.SERVICE_CLASS).set(clazz.getName()); } } - - @Override - public void undeploy(DeploymentUnit context) { - } }