ratpack.server.internal.DefaultServerConfigBuilder Maven / Gradle / Ivy
/*
* Copyright 2014 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
*
* 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 ratpack.server.internal;
import com.google.common.base.CaseFormat;
import com.google.common.base.StandardSystemProperty;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.common.io.ByteSource;
import com.google.common.io.Resources;
import ratpack.file.FileSystemBinding;
import ratpack.file.internal.DefaultFileSystemBinding;
import ratpack.func.Action;
import ratpack.func.Predicate;
import ratpack.server.ServerConfig;
import ratpack.server.ServerEnvironment;
import ratpack.ssl.SSLContexts;
import ratpack.util.internal.Paths2;
import javax.net.ssl.SSLContext;
import java.io.*;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.GeneralSecurityException;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static ratpack.util.ExceptionUtils.uncheck;
public class DefaultServerConfigBuilder implements ServerConfig.Builder {
private ServerEnvironment serverEnvironment;
private FileSystemBinding baseDir;
private int port;
private InetAddress address;
private boolean development;
private int threads = ServerConfig.DEFAULT_THREADS;
private URI publicAddress;
private SSLContext sslContext;
private int maxContentLength = ServerConfig.DEFAULT_MAX_CONTENT_LENGTH;
//Variables to support configuring SSL
private InputStream sslKeystore;
private String sslKeystorePassword = "";
private DefaultServerConfigBuilder(ServerEnvironment serverEnvironment, Optional baseDir) {
if (baseDir.isPresent()) {
this.baseDir = new DefaultFileSystemBinding(baseDir.get());
}
this.serverEnvironment = serverEnvironment;
this.port = serverEnvironment.getPort();
this.development = serverEnvironment.isDevelopment();
this.publicAddress = serverEnvironment.getPublicAddress();
}
public static ServerConfig.Builder noBaseDir(ServerEnvironment serverEnvironment) {
return new DefaultServerConfigBuilder(serverEnvironment, Optional.empty());
}
public static ServerConfig.Builder baseDir(ServerEnvironment serverEnvironment, Path baseDir) {
return new DefaultServerConfigBuilder(serverEnvironment, Optional.of(baseDir.toAbsolutePath().normalize()));
}
public static ServerConfig.Builder findBaseDirProps(ServerEnvironment serverEnvironment, String propertiesPath) {
String workingDir = StandardSystemProperty.USER_DIR.value();
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
BaseDirFinder.Result result = BaseDirFinder.find(workingDir, classLoader, propertiesPath)
.orElseThrow(() -> new IllegalStateException("Could not find properties file '" + propertiesPath + "' in working dir '" + workingDir + "' or context class loader classpath"));
return baseDir(serverEnvironment, result.getBaseDir()).props(result.getResource());
}
@Override
public ServerConfig.Builder port(int port) {
this.port = port;
return this;
}
@Override
public ServerConfig.Builder address(InetAddress address) {
this.address = address;
return this;
}
@Override
public ServerConfig.Builder development(boolean development) {
this.development = development;
return this;
}
@Override
public ServerConfig.Builder threads(int threads) {
if (threads < 1) {
throw new IllegalArgumentException("'threads' must be > 0");
}
this.threads = threads;
return this;
}
@Override
public ServerConfig.Builder publicAddress(URI publicAddress) {
this.publicAddress = publicAddress;
return this;
}
@Override
public ServerConfig.Builder maxContentLength(int maxContentLength) {
this.maxContentLength = maxContentLength;
return this;
}
@Override
public ServerConfig.Builder ssl(SSLContext sslContext) {
this.sslContext = sslContext;
return this;
}
@Override
public ServerConfig build() {
loadSSLIfConfigured();
return new DefaultServerConfig(baseDir, port, address, development, threads,
publicAddress, sslContext, maxContentLength);
}
@Override
public ServerConfig.Builder env() {
return env(DEFAULT_ENV_PREFIX);
}
@Override
public ServerConfig.Builder env(String prefix) {
Map filteredEnvVars = serverEnvironment.getenv().entrySet().stream()
.filter(entry -> entry.getKey().startsWith(prefix))
.collect(Collectors.toMap(
entry -> CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, entry.getKey().replace(prefix, "")),
Map.Entry::getValue));
return props(filteredEnvVars);
}
@Override
public ServerConfig.Builder props(ByteSource byteSource) {
Properties properties = new Properties();
try (InputStream is = byteSource.openStream()) {
properties.load(is);
} catch (IOException e) {
throw uncheck(e);
}
return props(properties);
}
@Override
public ServerConfig.Builder props(String path) {
return props(Paths.get(path));
}
@Override
public ServerConfig.Builder props(Path path) {
return props(Paths2.asByteSource(path));
}
@Override
public ServerConfig.Builder props(Map map) {
Map> propertyCoercions = createPropertyCoercions();
map.entrySet().forEach(entry -> {
BuilderAction> mapping = propertyCoercions.get(entry.getKey());
if (mapping != null) {
try {
mapping.apply(entry.getValue());
} catch (Exception e) {
throw uncheck(e);
}
}
});
return this;
}
@Override
public ServerConfig.Builder props(Properties properties) {
Map map = Maps.newHashMapWithExpectedSize(properties.size());
properties.entrySet().forEach(e -> map.put(e.getKey().toString(), e.getValue().toString()));
return props(map);
}
@Override
public ServerConfig.Builder props(URL url) {
return props(Resources.asByteSource(url));
}
@Override
public ServerConfig.Builder sysProps() {
return sysProps(DEFAULT_PROP_PREFIX);
}
@Override
public ServerConfig.Builder sysProps(String prefix) {
Map filteredProperties = filter(
serverEnvironment.getProperties().entrySet(),
entry -> entry.getKey().toString().startsWith(prefix)
).collect(
Collectors.toMap(
p -> p.getKey().toString().replace(prefix, ""),
p -> p.getValue().toString()
)
);
return props(filteredProperties);
}
private ServerConfig.Builder sslKeystore(InputStream is) {
sslKeystore = is;
return this;
}
private ServerConfig.Builder sslKeystorePassword(String password) {
this.sslKeystorePassword = password;
return this;
}
private void loadSSLIfConfigured() {
if (sslKeystore != null) {
try (InputStream stream = sslKeystore) {
this.ssl(SSLContexts.sslContext(stream, sslKeystorePassword));
} catch (IOException | GeneralSecurityException e) {
throw uncheck(e);
}
}
}
private static Stream filter(Collection collection, Predicate predicate) {
return collection.stream().filter(predicate.toPredicate());
}
private static class BuilderAction {
private final Function converter;
private final Action action;
public BuilderAction(Function converter, Action action) {
this.converter = converter;
this.action = action;
}
public void apply(String value) throws Exception {
action.execute(converter.apply(value));
}
}
/**
* Gets a property value as an InputStream. The property value can be any of:
*
* - An absolute file path to a file that exists.
* - A valid URI.
* - A classpath resource path loaded via the ClassLoader passed to the constructor.
*
*
* @param path the path to the resource
* @return an InputStream or null
if the property does not exist.
*/
private static InputStream asStream(String path) {
try {
InputStream stream = null;
if (path != null) {
// try to treat it as a File path
File file = new File(path);
if (file.isFile()) {
stream = new FileInputStream(file);
} else {
// try to treat it as a URL
try {
URL url = new URL(path);
stream = url.openStream();
} catch (MalformedURLException e) {
// try to treat it as a resource path
stream = DefaultServerConfigBuilder.class.getClassLoader().getResourceAsStream(path);
if (stream == null) {
throw new FileNotFoundException(path);
}
}
}
}
return stream;
} catch (IOException e) {
throw uncheck(e);
}
}
private static InetAddress inetAddress(String s) {
return uncheck(() -> InetAddress.getByName(s));
}
private Map> createPropertyCoercions() {
return ImmutableMap.>builder()
.put("port", new BuilderAction<>(Integer::parseInt, DefaultServerConfigBuilder.this::port))
.put("address", new BuilderAction<>(DefaultServerConfigBuilder::inetAddress, DefaultServerConfigBuilder.this::address))
.put("development", new BuilderAction<>(Boolean::parseBoolean, DefaultServerConfigBuilder.this::development))
.put("threads", new BuilderAction<>(Integer::parseInt, DefaultServerConfigBuilder.this::threads))
.put("publicAddress", new BuilderAction<>(URI::create, DefaultServerConfigBuilder.this::publicAddress))
.put("maxContentLength", new BuilderAction<>(Integer::parseInt, DefaultServerConfigBuilder.this::maxContentLength))
.put("sslKeystoreFile", new BuilderAction<>(DefaultServerConfigBuilder::asStream, DefaultServerConfigBuilder.this::sslKeystore))
.put("sslKeystorePassword", new BuilderAction<>(Function.identity(), DefaultServerConfigBuilder.this::sslKeystorePassword))
.build();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy