org.elasticsearch.common.util.FeatureFlag Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of elasticsearch Show documentation
Show all versions of elasticsearch Show documentation
Elasticsearch subproject :server
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
package org.elasticsearch.common.util;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.elasticsearch.Build;
import org.elasticsearch.core.Booleans;
import java.util.function.Function;
/**
* A utility class for registering feature flags in Elasticsearch code.
*
* Typical usage:
* {
* public static final FeatureFlag XYZZY_FEATURE_FLAG = new FeatureFlag("xyzzy");
* // ...
* if (XYZZY_FEATURE_FLAG.isEnabled()) {
* addRestHandlers();
* registerSettings();
* // etc
* }
* }
*
* The feature flag will be enabled automatically in all {@link Build#isSnapshot() snapshot} builds.
* The feature flag can be enabled in release builds by setting the system property "es.{name}_feature_flag_enabled" to {@code true}
* (e.g. {@code -Des.xyzzy_feature_flag_enabled=true}
*
*/
public class FeatureFlag {
private static final Logger logger = LogManager.getLogger(FeatureFlag.class);
private final String name;
private final boolean enabled;
private static final Function GET_SYSTEM_PROPERTY = System::getProperty;
public FeatureFlag(String name) {
this(name, "enabled", Build.current(), GET_SYSTEM_PROPERTY);
}
/**
* This method exists to register feature flags that use the old {@code {name}_feature_flag_registered} naming for their system
* property, instead of the preferred {@code {name}_feature_flag_enabled} name.
* It exists so that old style feature flag implementations can be converted to use this utility class without changing the name of
* their system property (which would affect consumers of the feature).
* @deprecated Use {@link FeatureFlag#FeatureFlag(String)} instead
*/
@Deprecated
public static FeatureFlag legacyRegisteredFlag(String name) {
return new FeatureFlag(name, "registered", Build.current(), GET_SYSTEM_PROPERTY);
}
/**
* Accessible for testing only
*/
FeatureFlag(String name, String suffix, Build build, Function getSystemProperty) {
this.name = name;
assert name.indexOf('.') == -1 : "Feature flag names may not contain a '.' character";
assert name.contains("feature_flag") == false : "Feature flag names may not contain the string 'feature_flag'";
final String propertyName = "es." + name + "_feature_flag_" + suffix;
if (build.isSnapshot()) {
enabled = parseSystemProperty(getSystemProperty, propertyName, true);
if (enabled == false) {
throw new IllegalArgumentException(
"Feature flag " + name + " (via system property '" + propertyName + "') cannot be disabled in snapshot builds"
);
}
logger.info("The current build is a snapshot, feature flag [{}] is enabled", name);
} else {
enabled = parseSystemProperty(getSystemProperty, propertyName, false);
logger.debug("The current build is a not snapshot, feature flag [{}] is {}", name, enabled ? "enabled" : "disabled");
}
}
private boolean parseSystemProperty(Function getProperty, String propertyName, boolean defaultValue) {
final String propertyValue = getProperty.apply(propertyName);
logger.trace("Feature flag system property [{}] is set to [{}]", propertyName, propertyValue);
try {
return Booleans.parseBoolean(propertyValue, defaultValue);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Invalid value [" + propertyValue + "] for system property [" + propertyName + "]", e);
}
}
public boolean isEnabled() {
return enabled;
}
@Override
public String toString() {
return "Feature-Flag(" + name + "=" + enabled + ")";
}
}