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

com.linecorp.armeria.common.Flags Maven / Gradle / Ivy

/*
 *  Copyright 2017 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:
 *
 *    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 com.linecorp.armeria.common;

import java.util.List;
import java.util.Optional;
import java.util.function.IntPredicate;
import java.util.function.LongPredicate;
import java.util.function.Predicate;

import javax.annotation.Nullable;
import javax.net.ssl.SSLEngine;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.CaffeineSpec;
import com.google.common.base.Ascii;
import com.google.common.base.CharMatcher;
import com.google.common.base.Splitter;

import com.linecorp.armeria.client.ClientFactoryBuilder;
import com.linecorp.armeria.client.retry.Backoff;
import com.linecorp.armeria.client.retry.RetryingHttpClient;
import com.linecorp.armeria.client.retry.RetryingRpcClient;
import com.linecorp.armeria.common.util.Exceptions;
import com.linecorp.armeria.server.PathMappingContext;
import com.linecorp.armeria.server.ServiceConfig;

import io.netty.channel.epoll.Epoll;
import io.netty.handler.ssl.OpenSsl;

/**
 * The system properties that affect Armeria's runtime behavior.
 */
public final class Flags {

    private static final Logger logger = LoggerFactory.getLogger(Flags.class);

    private static final Splitter CSV_SPLITTER = Splitter.on(',').trimResults().omitEmptyStrings();

    private static final String PREFIX = "com.linecorp.armeria.";

    private static final int NUM_CPU_CORES = Runtime.getRuntime().availableProcessors();

    private static final boolean VERBOSE_EXCEPTIONS = getBoolean("verboseExceptions", false);

    private static final boolean VERBOSE_RESPONSES = getBoolean("verboseResponses", false);

    private static final boolean USE_EPOLL = getBoolean("useEpoll", Epoll.isAvailable(),
                                                        value -> Epoll.isAvailable() || !value);
    private static final boolean USE_OPENSSL = getBoolean("useOpenSsl", OpenSsl.isAvailable(),
                                                          value -> OpenSsl.isAvailable() || !value);

    private static final int DEFAULT_MAX_NUM_CONNECTIONS = Integer.MAX_VALUE;
    private static final int MAX_NUM_CONNECTIONS =
            getInt("maxNumConnections", DEFAULT_MAX_NUM_CONNECTIONS, value -> value > 0);

    private static final int DEFAULT_NUM_COMMON_WORKERS = NUM_CPU_CORES * 2;
    private static final int NUM_COMMON_WORKERS =
            getInt("numCommonWorkers", DEFAULT_NUM_COMMON_WORKERS, value -> value > 0);

    private static final int DEFAULT_NUM_COMMON_BLOCKING_TASK_THREADS = 200; // from Tomcat default maxThreads
    private static final int NUM_COMMON_BLOCKING_TASK_THREADS =
            getInt("numCommonBlockingTaskThreads",
                   DEFAULT_NUM_COMMON_BLOCKING_TASK_THREADS,
                   value -> value > 0);

    private static final long DEFAULT_DEFAULT_MAX_REQUEST_LENGTH = 10 * 1024 * 1024; // 10 MiB
    private static final long DEFAULT_MAX_REQUEST_LENGTH =
            getLong("defaultMaxRequestLength",
                    DEFAULT_DEFAULT_MAX_REQUEST_LENGTH,
                    value -> value >= 0);

    private static final long DEFAULT_DEFAULT_MAX_RESPONSE_LENGTH = 10 * 1024 * 1024; // 10 MiB
    private static final long DEFAULT_MAX_RESPONSE_LENGTH =
            getLong("defaultMaxResponseLength",
                    DEFAULT_DEFAULT_MAX_RESPONSE_LENGTH,
                    value -> value >= 0);

    private static final long DEFAULT_DEFAULT_REQUEST_TIMEOUT_MILLIS = 10 * 1000; // 10 seconds
    private static final long DEFAULT_REQUEST_TIMEOUT_MILLIS =
            getLong("defaultRequestTimeoutMillis",
                    DEFAULT_DEFAULT_REQUEST_TIMEOUT_MILLIS,
                    value -> value >= 0);

    // Use slightly greater value than the default request timeout so that clients have a higher chance of
    // getting proper 503 Service Unavailable response when server-side timeout occurs.
    private static final long DEFAULT_DEFAULT_RESPONSE_TIMEOUT_MILLIS = 15 * 1000; // 15 seconds
    private static final long DEFAULT_RESPONSE_TIMEOUT_MILLIS =
            getLong("defaultResponseTimeoutMillis",
                    DEFAULT_DEFAULT_RESPONSE_TIMEOUT_MILLIS,
                    value -> value >= 0);

    private static final long DEFAULT_DEFAULT_CONNECT_TIMEOUT_MILLIS = 3200; // 3.2 seconds
    private static final long DEFAULT_CONNECT_TIMEOUT_MILLIS =
            getLong("defaultConnectTimeoutMillis",
                    DEFAULT_DEFAULT_CONNECT_TIMEOUT_MILLIS,
                    value -> value > 0);

    // Use slightly greater value than the client-side default so that clients close the connection more often.
    private static final long DEFAULT_DEFAULT_SERVER_IDLE_TIMEOUT_MILLIS = 15000; // 15 seconds
    private static final long DEFAULT_SERVER_IDLE_TIMEOUT_MILLIS =
            getLong("defaultServerIdleTimeoutMillis",
                    DEFAULT_DEFAULT_SERVER_IDLE_TIMEOUT_MILLIS,
                    value -> value >= 0);

    private static final long DEFAULT_DEFAULT_CLIENT_IDLE_TIMEOUT_MILLIS = 10000; // 10 seconds
    private static final long DEFAULT_CLIENT_IDLE_TIMEOUT_MILLIS =
            getLong("defaultClientIdleTimeoutMillis",
                    DEFAULT_DEFAULT_CLIENT_IDLE_TIMEOUT_MILLIS,
                    value -> value >= 0);

    private static final int DEFAULT_DEFAULT_MAX_HTTP1_INITIAL_LINE_LENGTH =
            4096; // from Netty default maxHttp1InitialLineLength
    private static final int DEFAULT_MAX_HTTP1_INITIAL_LINE_LENGTH =
            getInt("defaultMaxHttp1InitialLineLength",
                   DEFAULT_DEFAULT_MAX_HTTP1_INITIAL_LINE_LENGTH,
                   value -> value >= 0);

    private static final int DEFAULT_DEFAULT_MAX_HTTP1_HEADER_SIZE = 8192; // from Netty default maxHeaderSize
    private static final int DEFAULT_MAX_HTTP1_HEADER_SIZE =
            getInt("defaultMaxHttp1HeaderSize",
                   DEFAULT_DEFAULT_MAX_HTTP1_HEADER_SIZE,
                   value -> value >= 0);

    private static final int DEFAULT_DEFAULT_MAX_HTTP1_CHUNK_SIZE = 8192; // from Netty default maxChunkSize
    private static final int DEFAULT_MAX_HTTP1_CHUNK_SIZE =
            getInt("defaultMaxHttp1ChunkSize",
                   DEFAULT_DEFAULT_MAX_HTTP1_CHUNK_SIZE,
                   value -> value >= 0);

    private static final boolean DEFAULT_USE_HTTP2_PREFACE = getBoolean("defaultUseHttp2Preface", true);
    private static final boolean DEFAULT_USE_HTTP1_PIPELINING = getBoolean("defaultUseHttp1Pipelining", false);

    private static final String DEFAULT_DEFAULT_BACKOFF_SPEC =
            "exponential=200:10000,jitter=0.2";
    private static final String DEFAULT_BACKOFF_SPEC =
            getNormalized("defaultBackoffSpec", DEFAULT_DEFAULT_BACKOFF_SPEC, value -> {
                try {
                    Backoff.of(value);
                    return true;
                } catch (Exception e) {
                    // Invalid backoff specification
                    return false;
                }
            });

    private static final int DEFAULT_DEFAULT_MAX_TOTAL_ATTEMPTS = 10;
    private static final int DEFAULT_MAX_TOTAL_ATTEMPTS =
            getInt("defaultMaxTotalAttempts",
                   DEFAULT_DEFAULT_MAX_TOTAL_ATTEMPTS,
                   value -> value > 0);

    private static final String DEFAULT_ROUTE_CACHE_SPEC = "maximumSize=4096";
    private static final Optional ROUTE_CACHE_SPEC =
            caffeineSpec("routeCache", DEFAULT_ROUTE_CACHE_SPEC);

    private static final String DEFAULT_COMPOSITE_SERVICE_CACHE_SPEC = "maximumSize=256";
    private static final Optional COMPOSITE_SERVICE_CACHE_SPEC =
            caffeineSpec("compositeServiceCache", DEFAULT_COMPOSITE_SERVICE_CACHE_SPEC);

    private static final String DEFAULT_PARSED_PATH_CACHE_SPEC = "maximumSize=4096";
    private static final Optional PARSED_PATH_CACHE_SPEC =
            caffeineSpec("parsedPathCache", DEFAULT_PARSED_PATH_CACHE_SPEC);

    private static final String DEFAULT_HEADER_VALUE_CACHE_SPEC = "maximumSize=4096";
    private static final Optional HEADER_VALUE_CACHE_SPEC =
            caffeineSpec("headerValueCache", DEFAULT_HEADER_VALUE_CACHE_SPEC);

    private static final String DEFAULT_CACHED_HEADERS =
            ":authority,:scheme,:method,accept-encoding,content-type";
    private static final List CACHED_HEADERS =
            CSV_SPLITTER.splitToList(getNormalized(
                    "cachedHeaders", DEFAULT_CACHED_HEADERS, CharMatcher.ascii()::matchesAllOf));

    static {
        if (!Epoll.isAvailable()) {
            final Throwable cause = Exceptions.peel(Epoll.unavailabilityCause());
            logger.info("/dev/epoll not available: {}", cause.toString());
        } else if (USE_EPOLL) {
            logger.info("Using /dev/epoll");
        }

        if (!OpenSsl.isAvailable()) {
            final Throwable cause = Exceptions.peel(OpenSsl.unavailabilityCause());
            logger.info("OpenSSL not available: {}", cause.toString());
        } else if (USE_OPENSSL) {
            logger.info("Using OpenSSL: {}, 0x{}",
                        OpenSsl.versionString(),
                        Long.toHexString(OpenSsl.version() & 0xFFFFFFFFL));
        }
    }

    /**
     * Returns whether the verbose exception mode is enabled. When enabled, the exceptions frequently thrown by
     * Armeria will have full stack trace. When disabled, such exceptions will have empty stack trace to
     * eliminate the cost of capturing the stack trace.
     *
     * 

This flag is disabled by default. Specify the {@code -Dcom.linecorp.armeria.verboseExceptions=true} * JVM option to enable it. */ public static boolean verboseExceptions() { return VERBOSE_EXCEPTIONS; } /** * Returns whether the verbose response mode is enabled. When enabled, the server responses will contain * the exception type and its full stack trace, which may be useful for debugging while potentially * insecure. When disabled, the server responses will not expose such server-side details to the client. * *

This flag is disabled by default. Specify the {@code -Dcom.linecorp.armeria.verboseResponses=true} * JVM option to enable it. */ public static boolean verboseResponses() { return VERBOSE_RESPONSES; } /** * Returns whether the JNI-based {@code /dev/epoll} socket I/O is enabled. When enabled on Linux, Armeria * uses {@code /dev/epoll} directly for socket I/O. When disabled, {@code java.nio} socket API is used * instead. * *

This flag is enabled by default for supported platforms. Specify the * {@code -Dcom.linecorp.armeria.useEpoll=false} JVM option to disable it. */ public static boolean useEpoll() { return USE_EPOLL; } /** * Returns whether the JNI-based TLS support with OpenSSL is enabled. When enabled, Armeria uses OpenSSL * for processing TLS connections. When disabled, the current JVM's default {@link SSLEngine} is used * instead. * *

This flag is enabled by default for supported platforms. Specify the * {@code -Dcom.linecorp.armeria.useOpenSsl=false} JVM option to disable it. */ public static boolean useOpenSsl() { return USE_OPENSSL; } /** * Returns the default server-side maximum number of connections. * *

The default value of this flag is {@value #DEFAULT_MAX_NUM_CONNECTIONS}. Specify the * {@code -Dcom.linecorp.armeria.maxNumConnections=} JVM option to override * the default value. */ public static int maxNumConnections() { return MAX_NUM_CONNECTIONS; } /** * Returns the default number of {@linkplain CommonPools#workerGroup() common worker group} threads. * Note that this value has effect only if a user did not specify a worker group. * *

The default value of this flag is {@code 2 * }. Specify the * {@code -Dcom.linecorp.armeria.numCommonWorkers=} JVM option to override the default value. */ public static int numCommonWorkers() { return NUM_COMMON_WORKERS; } /** * Returns the default number of {@linkplain CommonPools#blockingTaskExecutor() blocking task executor} * threads. Note that this value has effect only if a user did not specify a blocking task executor. * *

The default value of this flag is {@value #DEFAULT_NUM_COMMON_BLOCKING_TASK_THREADS}. Specify the * {@code -Dcom.linecorp.armeria.numCommonBlockingTaskThreads=} JVM option to override * the default value. */ public static int numCommonBlockingTaskThreads() { return NUM_COMMON_BLOCKING_TASK_THREADS; } /** * Returns the default server-side maximum length of a request. Note that this value has effect * only if a user did not specify it. * *

The default value of this flag is {@value #DEFAULT_DEFAULT_MAX_REQUEST_LENGTH}. Specify the * {@code -Dcom.linecorp.armeria.defaultMaxRequestLength=} to override the default value. * {@code 0} disables the length limit. */ public static long defaultMaxRequestLength() { return DEFAULT_MAX_REQUEST_LENGTH; } /** * Returns the default client-side maximum length of a response. Note that this value has effect * only if a user did not specify it. * *

The default value of this flag is {@value #DEFAULT_DEFAULT_MAX_RESPONSE_LENGTH}. Specify the * {@code -Dcom.linecorp.armeria.defaultMaxResponseLength=} to override the default value. * {@code 0} disables the length limit. */ public static long defaultMaxResponseLength() { return DEFAULT_MAX_RESPONSE_LENGTH; } /** * Returns the default server-side timeout of a request in milliseconds. Note that this value has effect * only if a user did not specify it. * *

The default value of this flag is {@value #DEFAULT_DEFAULT_REQUEST_TIMEOUT_MILLIS}. * Specify the {@code -Dcom.linecorp.armeria.defaultRequestTimeoutMillis=} to override * the default value. {@code 0} disables the timeout. */ public static long defaultRequestTimeoutMillis() { return DEFAULT_REQUEST_TIMEOUT_MILLIS; } /** * Returns the default client-side timeout of a response in milliseconds. Note that this value has effect * only if a user did not specify it. * *

The default value of this flag is {@value #DEFAULT_DEFAULT_RESPONSE_TIMEOUT_MILLIS}. * Specify the {@code -Dcom.linecorp.armeria.defaultResponseTimeoutMillis=} to override * the default value. {@code 0} disables the timeout. */ public static long defaultResponseTimeoutMillis() { return DEFAULT_RESPONSE_TIMEOUT_MILLIS; } /** * Returns the default client-side timeout of a socket connection attempt in milliseconds. * Note that this value has effect only if a user did not specify it. * *

The default value of this flag is {@value #DEFAULT_DEFAULT_CONNECT_TIMEOUT_MILLIS}. Specify the * {@code -Dcom.linecorp.armeria.defaultConnectTimeoutMillis=} JVM option to override * the default value. */ public static long defaultConnectTimeoutMillis() { return DEFAULT_CONNECT_TIMEOUT_MILLIS; } /** * Returns the default server-side idle timeout of a connection for keep-alive in milliseconds. * Note that this value has effect only if a user did not specify it. * *

The default value of this flag is {@value #DEFAULT_DEFAULT_SERVER_IDLE_TIMEOUT_MILLIS}. Specify the * {@code -Dcom.linecorp.armeria.defaultServerIdleTimeoutMillis=} JVM option to override * the default value. */ public static long defaultServerIdleTimeoutMillis() { return DEFAULT_SERVER_IDLE_TIMEOUT_MILLIS; } /** * Returns the default client-side idle timeout of a connection for keep-alive in milliseconds. * Note that this value has effect only if a user did not specify it. * *

This default value of this flag is {@value #DEFAULT_DEFAULT_CLIENT_IDLE_TIMEOUT_MILLIS}. Specify the * {@code -Dcom.linecorp.armeria.defaultClientIdleTimeoutMillis=} JVM option to override * the default value. */ public static long defaultClientIdleTimeoutMillis() { return DEFAULT_CLIENT_IDLE_TIMEOUT_MILLIS; } /** * Returns the default maximum length of an HTTP/1 response initial line. * Note that this value has effect only if a user did not specify it. * *

This default value of this flag is {@value #DEFAULT_DEFAULT_MAX_HTTP1_INITIAL_LINE_LENGTH}. * Specify the {@code -Dcom.linecorp.armeria.defaultMaxHttp1InitialLineLength=} JVM option * to override the default value. */ public static int defaultMaxHttp1InitialLineLength() { return DEFAULT_MAX_HTTP1_INITIAL_LINE_LENGTH; } /** * Returns the default maximum length of all headers in an HTTP/1 response. * Note that this value has effect only if a user did not specify it. * *

This default value of this flag is {@value #DEFAULT_DEFAULT_MAX_HTTP1_HEADER_SIZE}. * Specify the {@code -Dcom.linecorp.armeria.defaultMaxHttp1HeaderSize=} JVM option * to override the default value. */ public static int defaultMaxHttp1HeaderSize() { return DEFAULT_MAX_HTTP1_HEADER_SIZE; } /** * Returns the default maximum length of each chunk in an HTTP/1 response content. * The content or a chunk longer than this value will be split into smaller chunks * so that their lengths never exceed it. * Note that this value has effect only if a user did not specify it. * *

This default value of this flag is {@value #DEFAULT_DEFAULT_MAX_HTTP1_CHUNK_SIZE}. * Specify theb {@code -Dcom.linecorp.armeria.defaultMaxHttp1ChunkSize=} JVM option * to override the default value. */ public static int defaultMaxHttp1ChunkSize() { return DEFAULT_MAX_HTTP1_CHUNK_SIZE; } /** * Returns the default value of the {@link ClientFactoryBuilder#useHttp2Preface(boolean)} option. * Note that this value has effect only if a user did not specify it. * *

This flag is enabled by default. Specify the * {@code -Dcom.linecorp.armeria.defaultUseHttp2Preface=false} JVM option to disable it. */ public static boolean defaultUseHttp2Preface() { return DEFAULT_USE_HTTP2_PREFACE; } /** * Returns the default value of the {@link ClientFactoryBuilder#useHttp1Pipelining(boolean)} option. * Note that this value has effect only if a user did not specify it. * *

This flag is disabled by default. Specify the * {@code -Dcom.linecorp.armeria.defaultUseHttp1Pipelining=true} JVM option to enable it. */ public static boolean defaultUseHttp1Pipelining() { return DEFAULT_USE_HTTP1_PIPELINING; } /** * Returns the default value of the {@code backoffSpec} parameter when instantiating a {@link Backoff} * using {@link Backoff#of(String)}. Note that this value has effect only if a user did not specify the * {@code defaultBackoffSpec} in the constructor call. * *

The default value of this flag is {@value DEFAULT_DEFAULT_BACKOFF_SPEC}. Specify the * {@code -Dcom.linecorp.armeria.defaultBackoffSpec=} JVM option to override the default value. */ public static String defaultBackoffSpec() { return DEFAULT_BACKOFF_SPEC; } /** * Returns the default maximum number of total attempts. Note that this value has effect only if a user * did not specify it when creating a {@link RetryingHttpClient} or a {@link RetryingRpcClient}. * *

The default value of this flag is {@value #DEFAULT_DEFAULT_MAX_TOTAL_ATTEMPTS}. Specify the * {@code -Dcom.linecorp.armeria.defaultMaxTotalAttempts=} JVM option to * override the default value. */ public static int defaultMaxTotalAttempts() { return DEFAULT_MAX_TOTAL_ATTEMPTS; } /** * Returns the value of the {@code routeCache} parameter. It would be used to create a Caffeine * {@link Cache} instance using {@link Caffeine#from(String)} for routing a request. The {@link Cache} * would hold the mappings of {@link PathMappingContext} and the designated {@link ServiceConfig} * for a request to improve server performance. * *

The default value of this flag is {@value DEFAULT_ROUTE_CACHE_SPEC}. Specify the * {@code -Dcom.linecorp.armeria.routeCache=} JVM option to override the default value. * Also, specify {@code -Dcom.linecorp.armeria.routeCache=off} JVM option to disable it. */ public static Optional routeCacheSpec() { return ROUTE_CACHE_SPEC; } /** * Returns the value of the {@code parsedPathCache} parameter. It would be used to create a Caffeine * {@link Cache} instance using {@link Caffeine#from(String)} mapping raw HTTP paths to parsed pair of * path and query, after validation. * *

The default value of this flag is {@value DEFAULT_PARSED_PATH_CACHE_SPEC}. Specify the * {@code -Dcom.linecorp.armeria.parsedPathCache=} JVM option to override the default value. * Also, specify {@code -Dcom.linecorp.armeria.parsedPathCache=off} JVM option to disable it. */ public static Optional parsedPathCacheSpec() { return PARSED_PATH_CACHE_SPEC; } /** * Returns the value of the {@code headerValueCache} parameter. It would be used to create a Caffeine * {@link Cache} instance using {@link Caffeine#from(String)} mapping raw HTTP ascii header values to * {@link String}. * *

The default value of this flag is {@value DEFAULT_HEADER_VALUE_CACHE_SPEC}. Specify the * {@code -Dcom.linecorp.armeria.headerValueCache=} JVM option to override the default value. * Also, specify {@code -Dcom.linecorp.armeria.headerValueCache=off} JVM option to disable it. */ public static Optional headerValueCacheSpec() { return HEADER_VALUE_CACHE_SPEC; } /** * Returns the value of the {@code cachedHeaders} parameter which contains a comma-separated list of * headers whose values are cached using {@code headerValueCache}. * *

The default value of this flag is {@value DEFAULT_CACHED_HEADERS}. Specify the * {@code -Dcom.linecorp.armeria.cachedHeaders=} JVM option to override the default value. */ public static List cachedHeaders() { return CACHED_HEADERS; } /** * Returns the value of the {@code compositeServiceCache} parameter. It would be used to create a * Caffeine {@link Cache} instance using {@link Caffeine#from(String)} for routing a request. * The {@link Cache} would hold the mappings of {@link PathMappingContext} and the designated * {@link ServiceConfig} for a request to improve server performance. * *

The default value of this flag is {@value DEFAULT_COMPOSITE_SERVICE_CACHE_SPEC}. Specify the * {@code -Dcom.linecorp.armeria.compositeServiceCache=} JVM option to override the default value. * Also, specify {@code -Dcom.linecorp.armeria.compositeServiceCache=off} JVM option to disable it. */ public static Optional compositeServiceCacheSpec() { return COMPOSITE_SERVICE_CACHE_SPEC; } private static Optional caffeineSpec(String name, String defaultValue) { final String spec = get(name, defaultValue, value -> { try { if (!"off".equals(value)) { CaffeineSpec.parse(value); } return true; } catch (Exception e) { return false; } }); return "off".equals(spec) ? Optional.empty() : Optional.of(spec); } private static boolean getBoolean(String name, boolean defaultValue) { return getBoolean(name, defaultValue, value -> true); } private static boolean getBoolean(String name, boolean defaultValue, Predicate validator) { return "true".equals(getNormalized(name, String.valueOf(defaultValue), value -> { if ("true".equals(value)) { return validator.test(true); } if ("false".equals(value)) { return validator.test(false); } return false; })); } private static int getInt(String name, int defaultValue, IntPredicate validator) { return Integer.parseInt(getNormalized(name, String.valueOf(defaultValue), value -> { try { return validator.test(Integer.parseInt(value)); } catch (Exception e) { // null or non-integer return false; } })); } private static long getLong(String name, long defaultValue, LongPredicate validator) { return Long.parseLong(getNormalized(name, String.valueOf(defaultValue), value -> { try { return validator.test(Long.parseLong(value)); } catch (Exception e) { // null or non-integer return false; } })); } private static String get(String name, String defaultValue, Predicate validator) { final String fullName = PREFIX + name; final String value = System.getProperty(fullName); if (value == null) { logger.info("{}: {} (default)", fullName, defaultValue); return defaultValue; } if (validator.test(value)) { logger.info("{}: {}", fullName, value); return value; } logger.info("{}: {} (default instead of: {})", fullName, defaultValue, value); return defaultValue; } private static String getNormalized(String name, String defaultValue, Predicate validator) { final String fullName = PREFIX + name; final String value = getLowerCased(fullName); if (value == null) { logger.info("{}: {} (default)", fullName, defaultValue); return defaultValue; } if (validator.test(value)) { logger.info("{}: {}", fullName, value); return value; } logger.info("{}: {} (default instead of: {})", fullName, defaultValue, value); return defaultValue; } @Nullable private static String getLowerCased(String fullName) { String value = System.getProperty(fullName); if (value != null) { value = Ascii.toLowerCase(value); } return value; } private Flags() {} }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy