
com.linecorp.armeria.server.ServerBuilder Maven / Gradle / Ivy
/*
* Copyright 2015 LINE Corporation
*
* LINE Corporation licenses this file to you 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:
*
* http://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 com.linecorp.armeria.server;
import static com.linecorp.armeria.common.SessionProtocol.HTTP;
import static com.linecorp.armeria.server.ServerConfig.validateDefaultMaxRequestLength;
import static com.linecorp.armeria.server.ServerConfig.validateDefaultRequestTimeoutMillis;
import static java.util.Objects.requireNonNull;
import java.io.File;
import java.net.InetSocketAddress;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.net.ssl.SSLException;
import com.google.common.collect.ImmutableMap;
import com.linecorp.armeria.common.CommonPools;
import com.linecorp.armeria.common.Flags;
import com.linecorp.armeria.common.HttpRequest;
import com.linecorp.armeria.common.HttpResponse;
import com.linecorp.armeria.common.Request;
import com.linecorp.armeria.common.SessionProtocol;
import com.linecorp.armeria.server.annotation.ResponseConverter;
import io.netty.channel.EventLoopGroup;
import io.netty.handler.ssl.SslContext;
/**
* Builds a new {@link Server} and its {@link ServerConfig}.
* Example
* {@code
* ServerBuilder sb = new ServerBuilder();
* // Add a port to listen
* sb.port(8080, SessionProtocol.HTTP);
* // Build and add a virtual host.
* sb.virtualHost(new VirtualHostBuilder("*.foo.com").service(...).build());
* // Add services to the default virtual host.
* sb.service(...);
* sb.serviceUnder(...);
* // Build a server.
* Server s = sb.build();
* }
*
* Example 2
* {@code
* ServerBuilder sb = new ServerBuilder();
* Server server =
* sb.port(8080, SessionProtocol.HTTP) // Add a port to listen
* .withDefaultVirtualHost() // Add services to the default virtual host.
* .service(...)
* .serviceUnder(...)
* .and().withVirtualHost("*.foo.com") // Add a another virtual host.
* .service(...)
* .serviceUnder(...)
* .and().build(); // Build a server.
* }
* @see VirtualHostBuilder
*/
public final class ServerBuilder {
// Use Integer.MAX_VALUE not to limit open connections by default.
private static final int DEFAULT_MAX_NUM_CONNECTIONS = Integer.MAX_VALUE;
private static final long DEFAULT_DEFAULT_REQUEST_TIMEOUT_MILLIS = Duration.ofSeconds(10).toMillis();
private static final long DEFAULT_DEFAULT_MAX_REQUEST_LENGTH = 10 * 1024 * 1024; // 10 MB
// Defaults to no graceful shutdown.
private static final Duration DEFAULT_GRACEFUL_SHUTDOWN_QUIET_PERIOD = Duration.ZERO;
private static final Duration DEFAULT_GRACEFUL_SHUTDOWN_TIMEOUT = Duration.ZERO;
private static final String DEFAULT_SERVICE_LOGGER_PREFIX = "armeria.services";
private final List ports = new ArrayList<>();
private final List serverListeners = new ArrayList<>();
private final List virtualHosts = new ArrayList<>();
private final List virtualHostBuilders = new ArrayList<>();
private final ChainedVirtualHostBuilder defaultVirtualHostBuilder = new ChainedVirtualHostBuilder(this);
private boolean updatedDefaultVirtualHostBuilder;
private VirtualHost defaultVirtualHost;
private EventLoopGroup workerGroup = CommonPools.workerGroup();
private boolean shutdownWorkerGroupOnStop;
private int maxNumConnections = DEFAULT_MAX_NUM_CONNECTIONS;
private long idleTimeoutMillis = Flags.defaultServerIdleTimeoutMillis();
private long defaultRequestTimeoutMillis = DEFAULT_DEFAULT_REQUEST_TIMEOUT_MILLIS;
private long defaultMaxRequestLength = DEFAULT_DEFAULT_MAX_REQUEST_LENGTH;
private Duration gracefulShutdownQuietPeriod = DEFAULT_GRACEFUL_SHUTDOWN_QUIET_PERIOD;
private Duration gracefulShutdownTimeout = DEFAULT_GRACEFUL_SHUTDOWN_TIMEOUT;
private Executor blockingTaskExecutor = CommonPools.blockingTaskExecutor();
private String serviceLoggerPrefix = DEFAULT_SERVICE_LOGGER_PREFIX;
private Function, Service> decorator;
/**
* Adds a new {@link ServerPort} that listens to the specified {@code port} of all available network
* interfaces using the specified protocol. If no port is added (i.e. no {@code port()} method is called),
* a default of {@code 0} (randomly-assigned port) and {@code "http"} will be used.
*/
public ServerBuilder port(int port, String protocol) {
return port(port, SessionProtocol.of(requireNonNull(protocol, "protocol")));
}
/**
* Adds a new {@link ServerPort} that listens to the specified {@code port} of all available network
* interfaces using the specified {@link SessionProtocol}. If no port is added (i.e. no {@code port()}
* method is called), a default of {@code 0} (randomly-assigned port) and {@code "http"} will be used.
*/
public ServerBuilder port(int port, SessionProtocol protocol) {
ports.add(new ServerPort(port, protocol));
return this;
}
/**
* Adds a new {@link ServerPort} that listens to the specified {@code localAddress} using the specified
* protocol. If no port is added (i.e. no {@code port()} method is called), a default of {@code 0}
* (randomly-assigned port) and {@code "http"} will be used.
*/
public ServerBuilder port(InetSocketAddress localAddress, String protocol) {
return port(localAddress, SessionProtocol.of(requireNonNull(protocol, "protocol")));
}
/**
* Adds a new {@link ServerPort} that listens to the specified {@code localAddress} using the specified
* {@link SessionProtocol}. If no port is added (i.e. no {@code port()} method is called), a default of
* {@code 0} (randomly-assigned port) and {@code "http"} will be used.
*/
public ServerBuilder port(InetSocketAddress localAddress, SessionProtocol protocol) {
ports.add(new ServerPort(localAddress, protocol));
return this;
}
/**
* Adds the specified {@link ServerPort}. If no port is added (i.e. no {@code port()} method is called),
* a default of {@code 0} (randomly-assigned port) and {@code "http"} will be used.
*/
public ServerBuilder port(ServerPort port) {
ports.add(requireNonNull(port, "port"));
return this;
}
/**
* Adds the name-based virtual host
* specified by {@link VirtualHost}.
*/
public ServerBuilder virtualHost(VirtualHost virtualHost) {
virtualHosts.add(requireNonNull(virtualHost, "virtualHost"));
return this;
}
/**
* Sets the worker {@link EventLoopGroup} which is responsible for performing socket I/O and running
* {@link Service#serve(ServiceRequestContext, Request)}.
* If not set, {@linkplain CommonPools#workerGroup() the common worker group} is used.
*
* @param shutdownOnStop whether to shut down the worker {@link EventLoopGroup}
* when the {@link Server} stops
*/
public ServerBuilder workerGroup(EventLoopGroup workerGroup, boolean shutdownOnStop) {
this.workerGroup = requireNonNull(workerGroup, "workerGroup");
shutdownWorkerGroupOnStop = shutdownOnStop;
return this;
}
/**
* Sets the maximum allowed number of open connections.
*/
public ServerBuilder maxNumConnections(int maxNumConnections) {
this.maxNumConnections = ServerConfig.validateMaxNumConnections(maxNumConnections);
return this;
}
/**
* Sets the idle timeout of a connection in milliseconds for keep-alive.
*
* @param idleTimeoutMillis the timeout in milliseconds. {@code 0} disables the timeout.
*/
public ServerBuilder idleTimeoutMillis(long idleTimeoutMillis) {
return idleTimeout(Duration.ofMillis(idleTimeoutMillis));
}
/**
* Sets the idle timeout of a connection for keep-alive.
*
* @param idleTimeout the timeout. {@code 0} disables the timeout.
*/
public ServerBuilder idleTimeout(Duration idleTimeout) {
requireNonNull(idleTimeout, "idleTimeout");
idleTimeoutMillis = ServerConfig.validateIdleTimeoutMillis(idleTimeout.toMillis());
return this;
}
/**
* Sets the default timeout of a request in milliseconds.
*
* @param defaultRequestTimeoutMillis the timeout in milliseconds. {@code 0} disables the timeout.
*/
public ServerBuilder defaultRequestTimeoutMillis(long defaultRequestTimeoutMillis) {
this.defaultRequestTimeoutMillis = validateDefaultRequestTimeoutMillis(defaultRequestTimeoutMillis);
return this;
}
/**
* Sets the default timeout of a request.
*
* @param defaultRequestTimeout the timeout. {@code 0} disables the timeout.
*/
public ServerBuilder defaultRequestTimeout(Duration defaultRequestTimeout) {
return defaultRequestTimeoutMillis(
requireNonNull(defaultRequestTimeout, "defaultRequestTimeout").toMillis());
}
/**
* Sets the maximum allowed length of the content decoded at the session layer.
* e.g. the content length of an HTTP request.
*
* @param defaultMaxRequestLength the maximum allowed length. {@code 0} disables the length limit.
*/
public ServerBuilder defaultMaxRequestLength(long defaultMaxRequestLength) {
this.defaultMaxRequestLength = validateDefaultMaxRequestLength(defaultMaxRequestLength);
return this;
}
/**
* Sets the amount of time to wait after calling {@link Server#stop()} for
* requests to go away before actually shutting down.
*
* @param quietPeriodMillis the number of milliseconds to wait for active
* requests to go end before shutting down. 0 means the server will
* stop right away without waiting.
* @param timeoutMillis the number of milliseconds to wait before shutting
* down the server regardless of active requests. This should be set to
* a time greater than {@code quietPeriodMillis} to ensure the server
* shuts down even if there is a stuck request.
*/
public ServerBuilder gracefulShutdownTimeout(long quietPeriodMillis, long timeoutMillis) {
return gracefulShutdownTimeout(
Duration.ofMillis(quietPeriodMillis), Duration.ofMillis(timeoutMillis));
}
/**
* Sets the amount of time to wait after calling {@link Server#stop()} for
* requests to go away before actually shutting down.
*
* @param quietPeriod the number of milliseconds to wait for active
* requests to go end before shutting down. {@link Duration#ZERO} means
* the server will stop right away without waiting.
* @param timeout the number of milliseconds to wait before shutting
* down the server regardless of active requests. This should be set to
* a time greater than {@code quietPeriod} to ensure the server shuts
* down even if there is a stuck request.
*/
public ServerBuilder gracefulShutdownTimeout(Duration quietPeriod, Duration timeout) {
requireNonNull(quietPeriod, "quietPeriod");
requireNonNull(timeout, "timeout");
gracefulShutdownQuietPeriod = ServerConfig.validateNonNegative(quietPeriod, "quietPeriod");
gracefulShutdownTimeout = ServerConfig.validateNonNegative(timeout, "timeout");
ServerConfig.validateGreaterThanOrEqual(gracefulShutdownTimeout, "quietPeriod",
gracefulShutdownQuietPeriod, "timeout");
return this;
}
/**
* Sets the {@link Executor} dedicated to the execution of blocking tasks or invocations.
* If not set, {@linkplain CommonPools#blockingTaskExecutor() the common pool} is used.
*/
public ServerBuilder blockingTaskExecutor(Executor blockingTaskExecutor) {
this.blockingTaskExecutor = requireNonNull(blockingTaskExecutor, "blockingTaskExecutor");
return this;
}
/**
* Sets the prefix of {@linkplain ServiceRequestContext#logger() service logger} names.
* The default value is "{@value #DEFAULT_SERVICE_LOGGER_PREFIX}". A service logger name prefix must be
* a string of valid Java identifier names concatenated by period ({@code '.'}), such as a package name.
*/
public ServerBuilder serviceLoggerPrefix(String serviceLoggerPrefix) {
this.serviceLoggerPrefix = ServiceConfig.validateLoggerName(serviceLoggerPrefix, "serviceLoggerPrefix");
return this;
}
/**
* Sets the {@link SslContext} of the default {@link VirtualHost}.
*
* @throws IllegalStateException if the default {@link VirtualHost} has been set via
* {@link #defaultVirtualHost(VirtualHost)} already
*/
public ServerBuilder sslContext(SslContext sslContext) {
defaultVirtualHostBuilderUpdated();
defaultVirtualHostBuilder.sslContext(sslContext);
return this;
}
/**
* Sets the {@link SslContext} of the default {@link VirtualHost} from the specified
* {@link SessionProtocol}, {@code keyCertChainFile} and cleartext {@code keyFile}.
*
* @throws IllegalStateException if the default {@link VirtualHost} has been set via
* {@link #defaultVirtualHost(VirtualHost)} already
*/
public ServerBuilder sslContext(
SessionProtocol protocol, File keyCertChainFile, File keyFile) throws SSLException {
defaultVirtualHostBuilderUpdated();
defaultVirtualHostBuilder.sslContext(protocol, keyCertChainFile, keyFile);
return this;
}
/**
* Sets the {@link SslContext} of the default {@link VirtualHost} from the specified
* {@link SessionProtocol}, {@code keyCertChainFile}, {@code keyFile} and {@code keyPassword}.
*
* @throws IllegalStateException if the default {@link VirtualHost} has been set via
* {@link #defaultVirtualHost(VirtualHost)} already
*/
public ServerBuilder sslContext(
SessionProtocol protocol,
File keyCertChainFile, File keyFile, String keyPassword) throws SSLException {
defaultVirtualHostBuilderUpdated();
defaultVirtualHostBuilder.sslContext(protocol, keyCertChainFile, keyFile, keyPassword);
return this;
}
/**
* @deprecated Use {@link #service(String, Service)} instead.
*/
@Deprecated
public ServerBuilder serviceAt(String pathPattern, Service service) {
return service(pathPattern, service);
}
/**
* Binds the specified {@link Service} under the specified directory of the default {@link VirtualHost}.
*
* @throws IllegalStateException if the default {@link VirtualHost} has been set via
* {@link #defaultVirtualHost(VirtualHost)} already
*/
public ServerBuilder serviceUnder(String pathPrefix, Service service) {
defaultVirtualHostBuilderUpdated();
defaultVirtualHostBuilder.serviceUnder(pathPrefix, service);
return this;
}
/**
* Binds the specified {@link Service} at the specified path pattern of the default {@link VirtualHost}.
* e.g.
*
* - {@code /login} (no path parameters)
* - {@code /users/{userId}} (curly-brace style)
* - {@code /list/:productType/by/:ordering} (colon style)
* - {@code exact:/foo/bar} (exact match)
* - {@code prefix:/files} (prefix match)
* glob:/~*/downloads/**
(glob pattern)
* - {@code regex:^/files/(?
.*)$} (regular expression)
*
*
* @throws IllegalArgumentException if the specified path pattern is invalid
* @throws IllegalStateException if the default {@link VirtualHost} has been set via
* {@link #defaultVirtualHost(VirtualHost)} already
*/
public ServerBuilder service(String pathPattern, Service service) {
defaultVirtualHostBuilderUpdated();
defaultVirtualHostBuilder.service(pathPattern, service);
return this;
}
/**
* Binds the specified {@link Service} at the specified {@link PathMapping} of the default
* {@link VirtualHost}.
*
* @throws IllegalStateException if the default {@link VirtualHost} has been set via
* {@link #defaultVirtualHost(VirtualHost)} already
*/
public ServerBuilder service(PathMapping pathMapping, Service service) {
defaultVirtualHostBuilderUpdated();
defaultVirtualHostBuilder.service(pathMapping, service);
return this;
}
/**
* @deprecated Use a logging framework integration such as {@code RequestContextExportingAppender} in
* {@code armeria-logback}.
*/
@Deprecated
public ServerBuilder service(PathMapping pathMapping, Service service,
String loggerName) {
defaultVirtualHostBuilderUpdated();
defaultVirtualHostBuilder.service(pathMapping, service, loggerName);
return this;
}
/**
* Binds the specified annotated service object under the path prefix {@code "/"}.
*/
public ServerBuilder annotatedService(Object service) {
return annotatedService("/", service);
}
/**
* Binds the specified annotated service object.
*/
public ServerBuilder annotatedService(
Object service,
Function,
? extends Service> decorator) {
return annotatedService("/", service, decorator);
}
/**
* Binds the specified annotated service object under the path prefix {@code "/"}.
*/
public ServerBuilder annotatedService(Object service, Map, ResponseConverter> converters) {
return annotatedService("/", service, converters);
}
/**
* Binds the specified annotated service object under the path prefix {@code "/"}.
*/
public ServerBuilder annotatedService(
Object service, Map, ResponseConverter> converters,
Function,
? extends Service> decorator) {
return annotatedService("/", service, converters, decorator);
}
/**
* Binds the specified annotated service object under the specified path prefix.
*/
public ServerBuilder annotatedService(String pathPrefix, Object service) {
return annotatedService(pathPrefix, service, ImmutableMap.of(), Function.identity());
}
/**
* Binds the specified annotated service object under the specified path prefix.
*/
public ServerBuilder annotatedService(
String pathPrefix, Object service,
Function,
? extends Service> decorator) {
return annotatedService(pathPrefix, service, ImmutableMap.of(), decorator);
}
/**
* Binds the specified annotated service object under the specified path prefix.
*/
public ServerBuilder annotatedService(String pathPrefix, Object service,
Map, ResponseConverter> converters) {
return annotatedService(pathPrefix, service, converters, Function.identity());
}
/**
* Binds the specified annotated service object under the specified path prefix.
*/
public ServerBuilder annotatedService(
String pathPrefix, Object service, Map, ResponseConverter> converters,
Function,
? extends Service> decorator) {
defaultVirtualHostBuilderUpdated();
defaultVirtualHostBuilder.annotatedService(pathPrefix, service, converters, decorator);
return this;
}
private void defaultVirtualHostBuilderUpdated() {
updatedDefaultVirtualHostBuilder = true;
if (defaultVirtualHost != null) {
throw new IllegalStateException("ServerBuilder.defaultVirtualHost() invoked already.");
}
}
/**
* Sets the default {@link VirtualHost}, which is used when no other {@link VirtualHost}s match the
* host name of a client request. e.g. the {@code "Host"} header in HTTP or host name in TLS SNI extension
*
* @throws IllegalStateException
* if other default {@link VirtualHost} builder methods have been invoked already, including:
*
* - {@link #sslContext(SslContext)}
* - {@link #service(String, Service)}
* - {@link #serviceUnder(String, Service)}
* - {@link #service(PathMapping, Service)}
*
*
* @see #virtualHost(VirtualHost)
*/
public ServerBuilder defaultVirtualHost(VirtualHost defaultVirtualHost) {
requireNonNull(defaultVirtualHost, "defaultVirtualHost");
if (updatedDefaultVirtualHostBuilder) {
throw new IllegalStateException("invoked other default VirtualHost builder methods already");
}
this.defaultVirtualHost = defaultVirtualHost;
return this;
}
/**
* Adds the specified {@link ServerListener}.
*/
public ServerBuilder serverListener(ServerListener serverListener) {
requireNonNull(serverListener, "serverListener");
serverListeners.add(serverListener);
return this;
}
/**
* Adds the name-based virtual host
* specified by {@link VirtualHost}.
*
* @return {@link VirtualHostBuilder} for build the default virtual host
*/
public ChainedVirtualHostBuilder withDefaultVirtualHost() {
defaultVirtualHostBuilderUpdated();
return defaultVirtualHostBuilder;
}
/**
* Adds the name-based virtual host
* specified by {@link VirtualHost}.
*
* @param hostnamePattern virtual host name regular expression
* @return {@link VirtualHostBuilder} for build the virtual host
*/
public ChainedVirtualHostBuilder withVirtualHost(String hostnamePattern) {
ChainedVirtualHostBuilder virtualHostBuilder = new ChainedVirtualHostBuilder(hostnamePattern, this);
virtualHostBuilders.add(virtualHostBuilder);
return virtualHostBuilder;
}
/**
* Adds the name-based virtual host
* specified by {@link VirtualHost}.
*
* @param defaultHostname default hostname of this virtual host
* @param hostnamePattern virtual host name regular expression
* @return {@link VirtualHostBuilder} for build the virtual host
*/
public ChainedVirtualHostBuilder withVirtualHost(String defaultHostname, String hostnamePattern) {
ChainedVirtualHostBuilder virtualHostBuilder =
new ChainedVirtualHostBuilder(defaultHostname, hostnamePattern, this);
virtualHostBuilders.add(virtualHostBuilder);
return virtualHostBuilder;
}
/**
* Decorates all {@link Service}s with the specified {@code decorator}.
*
* @param decorator the {@link Function} that decorates a {@link Service}
* @param the type of the {@link Service} being decorated
* @param the type of the {@link Service} {@code decorator} will produce
*/
public , R extends Service>
ServerBuilder decorator(Function decorator) {
requireNonNull(decorator, "decorator");
@SuppressWarnings("unchecked")
Function, Service> castDecorator =
(Function, Service>) decorator;
if (this.decorator != null) {
this.decorator = this.decorator.andThen(castDecorator);
} else {
this.decorator = castDecorator;
}
return this;
}
/**
* Returns a newly-created {@link Server} based on the configuration properties set so far.
*/
public Server build() {
final List ports =
!this.ports.isEmpty() ? this.ports
: Collections.singletonList(new ServerPort(0, HTTP));
final VirtualHost defaultVirtualHost;
if (this.defaultVirtualHost != null) {
defaultVirtualHost = this.defaultVirtualHost.decorate(decorator);
} else {
defaultVirtualHost = defaultVirtualHostBuilder.build().decorate(decorator);
}
virtualHostBuilders.forEach(vhb -> virtualHosts.add(vhb.build()));
final List virtualHosts;
if (decorator != null) {
virtualHosts = this.virtualHosts.stream()
.map(h -> h.decorate(decorator))
.collect(Collectors.toList());
} else {
virtualHosts = this.virtualHosts;
}
final Server server = new Server(new ServerConfig(
ports, defaultVirtualHost, virtualHosts, workerGroup, shutdownWorkerGroupOnStop,
maxNumConnections, idleTimeoutMillis, defaultRequestTimeoutMillis, defaultMaxRequestLength,
gracefulShutdownQuietPeriod, gracefulShutdownTimeout,
blockingTaskExecutor, serviceLoggerPrefix));
serverListeners.forEach(server::addListener);
return server;
}
@Override
public String toString() {
return ServerConfig.toString(
getClass(), ports, defaultVirtualHost, virtualHosts, workerGroup, shutdownWorkerGroupOnStop,
maxNumConnections, idleTimeoutMillis, defaultRequestTimeoutMillis, defaultMaxRequestLength,
gracefulShutdownQuietPeriod, gracefulShutdownTimeout,
blockingTaskExecutor, serviceLoggerPrefix);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy