Skip to content

Commit 23e33a3

Browse files
committed
Add support to generate DefaultsMode and implement configuration resolution
1 parent 07a790b commit 23e33a3

File tree

21 files changed

+1097
-0
lines changed

21 files changed

+1097
-0
lines changed
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.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.DefaultsModeGenerator;
29+
30+
/**
31+
* The Maven mojo to generate defaults mode related classes.
32+
*/
33+
@Mojo(name = "generate-defaults-mode")
34+
public class DefaultsModeGenerationMojo extends AbstractMojo {
35+
36+
private static final String DEFAULTS_MODE_BASE = "software.amazon.awssdk.defaultsmode";
37+
38+
@Parameter(property = "outputDirectory", defaultValue = "${project.build.directory}")
39+
private String outputDirectory;
40+
41+
@Parameter(defaultValue = "${project}", readonly = true)
42+
private MavenProject project;
43+
44+
@Parameter(property = "defaultConfigurationFile", defaultValue =
45+
"${basedir}/src/main/resources/software/amazon/awssdk/internal/defaults/sdk-default-configuration.json")
46+
private File defaultConfigurationFile;
47+
48+
public void execute() {
49+
Path baseSourcesDirectory = Paths.get(outputDirectory).resolve("generated-sources").resolve("sdk");
50+
Path testsDirectory = Paths.get(outputDirectory).resolve("generated-test-sources").resolve("sdk-tests");
51+
52+
DefaultConfiguration configuration = DefaultsLoader.load(defaultConfigurationFile);
53+
54+
generateDefaultsModeClass(baseSourcesDirectory, configuration);
55+
56+
project.addCompileSourceRoot(baseSourcesDirectory.toFile().getAbsolutePath());
57+
project.addTestCompileSourceRoot(testsDirectory.toFile().getAbsolutePath());
58+
}
59+
60+
public void generateDefaultsModeClass(Path baseSourcesDirectory, DefaultConfiguration configuration) {
61+
Path sourcesDirectory = baseSourcesDirectory.resolve(DEFAULTS_MODE_BASE.replace(".", "/"));
62+
new CodeGenerator(sourcesDirectory.toString(), new DefaultsModeGenerator(DEFAULTS_MODE_BASE, configuration)).generate();
63+
}
64+
65+
}

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: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
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+
UNSUPPORTED_OPTIONS.add("tlsNegotiationTimeoutInMillis");
44+
}
45+
46+
private DefaultsLoader() {
47+
}
48+
49+
public static DefaultConfiguration load(File path) {
50+
return loadDefaultsFromFile(path);
51+
}
52+
53+
private static DefaultConfiguration loadDefaultsFromFile(File path) {
54+
DefaultConfiguration defaultsResolution = new DefaultConfiguration();
55+
Map<String, Map<String, String>> resolvedDefaults = new HashMap<>();
56+
57+
try (FileInputStream fileInputStream = new FileInputStream(path)) {
58+
JsonNodeParser jsonNodeParser = JsonNodeParser.builder().build();
59+
60+
Map<String, JsonNode> sdkDefaultConfiguration = jsonNodeParser.parse(fileInputStream)
61+
.asObject();
62+
63+
Map<String, JsonNode> base = sdkDefaultConfiguration.get("base").asObject();
64+
Map<String, JsonNode> modes = sdkDefaultConfiguration.get("modes").asObject();
65+
66+
modes.forEach((mode, modifiers) -> applyModificationToOneMode(resolvedDefaults, base, mode, modifiers));
67+
68+
Map<String, JsonNode> documentation = sdkDefaultConfiguration.get("documentation").asObject();
69+
Map<String, JsonNode> modesDocumentation = documentation.get("modes").asObject();
70+
Map<String, JsonNode> configDocumentation = documentation.get("configuration").asObject();
71+
72+
defaultsResolution.modesDocumentation(
73+
modesDocumentation.entrySet()
74+
.stream()
75+
.collect(HashMap::new, (m, e) -> m.put(e.getKey(), e.getValue().asString()), Map::putAll));
76+
defaultsResolution.configurationDocumentation(
77+
configDocumentation.entrySet()
78+
.stream()
79+
.filter(e -> !UNSUPPORTED_OPTIONS.contains(e.getKey()))
80+
.collect(HashMap::new, (m, e) -> m.put(e.getKey(), e.getValue().asString()), Map::putAll));
81+
82+
} catch (IOException e) {
83+
throw new RuntimeException(e);
84+
}
85+
86+
defaultsResolution.modeDefaults(resolvedDefaults);
87+
88+
return defaultsResolution;
89+
}
90+
91+
private static void applyModificationToOneConfigurationOption(Map<String, String> resolvedDefaultsForCurrentMode,
92+
String option,
93+
JsonNode modifier) {
94+
String resolvedValue;
95+
String baseValue = resolvedDefaultsForCurrentMode.get(option);
96+
97+
if (UNSUPPORTED_OPTIONS.contains(option)) {
98+
return;
99+
}
100+
101+
Map<String, JsonNode> modifierMap = modifier.asObject();
102+
103+
if (modifierMap.size() != 1) {
104+
throw new IllegalStateException("More than one modifier exists for option " + option);
105+
}
106+
107+
String modifierString = modifierMap.keySet().iterator().next();
108+
109+
switch (modifierString) {
110+
case "override":
111+
resolvedValue = modifierMap.get("override").visit(new StringJsonNodeVisitor());
112+
break;
113+
case "multiply":
114+
resolvedValue = processMultiply(baseValue, modifierMap);
115+
break;
116+
case "add":
117+
resolvedValue = processAdd(baseValue, modifierMap);
118+
break;
119+
default:
120+
throw new UnsupportedOperationException("Unsupported modifier: " + modifierString);
121+
}
122+
123+
resolvedDefaultsForCurrentMode.put(option, resolvedValue);
124+
}
125+
126+
private static void applyModificationToOneMode(Map<String, Map<String, String>> resolvedDefaults,
127+
Map<String, JsonNode> base,
128+
String mode,
129+
JsonNode modifiers) {
130+
131+
log.info(() -> "Apply modification for mode: " + mode);
132+
Map<String, String> resolvedDefaultsForCurrentMode =
133+
base.entrySet().stream().filter(e -> !UNSUPPORTED_OPTIONS.contains(e.getKey()))
134+
.collect(HashMap::new, (m, e) -> m.put(e.getKey(),
135+
e.getValue().visit(new StringJsonNodeVisitor())), Map::putAll);
136+
137+
138+
// Iterate the configuration options and apply modification.
139+
modifiers.asObject().forEach((option, modifier) -> applyModificationToOneConfigurationOption(
140+
resolvedDefaultsForCurrentMode, option, modifier));
141+
142+
resolvedDefaults.put(mode, resolvedDefaultsForCurrentMode);
143+
}
144+
145+
private static String processAdd(String baseValue, Map<String, JsonNode> modifierMap) {
146+
String resolvedValue;
147+
String add = modifierMap.get("add").asNumber();
148+
int parsedAdd = Integer.parseInt(add);
149+
int number = Math.addExact(Integer.parseInt(baseValue), parsedAdd);
150+
resolvedValue = String.valueOf(number);
151+
return resolvedValue;
152+
}
153+
154+
private static String processMultiply(String baseValue, Map<String, JsonNode> modifierMap) {
155+
String resolvedValue;
156+
String multiply = modifierMap.get("multiply").asNumber();
157+
double parsedValue = Double.parseDouble(multiply);
158+
159+
double resolvedNumber = Integer.parseInt(baseValue) * parsedValue;
160+
int castValue = (int) resolvedNumber;
161+
162+
if (castValue != resolvedNumber) {
163+
throw new IllegalStateException("The transformed value must be be a float number: " + castValue);
164+
}
165+
166+
resolvedValue = String.valueOf(castValue);
167+
return resolvedValue;
168+
}
169+
170+
private static final class StringJsonNodeVisitor implements JsonNodeVisitor<String> {
171+
@Override
172+
public String visitNull() {
173+
throw new IllegalStateException("Invalid type encountered");
174+
}
175+
176+
@Override
177+
public String visitBoolean(boolean b) {
178+
throw new IllegalStateException("Invalid type (boolean) encountered " + b);
179+
}
180+
181+
@Override
182+
public String visitNumber(String s) {
183+
return s;
184+
}
185+
186+
@Override
187+
public String visitString(String s) {
188+
return s;
189+
}
190+
191+
@Override
192+
public String visitArray(List<JsonNode> list) {
193+
throw new IllegalStateException("Invalid type (list) encountered: " + list);
194+
}
195+
196+
@Override
197+
public String visitObject(Map<String, JsonNode> map) {
198+
throw new IllegalStateException("Invalid type (map) encountered: " + map);
199+
}
200+
201+
@Override
202+
public String visitEmbeddedObject(Object o) {
203+
throw new IllegalStateException("Invalid type (embedded) encountered: " + o);
204+
}
205+
}
206+
}

0 commit comments

Comments
 (0)