ServiceBusAdministrationClientBuilder.java
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.messaging.servicebus.administration;
import com.azure.core.amqp.implementation.ConnectionStringProperties;
import com.azure.core.annotation.ServiceClientBuilder;
import com.azure.core.credential.TokenCredential;
import com.azure.core.exception.AzureException;
import com.azure.core.http.HttpClient;
import com.azure.core.http.HttpPipeline;
import com.azure.core.http.HttpPipelineBuilder;
import com.azure.core.http.policy.AddHeadersFromContextPolicy;
import com.azure.core.http.policy.HttpLogDetailLevel;
import com.azure.core.http.policy.HttpLogOptions;
import com.azure.core.http.policy.HttpLoggingPolicy;
import com.azure.core.http.policy.HttpPipelinePolicy;
import com.azure.core.http.policy.HttpPolicyProviders;
import com.azure.core.http.policy.RetryPolicy;
import com.azure.core.http.policy.UserAgentPolicy;
import com.azure.core.util.ClientOptions;
import com.azure.core.util.Configuration;
import com.azure.core.util.CoreUtils;
import com.azure.core.util.logging.ClientLogger;
import com.azure.messaging.servicebus.ServiceBusServiceVersion;
import com.azure.messaging.servicebus.implementation.ServiceBusConstants;
import com.azure.messaging.servicebus.implementation.ServiceBusManagementClientImpl;
import com.azure.messaging.servicebus.implementation.ServiceBusManagementClientImplBuilder;
import com.azure.messaging.servicebus.implementation.ServiceBusManagementSerializer;
import com.azure.messaging.servicebus.implementation.ServiceBusSharedKeyCredential;
import com.azure.messaging.servicebus.implementation.ServiceBusTokenCredentialHttpPolicy;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* This class provides a fluent builder API to help aid the configuration and instantiation of {@link
* ServiceBusAdministrationClient} and {@link ServiceBusAdministrationAsyncClient}. Call
* {@link #buildClient() buildClient} and {@link #buildAsyncClient() buildAsyncClient} respectively to construct an
* instance of the desired client.
*
* @see ServiceBusAdministrationClient
* @see ServiceBusAdministrationAsyncClient
*/
@ServiceClientBuilder(serviceClients = {ServiceBusAdministrationClient.class,
ServiceBusAdministrationAsyncClient.class})
public final class ServiceBusAdministrationClientBuilder {
private final ClientLogger logger = new ClientLogger(ServiceBusAdministrationClientBuilder.class);
private final ServiceBusManagementSerializer serializer = new ServiceBusManagementSerializer();
private final List<HttpPipelinePolicy> userPolicies = new ArrayList<>();
private final Map<String, String> properties =
CoreUtils.getProperties("azure-messaging-servicebus.properties");
private Configuration configuration;
// Endpoint of the Service Bus resource. It will be the fully-qualified domain name of the Service Bus namespace.
private String endpoint;
private HttpClient httpClient;
private HttpLogOptions httpLogOptions = new HttpLogOptions();
private HttpPipeline pipeline;
private HttpPipelinePolicy retryPolicy;
private TokenCredential tokenCredential;
private ServiceBusServiceVersion serviceVersion;
private ClientOptions clientOptions;
/**
* Constructs a builder with the default parameters.
*/
public ServiceBusAdministrationClientBuilder() {
}
/**
* Creates a {@link ServiceBusAdministrationAsyncClient} based on options set in the builder. Every time {@code
* buildAsyncClient} is invoked, a new instance of the client is created.
*
* <p>If {@link #pipeline(HttpPipeline) pipeline} is set, then the {@code pipeline} and
* {@link #endpoint(String) endpoint} are used to create the {@link ServiceBusAdministrationAsyncClient client}. All
* other builder settings are ignored.</p>
*
* @return A {@link ServiceBusAdministrationAsyncClient} with the options set in the builder.
* @throws NullPointerException if {@code endpoint} has not been set. This is automatically set when {@link
* #connectionString(String) connectionString} is set. Explicitly through {@link #endpoint(String)}, or through
* {@link #credential(String, TokenCredential)}.
* @throws IllegalStateException If applicationId if set in both {@code httpLogOptions} and {@code clientOptions}
* and not same.
*/
public ServiceBusAdministrationAsyncClient buildAsyncClient() {
if (endpoint == null) {
throw logger.logExceptionAsError(new NullPointerException("'endpoint' cannot be null."));
}
final ServiceBusServiceVersion apiVersion = serviceVersion == null
? ServiceBusServiceVersion.getLatest()
: serviceVersion;
final HttpPipeline httpPipeline = createPipeline();
final ServiceBusManagementClientImpl client = new ServiceBusManagementClientImplBuilder()
.pipeline(httpPipeline)
.serializerAdapter(serializer)
.endpoint(endpoint)
.apiVersion(apiVersion.getVersion())
.buildClient();
return new ServiceBusAdministrationAsyncClient(client, serializer);
}
/**
* Creates a {@link ServiceBusAdministrationClient} based on options set in the builder. Every time {@code
* buildClient} is invoked, a new instance of the client is created.
*
* <p>If {@link #pipeline(HttpPipeline) pipeline} is set, then the {@code pipeline} and
* {@link #endpoint(String) endpoint} are used to create the {@link ServiceBusAdministrationClient client}. All
* other builder settings are ignored.</p>
*
* @return A {@link ServiceBusAdministrationClient} with the options set in the builder.
* @throws NullPointerException if {@code endpoint} has not been set. This is automatically set when {@link
* #connectionString(String) connectionString} is set. Explicitly through {@link #endpoint(String)}, or through
* {@link #credential(String, TokenCredential)}.
* @throws IllegalStateException If applicationId if set in both {@code httpLogOptions} and {@code clientOptions}
* and not same.
*/
public ServiceBusAdministrationClient buildClient() {
return new ServiceBusAdministrationClient(buildAsyncClient());
}
/**
* Adds a policy to the set of existing policies that are executed after required policies.
*
* @param policy The retry policy for service requests.
*
* @return The updated {@link ServiceBusAdministrationClientBuilder} object.
* @throws NullPointerException If {@code policy} is {@code null}.
*/
public ServiceBusAdministrationClientBuilder addPolicy(HttpPipelinePolicy policy) {
Objects.requireNonNull(policy);
userPolicies.add(policy);
return this;
}
/**
* Sets the service endpoint for the Service Bus namespace.
*
* @param endpoint The URL of the Service Bus namespace.
*
* @return The updated {@link ServiceBusAdministrationClientBuilder} object.
* @throws NullPointerException if {@code endpoint} is null.
* @throws IllegalArgumentException if {@code endpoint} cannot be parsed into a valid URL.
*/
public ServiceBusAdministrationClientBuilder endpoint(String endpoint) {
final URL url;
try {
url = new URL(Objects.requireNonNull(endpoint, "'endpoint' cannot be null."));
} catch (MalformedURLException ex) {
throw logger.logExceptionAsWarning(new IllegalArgumentException("'endpoint' must be a valid URL"));
}
this.endpoint = url.getHost();
return this;
}
/**
* Sets the configuration store that is used during construction of the service client.
*
* The default configuration store is a clone of the {@link Configuration#getGlobalConfiguration() global
* configuration store}, use {@link Configuration#NONE} to bypass using configuration settings during construction.
*
* @param configuration The configuration store used to
*
* @return The updated {@link ServiceBusAdministrationClientBuilder} object.
*/
public ServiceBusAdministrationClientBuilder configuration(Configuration configuration) {
this.configuration = configuration;
return this;
}
/**
* Sets the connection string for a Service Bus namespace or a specific Service Bus resource.
*
* @param connectionString Connection string for a Service Bus namespace or a specific Service Bus resource.
*
* @return The updated {@link ServiceBusAdministrationClientBuilder} object.
* @throws NullPointerException If {@code connectionString} is {@code null}.
* @throws IllegalArgumentException If {@code connectionString} is an entity specific connection string, and not
* a {@code connectionString} for the Service Bus namespace.
*/
public ServiceBusAdministrationClientBuilder connectionString(String connectionString) {
Objects.requireNonNull(connectionString, "'connectionString' cannot be null.");
final ConnectionStringProperties properties = new ConnectionStringProperties(connectionString);
final TokenCredential tokenCredential;
try {
tokenCredential = new ServiceBusSharedKeyCredential(properties.getSharedAccessKeyName(),
properties.getSharedAccessKey(), ServiceBusConstants.TOKEN_VALIDITY);
} catch (Exception e) {
throw logger.logExceptionAsError(
new AzureException("Could not create the ServiceBusSharedKeyCredential.", e));
}
this.endpoint = properties.getEndpoint().getHost();
if (properties.getEntityPath() != null && !properties.getEntityPath().isEmpty()) {
throw logger.logExceptionAsError(new IllegalArgumentException(
"'connectionString' cannot contain an EntityPath. It should be a namespace connection string."));
}
return credential(properties.getEndpoint().getHost(), tokenCredential);
}
/**
* Sets the credential used to authenticate HTTP requests to the Service Bus namespace.
*
* @param fullyQualifiedNamespace for the Service Bus.
* @param credential {@link TokenCredential} to be used for authentication.
*
* @return The updated {@link ServiceBusAdministrationClientBuilder} object.
*/
public ServiceBusAdministrationClientBuilder credential(String fullyQualifiedNamespace,
TokenCredential credential) {
this.endpoint = Objects.requireNonNull(fullyQualifiedNamespace,
"'fullyQualifiedNamespace' cannot be null.");
this.tokenCredential = Objects.requireNonNull(credential, "'credential' cannot be null.");
if (CoreUtils.isNullOrEmpty(fullyQualifiedNamespace)) {
throw logger.logExceptionAsError(
new IllegalArgumentException("'fullyQualifiedNamespace' cannot be an empty string."));
}
return this;
}
/**
* Sets the HTTP client to use for sending and receiving requests to and from the service.
*
* @param client The HTTP client to use for requests.
*
* @return The updated {@link ServiceBusAdministrationClientBuilder} object.
*/
public ServiceBusAdministrationClientBuilder httpClient(HttpClient client) {
if (this.httpClient != null && client == null) {
logger.info("HttpClient is being set to 'null' when it was previously configured.");
}
this.httpClient = client;
return this;
}
/**
* Sets the logging configuration for HTTP requests and responses.
*
* <p>If logLevel is not provided, default value of {@link HttpLogDetailLevel#NONE} is set.</p>
*
* @param logOptions The logging configuration to use when sending and receiving HTTP requests/responses.
*
* @return The updated {@link ServiceBusAdministrationClientBuilder} object.
*/
public ServiceBusAdministrationClientBuilder httpLogOptions(HttpLogOptions logOptions) {
httpLogOptions = logOptions;
return this;
}
/**
* Sets the {@link ClientOptions} which enables various options to be set on the client. For example setting
* {@code applicationId} using {@link ClientOptions#setApplicationId(String)} to configure {@link UserAgentPolicy}
* for telemetry/monitoring purpose.
* <p>
*
* @param clientOptions to be set on the client.
*
* @return The updated {@link ServiceBusAdministrationClientBuilder} object.
*
* @see <a href="https://azure.github.io/azure-sdk/general_azurecore.html#telemetry-policy">Azure Core: Telemetry
* policy</a>
*/
public ServiceBusAdministrationClientBuilder clientOptions(ClientOptions clientOptions) {
this.clientOptions = clientOptions;
return this;
}
/**
* Sets the HTTP pipeline to use for the service client.
*
* If {@code pipeline} is set, all other settings are ignored, aside from {@link
* ServiceBusAdministrationClientBuilder#endpoint(String) endpoint} to build {@link ServiceBusAdministrationClient}
* or {@link ServiceBusAdministrationAsyncClient}.
*
* @param pipeline The HTTP pipeline to use for sending service requests and receiving responses.
*
* @return The updated {@link ServiceBusAdministrationClientBuilder} object.
*/
public ServiceBusAdministrationClientBuilder pipeline(HttpPipeline pipeline) {
if (this.pipeline != null && pipeline == null) {
logger.info("HttpPipeline is being set to 'null' when it was previously configured.");
}
this.pipeline = pipeline;
return this;
}
/**
* Sets the {@link HttpPipelinePolicy} that is used when each request is sent.
*
* The default retry policy will be used if not provided {@link #buildAsyncClient()}
* to build {@link ServiceBusAdministrationClient} or {@link ServiceBusAdministrationAsyncClient}.
*
* @param retryPolicy The user's retry policy applied to each request.
*
* @return The updated {@link ServiceBusAdministrationClientBuilder} object.
*/
public ServiceBusAdministrationClientBuilder retryPolicy(HttpPipelinePolicy retryPolicy) {
this.retryPolicy = retryPolicy;
return this;
}
/**
* Sets the {@link ServiceBusServiceVersion} that is used. By default {@link ServiceBusServiceVersion#getLatest()}
* is used when none is specified.
*
* @param serviceVersion Service version to use.
* @return The updated {@link ServiceBusAdministrationClientBuilder} object.
*/
public ServiceBusAdministrationClientBuilder serviceVersion(ServiceBusServiceVersion serviceVersion) {
this.serviceVersion = serviceVersion;
return this;
}
/**
* Builds a new HTTP pipeline if none is set, or returns a user-provided one.
*
* @return A new HTTP pipeline or the user-defined one from {@link #pipeline(HttpPipeline)}.
* @throws IllegalStateException if applicationId is not same in httpLogOptions and clientOptions.
*/
private HttpPipeline createPipeline() {
if (pipeline != null) {
return pipeline;
}
final Configuration buildConfiguration = configuration == null
? Configuration.getGlobalConfiguration().clone()
: configuration;
// Closest to API goes first, closest to wire goes last.
final List<HttpPipelinePolicy> httpPolicies = new ArrayList<>();
final String clientName = properties.getOrDefault("name", "UnknownName");
final String clientVersion = properties.getOrDefault("version", "UnknownVersion");
// Find applicationId to use
String logApplicationId = null;
if (httpLogOptions != null) {
logApplicationId = httpLogOptions.getApplicationId();
}
String clientApplicationId = null;
if (clientOptions != null && clientOptions.getApplicationId() != null) {
clientApplicationId = clientOptions.getApplicationId();
}
if (logApplicationId != null && clientApplicationId != null
&& !logApplicationId.equalsIgnoreCase(clientApplicationId)) {
throw logger.logExceptionAsError(new IllegalStateException(
"'httpLogOptions.getApplicationId() and clientOptions.getApplicationId()' cannot be different."));
}
// We prioritize application id set in ClientOptions.
final String applicationId = clientApplicationId != null ? clientApplicationId : logApplicationId;
httpPolicies.add(new UserAgentPolicy(applicationId, clientName, clientVersion,
buildConfiguration));
httpPolicies.add(new ServiceBusTokenCredentialHttpPolicy(tokenCredential));
httpPolicies.add(new AddHeadersFromContextPolicy());
HttpPolicyProviders.addBeforeRetryPolicies(httpPolicies);
httpPolicies.add(retryPolicy == null ? new RetryPolicy() : retryPolicy);
httpPolicies.addAll(userPolicies);
httpPolicies.add(new HttpLoggingPolicy(httpLogOptions));
HttpPolicyProviders.addAfterRetryPolicies(httpPolicies);
return new HttpPipelineBuilder()
.policies(httpPolicies.toArray(new HttpPipelinePolicy[0]))
.httpClient(httpClient)
.build();
}
}