Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions platform-http-service-framework/build.gradle.kts
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;
Copy link
Contributor

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?

Copy link
Contributor Author

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.

@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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing more in this one?

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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();
}
}
Loading