All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ratpack.server.internal.DefaultServerConfigBuilder Maven / Gradle / Ivy

There is a newer version: 2.0.0-rc-1
Show newest version
/*
 * 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.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.common.io.ByteSource;
import com.google.common.reflect.TypeToken;
import ratpack.config.*;
import ratpack.config.internal.DefaultConfigData;
import ratpack.config.internal.DefaultConfigDataBuilder;
import ratpack.config.internal.module.SSLContextDeserializer;
import ratpack.config.internal.module.ServerConfigDataDeserializer;
import ratpack.file.FileSystemBinding;
import ratpack.func.Action;
import ratpack.impose.ForceDevelopmentImposition;
import ratpack.impose.ForceServerListenPortImposition;
import ratpack.impose.Impositions;
import ratpack.impose.ServerConfigImposition;
import ratpack.server.ServerConfig;
import ratpack.server.ServerConfigBuilder;

import javax.net.ssl.SSLContext;
import java.net.InetAddress;
import java.net.URI;
import java.net.URL;
import java.nio.file.Path;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.function.Consumer;
import java.util.function.Supplier;

public class DefaultServerConfigBuilder implements ServerConfigBuilder {

  private final DefaultConfigDataBuilder configDataBuilder;
  private final Map> required = Maps.newHashMap();
  private final BaseDirSupplier baseDirSupplier = new BaseDirSupplier();
  private final ServerEnvironment serverEnvironment;
  private final Impositions impositions;

  public DefaultServerConfigBuilder(ServerEnvironment serverEnvironment, Impositions impositions) {
    this.impositions = impositions;
    this.configDataBuilder = new DefaultConfigDataBuilder(serverEnvironment);
    this.serverEnvironment = serverEnvironment;
  }

  private DefaultServerConfigBuilder(DefaultConfigDataBuilder configDataBuilder, Map> required, BaseDirSupplier baseDirSupplier, ServerEnvironment serverEnvironment, Impositions impositions) {
    this.configDataBuilder = configDataBuilder.copy();
    this.required.putAll(required);
    this.baseDirSupplier.baseDir = baseDirSupplier.baseDir;
    this.serverEnvironment = serverEnvironment;
    this.impositions = impositions;
  }

  private DefaultServerConfigBuilder copy() {
    return new DefaultServerConfigBuilder(configDataBuilder, required, baseDirSupplier, serverEnvironment, impositions);
  }

  @Override
  public ObjectMapper getObjectMapper() {
    return configDataBuilder.getObjectMapper();
  }

  @Override
  public ServerConfigBuilder baseDir(Path baseDir) {
    this.baseDirSupplier.baseDir = FileSystemBinding.of(baseDir);
    return this;
  }

  private ServerConfigBuilder addToServer(Consumer action) {
    addToServer(configDataBuilder, action);
    return this;
  }

  private static void addToServer(ConfigDataBuilder configDataBuilder, Consumer action) {
    configDataBuilder.add((m, f) -> {
      ObjectNode rootNode = new ObjectNode(configDataBuilder.getObjectMapper().getNodeFactory());
      ObjectNode server = rootNode.putObject("server");
      action.accept(server);
      return rootNode;
    });
  }

  @Override
  public ServerConfigBuilder port(int port) {
    return addToServer(n -> n.put("port", port));
  }

  @Override
  public ServerConfigBuilder address(InetAddress address) {
    return addToServer(n -> n.putPOJO("address", address));
  }

  @Override
  public ServerConfigBuilder development(boolean development) {
    return addToServer(n -> n.put("development", development));
  }

  @Override
  public ServerConfigBuilder threads(int threads) {
    if (threads < 1) {
      throw new IllegalArgumentException("'threads' must be > 0");
    }
    return addToServer(n -> n.put("threads", threads));
  }

  @Override
  public ServerConfigBuilder publicAddress(URI publicAddress) {
    return addToServer(n -> n.putPOJO("publicAddress", publicAddress));
  }

  @Override
  public ServerConfigBuilder maxContentLength(int maxContentLength) {
    return addToServer(n -> n.put("maxContentLength", maxContentLength));
  }

  @Override
  public ServerConfigBuilder maxChunkSize(int maxChunkSize) {
    return addToServer(n -> n.put("maxChunkSize", maxChunkSize));
  }

  @Override
  public ServerConfigBuilder maxInitialLineLength(int maxInitialLineLength) {
    return addToServer(n -> n.put("maxInitialLineLength", maxInitialLineLength));
  }

  @Override
  public ServerConfigBuilder maxHeaderSize(int maxHeaderSize) {
    return addToServer(n -> n.put("maxHeaderSize", maxHeaderSize));
  }

  @Override
  public ServerConfigBuilder connectTimeoutMillis(int connectTimeoutMillis) {
    return addToServer(n -> n.put("connectTimeoutMillis", connectTimeoutMillis));
  }

  @Override
  public ServerConfigBuilder maxMessagesPerRead(int maxMessagesPerRead) {
    return addToServer(n -> n.put("maxMessagesPerRead", maxMessagesPerRead));
  }

  @Override
  public ServerConfigBuilder receiveBufferSize(int receiveBufferSize) {
    return addToServer(n -> n.put("receiveBufferSize", receiveBufferSize));
  }

  @Override
  public ServerConfigBuilder writeSpinCount(int writeSpinCount) {
    return addToServer(n -> n.put("writeSpinCount", writeSpinCount));
  }

  @Override
  public ServerConfigBuilder ssl(SSLContext sslContext) {
    return addToServer(n -> n.putPOJO("ssl", sslContext));
  }

  @Override
  public ServerConfigBuilder requireClientSslAuth(boolean requireClientSslAuth) {
    return addToServer(n -> n.put("requireClientSslAuth", requireClientSslAuth));
  }

  @Override
  public ServerConfigBuilder configureObjectMapper(Action action) {
    configDataBuilder.configureObjectMapper(action);
    return this;
  }

  @Override
  public ServerConfigBuilder add(ConfigSource configSource) {
    configDataBuilder.add(configSource);
    return this;
  }

  @Override
  public ServerConfigBuilder env(String prefix, ratpack.func.Function mapFunc) {
    configDataBuilder.env(prefix, mapFunc);
    return this;
  }

  @Override
  public ServerConfigBuilder env(EnvironmentParser environmentParser) {
    configDataBuilder.env(environmentParser);
    return this;
  }

  @Override
  public ServerConfigBuilder env() {
    configDataBuilder.env();
    return this;
  }

  @Override
  public ServerConfigBuilder env(String prefix) {
    configDataBuilder.env(prefix);
    return this;
  }

  @Override
  public ServerConfigBuilder args(String[] args) {
    configDataBuilder.args(args);
    return this;

  }

  @Override
  public ServerConfigBuilder args(String separator, String[] args) {
    configDataBuilder.args(separator, args);
    return this;
  }

  @Override
  public ServerConfigBuilder args(String prefix, String separator, String[] args) {
    configDataBuilder.args(prefix, separator, args);
    return this;
  }

  @Override
  public ServerConfigBuilder json(ByteSource byteSource) {
    configDataBuilder.json(byteSource);
    return this;
  }

  @Override
  public ServerConfigBuilder json(Path path) {
    configDataBuilder.json(path);
    return this;
  }

  @Override
  public ServerConfigBuilder json(URL url) {
    configDataBuilder.json(url);
    return this;
  }

  @Override
  public ServerConfigBuilder props(ByteSource byteSource) {
    configDataBuilder.props(byteSource);
    return this;
  }

  @Override
  public ServerConfigBuilder props(Path path) {
    configDataBuilder.props(path);
    return this;
  }

  @Override
  public ServerConfigBuilder props(Properties properties) {
    configDataBuilder.props(properties);
    return this;
  }

  @Override
  public ServerConfigBuilder props(URL url) {
    configDataBuilder.props(url);
    return this;
  }

  @Override
  public ServerConfigBuilder props(Map map) {
    configDataBuilder.props(map);
    return this;
  }

  @Override
  public ServerConfigBuilder sysProps() {
    configDataBuilder.sysProps();
    return this;
  }

  @Override
  public ServerConfigBuilder sysProps(String prefix) {
    configDataBuilder.sysProps(prefix);
    return this;
  }

  @Override
  public ServerConfigBuilder yaml(ByteSource byteSource) {
    configDataBuilder.yaml(byteSource);
    return this;
  }

  @Override
  public ServerConfigBuilder yaml(Path path) {
    configDataBuilder.yaml(path);
    return this;
  }

  @Override
  public ServerConfigBuilder yaml(URL url) {
    configDataBuilder.yaml(url);
    return this;
  }

  @Override
  public ServerConfigBuilder object(String path, Object object) {
    configDataBuilder.object(path, object);
    return this;
  }

  @Override
  public ServerConfigBuilder require(String pointer, TypeToken type) {
    TypeToken previous = required.put(
      Objects.requireNonNull(pointer, "pointer cannot be null"),
      Objects.requireNonNull(type, "type cannot be null")
    );
    if (previous != null) {
      throw new IllegalArgumentException("Cannot require config of type '" + type + "' at '" + pointer + "' as '" + previous + " has already been registered for this path");
    }
    return this;
  }

  @Override
  public ServerConfigBuilder onError(Action errorHandler) {
    configDataBuilder.onError(errorHandler);
    return this;
  }

  @Override
  public ServerConfigBuilder json(String path) {
    configDataBuilder.json(path);
    return this;
  }

  @Override
  public ServerConfigBuilder props(String path) {
    configDataBuilder.props(path);
    return this;
  }

  @Override
  public ServerConfigBuilder yaml(String path) {
    configDataBuilder.yaml(path);
    return this;
  }

  @Override
  public ImmutableList getConfigSources() {
    return configDataBuilder.getConfigSources();
  }

  @Override
  public ServerConfig build() {
    DefaultServerConfigBuilder copy = copy();

    impositions.get(ServerConfigImposition.class)
      .ifPresent(c -> c.apply(copy));

    impositions.get(ForceServerListenPortImposition.class)
      .map(ForceServerListenPortImposition::getPort)
      .ifPresent(copy::port);

    impositions.get(ForceDevelopmentImposition.class)
      .map(ForceDevelopmentImposition::isDevelopment)
      .ifPresent(copy::development);

    copy.configDataBuilder.jacksonModules(new ConfigModule(copy.serverEnvironment, copy.baseDirSupplier));
    ConfigData configData = new DefaultConfigData(copy.configDataBuilder.getObjectMapper(), copy.getConfigSources(), MoreObjects.firstNonNull(copy.baseDirSupplier.baseDir, FileSystemBinding.root()));
    ImmutableSet> requiredConfig = extractRequiredConfig(configData, copy.required);
    return new DefaultServerConfig(configData, requiredConfig);
  }

  private static ImmutableSet> extractRequiredConfig(ConfigData configData, Map> required) {
    RuntimeException badConfig = new IllegalStateException("Failed to build required config items");
    ImmutableSet.Builder> config = ImmutableSet.builder();
    for (Map.Entry> requiredConfig : required.entrySet()) {
      String path = requiredConfig.getKey();
      TypeToken type = requiredConfig.getValue();
      try {
        config.add(configData.getAsConfigObject(path, type));
      } catch (Exception e) {
        badConfig.addSuppressed(new IllegalStateException("Could not bind config at '" + path + "' to '" + type + "'", e));
      }
    }

    if (badConfig.getSuppressed().length > 0) {
      throw badConfig;
    } else {
      return config.build();
    }
  }

  public static class ConfigModule extends SimpleModule {
    public ConfigModule(ServerEnvironment serverEnvironment, Supplier baseDirSupplier) {
      super("ratpack");
      addDeserializer(ServerConfigData.class, new ServerConfigDataDeserializer(
        serverEnvironment.getPort(),
        serverEnvironment.isDevelopment(),
        serverEnvironment.getPublicAddress(),
        baseDirSupplier
      ));
      addDeserializer(SSLContext.class, new SSLContextDeserializer());
    }
  }

  private static class BaseDirSupplier implements Supplier {
    private FileSystemBinding baseDir;

    @Override
    public FileSystemBinding get() {
      return baseDir;
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy