com.turbospaces.jetty.JettyChannel Maven / Gradle / Ivy
package com.turbospaces.jetty;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.eclipse.jetty.io.ConnectionStatistics;
import org.eclipse.jetty.server.ConnectionFactory;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.server.handler.HandlerWrapper;
import org.eclipse.jetty.server.handler.StatisticsHandler;
import org.eclipse.jetty.server.handler.gzip.GzipHandler;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.util.BlockingArrayQueue;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.util.thread.ThreadPool;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.context.SmartLifecycle;
import com.turbospaces.boot.AbstractBootstrapAware;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tags;
import io.micrometer.core.instrument.binder.jetty.JettyConnectionMetrics;
import io.micrometer.core.instrument.binder.jetty.JettyServerThreadPoolMetrics;
import io.micrometer.core.instrument.binder.jetty.JettySslHandshakeMetrics;
import io.micrometer.core.instrument.binder.jetty.TimedHandler;
public abstract class JettyChannel extends AbstractBootstrapAware implements SmartLifecycle {
public static final String WEB_XML = "WEB-INF/web.xml";
private final ConnectionStatistics stats = new ConnectionStatistics();
private final StatisticsHandler statsHandler = new StatisticsHandler();
private final Set handlers = new HashSet<>();
private ServerConnector connector;
private boolean running;
private Server jetty;
private final int port;
protected JettyChannel(int port) {
this.port = port;
}
@Override
public void start() {
try {
int maxWorkers = bootstrap.props().JETTY_POOL_MAX_SIZE.get();
int minWorkers = bootstrap.props().JETTY_POOL_MIN_SIZE.get();
int maxIdle = (int) bootstrap.props().JETTY_POOL_MAX_IDLE.get().toMillis();
int queueCapacity = bootstrap.props().JETTY_POOL_QUEUE_MAX_SIZE.get();
BlockingQueue queue = new BlockingArrayQueue<>(queueCapacity);
QueuedThreadPool threadPool = new QueuedThreadPool(maxWorkers, minWorkers, maxIdle, queue);
jetty = new Server(threadPool);
createConnector();
startServer();
this.running = true;
} catch (Exception err) {
throw new BeanInitializationException(err.getMessage(), err);
}
}
@Override
public void stop() {
try {
if (Objects.nonNull(jetty)) {
long connections = stats.getConnections();
int requestsActive = statsHandler.getRequestsActive();
logger.info("stopping jetty, active connections: {}, pending requests: {}...", connections, requestsActive);
jetty.stop();
//
// just in case
//
QueuedThreadPool threadPool = (QueuedThreadPool) jetty.getServer().getThreadPool();
threadPool.stop();
this.running = false;
}
} catch (Exception err) {
ExceptionUtils.rethrow(err);
}
}
@Override
public boolean isRunning() {
return running;
}
public Server server() {
return jetty;
}
public StatisticsHandler statsHandler() {
return statsHandler;
}
public ConnectionStatistics connectorStats() {
return stats;
}
protected void addHandler(ServletContextHandler handler) {
handlers.add(handler);
}
protected void startServer() throws Exception {
HandlerCollection hc = new HandlerCollection();
ContextHandlerCollection contexts = new ContextHandlerCollection();
for (Handler handler : handlers) {
contexts.addHandler(handler);
}
hc.addHandler(contexts);
if (bootstrap.props().JETTY_GZIP_ENABLED.get()) {
HandlerWrapper gzipHandler = createGzipHandler();
gzipHandler.setHandler(hc);
statsHandler.setHandler(gzipHandler);
} else {
statsHandler.setHandler(hc);
}
jetty.setHandler(statsHandler);
jetty.setStopAtShutdown(false);
jetty.setStopTimeout(bootstrap.props().APP_PLATFORM_GRACEFUL_SHUTDOWN_TIMEOUT.get().toMillis());
jetty.setConnectors(new Connector[] { connector });
for (Connector next : jetty.getConnectors()) {
next.addBean(stats);
}
jetty.start();
String dump = jetty.dump();
logger.debug(dump);
logger.info("jetty server is up and running on {} port", port);
}
protected void createConnector() {
HttpConfiguration httpConfiguration = new HttpConfiguration();
httpConfiguration.setSendServerVersion(bootstrap.isDevMode());
httpConfiguration.setSendDateHeader(bootstrap.props().JETTY_HTTP_SEND_DATE_HEADER.get());
httpConfiguration.setRequestHeaderSize(bootstrap.props().HTTP_HEADER_MAX_SIZE.get());
httpConfiguration.addCustomizer(new org.eclipse.jetty.server.ForwardedRequestCustomizer());
ConnectionFactory http = new HttpConnectionFactory(httpConfiguration);
MeterRegistry registry = bootstrap.meterRegistry();
connector = new ServerConnector(jetty, http);
connector.addBean(new JettyConnectionMetrics(registry));
connector.addBean(new JettySslHandshakeMetrics(registry));
connector.addBean(new TimedHandler(bootstrap.meterRegistry(), Tags.empty()));
connector.addBean(buildThreadPoolMetrics(jetty.getThreadPool(), registry));
connector.setReuseAddress(true);
connector.setPort(port);
}
protected JettyServerThreadPoolMetrics buildThreadPoolMetrics(ThreadPool threadPool, MeterRegistry registry) {
JettyServerThreadPoolMetrics threadPoolMetrics = new JettyServerThreadPoolMetrics(threadPool, Tags.empty());
threadPoolMetrics.bindTo(registry);
return threadPoolMetrics;
}
protected GzipHandler createGzipHandler() {
GzipHandler handler = new GzipHandler();
logger.info("configured gzip handler with min-size={} ...", handler.getMinGzipSize());
return handler;
}
}