Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changes/next-release/feature-AWSSDKforJavav2-eea40a4.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"type": "feature",
"category": "AWS SDK for Java v2",
"contributor": "",
"description": "Add support for specify endpoint overrides using environment variables, system properties or profile files. More information about this feature is available here: https://docs.aws.amazon.com/sdkref/latest/guide/feature-ss-endpoints.html"
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package software.amazon.awssdk.codegen.internal;

import static java.util.stream.Collectors.toList;
import static software.amazon.awssdk.utils.StringUtils.lowerCase;

import java.io.Closeable;
import java.io.File;
Expand Down Expand Up @@ -133,7 +134,7 @@ public static String removeLeading(String str, String toRemove) {
if (str == null) {
return null;
}
if (str.startsWith(toRemove)) {
if (lowerCase(str).startsWith(lowerCase(toRemove))) {
return str.substring(toRemove.length());
}
return str;
Expand All @@ -143,7 +144,7 @@ public static String removeTrailing(String str, String toRemove) {
if (str == null) {
return null;
}
if (str.endsWith(toRemove)) {
if (lowerCase(str).endsWith(lowerCase(toRemove))) {
return str.substring(0, str.length() - toRemove.length());
}
return str;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,19 +114,42 @@ private static boolean isJavaKeyword(String word) {

@Override
public String getServiceName() {
String baseName = Stream.of(serviceModel.getMetadata().getServiceId())
.filter(Objects::nonNull)
.filter(s -> !s.trim().isEmpty())
.findFirst()
.orElseThrow(() -> new IllegalStateException("ServiceId is missing in the c2j model."));

String baseName = serviceId();
baseName = pascalCase(baseName);
baseName = removeRedundantPrefixesAndSuffixes(baseName);
return baseName;
}

// Special cases
baseName = Utils.removeLeading(baseName, "Amazon");
baseName = Utils.removeLeading(baseName, "Aws");
baseName = Utils.removeTrailing(baseName, "Service");
@Override
public String getServiceNameForEnvironmentVariables() {
String baseName = serviceId();
baseName = removeRedundantPrefixesAndSuffixes(baseName);
baseName = screamCase(baseName);
return baseName;
}

@Override
public String getServiceNameForSystemProperties() {
return getServiceName();
}

@Override
public String getServiceNameForProfileFile() {
return StringUtils.lowerCase(getServiceNameForEnvironmentVariables());
}

private String serviceId() {
return Stream.of(serviceModel.getMetadata().getServiceId())
.filter(Objects::nonNull)
.filter(s -> !s.trim().isEmpty())
.findFirst()
.orElseThrow(() -> new IllegalStateException("ServiceId is missing in the c2j model."));
}

private static String removeRedundantPrefixesAndSuffixes(String baseName) {
baseName = Utils.removeLeading(baseName, "amazon");
baseName = Utils.removeLeading(baseName, "aws");
baseName = Utils.removeTrailing(baseName, "service");
return baseName;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,21 @@ public interface NamingStrategy {
*/
String getServiceName();

/**
* Retrieve the service name that should be used for environment variables.
*/
String getServiceNameForEnvironmentVariables();

/**
* Retrieve the service name that should be used for system properties.
*/
String getServiceNameForSystemProperties();

/**
* Retrieve the service name that should be used for profile properties.
*/
String getServiceNameForProfileFile();

/**
* Retrieve the client package name that should be used based on the service name.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import software.amazon.awssdk.auth.token.signer.aws.BearerTokenSigner;
import software.amazon.awssdk.awscore.client.builder.AwsDefaultClientBuilder;
import software.amazon.awssdk.awscore.client.config.AwsClientOption;
import software.amazon.awssdk.awscore.endpoint.AwsClientEndpointProvider;
import software.amazon.awssdk.codegen.internal.Utils;
import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel;
import software.amazon.awssdk.codegen.model.intermediate.OperationModel;
Expand Down Expand Up @@ -72,6 +73,7 @@
import software.amazon.awssdk.identity.spi.IdentityProvider;
import software.amazon.awssdk.identity.spi.IdentityProviders;
import software.amazon.awssdk.identity.spi.TokenIdentity;
import software.amazon.awssdk.regions.ServiceMetadataAdvancedOption;
import software.amazon.awssdk.utils.AttributeMap;
import software.amazon.awssdk.utils.CollectionUtils;
import software.amazon.awssdk.utils.StringUtils;
Expand Down Expand Up @@ -452,6 +454,27 @@ private MethodSpec finalizeServiceConfigurationMethod() {
builder.addStatement("builder.option($T.$L, resolveAccountIdEndpointMode(config))",
AwsClientOption.class, model.getNamingStrategy().getEnumValueName("accountIdEndpointMode"));
}

String serviceNameForEnvVar = model.getNamingStrategy().getServiceNameForEnvironmentVariables();
String serviceNameForSystemProperty = model.getNamingStrategy().getServiceNameForSystemProperties();
String serviceNameForProfileFile = model.getNamingStrategy().getServiceNameForProfileFile();

builder.addCode("builder.lazyOptionIfAbsent($T.CLIENT_ENDPOINT_PROVIDER, c ->", SdkClientOption.class)
.addCode(" $T.builder()", AwsClientEndpointProvider.class)
.addCode(" .serviceEndpointOverrideEnvironmentVariable($S)", "AWS_ENDPOINT_URL_" + serviceNameForEnvVar)
.addCode(" .serviceEndpointOverrideSystemProperty($S)", "aws.endpointUrl" + serviceNameForSystemProperty)
.addCode(" .serviceProfileProperty($S)", serviceNameForProfileFile)
.addCode(" .serviceEndpointPrefix(serviceEndpointPrefix())")
.addCode(" .defaultProtocol($S)", "https")
.addCode(" .region(c.get($T.AWS_REGION))", AwsClientOption.class)
.addCode(" .profileFile(c.get($T.PROFILE_FILE_SUPPLIER))", SdkClientOption.class)
.addCode(" .profileName(c.get($T.PROFILE_NAME))", SdkClientOption.class)
.addCode(" .putAdvancedOption($T.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT,", ServiceMetadataAdvancedOption.class)
.addCode(" c.get($T.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT))", ServiceMetadataAdvancedOption.class)
.addCode(" .dualstackEnabled(c.get($T.DUALSTACK_ENDPOINT_ENABLED))", AwsClientOption.class)
.addCode(" .fipsEnabled(c.get($T.FIPS_ENDPOINT_ENABLED))", AwsClientOption.class)
.addCode(" .build());");

builder.addStatement("return builder.build()");
return builder.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -251,8 +251,8 @@ private MethodSpec constructor(TypeSpec.Builder classBuilder) {
"AsyncEndpointDiscoveryCacheLoader"));

if (model.getCustomizationConfig().allowEndpointOverrideForEndpointDiscoveryRequiredOperations()) {
builder.beginControlFlow("if (clientConfiguration.option(SdkClientOption.ENDPOINT_OVERRIDDEN) == "
+ "Boolean.TRUE)");
builder.beginControlFlow("if (clientConfiguration.option(SdkClientOption.CLIENT_ENDPOINT_PROVIDER)"
+ ".isEndpointOverridden())");
builder.addStatement("log.warn($S)",
"Endpoint discovery is enabled for this client, and an endpoint override was also "
+ "specified. This will disable endpoint discovery for methods that require it, instead "
Expand Down Expand Up @@ -394,7 +394,8 @@ protected MethodSpec.Builder operationBody(MethodSpec.Builder builder, Operation
builder.addStatement("boolean endpointDiscoveryEnabled = "
+ "clientConfiguration.option(SdkClientOption.ENDPOINT_DISCOVERY_ENABLED)");
builder.addStatement("boolean endpointOverridden = "
+ "clientConfiguration.option(SdkClientOption.ENDPOINT_OVERRIDDEN) == Boolean.TRUE");
+ "clientConfiguration.option(SdkClientOption.CLIENT_ENDPOINT_PROVIDER)"
+ ".isEndpointOverridden()");

if (opModel.getEndpointDiscovery().isRequired()) {
if (!model.getCustomizationConfig().allowEndpointOverrideForEndpointDiscoveryRequiredOperations()) {
Expand Down Expand Up @@ -436,7 +437,8 @@ protected MethodSpec.Builder operationBody(MethodSpec.Builder builder, Operation
builder.addCode("endpointFuture = identityFuture.thenCompose(credentials -> {")
.addCode(" $1T endpointDiscoveryRequest = $1T.builder()", EndpointDiscoveryRequest.class)
.addCode(" .required($L)", opModel.getInputShape().getEndpointDiscovery().isRequired())
.addCode(" .defaultEndpoint(clientConfiguration.option($T.ENDPOINT))", SdkClientOption.class)
.addCode(" .defaultEndpoint(clientConfiguration.option($T.CLIENT_ENDPOINT_PROVIDER).clientEndpoint())",
SdkClientOption.class)
.addCode(" .overrideConfiguration($N.overrideConfiguration().orElse(null))",
opModel.getInput().getVariableName())
.addCode(" .build();")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,8 +219,8 @@ private MethodSpec constructor() {
"EndpointDiscoveryCacheLoader"));

if (model.getCustomizationConfig().allowEndpointOverrideForEndpointDiscoveryRequiredOperations()) {
builder.beginControlFlow("if (clientConfiguration.option(SdkClientOption.ENDPOINT_OVERRIDDEN) == "
+ "Boolean.TRUE)");
builder.beginControlFlow("if (clientConfiguration.option(SdkClientOption.CLIENT_ENDPOINT_PROVIDER)"
+ ".isEndpointOverridden())");
builder.addStatement("log.warn(() -> $S)",
"Endpoint discovery is enabled for this client, and an endpoint override was also "
+ "specified. This will disable endpoint discovery for methods that require it, instead "
Expand Down Expand Up @@ -265,7 +265,8 @@ private MethodSpec traditionalMethod(OperationModel opModel) {
method.addStatement("boolean endpointDiscoveryEnabled = "
+ "clientConfiguration.option(SdkClientOption.ENDPOINT_DISCOVERY_ENABLED)");
method.addStatement("boolean endpointOverridden = "
+ "clientConfiguration.option(SdkClientOption.ENDPOINT_OVERRIDDEN) == Boolean.TRUE");
+ "clientConfiguration.option(SdkClientOption.CLIENT_ENDPOINT_PROVIDER)"
+ ".isEndpointOverridden()");

if (opModel.getEndpointDiscovery().isRequired()) {
if (!model.getCustomizationConfig().allowEndpointOverrideForEndpointDiscoveryRequiredOperations()) {
Expand Down Expand Up @@ -309,7 +310,8 @@ private MethodSpec traditionalMethod(OperationModel opModel) {

method.addCode("$1T endpointDiscoveryRequest = $1T.builder()", EndpointDiscoveryRequest.class)
.addCode(" .required($L)", opModel.getInputShape().getEndpointDiscovery().isRequired())
.addCode(" .defaultEndpoint(clientConfiguration.option($T.ENDPOINT))", SdkClientOption.class)
.addCode(" .defaultEndpoint(clientConfiguration.option($T.CLIENT_ENDPOINT_PROVIDER).clientEndpoint())",
SdkClientOption.class)
.addCode(" .overrideConfiguration($N.overrideConfiguration().orElse(null))",
opModel.getInput().getVariableName())
.addCode(" .build();");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel;
import software.amazon.awssdk.codegen.poet.auth.scheme.AuthSchemeSpecUtils;
import software.amazon.awssdk.codegen.poet.rules.EndpointRulesSpecUtils;
import software.amazon.awssdk.core.ClientEndpointProvider;
import software.amazon.awssdk.core.client.config.ClientOption;
import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration;
import software.amazon.awssdk.core.client.config.SdkClientConfiguration;
Expand Down Expand Up @@ -161,21 +162,21 @@ private Field endpointOverrideField() {
private CodeBlock endpointOverrideConfigSetter() {
return CodeBlock.builder()
.beginControlFlow("if (endpointOverride != null)")
.addStatement("config.option($T.ENDPOINT, endpointOverride)", SdkClientOption.class)
.addStatement("config.option($T.ENDPOINT_OVERRIDDEN, true)", SdkClientOption.class)
.addStatement("config.option($T.CLIENT_ENDPOINT_PROVIDER, $T.forEndpointOverride(endpointOverride))",
SdkClientOption.class, ClientEndpointProvider.class)
.nextControlFlow("else")
.addStatement("config.option($T.ENDPOINT, null)", SdkClientOption.class)
.addStatement("config.option($T.ENDPOINT_OVERRIDDEN, false)", SdkClientOption.class)
.addStatement("config.option($T.CLIENT_ENDPOINT_PROVIDER, null)", SdkClientOption.class)
.endControlFlow()
.addStatement("return this")
.build();
}

private CodeBlock endpointOverrideConfigGetter() {
return CodeBlock.builder()
.beginControlFlow("if (Boolean.TRUE.equals(config.option($T.ENDPOINT_OVERRIDDEN)))",
SdkClientOption.class)
.addStatement("return config.option($T.ENDPOINT)", SdkClientOption.class)
.addStatement("$T clientEndpoint = config.option($T.CLIENT_ENDPOINT_PROVIDER)",
ClientEndpointProvider.class, SdkClientOption.class)
.beginControlFlow("if (clientEndpoint != null && clientEndpoint.isEndpointOverridden())")
.addStatement("return clientEndpoint.clientEndpoint()")
.endControlFlow()
.addStatement("return null")
.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
import software.amazon.awssdk.core.interceptor.Context;
import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
import software.amazon.awssdk.core.interceptor.ExecutionInterceptor;
import software.amazon.awssdk.core.interceptor.SdkExecutionAttribute;
import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute;
import software.amazon.awssdk.endpoints.Endpoint;
import software.amazon.awssdk.http.SdkHttpRequest;
Expand Down Expand Up @@ -71,14 +70,14 @@ private MethodSpec modifyHttpRequestMethod() {
.addStatement("return context.httpRequest()")
.endControlFlow().build();

b.addStatement("$1T endpoint = ($1T) executionAttributes.getAttribute($2T.RESOLVED_ENDPOINT)",
b.addStatement("$T endpoint = executionAttributes.getAttribute($T.RESOLVED_ENDPOINT)",
Endpoint.class,
SdkInternalExecutionAttribute.class);
b.addStatement("return $T.setUri(context.httpRequest(),"
+ "executionAttributes.getAttribute($T.CLIENT_ENDPOINT),"
+ "executionAttributes.getAttribute($T.CLIENT_ENDPOINT_PROVIDER).clientEndpoint(),"
+ "endpoint.url())",
endpointRulesSpecUtils.rulesRuntimeClassName("AwsEndpointProviderUtils"),
SdkExecutionAttribute.class);
SdkInternalExecutionAttribute.class);
return b.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ public final class AwsEndpointProviderUtils {
public static String endpointBuiltIn(ExecutionAttributes executionAttributes) {
if (endpointIsOverridden(executionAttributes)) {
return invokeSafely(() -> {
URI endpointOverride = executionAttributes.getAttribute(SdkExecutionAttribute.CLIENT_ENDPOINT);
URI endpointOverride = executionAttributes.getAttribute(SdkInternalExecutionAttribute.CLIENT_ENDPOINT_PROVIDER)
.clientEndpoint();
return new URI(endpointOverride.getScheme(), null, endpointOverride.getHost(), endpointOverride.getPort(),
endpointOverride.getPath(), null, endpointOverride.getFragment()).toString();
});
Expand All @@ -53,11 +54,10 @@ public final class AwsEndpointProviderUtils {
}

/**
* True if the the {@link SdkExecutionAttribute#ENDPOINT_OVERRIDDEN} attribute is present and its value is
* {@code true}, {@code false} otherwise.
* Read {@link SdkExecutionAttribute#CLIENT_ENDPOINT_PROVIDER}'s isEndpointOverridden attribute.
*/
public static boolean endpointIsOverridden(ExecutionAttributes attrs) {
return attrs.getOptionalAttribute(SdkExecutionAttribute.ENDPOINT_OVERRIDDEN).orElse(false);
return attrs.getAttribute(SdkInternalExecutionAttribute.CLIENT_ENDPOINT_PROVIDER).isEndpointOverridden();
}

/**
Expand Down
Loading