package io.dropwizard.server;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.jetty9.InstrumentedQueuedThreadPool;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeName;
import io.dropwizard.jetty.ConnectorFactory;
import io.dropwizard.jetty.HttpConnectorFactory;
import io.dropwizard.jetty.RoutingHandler;
import io.dropwizard.setup.Environment;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.util.thread.ThreadPool;
import javax.validation.constraints.NotEmpty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.validation.Valid;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@JsonTypeName("default")
public class DefaultServerFactory extends AbstractServerFactory {
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultServerFactory.class);
@Valid
@NotNull
private List<ConnectorFactory> applicationConnectors = Collections.singletonList(HttpConnectorFactory.application());
@Valid
@NotNull
private List<ConnectorFactory> adminConnectors = Collections.singletonList(HttpConnectorFactory.admin());
@Min(4)
private int adminMaxThreads = 64;
@Min(1)
private int adminMinThreads = 1;
@NotEmpty
private String applicationContextPath = "/";
@NotEmpty
private String adminContextPath = "/";
@JsonProperty
public List<ConnectorFactory> getApplicationConnectors() {
return applicationConnectors;
}
@JsonProperty
public void setApplicationConnectors(List<ConnectorFactory> connectors) {
this.applicationConnectors = connectors;
}
@JsonProperty
public List<ConnectorFactory> getAdminConnectors() {
return adminConnectors;
}
@JsonProperty
public void setAdminConnectors(List<ConnectorFactory> connectors) {
this.adminConnectors = connectors;
}
@JsonProperty
public int getAdminMaxThreads() {
return adminMaxThreads;
}
@JsonProperty
public void setAdminMaxThreads(int adminMaxThreads) {
this.adminMaxThreads = adminMaxThreads;
}
@JsonProperty
public int getAdminMinThreads() {
return adminMinThreads;
}
@JsonProperty
public void setAdminMinThreads(int adminMinThreads) {
this.adminMinThreads = adminMinThreads;
}
@JsonProperty
public String getApplicationContextPath() {
return applicationContextPath;
}
@JsonProperty
public void setApplicationContextPath(final String applicationContextPath) {
this.applicationContextPath = applicationContextPath;
}
@JsonProperty
public String getAdminContextPath() {
return adminContextPath;
}
@JsonProperty
public void setAdminContextPath(final String adminContextPath) {
this.adminContextPath = adminContextPath;
}
@Override
public Server build(Environment environment) {
printBanner(environment.getName());
final ThreadPool threadPool = createThreadPool(environment.metrics());
final Server server = buildServer(environment.lifecycle(), threadPool);
final Handler applicationHandler = createAppServlet(server,
environment.jersey(),
environment.getObjectMapper(),
environment.getValidator(),
environment.getApplicationContext(),
environment.getJerseyServletContainer(),
environment.metrics());
final Handler adminHandler = createAdminServlet(server,
environment.getAdminContext(),
environment.metrics(),
environment.healthChecks());
final RoutingHandler routingHandler = buildRoutingHandler(environment.metrics(),
server,
applicationHandler,
adminHandler);
final Handler gzipHandler = buildGzipHandler(routingHandler);
server.setHandler(addStatsHandler(addRequestLog(server, gzipHandler, environment.getName())));
return server;
}
@Override
public void configure(Environment environment) {
LOGGER.info("Registering jersey handler with root path prefix: {}", applicationContextPath);
environment.getApplicationContext().setContextPath(applicationContextPath);
LOGGER.info("Registering admin handler with root path prefix: {}", adminContextPath);
environment.getAdminContext().setContextPath(adminContextPath);
}
private RoutingHandler buildRoutingHandler(MetricRegistry metricRegistry,
Server server,
Handler applicationHandler,
Handler adminHandler) {
final List<Connector> appConnectors = buildAppConnectors(metricRegistry, server);
final List<Connector> adConnectors = buildAdminConnectors(metricRegistry, server);
final Map<Connector, Handler> handlers = new LinkedHashMap<>();
for (Connector connector : appConnectors) {
server.addConnector(connector);
handlers.put(connector, applicationHandler);
}
for (Connector connector : adConnectors) {
server.addConnector(connector);
handlers.put(connector, adminHandler);
}
return new RoutingHandler(handlers);
}
private List<Connector> buildAdminConnectors(MetricRegistry metricRegistry, Server server) {
final QueuedThreadPool threadPool = new InstrumentedQueuedThreadPool(metricRegistry, adminMaxThreads, adminMinThreads);
threadPool.setName("dw-admin");
server.addBean(threadPool);
final List<Connector> connectors = new ArrayList<>();
for (ConnectorFactory factory : adminConnectors) {
final Connector connector = factory.build(server, metricRegistry, "admin", threadPool);
if (connector instanceof ContainerLifeCycle) {
((ContainerLifeCycle) connector).unmanage(threadPool);
}
connectors.add(connector);
}
return connectors;
}
private List<Connector> buildAppConnectors(MetricRegistry metricRegistry, Server server) {
final List<Connector> connectors = new ArrayList<>();
for (ConnectorFactory factory : applicationConnectors) {
connectors.add(factory.build(server, metricRegistry, "application", null));
}
return connectors;
}
@Override
public String toString() {
return "DefaultServerFactory{" +
"applicationConnectors=" + applicationConnectors +
", adminConnectors=" + adminConnectors +
", adminMaxThreads=" + adminMaxThreads +
", adminMinThreads=" + adminMinThreads +
", applicationContextPath='" + applicationContextPath + '\'' +
", adminContextPath='" + adminContextPath + '\'' +
'}';
}
}