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

com.azure.core.util.Configuration Maven / Gradle / Ivy

There is a newer version: 1.54.1
Show newest version
// 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; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy