-
Notifications
You must be signed in to change notification settings - Fork 1
feat: add http service framework #50
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| plugins { | ||
| `java-library` | ||
| id("org.hypertrace.publish-plugin") | ||
| } | ||
|
|
||
| dependencies { | ||
| api(project(":platform-service-framework")) | ||
| api("org.hypertrace.core.grpcutils:grpc-client-utils:0.7.6") | ||
| api("com.typesafe:config:1.4.2") | ||
| api("javax.servlet:javax.servlet-api:4.0.1") | ||
| api("com.google.inject:guice:5.1.0") | ||
| api(project(":service-framework-spi")) | ||
|
|
||
| implementation("org.slf4j:slf4j-api:1.7.36") | ||
| implementation("com.google.inject.extensions:guice-servlet:5.1.0") | ||
| implementation("com.google.guava:guava:31.1-jre") | ||
| implementation("org.eclipse.jetty:jetty-servlet:9.4.48.v20220622") | ||
| implementation("org.eclipse.jetty:jetty-server:9.4.48.v20220622") | ||
| implementation("org.eclipse.jetty:jetty-servlets:9.4.48.v20220622") | ||
|
|
||
| annotationProcessor("org.projectlombok:lombok:1.18.24") | ||
| compileOnly("org.projectlombok:lombok:1.18.24") | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| package org.hypertrace.core.serviceframework.http; | ||
|
|
||
| public interface HttpContainer { | ||
| void start(); | ||
|
|
||
| void blockUntilStopped(); | ||
|
|
||
| void stop(); | ||
|
|
||
| boolean isStopped(); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| package org.hypertrace.core.serviceframework.http; | ||
|
|
||
| import com.typesafe.config.Config; | ||
| import org.hypertrace.core.grpcutils.client.GrpcChannelRegistry; | ||
| import org.hypertrace.core.serviceframework.spi.PlatformServiceLifecycle; | ||
|
|
||
| public interface HttpContainerEnvironment { | ||
| GrpcChannelRegistry getChannelRegistry(); | ||
|
|
||
| Config getConfig(String serviceName); | ||
|
|
||
| PlatformServiceLifecycle getLifecycle(); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| package org.hypertrace.core.serviceframework.http; | ||
|
|
||
| import com.google.inject.Injector; | ||
| import java.util.List; | ||
| import javax.servlet.MultipartConfigElement; | ||
| import javax.servlet.Servlet; | ||
| import lombok.Builder; | ||
| import lombok.Value; | ||
| import lombok.experimental.Accessors; | ||
|
|
||
| @Value | ||
| @Builder | ||
| public class HttpHandlerDefinition { | ||
| String name; | ||
| int port; | ||
| String contextPath; | ||
| Servlet servlet; | ||
| int maxHeaderSizeBytes; | ||
| CorsConfig corsConfig; | ||
| Injector injector; | ||
| MultipartConfigElement multipartConfig; | ||
|
|
||
| @Accessors(fluent = true) | ||
| boolean useSessions; | ||
|
|
||
| @Value | ||
| @Builder | ||
| public static class CorsConfig { | ||
| List<String> allowedHeaders; | ||
| List<String> allowedOrigins; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| package org.hypertrace.core.serviceframework.http; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| @FunctionalInterface | ||
| public interface HttpHandlerFactory { | ||
| List<HttpHandlerDefinition> buildHandlers(HttpContainerEnvironment containerEnvironment); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| package org.hypertrace.core.serviceframework.http; | ||
|
|
||
| import java.util.List; | ||
| import java.util.concurrent.ExecutorService; | ||
|
|
||
| public interface ServerBuilder<T extends ServerBuilder> { | ||
| T addHandler(HttpHandlerDefinition handlerDefinition); | ||
|
|
||
| T addHandlers(List<HttpHandlerDefinition> handlerDefinitions); | ||
|
|
||
| T setExecutor(ExecutorService executorService); | ||
|
|
||
| HttpContainer build(); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| package org.hypertrace.core.serviceframework.http; | ||
|
|
||
| import com.typesafe.config.Config; | ||
| import lombok.AllArgsConstructor; | ||
| import lombok.Getter; | ||
| import org.hypertrace.core.grpcutils.client.GrpcChannelRegistry; | ||
| import org.hypertrace.core.serviceframework.config.ConfigClient; | ||
| import org.hypertrace.core.serviceframework.spi.PlatformServiceLifecycle; | ||
|
|
||
| @AllArgsConstructor | ||
| public class StandAloneHttpContainerEnvironment implements HttpContainerEnvironment { | ||
| @Getter private final GrpcChannelRegistry channelRegistry; | ||
| @Getter private final PlatformServiceLifecycle lifecycle; | ||
| private final ConfigClient configClient; | ||
|
|
||
| @Override | ||
| public Config getConfig(String serviceName) { | ||
| return this.configClient.getConfig(serviceName, null, null, null); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| package org.hypertrace.core.serviceframework.http; | ||
|
|
||
| import static io.grpc.Deadline.after; | ||
| import static java.util.concurrent.TimeUnit.SECONDS; | ||
|
|
||
| import java.util.List; | ||
| import java.util.stream.Collectors; | ||
| import lombok.extern.slf4j.Slf4j; | ||
| import org.hypertrace.core.grpcutils.client.GrpcChannelRegistry; | ||
| import org.hypertrace.core.serviceframework.PlatformService; | ||
| import org.hypertrace.core.serviceframework.config.ConfigClient; | ||
| import org.hypertrace.core.serviceframework.http.jetty.JettyHttpServerBuilder; | ||
|
|
||
| @Slf4j | ||
| public abstract class StandAloneHttpPlatformServiceContainer extends PlatformService { | ||
| private HttpContainer container; | ||
| private final GrpcChannelRegistry grpcChannelRegistry = new GrpcChannelRegistry(); | ||
|
|
||
| public StandAloneHttpPlatformServiceContainer(ConfigClient config) { | ||
| super(config); | ||
| } | ||
|
|
||
| protected abstract List<HttpHandlerFactory> getHandlerFactories(); | ||
|
|
||
| @Override | ||
| protected void doInit() { | ||
| this.container = | ||
| new JettyHttpServerBuilder().addHandlers(this.buildHandlerDefinitions()).build(); | ||
| } | ||
|
|
||
| @Override | ||
| protected void doStart() { | ||
| log.info("Starting service {}", this.getServiceName()); | ||
| this.container.start(); | ||
| this.container.blockUntilStopped(); | ||
| } | ||
|
|
||
| @Override | ||
| protected void doStop() { | ||
| log.info("Stopping service {}", this.getServiceName()); | ||
| grpcChannelRegistry.shutdown(after(10, SECONDS)); | ||
| this.container.stop(); | ||
| } | ||
|
|
||
| @Override | ||
| public boolean healthCheck() { | ||
| return true; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nothing more in this one?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I resisted 😉 let's file this under the Future Work section (it's not a regression and it's still overridable - and more importantly, we didn't really have a ready replacement lined up like we did with GRPC) |
||
| } | ||
|
|
||
| private List<HttpHandlerDefinition> buildHandlerDefinitions() { | ||
| HttpContainerEnvironment environment = | ||
| new StandAloneHttpContainerEnvironment( | ||
| this.grpcChannelRegistry, this.getLifecycle(), this.configClient); | ||
| return this.getHandlerFactories().stream() | ||
| .flatMap(handlerFactory -> handlerFactory.buildHandlers(environment).stream()) | ||
| .collect(Collectors.toUnmodifiableList()); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| package org.hypertrace.core.serviceframework.http.guice; | ||
|
|
||
| import com.google.inject.Injector; | ||
| import com.google.inject.servlet.GuiceServletContextListener; | ||
| import lombok.RequiredArgsConstructor; | ||
|
|
||
| @RequiredArgsConstructor | ||
| public class SimpleGuiceServletContextListener extends GuiceServletContextListener { | ||
| private final Injector injector; | ||
|
|
||
| @Override | ||
| protected Injector getInjector() { | ||
| return injector; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| package org.hypertrace.core.serviceframework.http.jetty; | ||
|
|
||
| import static java.util.concurrent.TimeUnit.SECONDS; | ||
|
|
||
| import java.util.concurrent.ExecutorService; | ||
| import java.util.concurrent.Future; | ||
| import lombok.RequiredArgsConstructor; | ||
| import lombok.SneakyThrows; | ||
| import org.eclipse.jetty.server.Server; | ||
| import org.hypertrace.core.serviceframework.http.HttpContainer; | ||
|
|
||
| @RequiredArgsConstructor | ||
| class JettyHttpContainer implements HttpContainer { | ||
| private final Server server; | ||
| private final ExecutorService executorService; | ||
| private Future<?> future; | ||
|
|
||
| @Override | ||
| public void start() { | ||
| this.future = this.executorService.submit(this::startAndWaitUnchecked); | ||
| } | ||
|
|
||
| @SneakyThrows | ||
| @Override | ||
| public void stop() { | ||
| this.executorService.shutdown(); | ||
| this.executorService.awaitTermination(30, SECONDS); | ||
| } | ||
|
|
||
| @SneakyThrows | ||
| @Override | ||
| public void blockUntilStopped() { | ||
| this.future.get(); | ||
| } | ||
|
|
||
| @Override | ||
| public boolean isStopped() { | ||
| return this.server.isStopped(); | ||
| } | ||
|
|
||
| @SneakyThrows | ||
| private void startAndWaitUnchecked() { | ||
| this.server.start(); | ||
| this.server.join(); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this for GRPC clients used by the HTTP server?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct, even though they're hosting HTTP (or more specifically, REST) APIs, they're still communicating downstream to GRPC services.