com.azure.core.util.Configuration Maven / Gradle / Ivy
Show all versions of azure-core Show documentation
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.core.util;
import com.azure.core.http.HttpClient;
import com.azure.core.http.HttpClientProvider;
import com.azure.core.implementation.util.EnvironmentConfiguration;
import com.azure.core.util.logging.ClientLogger;
import com.azure.core.util.metrics.Meter;
import com.azure.core.util.metrics.MeterProvider;
import com.azure.core.util.tracing.Tracer;
import com.azure.core.util.tracing.TracerProvider;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
/**
* Contains configuration information that is used during construction of client libraries.
*
*
*
* Configuration configuration = new ConfigurationBuilder(new SampleSource(properties))
* .root("azure.sdk")
* .buildSection("client-name");
*
* ConfigurationProperty<String> proxyHostnameProperty = ConfigurationPropertyBuilder.ofString("http.proxy.hostname")
* .shared(true)
* .build();
* System.out.println(configuration.get(proxyHostnameProperty));
*
*
*/
public class Configuration implements Cloneable {
// Default properties - these are what we read from the environment
/**
* URL of the proxy for HTTP connections.
*/
public static final String PROPERTY_HTTP_PROXY = "HTTP_PROXY";
/**
* URL of the proxy for HTTPS connections.
*/
public static final String PROPERTY_HTTPS_PROXY = "HTTPS_PROXY";
/**
* Endpoint to connect to when using Azure Active Directory managed service identity (MSI).
*/
public static final String PROPERTY_IDENTITY_ENDPOINT = "IDENTITY_ENDPOINT";
/**
* Header when connecting to Azure Active Directory using managed service identity (MSI).
*/
public static final String PROPERTY_IDENTITY_HEADER = "IDENTITY_HEADER";
/**
* A list of hosts or CIDR to not use proxy HTTP/HTTPS connections through.
*/
public static final String PROPERTY_NO_PROXY = "NO_PROXY";
/**
* Endpoint to connect to when using Azure Active Directory managed service identity (MSI).
*/
public static final String PROPERTY_MSI_ENDPOINT = "MSI_ENDPOINT";
/**
* Secret when connecting to Azure Active Directory using managed service identity (MSI).
*/
public static final String PROPERTY_MSI_SECRET = "MSI_SECRET";
/**
* Subscription id to use when connecting to Azure resources.
*/
public static final String PROPERTY_AZURE_SUBSCRIPTION_ID = "AZURE_SUBSCRIPTION_ID";
/**
* Username to use when performing username/password authentication with Azure.
*/
public static final String PROPERTY_AZURE_USERNAME = "AZURE_USERNAME";
/**
* Username to use when performing username/password authentication with Azure.
*/
public static final String PROPERTY_AZURE_PASSWORD = "AZURE_PASSWORD";
/**
* Client id to use when performing service principal authentication with Azure.
*/
public static final String PROPERTY_AZURE_CLIENT_ID = "AZURE_CLIENT_ID";
/**
* Client secret to use when performing service principal authentication with Azure.
*/
public static final String PROPERTY_AZURE_CLIENT_SECRET = "AZURE_CLIENT_SECRET";
/**
* Tenant id for the Azure resources.
*/
public static final String PROPERTY_AZURE_TENANT_ID = "AZURE_TENANT_ID";
/**
* Path of a PFX/PEM certificate file to use when performing service principal authentication with Azure.
*/
public static final String PROPERTY_AZURE_CLIENT_CERTIFICATE_PATH = "AZURE_CLIENT_CERTIFICATE_PATH";
/**
* Password for a PFX/PEM certificate used when performing service principal authentication with Azure.
*/
public static final String PROPERTY_AZURE_CLIENT_CERTIFICATE_PASSWORD = "AZURE_CLIENT_CERTIFICATE_PASSWORD";
/**
* Flag to disable the CP1 client capabilities in Azure Identity Token credentials.
*/
public static final String PROPERTY_AZURE_IDENTITY_DISABLE_CP1 = "AZURE_IDENTITY_DISABLE_CP1";
/**
* URL used by Bridge To Kubernetes to redirect IMDS calls in the development environment.
*/
public static final String PROPERTY_AZURE_POD_IDENTITY_TOKEN_URL = "AZURE_POD_IDENTITY_TOKEN_URL";
/**
* Name of Azure AAD regional authority.
*/
public static final String PROPERTY_AZURE_REGIONAL_AUTHORITY_NAME = "AZURE_REGIONAL_AUTHORITY_NAME";
/**
* Name of the Azure resource group.
*/
public static final String PROPERTY_AZURE_RESOURCE_GROUP = "AZURE_RESOURCE_GROUP";
/**
* Name of the Azure cloud to connect to.
*/
public static final String PROPERTY_AZURE_CLOUD = "AZURE_CLOUD";
/**
* The Azure Active Directory endpoint to connect to.
*/
public static final String PROPERTY_AZURE_AUTHORITY_HOST = "AZURE_AUTHORITY_HOST";
/**
* Disables telemetry collection.
*/
public static final String PROPERTY_AZURE_TELEMETRY_DISABLED = "AZURE_TELEMETRY_DISABLED";
/**
* Enables logging by setting a log level.
*/
public static final String PROPERTY_AZURE_LOG_LEVEL = "AZURE_LOG_LEVEL";
/**
* Enables HTTP request/response logging by setting an HTTP log detail level.
*/
public static final String PROPERTY_AZURE_HTTP_LOG_DETAIL_LEVEL = "AZURE_HTTP_LOG_DETAIL_LEVEL";
/**
* Disables tracing.
*/
public static final String PROPERTY_AZURE_TRACING_DISABLED = "AZURE_TRACING_DISABLED";
/**
* Sets the name of the {@link TracerProvider} implementation that should be used to construct instances of
* {@link Tracer}.
*
* The name must be the full class name, e.g. {@code com.azure.core.tracing.opentelemetry.OpenTelemetryTracerProvider} and not
* {@code OpenTelemetryTracerProvider}.
*
* If the value isn't set or is an empty string the first {@link TracerProvider} resolved by {@link java.util.ServiceLoader} will be
* used to create an instance of {@link Tracer}. If the value is set and doesn't match any
* {@link TracerProvider} resolved by {@link java.util.ServiceLoader} an {@link IllegalStateException} will be thrown when
* attempting to create an instance of {@link TracerProvider}.
*/
public static final String PROPERTY_AZURE_TRACING_IMPLEMENTATION = "AZURE_TRACING_IMPLEMENTATION";
/**
* Disables metrics.
*/
public static final String PROPERTY_AZURE_METRICS_DISABLED = "AZURE_METRICS_DISABLED";
/**
* Sets the name of the {@link MeterProvider} implementation that should be used to construct instances of
* {@link Meter}.
*
* The name must be the full class name, e.g. {@code com.azure.core.tracing.opentelemetry.OpenTelemetryMeterProvider} and not
* {@code OpenTelemetryMeterProvider}.
*
* If the value isn't set or is an empty string the first {@link MeterProvider} resolved by {@link java.util.ServiceLoader} will be
* used to create an instance of {@link Meter}. If the value is set and doesn't match any
* {@link MeterProvider} resolved by {@link java.util.ServiceLoader} an {@link IllegalStateException} will be thrown when
* attempting to create an instance of {@link MeterProvider}.
*/
public static final String PROPERTY_AZURE_METRICS_IMPLEMENTATION = "AZURE_METRICS_IMPLEMENTATION";
/**
* Sets the default number of times a request will be retried, if it passes the conditions for retrying, before it
* fails.
*/
public static final String PROPERTY_AZURE_REQUEST_RETRY_COUNT = "AZURE_REQUEST_RETRY_COUNT";
/**
* Sets the default timeout, in milliseconds, for a request to connect to the remote host.
*
* If the configured value is equal to or less than 0 no timeout will be applied.
*/
public static final String PROPERTY_AZURE_REQUEST_CONNECT_TIMEOUT = "AZURE_REQUEST_CONNECT_TIMEOUT";
/**
* Sets the default timeout interval, in milliseconds, allowed between each byte written by a request.
*
* If the configured value is equal to or less than 0 no timeout will be applied.
*/
public static final String PROPERTY_AZURE_REQUEST_WRITE_TIMEOUT = "AZURE_REQUEST_WRITE_TIMEOUT";
/**
* Sets the default timeout, in milliseconds, for a request to receive a response from the remote host.
*
* If the configured value is equal to or less than 0 no timeout will be applied.
*/
public static final String PROPERTY_AZURE_REQUEST_RESPONSE_TIMEOUT = "AZURE_REQUEST_RESPONSE_TIMEOUT";
/**
* Sets the default timeout interval, in milliseconds, allowed between each byte read in a response.
*
* If the configured value is equal to or less than 0 no timeout will be applied.
*/
public static final String PROPERTY_AZURE_REQUEST_READ_TIMEOUT = "AZURE_REQUEST_READ_TIMEOUT";
/**
* Sets the name of the {@link HttpClientProvider} implementation that should be used to construct instances of
* {@link HttpClient}.
*
* The name must be the full class name, ex {@code com.azure.core.http.netty.NettyAsyncHttpClientProvider} and not
* {@code NettyAsyncHttpClientProvider}, to disambiguate multiple providers with the same name but from different
* packages.
*
* If the value isn't set or is an empty string the first {@link HttpClientProvider} resolved by {@link java.util.ServiceLoader} will be
* used to create an instance of {@link HttpClient}. If the value is set and doesn't match any
* {@link HttpClientProvider} resolved by {@link java.util.ServiceLoader} an {@link IllegalStateException} will be thrown when
* attempting to create an instance of {@link HttpClient}.
*/
public static final String PROPERTY_AZURE_HTTP_CLIENT_IMPLEMENTATION = "AZURE_HTTP_CLIENT_IMPLEMENTATION";
/*
* Gets the global configuration shared by all client libraries.
*/
private static final Configuration GLOBAL_CONFIGURATION = new Configuration();
/**
* No-op {@link Configuration} object used to opt out of using global configurations when constructing client
* libraries.
*/
@SuppressWarnings("StaticInitializerReferencesSubClass")
public static final Configuration NONE = new NoopConfiguration();
private static final ClientLogger LOGGER = new ClientLogger(Configuration.class);
private final EnvironmentConfiguration environmentConfiguration;
private final Map configurations;
private final String path;
private final Configuration sharedConfiguration;
private final boolean isEmpty;
/**
* Constructs a configuration containing the known Azure properties constants.
*
* @deprecated Use {@link ConfigurationBuilder} and {@link ConfigurationSource} that allow to provide all properties
* before creating configuration and keep it immutable.
*/
@Deprecated
public Configuration() {
this(Collections.emptyMap(), EnvironmentConfiguration.getGlobalConfiguration(), null, null);
}
/**
* Constructs a configuration containing the known Azure properties constants. Use {@link ConfigurationBuilder} to
* create instance of {@link Configuration}.
*
* @param configurationSource Configuration property source.
* @param environmentConfiguration instance of {@link EnvironmentConfiguration} to mock environment for testing.
* @param path Absolute path of current configuration section for logging and diagnostics purposes.
* @param sharedConfiguration Instance of shared {@link Configuration} section to retrieve shared properties.
*/
Configuration(ConfigurationSource configurationSource, EnvironmentConfiguration environmentConfiguration,
String path, Configuration sharedConfiguration) {
this(readConfigurations(configurationSource, path), environmentConfiguration, path, sharedConfiguration);
}
/**
* Constructs a configuration containing the known Azure properties constants. Use {@link ConfigurationBuilder} to
* create instance of {@link Configuration}.
*
* @param configurations map of all properties.
* @param environmentConfiguration instance of {@link EnvironmentConfiguration} to mock environment for testing.
* @param path Absolute path of current configuration section for logging and diagnostics purposes.
* @param sharedConfiguration Instance of shared {@link Configuration} section to retrieve shared properties.
*/
private Configuration(Map configurations, EnvironmentConfiguration environmentConfiguration,
String path, Configuration sharedConfiguration) {
this.configurations = configurations;
this.isEmpty = configurations.isEmpty();
this.environmentConfiguration = Objects.requireNonNull(environmentConfiguration,
"'environmentConfiguration' cannot be null");
this.path = path;
this.sharedConfiguration = sharedConfiguration;
}
/**
* Gets the global configuration store shared by all client libraries.
*
* @return The global configuration store.
*/
public static Configuration getGlobalConfiguration() {
return GLOBAL_CONFIGURATION;
}
/**
* Gets the value of system property or environment variable. Use {@link Configuration#get(ConfigurationProperty)}
* overload to get explicit configuration or environment configuration from specific source.
*
*
* This method first checks the values previously loaded from the environment, if the configuration is found there
* it will be returned. Otherwise, this will attempt to load the value from the environment.
*
* @param name Name of the configuration.
* @return Value of the configuration if found, otherwise null.
*/
public String get(String name) {
return environmentConfiguration.get(name);
}
/**
* Gets the value of system property or environment variable converted to given primitive {@code T} using
* corresponding {@code parse} method on this type.
*
* Use {@link Configuration#get(ConfigurationProperty)} overload to get explicit configuration or environment
* configuration from specific source.
*
*
* This method first checks the values previously loaded from the environment, if the configuration is found there
* it will be returned. Otherwise, this will attempt to load the value from the environment.
*
* If no configuration is found, the {@code defaultValue} is returned.
*
*
Following types are supported:
*
* - {@link Byte}
* - {@link Short}
* - {@link Integer}
* - {@link Long}
* - {@link Float}
* - {@link Double}
* - {@link Boolean}
*
*
* @param name Name of the configuration.
* @param defaultValue Value to return if the configuration isn't found.
* @param Type that the configuration is converted to if found.
* @return The converted configuration if found, otherwise the default value is returned.
*/
public T get(String name, T defaultValue) {
return convertToPrimitiveOrDefault(get(name), defaultValue);
}
/**
* Gets the value of system property or environment variable and converts it with the {@code converter}.
*
* This method first checks the values previously loaded from the environment, if the configuration is found there
* it will be returned. Otherwise, this will attempt to load the value from the environment.
*
* If no configuration is found the {@code converter} won't be called and null will be returned.
*
* @param name Name of the configuration.
* @param converter Converter used to map the configuration to {@code T}.
* @param Type that the configuration is converted to if found.
* @return The converted configuration if found, otherwise null.
*/
public T get(String name, Function converter) {
Objects.requireNonNull(converter, "'converter' can't be null");
return converter.apply(get(name));
}
/**
* Adds a configuration with the given value.
*
* This will overwrite the previous configuration value if it existed.
*
* @param name Name of the configuration.
* @param value Value of the configuration.
* @return The updated Configuration object.
* @deprecated Use {@link ConfigurationBuilder} and {@link ConfigurationSource} to provide all properties before
* creating configuration.
*/
@Deprecated
public Configuration put(String name, String value) {
environmentConfiguration.put(name, value);
return this;
}
/**
* Removes the configuration.
*
* This returns the value of the configuration if it previously existed.
*
* @param name Name of the configuration.
* @return The configuration if it previously existed, otherwise null.
* @deprecated Use {@link ConfigurationBuilder} and {@link ConfigurationSource} to provide all properties before
* creating configuration.
*/
@Deprecated
public String remove(String name) {
return environmentConfiguration.remove(name);
}
/**
* Determines if the system property or environment variable is defined.
*
* Use {@link Configuration#contains(ConfigurationProperty)} overload to get explicit configuration or environment
* configuration from specific source.
*
*
* This only checks against values previously loaded into the Configuration object, this won't inspect the
* environment for containing the value.
*
* @param name Name of the configuration.
* @return True if the configuration exists, otherwise false.
*/
public boolean contains(String name) {
return get(name) != null;
}
/**
* Clones this Configuration object.
*
* @return A clone of the Configuration object.
* @deprecated Use {@link ConfigurationBuilder} and {@link ConfigurationSource} to create configuration.
*/
@SuppressWarnings("CloneDoesntCallSuperClone")
@Deprecated
public Configuration clone() {
return new Configuration(configurations, new EnvironmentConfiguration(environmentConfiguration), path,
sharedConfiguration);
}
/**
* Checks if configuration contains the property. If property can be shared between clients, checks this
* {@code Configuration} and falls back to shared section. If property has aliases, system property or environment
* variable defined, checks them as well.
*
* Value is not validated.
*
* @param property instance.
* @return true if property is available, false otherwise.
*/
public boolean contains(ConfigurationProperty> property) {
Objects.requireNonNull(property, "'property' can't be null");
return getWithFallback(property) != null;
}
/**
* Gets property value from all available sources in the following order:
*
*
* - Explicit configuration from given {@link ConfigurationSource} by property name
* - Explicit configuration by property aliases in the order they were provided in {@link ConfigurationProperty}
* - Explicit configuration by property name in the shared section (if {@link ConfigurationProperty} is shared)
* - Explicit configuration by property aliases in the shared section (if {@link ConfigurationProperty} is shared)
* - System property (if set)
* - Environment variable (if set)
*
*
*
* Property value is converted to specified type. If property value is missing and not required, default value is returned.
*
*
*
* ConfigurationProperty<String> property = ConfigurationPropertyBuilder.ofString("http.proxy.hostname")
* .shared(true)
* .logValue(true)
* .systemPropertyName("http.proxyHost")
* .build();
*
* // attempts to get local `azure.sdk.<client-name>.http.proxy.host` property and falls back to
* // shared azure.sdk.http.proxy.port
* System.out.println(configuration.get(property));
*
*
*
* @param property instance.
* @param Type that the configuration is converted to if found.
* @return The value of the property if it exists, otherwise the default value of the property.
* @throws NullPointerException when property instance is null.
* @throws IllegalArgumentException when required property is missing.
* @throws RuntimeException when property value conversion (and validation) throws.
*/
public T get(ConfigurationProperty property) {
Objects.requireNonNull(property, "'property' cannot be null");
String value = getWithFallback(property);
if (value == null) {
if (property.isRequired()) {
throw LOGGER.atError()
.addKeyValue("name", property.getName())
.addKeyValue("path", path)
.log(new IllegalArgumentException("Missing required property."));
}
return property.getDefaultValue();
}
try {
return property.getConverter().apply(value);
} catch (RuntimeException ex) {
throw LOGGER.atError()
.addKeyValue("name", property.getName())
.addKeyValue("path", path)
.addKeyValue("value", property.getValueSanitizer().apply(value))
.log(ex);
}
}
private String getLocalProperty(String name, Iterable aliases, Function valueSanitizer) {
if (this.isEmpty) {
return null;
}
final String value = configurations.get(name);
if (value != null) {
LOGGER.atVerbose()
.addKeyValue("name", name)
.addKeyValue("path", path)
.addKeyValue("value", () -> valueSanitizer.apply(value))
.log("Got property value by name.");
return value;
}
for (String alias : aliases) {
final String valueByAlias = configurations.get(alias);
if (valueByAlias != null) {
LOGGER.atVerbose()
.addKeyValue("name", name)
.addKeyValue("path", path)
.addKeyValue("alias", alias)
.addKeyValue("value", () -> valueSanitizer.apply(valueByAlias))
.log("Got property value by alias.");
return valueByAlias;
}
}
return null;
}
private String getWithFallback(ConfigurationProperty property) {
String name = property.getName();
if (!CoreUtils.isNullOrEmpty(name)) {
String value = getLocalProperty(name, property.getAliases(), property.getValueSanitizer());
if (value != null) {
return value;
}
if (property.isShared() && sharedConfiguration != null) {
value = sharedConfiguration.getLocalProperty(name, property.getAliases(), property.getValueSanitizer());
if (value != null) {
return value;
}
}
}
return getFromEnvironment(property);
}
private String getFromEnvironment(ConfigurationProperty property) {
String systemProperty = property.getSystemPropertyName();
if (systemProperty != null) {
final String value = environmentConfiguration.getSystemProperty(systemProperty);
if (value != null) {
LOGGER.atVerbose()
.addKeyValue("name", property.getName())
.addKeyValue("systemProperty", systemProperty)
.addKeyValue("value", () -> property.getValueSanitizer().apply(value))
.log("Got property from system property.");
return value;
}
}
String envVar = property.getEnvironmentVariableName();
if (envVar != null) {
final String value = environmentConfiguration.getEnvironmentVariable(envVar);
if (value != null) {
LOGGER.atVerbose()
.addKeyValue("name", property.getName())
.addKeyValue("envVar", envVar)
.addKeyValue("value", () -> property.getValueSanitizer().apply(value))
.log("Got property from environment variable.");
return value;
}
}
return null;
}
private static Map readConfigurations(ConfigurationSource source, String path) {
Objects.requireNonNull(source, "'source' cannot be null");
Map configs = source.getProperties(path);
if (configs == null || configs.isEmpty()) {
return Collections.emptyMap();
}
Map props = new HashMap<>();
for (Map.Entry prop : configs.entrySet()) {
String key = CoreUtils.isNullOrEmpty(path) ? prop.getKey() : prop.getKey().substring(path.length() + 1);
String value = prop.getValue();
LOGGER.atVerbose()
.addKeyValue("name", prop.getKey())
.log("Got property from configuration source.");
if (key != null && value != null) {
props.put(key, value);
} else {
LOGGER.atWarning()
.addKeyValue("name", prop.getKey())
.log("Key or value is null, property is ignored.");
}
}
return props;
}
/**
* Attempts to convert the configuration value to given primitive {@code T} using corresponding {@code parse} method
* on this type.
*
* Following types are supported:
*
* - {@link Byte}
* - {@link Short}
* - {@link Integer}
* - {@link Long}
* - {@link Float}
* - {@link Double}
* - {@link Boolean}
*
*
* If the value is null or empty then the default value is returned.
*
* @param value Configuration value retrieved from the map.
* @param defaultValue Default value to return if the configuration value is null or empty.
* @param Generic type that the value is converted to if not null or empty.
* @return The converted configuration, if null or empty the default value.
*/
@SuppressWarnings("unchecked")
private static T convertToPrimitiveOrDefault(String value, T defaultValue) {
// Value is null or empty, return the default.
if (CoreUtils.isNullOrEmpty(value)) {
return defaultValue;
}
// Check the default value's type to determine how it needs to be converted.
Object convertedValue;
if (defaultValue instanceof Byte) {
convertedValue = Byte.parseByte(value);
} else if (defaultValue instanceof Short) {
convertedValue = Short.parseShort(value);
} else if (defaultValue instanceof Integer) {
convertedValue = Integer.parseInt(value);
} else if (defaultValue instanceof Long) {
convertedValue = Long.parseLong(value);
} else if (defaultValue instanceof Float) {
convertedValue = Float.parseFloat(value);
} else if (defaultValue instanceof Double) {
convertedValue = Double.parseDouble(value);
} else if (defaultValue instanceof Boolean) {
convertedValue = Boolean.parseBoolean(value);
} else {
convertedValue = value;
}
return (T) convertedValue;
}
}