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 DefaultsLoader()45 private DefaultsLoader() { 46 } 47 load(File path)48 public static DefaultConfiguration load(File path) { 49 return loadDefaultsFromFile(path); 50 } 51 loadDefaultsFromFile(File path)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 applyModificationToOneConfigurationOption(Map<String, String> resolvedDefaultsForCurrentMode, String option, JsonNode modifier)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 applyModificationToOneMode(Map<String, Map<String, String>> resolvedDefaults, Map<String, JsonNode> base, String mode, JsonNode modifiers)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 processAdd(String baseValue, Map<String, JsonNode> modifierMap)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 processMultiply(String baseValue, Map<String, JsonNode> modifierMap)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 visitNull()171 public String visitNull() { 172 throw new IllegalStateException("Invalid type encountered"); 173 } 174 175 @Override visitBoolean(boolean b)176 public String visitBoolean(boolean b) { 177 throw new IllegalStateException("Invalid type (boolean) encountered " + b); 178 } 179 180 @Override visitNumber(String s)181 public String visitNumber(String s) { 182 return s; 183 } 184 185 @Override visitString(String s)186 public String visitString(String s) { 187 return s; 188 } 189 190 @Override visitArray(List<JsonNode> list)191 public String visitArray(List<JsonNode> list) { 192 throw new IllegalStateException("Invalid type (list) encountered: " + list); 193 } 194 195 @Override visitObject(Map<String, JsonNode> map)196 public String visitObject(Map<String, JsonNode> map) { 197 throw new IllegalStateException("Invalid type (map) encountered: " + map); 198 } 199 200 @Override visitEmbeddedObject(Object o)201 public String visitEmbeddedObject(Object o) { 202 throw new IllegalStateException("Invalid type (embedded) encountered: " + o); 203 } 204 } 205 } 206