Skip to content

Instantly share code, notes, and snippets.

@ben-manes
Created July 12, 2015 18:40
Show Gist options
  • Save ben-manes/6aedde77035cd594d438 to your computer and use it in GitHub Desktop.
Save ben-manes/6aedde77035cd594d438 to your computer and use it in GitHub Desktop.
Jetty module
/**
* A qualifier for the server http port.
*
* @author [email protected] (Ben Manes)
*/
@BindingAnnotation
@Retention(RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
public @interface HttpPort {}
/**
* A qualifier for the server https port.
*
* @author [email protected] (Ben Manes)
*/
@BindingAnnotation
@Retention(RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
public @interface HttpsPort {}
/**
* A module that configures the Jetty webserver.
*
* @author [email protected] (Ben Manes)
*/
public final class JettyModule extends ServletModule {
final Config config = ConfigFactory.load().getConfig(getClass().getPackage().getName());
@Override
protected void configureServlets() {
configureAdminServlet();
configureMetricsFilter();
install(new MetricsModule());
bind(Server.class).toProvider(ServerProvider.class);
}
@Provides @HttpPort
OptionalInt providesHttpPort() {
return config.hasPath("http_port")
? OptionalInt.of(config.getInt("http_port"))
: OptionalInt.empty();
}
@Provides @HttpsPort
OptionalInt providesHttpsPort() {
return config.hasPath("https_port")
? OptionalInt.of(config.getInt("https_port"))
: OptionalInt.empty();
}
private void configureMetricsFilter() {
filter("/*").through(InstrumentedFilter.class);
bind(InstrumentedFilter.class).in(Singleton.class);
Multibinder.newSetBinder(binder(), ServletContextListener.class)
.addBinding().toInstance(new InstrumentedFilterContextListener() {
@Override protected MetricRegistry getMetricRegistry() {
return SharedRegistry.metricRegistry();
}
});
Multibinder.newSetBinder(binder(), ServletContextListener.class)
.addBinding().toInstance(new HealthCheckServlet.ContextListener() {
@Override protected HealthCheckRegistry getHealthCheckRegistry() {
return SharedRegistry.healthCheckRegistry();
}
});
Multibinder.newSetBinder(binder(), ServletContextListener.class)
.addBinding().toInstance(new MetricsServlet.ContextListener() {
@Override protected MetricRegistry getMetricRegistry() {
return SharedRegistry.metricRegistry();
}
});
}
private void configureAdminServlet() {
bind(AdminServlet.class).asEagerSingleton();
Map<String, String> initParams = ImmutableMap.of(
"healthcheck-uri", AdminServlet.DEFAULT_HEALTHCHECK_URI,
"threads-uri", AdminServlet.DEFAULT_THREADS_URI,
"metrics-uri",AdminServlet.DEFAULT_METRICS_URI,
"ping-uri", AdminServlet.DEFAULT_PING_URI);
serve("/admin/*").with(AdminServlet.class, initParams);
}
}
/**
* Provides a fully configured Jetty server.
*
* @author [email protected] (Ben Manes)
*/
final class ServerProvider implements Provider<Server> {
private final Config config = ConfigFactory.load().getConfig(getClass().getPackage().getName());
private final Set<ServletContextListener> listeners;
private final OptionalInt httpsPort;
private final OptionalInt httpPort;
@Inject
public ServerProvider(@HttpPort OptionalInt httpPort, @HttpsPort OptionalInt httpsPort,
Set<ServletContextListener> listeners) {
this.listeners = listeners;
this.httpsPort = httpsPort;
this.httpPort = httpPort;
}
@Override
public Server get() {
Server server = new Server(new InstrumentedQueuedThreadPool(SharedRegistry.metricRegistry()));
addConnectors(server);
addHandler(server);
configure(server);
return server;
}
private void configure(Server server) {
server.setRequestLog(new Slf4jRequestLog());
}
private void addHandler(Server server) {
InstrumentedHandler instrumented = new InstrumentedHandler(SharedRegistry.metricRegistry());
ServletContextHandler handler = new ServletContextHandler(instrumented, "/");
handler.addFilter(GuiceFilter.class, "/*", null);
handler.addServlet(DefaultServlet.class, "/");
for (ServletContextListener listener : listeners) {
handler.addEventListener(listener);
}
server.setHandler(instrumented);
}
private void addConnectors(Server server) {
checkState(httpPort.isPresent() || httpsPort.isPresent(), "No http/s port specified");
List<Connector> connectors = new ArrayList<>();
String host = config.hasPath("host") ? config.getString("host") : null;
if (httpPort.isPresent()) {
connectors.add(newHttpConnector(server, host));
}
if (httpsPort.isPresent()) {
connectors.add(newHttpsConnector(server, host));
}
server.setConnectors(connectors.toArray(new Connector[connectors.size()]));
}
private ServerConnector newHttpConnector(Server server, String host) {
HttpConfiguration httpConfig = new HttpConfiguration();
if (httpsPort.isPresent()) {
httpConfig.setSecurePort(httpsPort.getAsInt());
}
ServerConnector http = new ServerConnector(server,
new ProtocolAwareInstrumentedConnectionFactory(
new HttpConnectionFactory(httpConfig), "http.connection"));
http.setPort(httpPort.getAsInt());
http.setHost(host);
return http;
}
private ServerConnector newHttpsConnector(Server server, String host) {
HttpConfiguration httpsConfig = new HttpConfiguration();
httpsConfig.addCustomizer(new SecureRequestCustomizer());
ServerConnector https = new ServerConnector(server,
new SslConnectionFactory(newSslContextFactory(), "http/1.1"),
new ProtocolAwareInstrumentedConnectionFactory(
new HttpConnectionFactory(httpsConfig), "https.connection"));
https.setPort(httpsPort.getAsInt());
https.setHost(host);
return https;
}
private SslContextFactory newSslContextFactory() {
SslContextFactory sslContextFactory = new SslContextFactory();
sslContextFactory.setKeyStorePath(config.getString("key_store.path"));
sslContextFactory.setKeyStorePassword(config.getString("key_store.password"));
if (config.hasPath("key_store.manager_password")) {
sslContextFactory.setKeyManagerPassword(config.getString("key_store.manager_password"));
}
return sslContextFactory;
}
private static final class ProtocolAwareInstrumentedConnectionFactory
extends InstrumentedConnectionFactory {
final ConnectionFactory delegate;
ProtocolAwareInstrumentedConnectionFactory(ConnectionFactory delegate, String timerName) {
super(delegate, SharedRegistry.metricRegistry().timer(timerName));
this.delegate = delegate;
}
@Override
public List<String> getProtocols() {
return delegate.getProtocols();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment