/*
 * Copyright 2012-2020 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.boot.web.embedded.jetty;

import java.net.InetSocketAddress;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.jetty.server.AbstractConnector;
import org.eclipse.jetty.server.ConnectionFactory;
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.HandlerWrapper;
import org.eclipse.jetty.server.handler.StatisticsHandler;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.util.thread.ThreadPool;

import org.springframework.boot.web.reactive.server.AbstractReactiveWebServerFactory;
import org.springframework.boot.web.reactive.server.ReactiveWebServerFactory;
import org.springframework.boot.web.server.Shutdown;
import org.springframework.boot.web.server.WebServer;
import org.springframework.http.client.reactive.JettyResourceFactory;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.http.server.reactive.JettyHttpHandlerAdapter;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

ReactiveWebServerFactory that can be used to create JettyWebServers.
Author:Brian Clozel
Since:2.0.0
/** * {@link ReactiveWebServerFactory} that can be used to create {@link JettyWebServer}s. * * @author Brian Clozel * @since 2.0.0 */
public class JettyReactiveWebServerFactory extends AbstractReactiveWebServerFactory implements ConfigurableJettyWebServerFactory { private static final Log logger = LogFactory.getLog(JettyReactiveWebServerFactory.class);
The number of acceptor threads to use.
/** * The number of acceptor threads to use. */
private int acceptors = -1;
The number of selector threads to use.
/** * The number of selector threads to use. */
private int selectors = -1; private boolean useForwardHeaders; private Set<JettyServerCustomizer> jettyServerCustomizers = new LinkedHashSet<>(); private JettyResourceFactory resourceFactory; private ThreadPool threadPool;
Create a new JettyServletWebServerFactory instance.
/** * Create a new {@link JettyServletWebServerFactory} instance. */
public JettyReactiveWebServerFactory() { }
Create a new JettyServletWebServerFactory that listens for requests using the specified port.
Params:
  • port – the port to listen on
/** * Create a new {@link JettyServletWebServerFactory} that listens for requests using * the specified port. * @param port the port to listen on */
public JettyReactiveWebServerFactory(int port) { super(port); } @Override public void setUseForwardHeaders(boolean useForwardHeaders) { this.useForwardHeaders = useForwardHeaders; } @Override public void setAcceptors(int acceptors) { this.acceptors = acceptors; } @Override public WebServer getWebServer(HttpHandler httpHandler) { JettyHttpHandlerAdapter servlet = new JettyHttpHandlerAdapter(httpHandler); Server server = createJettyServer(servlet); return new JettyWebServer(server, getPort() >= 0); } @Override public void addServerCustomizers(JettyServerCustomizer... customizers) { Assert.notNull(customizers, "Customizers must not be null"); this.jettyServerCustomizers.addAll(Arrays.asList(customizers)); }
Sets JettyServerCustomizers that will be applied to the Server before it is started. Calling this method will replace any existing customizers.
Params:
  • customizers – the Jetty customizers to apply
/** * Sets {@link JettyServerCustomizer}s that will be applied to the {@link Server} * before it is started. Calling this method will replace any existing customizers. * @param customizers the Jetty customizers to apply */
public void setServerCustomizers(Collection<? extends JettyServerCustomizer> customizers) { Assert.notNull(customizers, "Customizers must not be null"); this.jettyServerCustomizers = new LinkedHashSet<>(customizers); }
Returns a mutable collection of Jetty JettyServerCustomizers that will be applied to the Server before it is created.
Returns:the Jetty customizers
/** * Returns a mutable collection of Jetty {@link JettyServerCustomizer}s that will be * applied to the {@link Server} before it is created. * @return the Jetty customizers */
public Collection<JettyServerCustomizer> getServerCustomizers() { return this.jettyServerCustomizers; }
Returns a Jetty ThreadPool that should be used by the Server.
Returns:a Jetty ThreadPool or null
/** * Returns a Jetty {@link ThreadPool} that should be used by the {@link Server}. * @return a Jetty {@link ThreadPool} or {@code null} */
public ThreadPool getThreadPool() { return this.threadPool; } @Override public void setThreadPool(ThreadPool threadPool) { this.threadPool = threadPool; } @Override public void setSelectors(int selectors) { this.selectors = selectors; }
Set the JettyResourceFactory to get the shared resources from.
Params:
  • resourceFactory – the server resources
Since:2.1.0
/** * Set the {@link JettyResourceFactory} to get the shared resources from. * @param resourceFactory the server resources * @since 2.1.0 */
public void setResourceFactory(JettyResourceFactory resourceFactory) { this.resourceFactory = resourceFactory; } protected JettyResourceFactory getResourceFactory() { return this.resourceFactory; } protected Server createJettyServer(JettyHttpHandlerAdapter servlet) { int port = Math.max(getPort(), 0); InetSocketAddress address = new InetSocketAddress(getAddress(), port); Server server = new Server(getThreadPool()); server.addConnector(createConnector(address, server)); ServletHolder servletHolder = new ServletHolder(servlet); servletHolder.setAsyncSupported(true); ServletContextHandler contextHandler = new ServletContextHandler(server, "/", false, false); contextHandler.addServlet(servletHolder, "/"); server.setHandler(addHandlerWrappers(contextHandler)); JettyReactiveWebServerFactory.logger.info("Server initialized with port: " + port); if (getSsl() != null && getSsl().isEnabled()) { customizeSsl(server, address); } for (JettyServerCustomizer customizer : getServerCustomizers()) { customizer.customize(server); } if (this.useForwardHeaders) { new ForwardHeadersCustomizer().customize(server); } if (getShutdown() == Shutdown.GRACEFUL) { StatisticsHandler statisticsHandler = new StatisticsHandler(); statisticsHandler.setHandler(server.getHandler()); server.setHandler(statisticsHandler); } return server; } private AbstractConnector createConnector(InetSocketAddress address, Server server) { ServerConnector connector; JettyResourceFactory resourceFactory = getResourceFactory(); if (resourceFactory != null) { connector = new ServerConnector(server, resourceFactory.getExecutor(), resourceFactory.getScheduler(), resourceFactory.getByteBufferPool(), this.acceptors, this.selectors, new HttpConnectionFactory()); } else { connector = new ServerConnector(server, this.acceptors, this.selectors); } connector.setHost(address.getHostString()); connector.setPort(address.getPort()); for (ConnectionFactory connectionFactory : connector.getConnectionFactories()) { if (connectionFactory instanceof HttpConfiguration.ConnectionFactory) { ((HttpConfiguration.ConnectionFactory) connectionFactory).getHttpConfiguration() .setSendServerVersion(false); } } return connector; } private Handler addHandlerWrappers(Handler handler) { if (getCompression() != null && getCompression().getEnabled()) { handler = applyWrapper(handler, JettyHandlerWrappers.createGzipHandlerWrapper(getCompression())); } if (StringUtils.hasText(getServerHeader())) { handler = applyWrapper(handler, JettyHandlerWrappers.createServerHeaderHandlerWrapper(getServerHeader())); } return handler; } private Handler applyWrapper(Handler handler, HandlerWrapper wrapper) { wrapper.setHandler(handler); return wrapper; } private void customizeSsl(Server server, InetSocketAddress address) { new SslServerCustomizer(address, getSsl(), getSslStoreProvider(), getHttp2()).customize(server); } }