Skip to content

Commit ee9ea08

Browse files
authored
Implement defaults mode (#2909)
* Add support to generate DefaultsMode and implement configuration resolution (#2781) * [Default Configuration Part 2]:Implement auto mode discovery (#2786) * Implement auto mode discovery * Fix tests on CodeBuild * Address feedback * Add comment and rename misnamed constant * [Default Configuration Part 3]: add defaults from defaults mode to the configuration resolution chain (#2803) * Wiring up configuration * Address comments * Add test * Update debug statement and add singleton for AttributeMap * Add tlsNegotiationTimeout (#2814) * [Default Configuration Part 5]: Move default configuration related classes to aws-core (#2816) * Move default configuration related classes to aws-core * Remove extra space and fix build * [Default Configuration Part 6]: apply default s3 us-east-1 regional setting (#2825) * Add s3 regional setting * Rename option name * Fix checkstyle * Update sdk-default-configuraiton.json * Add changelog entry
1 parent 7e499db commit ee9ea08

File tree

54 files changed

+2770
-82
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+2770
-82
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"category": "AWS SDK for Java v2",
3+
"contributor": "",
4+
"type": "feature",
5+
"description": "Introduce a defaults mode configuration that determines how certain default configuration options are resolved in the SDK. See `DefaultsMode` for more information."
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.awssdk.codegen.lite.maven.plugin;
17+
18+
import java.io.File;
19+
import java.nio.file.Path;
20+
import java.nio.file.Paths;
21+
import org.apache.maven.plugin.AbstractMojo;
22+
import org.apache.maven.plugins.annotations.Mojo;
23+
import org.apache.maven.plugins.annotations.Parameter;
24+
import org.apache.maven.project.MavenProject;
25+
import software.amazon.awssdk.codegen.lite.CodeGenerator;
26+
import software.amazon.awssdk.codegen.lite.defaultsmode.DefaultConfiguration;
27+
import software.amazon.awssdk.codegen.lite.defaultsmode.DefaultsLoader;
28+
import software.amazon.awssdk.codegen.lite.defaultsmode.DefaultsModeConfigurationGenerator;
29+
import software.amazon.awssdk.codegen.lite.defaultsmode.DefaultsModeGenerator;
30+
31+
/**
32+
* The Maven mojo to generate defaults mode related classes.
33+
*/
34+
@Mojo(name = "generate-defaults-mode")
35+
public class DefaultsModeGenerationMojo extends AbstractMojo {
36+
37+
private static final String DEFAULTS_MODE_BASE = "software.amazon.awssdk.awscore.defaultsmode";
38+
private static final String DEFAULTS_MODE_CONFIGURATION_BASE = "software.amazon.awssdk.awscore.internal.defaultsmode";
39+
40+
@Parameter(property = "outputDirectory", defaultValue = "${project.build.directory}")
41+
private String outputDirectory;
42+
43+
@Parameter(defaultValue = "${project}", readonly = true)
44+
private MavenProject project;
45+
46+
@Parameter(property = "defaultConfigurationFile", defaultValue =
47+
"${basedir}/src/main/resources/software/amazon/awssdk/awscore/internal/defaults/sdk-default-configuration.json")
48+
private File defaultConfigurationFile;
49+
50+
public void execute() {
51+
Path baseSourcesDirectory = Paths.get(outputDirectory).resolve("generated-sources").resolve("sdk");
52+
Path testsDirectory = Paths.get(outputDirectory).resolve("generated-test-sources").resolve("sdk-tests");
53+
54+
DefaultConfiguration configuration = DefaultsLoader.load(defaultConfigurationFile);
55+
56+
generateDefaultsModeClass(baseSourcesDirectory, configuration);
57+
generateDefaultsModeConfiguartionClass(baseSourcesDirectory, configuration);
58+
59+
project.addCompileSourceRoot(baseSourcesDirectory.toFile().getAbsolutePath());
60+
project.addTestCompileSourceRoot(testsDirectory.toFile().getAbsolutePath());
61+
}
62+
63+
public void generateDefaultsModeClass(Path baseSourcesDirectory, DefaultConfiguration configuration) {
64+
Path sourcesDirectory = baseSourcesDirectory.resolve(DEFAULTS_MODE_BASE.replace(".", "/"));
65+
new CodeGenerator(sourcesDirectory.toString(), new DefaultsModeGenerator(DEFAULTS_MODE_BASE, configuration)).generate();
66+
}
67+
68+
public void generateDefaultsModeConfiguartionClass(Path baseSourcesDirectory, DefaultConfiguration configuration) {
69+
Path sourcesDirectory = baseSourcesDirectory.resolve(DEFAULTS_MODE_CONFIGURATION_BASE.replace(".", "/"));
70+
new CodeGenerator(sourcesDirectory.toString(), new DefaultsModeConfigurationGenerator(DEFAULTS_MODE_CONFIGURATION_BASE,
71+
DEFAULTS_MODE_BASE,
72+
configuration)).generate();
73+
}
74+
}

codegen-lite/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@
5757
<artifactId>utils</artifactId>
5858
<version>${awsjavasdk.version}</version>
5959
</dependency>
60+
<dependency>
61+
<groupId>software.amazon.awssdk</groupId>
62+
<artifactId>json-utils</artifactId>
63+
<version>${awsjavasdk.version}</version>
64+
</dependency>
6065
<dependency>
6166
<groupId>com.squareup</groupId>
6267
<artifactId>javapoet</artifactId>
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.awssdk.codegen.lite.defaultsmode;
17+
18+
import java.util.Map;
19+
20+
/**
21+
* Container for default configuration
22+
*/
23+
public class DefaultConfiguration {
24+
/**
25+
* The transformed configuration values for each mode
26+
*/
27+
private Map<String, Map<String, String>> modeDefaults;
28+
29+
/**
30+
* The documentation for each mode
31+
*/
32+
private Map<String, String> modesDocumentation;
33+
34+
/*
35+
* The documentation for each configuration option
36+
*/
37+
private Map<String, String> configurationDocumentation;
38+
39+
public Map<String, Map<String, String>> modeDefaults() {
40+
return modeDefaults;
41+
}
42+
43+
public DefaultConfiguration modeDefaults(Map<String, Map<String, String>> modeDefaults) {
44+
this.modeDefaults = modeDefaults;
45+
return this;
46+
}
47+
48+
public Map<String, String> modesDocumentation() {
49+
return modesDocumentation;
50+
}
51+
52+
public DefaultConfiguration modesDocumentation(Map<String, String> documentation) {
53+
this.modesDocumentation = documentation;
54+
return this;
55+
}
56+
57+
public Map<String, String> configurationDocumentation() {
58+
return configurationDocumentation;
59+
}
60+
61+
public DefaultConfiguration configurationDocumentation(Map<String, String> configurationDocumentation) {
62+
this.configurationDocumentation = configurationDocumentation;
63+
return this;
64+
}
65+
}
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.awssdk.codegen.lite.defaultsmode;
17+
18+
import java.io.File;
19+
import java.io.FileInputStream;
20+
import java.io.IOException;
21+
import java.util.HashMap;
22+
import java.util.HashSet;
23+
import java.util.List;
24+
import java.util.Map;
25+
import java.util.Set;
26+
import software.amazon.awssdk.annotations.SdkInternalApi;
27+
import software.amazon.awssdk.protocols.jsoncore.JsonNode;
28+
import software.amazon.awssdk.protocols.jsoncore.JsonNodeParser;
29+
import software.amazon.awssdk.protocols.jsoncore.JsonNodeVisitor;
30+
import software.amazon.awssdk.utils.Logger;
31+
32+
/**
33+
* Loads sdk-default-configuration.json into memory. It filters out unsupported configuration options from the file
34+
*/
35+
@SdkInternalApi
36+
public final class DefaultsLoader {
37+
private static final Logger log = Logger.loggerFor(DefaultsLoader.class);
38+
39+
private static final Set<String> UNSUPPORTED_OPTIONS = new HashSet<>();
40+
41+
static {
42+
UNSUPPORTED_OPTIONS.add("stsRegionalEndpoints");
43+
}
44+
45+
private DefaultsLoader() {
46+
}
47+
48+
public static DefaultConfiguration load(File path) {
49+
return loadDefaultsFromFile(path);
50+
}
51+
52+
private static DefaultConfiguration loadDefaultsFromFile(File path) {
53+
DefaultConfiguration defaultsResolution = new DefaultConfiguration();
54+
Map<String, Map<String, String>> resolvedDefaults = new HashMap<>();
55+
56+
try (FileInputStream fileInputStream = new FileInputStream(path)) {
57+
JsonNodeParser jsonNodeParser = JsonNodeParser.builder().build();
58+
59+
Map<String, JsonNode> sdkDefaultConfiguration = jsonNodeParser.parse(fileInputStream)
60+
.asObject();
61+
62+
Map<String, JsonNode> base = sdkDefaultConfiguration.get("base").asObject();
63+
Map<String, JsonNode> modes = sdkDefaultConfiguration.get("modes").asObject();
64+
65+
modes.forEach((mode, modifiers) -> applyModificationToOneMode(resolvedDefaults, base, mode, modifiers));
66+
67+
Map<String, JsonNode> documentation = sdkDefaultConfiguration.get("documentation").asObject();
68+
Map<String, JsonNode> modesDocumentation = documentation.get("modes").asObject();
69+
Map<String, JsonNode> configDocumentation = documentation.get("configuration").asObject();
70+
71+
defaultsResolution.modesDocumentation(
72+
modesDocumentation.entrySet()
73+
.stream()
74+
.collect(HashMap::new, (m, e) -> m.put(e.getKey(), e.getValue().asString()), Map::putAll));
75+
defaultsResolution.configurationDocumentation(
76+
configDocumentation.entrySet()
77+
.stream()
78+
.filter(e -> !UNSUPPORTED_OPTIONS.contains(e.getKey()))
79+
.collect(HashMap::new, (m, e) -> m.put(e.getKey(), e.getValue().asString()), Map::putAll));
80+
81+
} catch (IOException e) {
82+
throw new RuntimeException(e);
83+
}
84+
85+
defaultsResolution.modeDefaults(resolvedDefaults);
86+
87+
return defaultsResolution;
88+
}
89+
90+
private static void applyModificationToOneConfigurationOption(Map<String, String> resolvedDefaultsForCurrentMode,
91+
String option,
92+
JsonNode modifier) {
93+
String resolvedValue;
94+
String baseValue = resolvedDefaultsForCurrentMode.get(option);
95+
96+
if (UNSUPPORTED_OPTIONS.contains(option)) {
97+
return;
98+
}
99+
100+
Map<String, JsonNode> modifierMap = modifier.asObject();
101+
102+
if (modifierMap.size() != 1) {
103+
throw new IllegalStateException("More than one modifier exists for option " + option);
104+
}
105+
106+
String modifierString = modifierMap.keySet().iterator().next();
107+
108+
switch (modifierString) {
109+
case "override":
110+
resolvedValue = modifierMap.get("override").visit(new StringJsonNodeVisitor());
111+
break;
112+
case "multiply":
113+
resolvedValue = processMultiply(baseValue, modifierMap);
114+
break;
115+
case "add":
116+
resolvedValue = processAdd(baseValue, modifierMap);
117+
break;
118+
default:
119+
throw new UnsupportedOperationException("Unsupported modifier: " + modifierString);
120+
}
121+
122+
resolvedDefaultsForCurrentMode.put(option, resolvedValue);
123+
}
124+
125+
private static void applyModificationToOneMode(Map<String, Map<String, String>> resolvedDefaults,
126+
Map<String, JsonNode> base,
127+
String mode,
128+
JsonNode modifiers) {
129+
130+
log.info(() -> "Apply modification for mode: " + mode);
131+
Map<String, String> resolvedDefaultsForCurrentMode =
132+
base.entrySet().stream().filter(e -> !UNSUPPORTED_OPTIONS.contains(e.getKey()))
133+
.collect(HashMap::new, (m, e) -> m.put(e.getKey(),
134+
e.getValue().visit(new StringJsonNodeVisitor())), Map::putAll);
135+
136+
137+
// Iterate the configuration options and apply modification.
138+
modifiers.asObject().forEach((option, modifier) -> applyModificationToOneConfigurationOption(
139+
resolvedDefaultsForCurrentMode, option, modifier));
140+
141+
resolvedDefaults.put(mode, resolvedDefaultsForCurrentMode);
142+
}
143+
144+
private static String processAdd(String baseValue, Map<String, JsonNode> modifierMap) {
145+
String resolvedValue;
146+
String add = modifierMap.get("add").asNumber();
147+
int parsedAdd = Integer.parseInt(add);
148+
int number = Math.addExact(Integer.parseInt(baseValue), parsedAdd);
149+
resolvedValue = String.valueOf(number);
150+
return resolvedValue;
151+
}
152+
153+
private static String processMultiply(String baseValue, Map<String, JsonNode> modifierMap) {
154+
String resolvedValue;
155+
String multiply = modifierMap.get("multiply").asNumber();
156+
double parsedValue = Double.parseDouble(multiply);
157+
158+
double resolvedNumber = Integer.parseInt(baseValue) * parsedValue;
159+
int castValue = (int) resolvedNumber;
160+
161+
if (castValue != resolvedNumber) {
162+
throw new IllegalStateException("The transformed value must be be a float number: " + castValue);
163+
}
164+
165+
resolvedValue = String.valueOf(castValue);
166+
return resolvedValue;
167+
}
168+
169+
private static final class StringJsonNodeVisitor implements JsonNodeVisitor<String> {
170+
@Override
171+
public String visitNull() {
172+
throw new IllegalStateException("Invalid type encountered");
173+
}
174+
175+
@Override
176+
public String visitBoolean(boolean b) {
177+
throw new IllegalStateException("Invalid type (boolean) encountered " + b);
178+
}
179+
180+
@Override
181+
public String visitNumber(String s) {
182+
return s;
183+
}
184+
185+
@Override
186+
public String visitString(String s) {
187+
return s;
188+
}
189+
190+
@Override
191+
public String visitArray(List<JsonNode> list) {
192+
throw new IllegalStateException("Invalid type (list) encountered: " + list);
193+
}
194+
195+
@Override
196+
public String visitObject(Map<String, JsonNode> map) {
197+
throw new IllegalStateException("Invalid type (map) encountered: " + map);
198+
}
199+
200+
@Override
201+
public String visitEmbeddedObject(Object o) {
202+
throw new IllegalStateException("Invalid type (embedded) encountered: " + o);
203+
}
204+
}
205+
}

0 commit comments

Comments
 (0)