@Override
 public ThreadPoolExecutor getThreadPool(
     HystrixThreadPoolKey threadPoolKey,
     HystrixProperty<Integer> corePoolSize,
     HystrixProperty<Integer> maximumPoolSize,
     HystrixProperty<Integer> keepAliveTime,
     TimeUnit unit,
     BlockingQueue<Runnable> workQueue) {
   final String nameFormat =
       Joiner.on('-').join(ImmutableList.of("hystrix"), threadPoolKey.name(), "%d");
   final ThreadFactory threadFactory =
       new ThreadFactoryBuilder().setNameFormat(nameFormat).build();
   final String key = threadPoolKey.name();
   final ThreadPoolExecutor existing =
       executors.putIfAbsent(
           key,
           new ThreadPoolExecutor(
               corePoolSize.get(),
               maximumPoolSize.get(),
               keepAliveTime.get(),
               unit,
               workQueue,
               threadFactory));
   final ThreadPoolExecutor threadPoolExecutor = executors.get(key);
   if (existing == null) {
     environment
         .lifecycle()
         .manage(new ExecutorServiceManager(threadPoolExecutor, Duration.seconds(5), nameFormat));
   }
   return threadPoolExecutor;
 }
  @Test
  public void testFullConfiguration() throws Exception {
    DataSourceFactory ds = getDataSourceFactory("yaml/full_db_pool.yml");

    assertThat(ds.getDriverClass()).isEqualTo("org.postgresql.Driver");
    assertThat(ds.getUser()).isEqualTo("pg-user");
    assertThat(ds.getUrl()).isEqualTo("jdbc:postgresql://db.example.com/db-prod");
    assertThat(ds.getPassword()).isEqualTo("iAMs00perSecrEET");
    assertThat(ds.getProperties()).containsEntry("charSet", "UTF-8");
    assertThat(ds.getMaxWaitForConnection()).isEqualTo(Duration.seconds(1));
    assertThat(ds.getValidationQuery()).isEqualTo("/* MyService Health Check */ SELECT 1");
    assertThat(ds.getMinSize()).isEqualTo(8);
    assertThat(ds.getInitialSize()).isEqualTo(15);
    assertThat(ds.getMaxSize()).isEqualTo(32);
    assertThat(ds.getCheckConnectionWhileIdle()).isFalse();
    assertThat(ds.getEvictionInterval()).isEqualTo(Duration.seconds(10));
    assertThat(ds.getMinIdleTime()).isEqualTo(Duration.minutes(1));
    assertThat(ds.getValidationInterval()).isEqualTo(Duration.minutes(1));
    assertThat(ds.isAutoCommentsEnabled()).isFalse();
    assertThat(ds.getReadOnlyByDefault()).isFalse();
    assertThat(ds.isRemoveAbandoned()).isTrue();
    assertThat(ds.getRemoveAbandonedTimeout()).isEqualTo(Duration.seconds(15L));
    assertThat(ds.getAbandonWhenPercentageFull()).isEqualTo(75);
    assertThat(ds.isAlternateUsernamesAllowed()).isTrue();
    assertThat(ds.getCommitOnReturn()).isTrue();
    assertThat(ds.getRollbackOnReturn()).isTrue();
    assertThat(ds.getAutoCommitByDefault()).isFalse();
    assertThat(ds.getDefaultCatalog()).isEqualTo("test_catalog");
    assertThat(ds.getDefaultTransactionIsolation())
        .isEqualTo(DataSourceFactory.TransactionIsolation.READ_COMMITTED);
    assertThat(ds.getUseFairQueue()).isFalse();
    assertThat(ds.getInitializationQuery())
        .isEqualTo("insert into connections_log(ts) values (now())");
    assertThat(ds.getLogAbandonedConnections()).isEqualTo(true);
    assertThat(ds.getLogValidationErrors()).isEqualTo(true);
    assertThat(ds.getMaxConnectionAge()).isEqualTo(Optional.of(Duration.hours(1)));
    assertThat(ds.getCheckConnectionOnBorrow()).isEqualTo(true);
    assertThat(ds.getCheckConnectionOnConnect()).isEqualTo(false);
    assertThat(ds.getCheckConnectionOnReturn()).isEqualTo(true);
    assertThat(ds.getValidationQueryTimeout()).isEqualTo(Optional.of(Duration.seconds(3)));
    assertThat(ds.getValidatorClassName())
        .isEqualTo(Optional.of("io.dropwizard.db.CustomConnectionValidator"));
  }
  @Test
  public void testMinimalConfiguration() throws Exception {
    DataSourceFactory ds = getDataSourceFactory("yaml/minimal_db_pool.yml");

    assertThat(ds.getDriverClass()).isEqualTo("org.postgresql.Driver");
    assertThat(ds.getUser()).isEqualTo("pg-user");
    assertThat(ds.getUrl()).isEqualTo("jdbc:postgresql://db.example.com/db-prod");
    assertThat(ds.getPassword()).isEqualTo("iAMs00perSecrEET");
    assertThat(ds.getProperties()).isEmpty();
    assertThat(ds.getMaxWaitForConnection()).isEqualTo(Duration.seconds(30));
    assertThat(ds.getValidationQuery()).isEqualTo("/* Health Check */ SELECT 1");
    assertThat(ds.getMinSize()).isEqualTo(10);
    assertThat(ds.getInitialSize()).isEqualTo(10);
    assertThat(ds.getMaxSize()).isEqualTo(100);
    assertThat(ds.getCheckConnectionWhileIdle()).isTrue();
    assertThat(ds.getEvictionInterval()).isEqualTo(Duration.seconds(5));
    assertThat(ds.getMinIdleTime()).isEqualTo(Duration.minutes(1));
    assertThat(ds.getValidationInterval()).isEqualTo(Duration.seconds(30));
    assertThat(ds.isAutoCommentsEnabled()).isTrue();
    assertThat(ds.getReadOnlyByDefault()).isNull();
    assertThat(ds.isRemoveAbandoned()).isFalse();
    assertThat(ds.getRemoveAbandonedTimeout()).isEqualTo(Duration.seconds(60L));
    assertThat(ds.getAbandonWhenPercentageFull()).isEqualTo(0);
    assertThat(ds.isAlternateUsernamesAllowed()).isFalse();
    assertThat(ds.getCommitOnReturn()).isFalse();
    assertThat(ds.getRollbackOnReturn()).isFalse();
    assertThat(ds.getAutoCommitByDefault()).isNull();
    assertThat(ds.getDefaultCatalog()).isNull();
    assertThat(ds.getDefaultTransactionIsolation())
        .isEqualTo(DataSourceFactory.TransactionIsolation.DEFAULT);
    assertThat(ds.getUseFairQueue()).isTrue();
    assertThat(ds.getInitializationQuery()).isNull();
    assertThat(ds.getLogAbandonedConnections()).isEqualTo(false);
    assertThat(ds.getLogValidationErrors()).isEqualTo(false);
    assertThat(ds.getMaxConnectionAge()).isEqualTo(Optional.empty());
    assertThat(ds.getCheckConnectionOnBorrow()).isEqualTo(false);
    assertThat(ds.getCheckConnectionOnConnect()).isEqualTo(true);
    assertThat(ds.getCheckConnectionOnReturn()).isEqualTo(false);
    assertThat(ds.getValidationQueryTimeout()).isEqualTo(Optional.empty());
  }
public class StreamCreateConfiguration {

  private static final Logger LOG = LoggerFactory.getLogger(StreamCreateConfiguration.class);

  @Min(1)
  private int shardCount = 1;

  @Valid @NotNull private Duration retryPeriod = Duration.seconds(5);

  @Min(1)
  private Integer maxAttempts;

  @JsonProperty
  public int getShardCount() {
    return shardCount;
  }

  @JsonProperty
  public void setShardCount(int shardCount) {
    this.shardCount = shardCount;
  }

  @JsonIgnore
  public StreamCreateConfiguration shardCount(int shardCount) {
    this.setShardCount(shardCount);
    return this;
  }

  @JsonProperty
  public Duration getRetryPeriod() {
    return retryPeriod;
  }

  @JsonProperty
  public void setRetryPeriod(Duration retryPeriod) {
    this.retryPeriod = retryPeriod;
  }

  @JsonProperty
  public StreamCreateConfiguration retryPeriod(Duration retryPeriod) {
    this.setRetryPeriod(retryPeriod);
    return this;
  }

  @JsonProperty
  public Integer getMaxAttempts() {
    return maxAttempts;
  }

  @JsonProperty
  public void setMaxAttempts(Integer maxAttempts) {
    this.maxAttempts = maxAttempts;
  }

  @JsonIgnore
  public StreamCreateConfiguration maxAttempts(Integer maxAttempts) {
    this.setMaxAttempts(maxAttempts);
    return this;
  }

  @JsonIgnore
  public boolean setupStream(AmazonKinesis kinesis, String streamName) {
    boolean setup = false;
    Preconditions.checkState(!Strings.isNullOrEmpty(streamName), "streamName was not specified");
    try {
      DescribeStreamResult result;
      if (getRetryPeriod() != null) {
        Integer retryAttempts = getMaxAttempts();
        while (retryAttempts == null || retryAttempts > 0) {
          try {
            result = kinesis.describeStream(streamName);
            if ("active".equalsIgnoreCase(result.getStreamDescription().getStreamStatus())) {
              LOG.info("stream {} is active", streamName);
              setup = true;
              break;
            }
          } catch (NullPointerException | ResourceNotFoundException e) {
            createStream(kinesis, streamName);
          }
          Thread.sleep(retryPeriod.toMilliseconds());
          if (retryAttempts != null) {
            retryAttempts--;
          }
        }
      }
    } catch (InterruptedException e) {
      LOG.error(
          "Needed to create stream {} but was interrupted, nothing is guaranteed now", streamName);
    }
    return setup;
  }

  private void createStream(AmazonKinesis kinesis, String streamName) {
    LOG.info(
        String.format(
            "stream %s was not found, creating with %d shards", streamName, getShardCount()));
    try {
      final CreateStreamResult stream = kinesis.createStream(streamName, getShardCount());

    } catch (ResourceInUseException ue) {
      LOG.info(String.format("failed to create stream %s because it already existed", streamName));
    } catch (Exception e) {
      LOG.error(String.format("failed to create stream %s", e), e);
    }
  }
}
/** Jackson friendly object for holding configuration information about a ZooKeeper ensemble. */
public class ZooKeeperConfiguration {
  private static final String DEFAULT_CONNECT_STRING = "localhost:2181";
  private static final RetryPolicy DEFAULT_RETRY_POLICY =
      new BoundedExponentialBackoffRetry(100, 1000, 5);

  @NotNull
  @JsonProperty("connectString")
  @UnwrapValidatedValue(false)
  private Optional<String> _connectString = Optional.absent();

  @JsonProperty("namespace")
  private Optional<String> _namespace = Optional.absent();

  @JsonProperty("retryPolicy")
  private RetryPolicy _configRetryPolicy = null;

  @JsonProperty("sessionTimeout")
  private Duration _sessionTimeout = Duration.seconds(60);

  @JsonProperty("connectionTimeout")
  private Duration _connectionTimeout = Duration.seconds(15);

  /**
   * Used to hold a retry policy provided by a setter. This needs to be separate from {@code
   * _retryPolicy} because we want callers to be able to specify any Curator {@link
   * org.apache.curator.RetryPolicy} implementation instead of the inner {@link RetryPolicy} and its
   * subclasses that are used entirely to hold Jackson annotations.
   */
  private Optional<org.apache.curator.RetryPolicy> _setterRetryPolicy = Optional.absent();

  /**
   * Return a new Curator connection to the ensemble. It is the caller's responsibility to start and
   * close the connection.
   */
  public CuratorFramework newCurator() {
    // Make all of the curator threads daemon threads so they don't block the JVM from terminating.
    // Also label them
    // with the ensemble they're connecting to, in case someone is trying to sort through a thread
    // dump.
    ThreadFactory threadFactory =
        new ThreadFactoryBuilder()
            .setNameFormat("CuratorFramework[" + _connectString.or(DEFAULT_CONNECT_STRING) + "]-%d")
            .setDaemon(true)
            .build();

    org.apache.curator.RetryPolicy retry =
        _setterRetryPolicy.or(
            (_configRetryPolicy != null) ? _configRetryPolicy : DEFAULT_RETRY_POLICY);
    return CuratorFrameworkFactory.builder()
        .ensembleProvider(new ResolvingEnsembleProvider(_connectString.or(DEFAULT_CONNECT_STRING)))
        .retryPolicy(retry)
        .sessionTimeoutMs(Ints.checkedCast(_sessionTimeout.toMilliseconds()))
        .connectionTimeoutMs(Ints.checkedCast(_connectionTimeout.toMilliseconds()))
        .namespace(_namespace.orNull())
        .threadFactory(threadFactory)
        .build();
  }

  /**
   * Return a managed Curator connection. This created connection will be wrapped in a {@link
   * ManagedCuratorFramework} and offered to the provided {@link Environment} parameter.
   *
   * @deprecated Use {@link #newManagedCurator(LifecycleEnvironment)} instead.
   */
  @Deprecated
  public CuratorFramework newManagedCurator(Environment env) {
    return newManagedCurator(env.lifecycle());
  }

  /**
   * Return a managed Curator connection. This created connection will be wrapped in a {@link
   * ManagedCuratorFramework} and offered to the provided {@link LifecycleEnvironment} parameter.
   */
  public CuratorFramework newManagedCurator(LifecycleEnvironment env) {
    CuratorFramework curator = newCurator();
    env.manage(new ManagedCuratorFramework(curator));
    return curator;
  }

  @JsonIgnore
  public Optional<String> getConnectString() {
    return _connectString;
  }

  @JsonIgnore
  public Optional<String> getNamespace() {
    return _namespace;
  }

  @JsonIgnore
  public Optional<org.apache.curator.RetryPolicy> getRetryPolicy() {
    if (_setterRetryPolicy.isPresent()) {
      return _setterRetryPolicy;
    }

    return Optional.<org.apache.curator.RetryPolicy>fromNullable(_configRetryPolicy);
  }

  @JsonIgnore
  public void setConnectString(String connectString) {
    _connectString = Optional.of(connectString);
  }

  @JsonIgnore
  public void setNamespace(String namespace) {
    _namespace = Optional.of(namespace);
  }

  @JsonIgnore
  public void setRetryPolicy(org.apache.curator.RetryPolicy retryPolicy) {
    _setterRetryPolicy = Optional.of(retryPolicy);
  }

  @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
  @JsonSubTypes({
    @JsonSubTypes.Type(
        value = BoundedExponentialBackoffRetry.class,
        name = "boundedExponentialBackoff"),
    @JsonSubTypes.Type(value = ExponentialBackoffRetry.class, name = "exponentialBackoff"),
    @JsonSubTypes.Type(value = RetryNTimes.class, name = "nTimes"),
    @JsonSubTypes.Type(value = RetryUntilElapsed.class, name = "untilElapsed")
  })
  static interface RetryPolicy extends org.apache.curator.RetryPolicy {}

  private static final class BoundedExponentialBackoffRetry
      extends org.apache.curator.retry.BoundedExponentialBackoffRetry implements RetryPolicy {
    @JsonCreator
    public BoundedExponentialBackoffRetry(
        @JsonProperty("baseSleepTimeMs") int baseSleepTimeMs,
        @JsonProperty("maxSleepTimeMs") int maxSleepTimeMs,
        @JsonProperty("maxRetries") int maxRetries) {
      super(baseSleepTimeMs, maxSleepTimeMs, maxRetries);
    }
  }

  private static final class ExponentialBackoffRetry
      extends org.apache.curator.retry.ExponentialBackoffRetry implements RetryPolicy {
    @JsonCreator
    public ExponentialBackoffRetry(
        @JsonProperty("baseSleepTimeMs") int baseSleepTimeMs,
        @JsonProperty("maxRetries") int maxRetries) {
      super(baseSleepTimeMs, maxRetries);
    }
  }

  private static final class RetryNTimes extends org.apache.curator.retry.RetryNTimes
      implements RetryPolicy {
    @JsonCreator
    public RetryNTimes(
        @JsonProperty("n") int n,
        @JsonProperty("sleepMsBetweenRetries") int sleepMsBetweenRetries) {
      super(n, sleepMsBetweenRetries);
    }
  }

  private static final class RetryUntilElapsed extends org.apache.curator.retry.RetryUntilElapsed
      implements RetryPolicy {
    public RetryUntilElapsed(
        @JsonProperty("maxElapsedTimeMs") int maxElapsedTimeMs,
        @JsonProperty("sleepMsBetweenRetries") int sleepMsBetweenRetries) {
      super(maxElapsedTimeMs, sleepMsBetweenRetries);
    }
  }
}
Ejemplo n.º 6
0
/**
 * A base class for {@link ServerFactory} implementations.
 *
 * <p><b>Configuration Parameters:</b>
 *
 * <table>
 *     <tr>
 *         <td>Name</td>
 *         <td>Default</td>
 *         <td>Description</td>
 *     </tr>
 *     <tr>
 *         <td>{@code requestLog}</td>
 *         <td></td>
 *         <td>The {@link RequestLogFactory request log} configuration.</td>
 *     </tr>
 *     <tr>
 *         <td>{@code gzip}</td>
 *         <td></td>
 *         <td>The {@link GzipHandlerFactory GZIP} configuration.</td>
 *     </tr>
 *     <tr>
 *         <td>{@code serverPush}</td>
 *         <td></td>
 *         <td>The {@link ServerPushFilterFactory} configuration.</td>
 *     </tr>
 *     <tr>
 *         <td>{@code maxThreads}</td>
 *         <td>1024</td>
 *         <td>The maximum number of threads to use for requests.</td>
 *     </tr>
 *     <tr>
 *         <td>{@code minThreads}</td>
 *         <td>8</td>
 *         <td>The minimum number of threads to use for requests.</td>
 *     </tr>
 *     <tr>
 *         <td>{@code maxQueuedRequests}</td>
 *         <td>1024</td>
 *         <td>The maximum number of requests to queue before blocking the acceptors.</td>
 *     </tr>
 *     <tr>
 *         <td>{@code idleThreadTimeout}</td>
 *         <td>1 minute</td>
 *         <td>The amount of time a worker thread can be idle before being stopped.</td>
 *     </tr>
 *     <tr>
 *         <td>{@code nofileSoftLimit}</td>
 *         <td>(none)</td>
 *         <td>
 *             The number of open file descriptors before a soft error is issued. <b>Requires Jetty's
 *             {@code libsetuid.so} on {@code java.library.path}.</b>
 *         </td>
 *     </tr>
 *     <tr>
 *         <td>{@code nofileHardLimit}</td>
 *         <td>(none)</td>
 *         <td>
 *             The number of open file descriptors before a hard error is issued. <b>Requires Jetty's
 *             {@code libsetuid.so} on {@code java.library.path}.</b>
 *         </td>
 *     </tr>
 *     <tr>
 *         <td>{@code gid}</td>
 *         <td>(none)</td>
 *         <td>
 *             The group ID to switch to once the connectors have started. <b>Requires Jetty's
 *             {@code libsetuid.so} on {@code java.library.path}.</b>
 *         </td>
 *     </tr>
 *     <tr>
 *         <td>{@code uid}</td>
 *         <td>(none)</td>
 *         <td>
 *             The user ID to switch to once the connectors have started. <b>Requires Jetty's
 *             {@code libsetuid.so} on {@code java.library.path}.</b>
 *         </td>
 *     </tr>
 *     <tr>
 *         <td>{@code user}</td>
 *         <td>(none)</td>
 *         <td>
 *             The username to switch to once the connectors have started. <b>Requires Jetty's
 *             {@code libsetuid.so} on {@code java.library.path}.</b>
 *         </td>
 *     </tr>
 *     <tr>
 *         <td>{@code group}</td>
 *         <td>(none)</td>
 *         <td>
 *             The group to switch to once the connectors have started. <b>Requires Jetty's
 *             {@code libsetuid.so} on {@code java.library.path}.</b>
 *         </td>
 *     </tr>
 *     <tr>
 *         <td>{@code umask}</td>
 *         <td>(none)</td>
 *         <td>
 *             The umask to switch to once the connectors have started. <b>Requires Jetty's
 *             {@code libsetuid.so} on {@code java.library.path}.</b>
 *         </td>
 *     </tr>
 *     <tr>
 *         <td>{@code startsAsRoot}</td>
 *         <td>(none)</td>
 *         <td>
 *             Whether or not the Dropwizard application is started as a root user. <b>Requires
 *             Jetty's {@code libsetuid.so} on {@code java.library.path}.</b>
 *         </td>
 *     </tr>
 *     <tr>
 *         <td>{@code registerDefaultExceptionMappers}</td>
 *         <td>true</td>
 *         <td>
 *            Whether or not the default Jersey ExceptionMappers should be registered.
 *            Set this to false if you want to register your own.
 *         </td>
 *     </tr>
 *     <tr>
 *         <td>{@code shutdownGracePeriod}</td>
 *         <td>30 seconds</td>
 *         <td>
 *             The maximum time to wait for Jetty, and all Managed instances, to cleanly shutdown
 *             before forcibly terminating them.
 *         </td>
 *     </tr>
 *     <tr>
 *         <td>{@code allowedMethods}</td>
 *         <td>GET, POST, PUT, DELETE, HEAD, OPTIONS, PATCH</td>
 *         <td>
 *             The set of allowed HTTP methods. Others will be rejected with a
 *             405 Method Not Allowed response.
 *         </td>
 *     </tr>
 *     <tr>
 *         <td>{@code rootPath}</td>
 *         <td>/*</td>
 *         <td>
 *           The URL pattern relative to {@code applicationContextPath} from which the JAX-RS resources will be served.
 *         </td>
 *     </tr>
 * </table>
 *
 * @see DefaultServerFactory
 * @see SimpleServerFactory
 */
public abstract class AbstractServerFactory implements ServerFactory {
  private static final Logger LOGGER = LoggerFactory.getLogger(ServerFactory.class);
  private static final Pattern WINDOWS_NEWLINE = Pattern.compile("\\r\\n?");

  @Valid @NotNull private RequestLogFactory requestLog = new LogbackAccessRequestLogFactory();

  @Valid @NotNull private GzipHandlerFactory gzip = new GzipHandlerFactory();

  @Valid @NotNull private ServerPushFilterFactory serverPush = new ServerPushFilterFactory();

  @Min(2)
  private int maxThreads = 1024;

  @Min(1)
  private int minThreads = 8;

  private int maxQueuedRequests = 1024;

  @MinDuration(1)
  private Duration idleThreadTimeout = Duration.minutes(1);

  @Min(1)
  private Integer nofileSoftLimit;

  @Min(1)
  private Integer nofileHardLimit;

  private Integer gid;

  private Integer uid;

  private String user;

  private String group;

  private String umask;

  private Boolean startsAsRoot;

  private Boolean registerDefaultExceptionMappers = Boolean.TRUE;

  private Duration shutdownGracePeriod = Duration.seconds(30);

  @NotNull private Set<String> allowedMethods = AllowedMethodsFilter.DEFAULT_ALLOWED_METHODS;

  private Optional<String> jerseyRootPath = Optional.absent();

  @JsonIgnore
  @ValidationMethod(message = "must have a smaller minThreads than maxThreads")
  public boolean isThreadPoolSizedCorrectly() {
    return minThreads <= maxThreads;
  }

  @JsonProperty("requestLog")
  public RequestLogFactory getRequestLogFactory() {
    return requestLog;
  }

  @JsonProperty("requestLog")
  public void setRequestLogFactory(RequestLogFactory requestLog) {
    this.requestLog = requestLog;
  }

  @JsonProperty("gzip")
  public GzipHandlerFactory getGzipFilterFactory() {
    return gzip;
  }

  @JsonProperty("gzip")
  public void setGzipFilterFactory(GzipHandlerFactory gzip) {
    this.gzip = gzip;
  }

  @JsonProperty("serverPush")
  public ServerPushFilterFactory getServerPush() {
    return serverPush;
  }

  @JsonProperty("serverPush")
  public void setServerPush(ServerPushFilterFactory serverPush) {
    this.serverPush = serverPush;
  }

  @JsonProperty
  public int getMaxThreads() {
    return maxThreads;
  }

  @JsonProperty
  public void setMaxThreads(int count) {
    this.maxThreads = count;
  }

  @JsonProperty
  public int getMinThreads() {
    return minThreads;
  }

  @JsonProperty
  public void setMinThreads(int count) {
    this.minThreads = count;
  }

  @JsonProperty
  public int getMaxQueuedRequests() {
    return maxQueuedRequests;
  }

  @JsonProperty
  public void setMaxQueuedRequests(int maxQueuedRequests) {
    this.maxQueuedRequests = maxQueuedRequests;
  }

  @JsonProperty
  public Duration getIdleThreadTimeout() {
    return idleThreadTimeout;
  }

  @JsonProperty
  public void setIdleThreadTimeout(Duration idleThreadTimeout) {
    this.idleThreadTimeout = idleThreadTimeout;
  }

  @JsonProperty
  public Integer getNofileSoftLimit() {
    return nofileSoftLimit;
  }

  @JsonProperty
  public void setNofileSoftLimit(Integer nofileSoftLimit) {
    this.nofileSoftLimit = nofileSoftLimit;
  }

  @JsonProperty
  public Integer getNofileHardLimit() {
    return nofileHardLimit;
  }

  @JsonProperty
  public void setNofileHardLimit(Integer nofileHardLimit) {
    this.nofileHardLimit = nofileHardLimit;
  }

  @JsonProperty
  public Integer getGid() {
    return gid;
  }

  @JsonProperty
  public void setGid(Integer gid) {
    this.gid = gid;
  }

  @JsonProperty
  public Integer getUid() {
    return uid;
  }

  @JsonProperty
  public void setUid(Integer uid) {
    this.uid = uid;
  }

  @JsonProperty
  public String getUser() {
    return user;
  }

  @JsonProperty
  public void setUser(String user) {
    this.user = user;
  }

  @JsonProperty
  public String getGroup() {
    return group;
  }

  @JsonProperty
  public void setGroup(String group) {
    this.group = group;
  }

  @JsonProperty
  public String getUmask() {
    return umask;
  }

  @JsonProperty
  public void setUmask(String umask) {
    this.umask = umask;
  }

  @JsonProperty
  public Boolean getStartsAsRoot() {
    return startsAsRoot;
  }

  @JsonProperty
  public void setStartsAsRoot(Boolean startsAsRoot) {
    this.startsAsRoot = startsAsRoot;
  }

  public Boolean getRegisterDefaultExceptionMappers() {
    return registerDefaultExceptionMappers;
  }

  public void setRegisterDefaultExceptionMappers(Boolean registerDefaultExceptionMappers) {
    this.registerDefaultExceptionMappers = registerDefaultExceptionMappers;
  }

  @JsonProperty
  public Duration getShutdownGracePeriod() {
    return shutdownGracePeriod;
  }

  @JsonProperty
  public void setShutdownGracePeriod(Duration shutdownGracePeriod) {
    this.shutdownGracePeriod = shutdownGracePeriod;
  }

  @JsonProperty
  public Set<String> getAllowedMethods() {
    return allowedMethods;
  }

  @JsonProperty
  public void setAllowedMethods(Set<String> allowedMethods) {
    this.allowedMethods = allowedMethods;
  }

  @JsonProperty("rootPath")
  public Optional<String> getJerseyRootPath() {
    return jerseyRootPath;
  }

  @JsonProperty("rootPath")
  public void setJerseyRootPath(String jerseyRootPath) {
    this.jerseyRootPath = Optional.fromNullable(jerseyRootPath);
  }

  protected Handler createAdminServlet(
      Server server,
      MutableServletContextHandler handler,
      MetricRegistry metrics,
      HealthCheckRegistry healthChecks) {
    configureSessionsAndSecurity(handler, server);
    handler.setServer(server);
    handler.getServletContext().setAttribute(MetricsServlet.METRICS_REGISTRY, metrics);
    handler
        .getServletContext()
        .setAttribute(HealthCheckServlet.HEALTH_CHECK_REGISTRY, healthChecks);
    handler.addServlet(new NonblockingServletHolder(new AdminServlet()), "/*");
    handler
        .addFilter(AllowedMethodsFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST))
        .setInitParameter(
            AllowedMethodsFilter.ALLOWED_METHODS_PARAM, Joiner.on(',').join(allowedMethods));
    return handler;
  }

  private void configureSessionsAndSecurity(MutableServletContextHandler handler, Server server) {
    handler.setServer(server);
    if (handler.isSecurityEnabled()) {
      handler.getSecurityHandler().setServer(server);
    }
    if (handler.isSessionsEnabled()) {
      handler.getSessionHandler().setServer(server);
    }
  }

  protected Handler createAppServlet(
      Server server,
      JerseyEnvironment jersey,
      ObjectMapper objectMapper,
      Validator validator,
      MutableServletContextHandler handler,
      @Nullable Servlet jerseyContainer,
      MetricRegistry metricRegistry) {
    configureSessionsAndSecurity(handler, server);
    handler
        .addFilter(AllowedMethodsFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST))
        .setInitParameter(
            AllowedMethodsFilter.ALLOWED_METHODS_PARAM, Joiner.on(',').join(allowedMethods));
    handler.addFilter(ThreadNameFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
    serverPush.addFilter(handler);
    if (jerseyContainer != null) {
      if (jerseyRootPath.isPresent()) {
        jersey.setUrlPattern(jerseyRootPath.get());
      }
      jersey.register(new JacksonMessageBodyProvider(objectMapper));
      jersey.register(new HibernateValidationFeature(validator));
      if (registerDefaultExceptionMappers == null || registerDefaultExceptionMappers) {
        jersey.register(new LoggingExceptionMapper<Throwable>() {});
        jersey.register(new JerseyViolationExceptionMapper());
        jersey.register(new JsonProcessingExceptionMapper());
        jersey.register(new EarlyEofExceptionMapper());
      }
      handler.addServlet(new NonblockingServletHolder(jerseyContainer), jersey.getUrlPattern());
    }
    final InstrumentedHandler instrumented = new InstrumentedHandler(metricRegistry);
    instrumented.setServer(server);
    instrumented.setHandler(handler);
    return instrumented;
  }

  protected ThreadPool createThreadPool(MetricRegistry metricRegistry) {
    final BlockingQueue<Runnable> queue =
        new BlockingArrayQueue<>(minThreads, maxThreads, maxQueuedRequests);
    final InstrumentedQueuedThreadPool threadPool =
        new InstrumentedQueuedThreadPool(
            metricRegistry,
            maxThreads,
            minThreads,
            (int) idleThreadTimeout.toMilliseconds(),
            queue);
    threadPool.setName("dw");
    return threadPool;
  }

  protected Server buildServer(LifecycleEnvironment lifecycle, ThreadPool threadPool) {
    final Server server = new Server(threadPool);
    server.addLifeCycleListener(buildSetUIDListener());
    lifecycle.attach(server);
    final ErrorHandler errorHandler = new ErrorHandler();
    errorHandler.setServer(server);
    errorHandler.setShowStacks(false);
    server.addBean(errorHandler);
    server.setStopAtShutdown(true);
    server.setStopTimeout(shutdownGracePeriod.toMilliseconds());
    return server;
  }

  protected SetUIDListener buildSetUIDListener() {
    final SetUIDListener listener = new SetUIDListener();

    if (startsAsRoot != null) {
      listener.setStartServerAsPrivileged(startsAsRoot);
    }

    if (gid != null) {
      listener.setGid(gid);
    }

    if (uid != null) {
      listener.setUid(uid);
    }

    if (user != null) {
      listener.setUsername(user);
    }

    if (group != null) {
      listener.setGroupname(group);
    }

    if (nofileHardLimit != null || nofileSoftLimit != null) {
      final RLimit rlimit = new RLimit();
      if (nofileHardLimit != null) {
        rlimit.setHard(nofileHardLimit);
      }

      if (nofileSoftLimit != null) {
        rlimit.setSoft(nofileSoftLimit);
      }

      listener.setRLimitNoFiles(rlimit);
    }

    if (umask != null) {
      listener.setUmaskOctal(umask);
    }

    return listener;
  }

  protected Handler addRequestLog(Server server, Handler handler, String name) {
    if (requestLog.isEnabled()) {
      final RequestLogHandler requestLogHandler = new RequestLogHandler();
      requestLogHandler.setRequestLog(requestLog.build(name));
      // server should own the request log's lifecycle since it's already started,
      // the handler might not become managed in case of an error which would leave
      // the request log stranded
      server.addBean(requestLogHandler.getRequestLog(), true);
      requestLogHandler.setHandler(handler);
      return requestLogHandler;
    }
    return handler;
  }

  protected Handler addStatsHandler(Handler handler) {
    // Graceful shutdown is implemented via the statistics handler,
    // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=420142
    final StatisticsHandler statisticsHandler = new StatisticsHandler();
    statisticsHandler.setHandler(handler);
    return statisticsHandler;
  }

  protected Handler buildGzipHandler(Handler handler) {
    return gzip.isEnabled() ? gzip.build(handler) : handler;
  }

  protected void printBanner(String name) {
    try {
      final String banner =
          WINDOWS_NEWLINE
              .matcher(
                  Resources.toString(Resources.getResource("banner.txt"), StandardCharsets.UTF_8))
              .replaceAll("\n")
              .replace("\n", String.format("%n"));
      LOGGER.info(String.format("Starting {}%n{}"), name, banner);
    } catch (IllegalArgumentException | IOException ignored) {
      // don't display the banner if there isn't one
      LOGGER.info("Starting {}", name);
    }
  }
}