ConnectionStringProperties.java
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.core.amqp.implementation;
import com.azure.core.util.CoreUtils;
import com.azure.core.util.logging.ClientLogger;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Locale;
import java.util.Objects;
/**
* The set of properties that comprise a connection string from the Azure portal.
*/
public class ConnectionStringProperties {
private final ClientLogger logger = new ClientLogger(ConnectionStringProperties.class);
private static final String TOKEN_VALUE_SEPARATOR = "=";
private static final String ENDPOINT_SCHEME_SB_PREFIX = "sb://";
private static final String ENDPOINT_SCHEME_HTTP_PREFIX = "http://";
private static final String ENDPOINT_SCHEME_HTTPS_PREFIX = "https://";
private static final String TOKEN_VALUE_PAIR_DELIMITER = ";";
private static final String ENDPOINT = "Endpoint";
private static final String SHARED_ACCESS_KEY_NAME = "SharedAccessKeyName";
private static final String SHARED_ACCESS_KEY = "SharedAccessKey";
private static final String SHARED_ACCESS_SIGNATURE = "SharedAccessSignature";
private static final String SAS_VALUE_PREFIX = "sharedaccesssignature ";
private static final String ENTITY_PATH = "EntityPath";
private static final String CONNECTION_STRING_WITH_ACCESS_KEY = "Endpoint={endpoint};"
+ "SharedAccessKeyName={sharedAccessKeyName};SharedAccessKey={sharedAccessKey};EntityPath={entityPath}";
private static final String CONNECTION_STRING_WITH_SAS = "Endpoint={endpoint};SharedAccessSignature="
+ "SharedAccessSignature {sharedAccessSignature};EntityPath={entityPath}";
private static final String ERROR_MESSAGE_FORMAT = "Could not parse 'connectionString'. Expected format: "
+ CONNECTION_STRING_WITH_ACCESS_KEY + " or " + CONNECTION_STRING_WITH_SAS + ". Actual: %s";
private static final String ERROR_MESSAGE_ENDPOINT_FORMAT = "'Endpoint' must be provided in 'connectionString'."
+ " Actual: %s";
private final URI endpoint;
private final String entityPath;
private final String sharedAccessKeyName;
private final String sharedAccessKey;
private final String sharedAccessSignature;
/**
* Creates a new instance by parsing the {@code connectionString} into its components.
* @param connectionString The connection string to the Event Hub instance.
*
* @throws NullPointerException if {@code connectionString} is null.
* @throws IllegalArgumentException if {@code connectionString} is an empty string or the connection string has
* an invalid format.
*/
public ConnectionStringProperties(String connectionString) {
Objects.requireNonNull(connectionString, "'connectionString' cannot be null.");
if (connectionString.isEmpty()) {
throw new IllegalArgumentException("'connectionString' cannot be an empty string.");
}
final String[] tokenValuePairs = connectionString.split(TOKEN_VALUE_PAIR_DELIMITER);
URI endpoint = null;
String entityPath = null;
String sharedAccessKeyName = null;
String sharedAccessKeyValue = null;
String sharedAccessSignature = null;
for (String tokenValuePair : tokenValuePairs) {
final String[] pair = tokenValuePair.split(TOKEN_VALUE_SEPARATOR, 2);
if (pair.length != 2) {
throw new IllegalArgumentException(String.format(
Locale.US,
"Connection string has invalid key value pair: %s",
tokenValuePair));
}
final String key = pair[0].trim();
final String value = pair[1].trim();
if (key.equalsIgnoreCase(ENDPOINT)) {
final String endpointUri = validateAndUpdateDefaultScheme(value, connectionString);
try {
endpoint = new URI(endpointUri);
} catch (URISyntaxException e) {
throw new IllegalArgumentException(
String.format(Locale.US, "Invalid endpoint: %s", tokenValuePair), e);
}
} else if (key.equalsIgnoreCase(SHARED_ACCESS_KEY_NAME)) {
sharedAccessKeyName = value;
} else if (key.equalsIgnoreCase(SHARED_ACCESS_KEY)) {
sharedAccessKeyValue = value;
} else if (key.equalsIgnoreCase(ENTITY_PATH)) {
entityPath = value;
} else if (key.equalsIgnoreCase(SHARED_ACCESS_SIGNATURE)
&& value.toLowerCase(Locale.ROOT).startsWith(SAS_VALUE_PREFIX)) {
sharedAccessSignature = value;
} else {
throw new IllegalArgumentException(
String.format(Locale.US, "Illegal connection string parameter name: %s", key));
}
}
// connection string should have an endpoint and either shared access signature or shared access key and value
boolean includesSharedKey = sharedAccessKeyName != null || sharedAccessKeyValue != null;
boolean hasSharedKeyAndValue = sharedAccessKeyName != null && sharedAccessKeyValue != null;
boolean includesSharedAccessSignature = sharedAccessSignature != null;
if (endpoint == null
|| (includesSharedKey && includesSharedAccessSignature) // includes both SAS and key or value
|| (!hasSharedKeyAndValue && !includesSharedAccessSignature)) { // invalid key, value and SAS
throw new IllegalArgumentException(String.format(Locale.US, ERROR_MESSAGE_FORMAT, connectionString));
}
this.endpoint = endpoint;
this.entityPath = entityPath;
this.sharedAccessKeyName = sharedAccessKeyName;
this.sharedAccessKey = sharedAccessKeyValue;
this.sharedAccessSignature = sharedAccessSignature;
}
/**
* Gets the endpoint to be used for connecting to the AMQP message broker.
* @return The endpoint address, including protocol, from the connection string.
*/
public URI getEndpoint() {
return endpoint;
}
/**
* Gets the entity path to connect to in the message broker.
* @return The entity path to connect to in the message broker.
*/
public String getEntityPath() {
return entityPath;
}
/**
* Gets the name of the shared access key, either for the Event Hubs namespace or the Event Hub instance.
* @return The name of the shared access key.
*/
public String getSharedAccessKeyName() {
return sharedAccessKeyName;
}
/**
* The value of the shared access key, either for the Event Hubs namespace or the Event Hub.
* @return The value of the shared access key.
*/
public String getSharedAccessKey() {
return sharedAccessKey;
}
/**
* The value of the shared access signature, if the connection string used to create this instance included the
* shared access signature component.
* @return The shared access signature value, if included in the connection string.
*/
public String getSharedAccessSignature() {
return sharedAccessSignature;
}
/*
* The function checks for pre existing scheme of "sb://" , "http://" or "https://". If the scheme is not provided
* in endpoint, it will set the default scheme to "sb://".
*/
private String validateAndUpdateDefaultScheme(final String endpoint, final String connectionString) {
String updatedEndpoint = endpoint.trim();
if (CoreUtils.isNullOrEmpty(endpoint)) {
throw logger.logExceptionAsError(new IllegalArgumentException(String.format(Locale.US,
ERROR_MESSAGE_ENDPOINT_FORMAT, connectionString)));
}
final String endpointLowerCase = endpoint.toLowerCase(Locale.getDefault());
if (!endpointLowerCase.startsWith(ENDPOINT_SCHEME_SB_PREFIX)
&& !endpointLowerCase.startsWith(ENDPOINT_SCHEME_HTTP_PREFIX)
&& !endpointLowerCase.startsWith(ENDPOINT_SCHEME_HTTPS_PREFIX)) {
updatedEndpoint = ENDPOINT_SCHEME_SB_PREFIX + endpoint;
}
return updatedEndpoint;
}
}