Skip to content

S3 Cross Region Client #4132

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Jul 5, 2023
Merged
Show file tree
Hide file tree
Changes from all 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-70eb163.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": "Cross region bucket access for S3 Client. This feature will allow users to access buckets of different region using a single cross region configured client."
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,6 @@ class CommonGeneratorTasks extends CompositeGeneratorTask {
new ModelClassGeneratorTasks(params),
new PackageInfoGeneratorTasks(params),
new BaseExceptionClassGeneratorTasks(params),
new ClientOptionsClassGeneratorTasks(params));
new CommonInternalGeneratorTasks(params));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +15,35 @@

package software.amazon.awssdk.codegen.emitters.tasks;

import java.util.Collections;
import java.util.Arrays;
import java.util.List;
import software.amazon.awssdk.codegen.emitters.GeneratorTask;
import software.amazon.awssdk.codegen.emitters.GeneratorTaskParams;
import software.amazon.awssdk.codegen.emitters.PoetGeneratorTask;
import software.amazon.awssdk.codegen.poet.client.SdkClientOptions;
import software.amazon.awssdk.codegen.poet.common.UserAgentUtilsSpec;

public class ClientOptionsClassGeneratorTasks extends BaseGeneratorTasks {
public class CommonInternalGeneratorTasks extends BaseGeneratorTasks {
private final GeneratorTaskParams params;

public ClientOptionsClassGeneratorTasks(GeneratorTaskParams params) {
public CommonInternalGeneratorTasks(GeneratorTaskParams params) {
super(params);
this.params = params;
}

@Override
protected List<GeneratorTask> createTasks() throws Exception {
return Collections.singletonList(
new PoetGeneratorTask(clientOptionsDir(), params.getModel().getFileHeader(), new SdkClientOptions(params.getModel()))
);
return Arrays.asList(createClientOptionTask(), createUserAgentTask());
}

private PoetGeneratorTask createClientOptionTask() {
return new PoetGeneratorTask(clientOptionsDir(), params.getModel().getFileHeader(),
new SdkClientOptions(params.getModel()));
}

private PoetGeneratorTask createUserAgentTask() {
return new PoetGeneratorTask(clientOptionsDir(), params.getModel().getFileHeader(),
new UserAgentUtilsSpec(params.getModel()));
}

private String clientOptionsDir() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import software.amazon.awssdk.codegen.model.service.ClientContextParam;
import software.amazon.awssdk.core.retry.RetryMode;
import software.amazon.awssdk.core.traits.PayloadTrait;
import software.amazon.awssdk.utils.AttributeMap;
Expand Down Expand Up @@ -212,6 +213,20 @@ public class CustomizationConfig {
*/
private boolean delegateSyncClientClass;

/**
* Fully qualified name of a class that given the default sync client instance can return the final client instance,
* for instance by decorating the client with specific-purpose implementations of the client interface.
* See S3 customization.config for an example.
*/
private String syncClientDecorator;

/**
* Fully qualified name of a class that given the default async client instance can return the final client instance,
* for instance by decorating the client with specific-purpose implementations of the client interface.
* See S3 customization.config for an example.
*/
private String asyncClientDecorator;

/**
* Whether to skip generating endpoint tests from endpoint-tests.json
*/
Expand All @@ -236,6 +251,11 @@ public class CustomizationConfig {
*/
private boolean requiredTraitValidationEnabled = false;

/**
* Customization to attach map of Custom client param configs that can be set on a client builder.
*/
private Map<String, ClientContextParam> customClientContextParams;

private CustomizationConfig() {
}

Expand Down Expand Up @@ -566,6 +586,22 @@ public void setDelegateAsyncClientClass(boolean delegateAsyncClientClass) {
this.delegateAsyncClientClass = delegateAsyncClientClass;
}

public String getSyncClientDecorator() {
return syncClientDecorator;
}

public void setSyncClientDecorator(String syncClientDecorator) {
this.syncClientDecorator = syncClientDecorator;
}

public String getAsyncClientDecorator() {
return asyncClientDecorator;
}

public void setAsyncClientDecorator(String asyncClientDecorator) {
this.asyncClientDecorator = asyncClientDecorator;
}

public boolean isDelegateSyncClientClass() {
return delegateSyncClientClass;
}
Expand Down Expand Up @@ -621,4 +657,12 @@ public boolean isRequiredTraitValidationEnabled() {
public void setRequiredTraitValidationEnabled(boolean requiredTraitValidationEnabled) {
this.requiredTraitValidationEnabled = requiredTraitValidationEnabled;
}

public Map<String, ClientContextParam> getCustomClientContextParams() {
return customClientContextParams;
}

public void setCustomClientContextParams(Map<String, ClientContextParam> customClientContextParams) {
this.customClientContextParams = customClientContextParams;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ public class ServiceConfig {

private boolean hasAccelerateModeEnabledProperty = false;

private boolean hasCrossRegionAccessEnabledProperty = false;

public String getClassName() {
return className;
}
Expand Down Expand Up @@ -95,6 +97,14 @@ public void setHasPathStyleAccessEnabledProperty(boolean hasPathStyleAccessEnabl
this.hasPathStyleAccessEnabledProperty = hasPathStyleAccessEnabledProperty;
}

public boolean hasCrossRegionAccessEnabledProperty() {
return hasCrossRegionAccessEnabledProperty;
}

public void setHasCrossRegionAccessEnabledProperty(boolean hasCrossRegionAccessEnabledProperty) {
this.hasCrossRegionAccessEnabledProperty = hasCrossRegionAccessEnabledProperty;
}

public boolean hasAccelerateModeEnabledProperty() {
return hasAccelerateModeEnabledProperty;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,21 @@ public String getSdkResponseBaseClassName() {
}
}

public Optional<String> syncClientDecoratorClassName() {
if (customizationConfig.getSyncClientDecorator() != null) {
return Optional.of(customizationConfig.getSyncClientDecorator());
}
return Optional.empty();
}

public Optional<String> asyncClientDecoratorClassName() {
String asyncClientDecorator = customizationConfig.getAsyncClientDecorator();
if (customizationConfig.getAsyncClientDecorator() != null) {
return Optional.of(asyncClientDecorator);
}
return Optional.empty();
}

public String getFileHeader() {
return FILE_HEADER;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ public ClassName getServiceConfigClass() {
+ model.getMetadata().getServiceName() + "ServiceClientConfiguration");
}

public ClassName getUserAgentClass() {
return ClassName.get(model.getMetadata().getFullClientInternalPackageName(), "UserAgentUtils");
}

/**
* @param operationName Name of the operation
* @return A Poet {@link ClassName} for the response type of a paginated operation in the base service package.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import software.amazon.awssdk.codegen.utils.AuthUtils;
import software.amazon.awssdk.core.client.config.SdkClientConfiguration;
import software.amazon.awssdk.core.client.config.SdkClientOption;
import software.amazon.awssdk.endpoints.EndpointProvider;

public class AsyncClientBuilderClass implements ClassSpec {
private final IntermediateModel model;
Expand Down Expand Up @@ -79,7 +80,10 @@ public TypeSpec poetSpec() {
builder.addMethod(bearerTokenProviderMethod());
}

return builder.addMethod(buildClientMethod()).build();
builder.addMethod(buildClientMethod());
builder.addMethod(initializeServiceClientConfigMethod());

return builder.build();
}

private MethodSpec endpointDiscoveryEnabled() {
Expand Down Expand Up @@ -119,26 +123,26 @@ private MethodSpec endpointProviderMethod() {
}

private MethodSpec buildClientMethod() {
return MethodSpec.methodBuilder("buildClient")
.addAnnotation(Override.class)
.addModifiers(Modifier.PROTECTED, Modifier.FINAL)
.returns(clientInterfaceName)
.addStatement("$T clientConfiguration = super.asyncClientConfiguration()", SdkClientConfiguration.class)
.addStatement("this.validateClientOptions(clientConfiguration)")
.addStatement("$T endpointOverride = null", URI.class)
.addCode("if (clientConfiguration.option($T.ENDPOINT_OVERRIDDEN) != null"
+ "&& $T.TRUE.equals(clientConfiguration.option($T.ENDPOINT_OVERRIDDEN))) {"
+ "endpointOverride = clientConfiguration.option($T.ENDPOINT);"
+ "}",
SdkClientOption.class, Boolean.class, SdkClientOption.class, SdkClientOption.class)
.addStatement("$T serviceClientConfiguration = $T.builder()"
+ ".overrideConfiguration(overrideConfiguration())"
+ ".region(clientConfiguration.option($T.AWS_REGION))"
+ ".endpointOverride(endpointOverride)"
+ ".build()",
serviceConfigClassName, serviceConfigClassName, AwsClientOption.class)
.addStatement("return new $T(serviceClientConfiguration, clientConfiguration)", clientClassName)
.build();
MethodSpec.Builder builder = MethodSpec.methodBuilder("buildClient")
.addAnnotation(Override.class)
.addModifiers(Modifier.PROTECTED, Modifier.FINAL)
.returns(clientInterfaceName)
.addStatement("$T clientConfiguration = super.asyncClientConfiguration()",
SdkClientConfiguration.class).addStatement("this.validateClientOptions"
+ "(clientConfiguration)")
.addStatement("$T serviceClientConfiguration = initializeServiceClientConfig"
+ "(clientConfiguration)",
serviceConfigClassName);

builder.addStatement("$1T client = new $2T(serviceClientConfiguration, clientConfiguration)",
clientInterfaceName, clientClassName);
if (model.asyncClientDecoratorClassName().isPresent()) {
builder.addStatement("return new $T().decorate(client, clientConfiguration, clientContextParams.copy().build())",
PoetUtils.classNameFromFqcn(model.asyncClientDecoratorClassName().get()));
} else {
builder.addStatement("return client");
}
return builder.build();
}

private MethodSpec bearerTokenProviderMethod() {
Expand All @@ -152,6 +156,29 @@ private MethodSpec bearerTokenProviderMethod() {
.build();
}

private MethodSpec initializeServiceClientConfigMethod() {
return MethodSpec.methodBuilder("initializeServiceClientConfig").addModifiers(Modifier.PRIVATE)
.addParameter(SdkClientConfiguration.class, "clientConfig")
.returns(serviceConfigClassName)
.addStatement("$T endpointOverride = null", URI.class)
.addStatement("$T endpointProvider = clientConfig.option($T.ENDPOINT_PROVIDER)",
EndpointProvider.class,
SdkClientOption.class)
.addCode("if (clientConfig.option($T.ENDPOINT_OVERRIDDEN) != null"
+ "&& $T.TRUE.equals(clientConfig.option($T.ENDPOINT_OVERRIDDEN))) {"
+ "endpointOverride = clientConfig.option($T.ENDPOINT);"
+ "}",
SdkClientOption.class, Boolean.class, SdkClientOption.class, SdkClientOption.class)
.addStatement("return $T.builder()"
+ ".overrideConfiguration(overrideConfiguration())"
+ ".region(clientConfig.option($T.AWS_REGION))"
+ ".endpointOverride(endpointOverride)"
+ ".endpointProvider(endpointProvider)"
+ ".build()",
serviceConfigClassName, AwsClientOption.class)
.build();
}

@Override
public ClassName className() {
return builderClassName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,12 @@ public TypeSpec poetSpec() {
});
}

if (hasSdkClientContextParams()) {
model.getCustomizationConfig().getCustomClientContextParams().forEach((n, m) -> {
builder.addMethod(clientContextParamSetter(n, m));
});
}

if (model.getCustomizationConfig().getServiceConfig().getClassName() != null) {
builder.addMethod(setServiceConfigurationMethod())
.addMethod(beanStyleSetServiceConfigurationMethod());
Expand Down Expand Up @@ -619,6 +625,12 @@ private boolean hasClientContextParams() {
return clientContextParams != null && !clientContextParams.isEmpty();
}

private boolean hasSdkClientContextParams() {
return model.getCustomizationConfig() != null
&& model.getCustomizationConfig().getCustomClientContextParams() != null
&& !model.getCustomizationConfig().getCustomClientContextParams().isEmpty();
}

private MethodSpec validateClientOptionsMethod() {
MethodSpec.Builder builder = MethodSpec.methodBuilder("validateClientOptions")
.addModifiers(PROTECTED, Modifier.STATIC)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ public TypeSpec poetSpec() {
});
}

if (hasSdkClientContextParams()) {
model.getCustomizationConfig().getCustomClientContextParams().forEach((n, m) -> {
builder.addMethod(clientContextParamSetter(n, m));
});
}

if (generateTokenProviderMethod()) {
builder.addMethod(tokenProviderMethod());
}
Expand Down Expand Up @@ -193,4 +199,10 @@ public ClassName className() {
private boolean hasClientContextParams() {
return model.getClientContextParams() != null && !model.getClientContextParams().isEmpty();
}

private boolean hasSdkClientContextParams() {
return model.getCustomizationConfig() != null
&& model.getCustomizationConfig().getCustomClientContextParams() != null
&& !model.getCustomizationConfig().getCustomClientContextParams().isEmpty();
}
}
Loading