in modules/configuration/src/main/java/org/apache/ignite/internal/configuration/ConfigurationChanger.java [316:476]
public <T> T getLatest(List<KeyPathNode> path) {
assert !path.isEmpty();
assert path instanceof RandomAccess : path.getClass();
assert !path.get(0).unresolvedName : path;
// This map will be merged into the data from the storage. It's required for the conversion into tree to work.
// Namely, named list order indexes and names are mandatory for conversion.
Map<String, Map<String, Serializable>> extras = new HashMap<>();
// Joiner for the prefix that will be used to fetch data from the storage.
StringJoiner prefixJoiner = new StringJoiner(KEY_SEPARATOR);
int pathSize = path.size();
KeyPathNode lastPathNode = path.get(pathSize - 1);
// This loop is required to accumulate prefix and resolve all unresolved named list elements' ids.
for (int idx = 0; idx < pathSize; idx++) {
KeyPathNode keyPathNode = path.get(idx);
// Regular keys and resolved ids go straight to the prefix.
if (!keyPathNode.unresolvedName) {
// Fake name and 0 index go to extras in case of resolved named list elements.
if (keyPathNode.namedListEntry) {
prefixJoiner.add(keyPathNode.key);
String prefix = prefixJoiner + KEY_SEPARATOR;
extras.put(prefix, Map.of(
prefix + NamedListNode.NAME, "<name_placeholder>",
prefix + NamedListNode.ORDER_IDX, 0
));
} else {
prefixJoiner.add(keyPathNode.key);
}
continue;
}
assert keyPathNode.namedListEntry : path;
// Here we have unresolved named list element. Name must be translated into the internal id.
// There's a special path for this purpose in the storage.
String unresolvedNameKey = prefixJoiner + KEY_SEPARATOR
+ NamedListNode.IDS + KEY_SEPARATOR
+ escape(keyPathNode.key);
// Data from the storage.
Serializable resolvedName = get(storage.readLatest(unresolvedNameKey));
if (resolvedName == null) {
throw new NoSuchElementException(prefixJoiner + KEY_SEPARATOR + escape(keyPathNode.key));
}
assert resolvedName instanceof UUID : resolvedName;
// Resolved internal id from the map.
UUID internalId = (UUID) resolvedName;
// There's a chance that this is exactly what user wants. If their request ends with
// `*.get("resourceName").internalId()` then the result can be returned straight away.
if (idx == pathSize - 2 && INTERNAL_ID.equals(lastPathNode.key)) {
assert !lastPathNode.unresolvedName : path;
// Despite the fact that this cast looks very stupid, it is correct. Internal ids are always UUIDs.
return (T) internalId;
}
prefixJoiner.add(internalId.toString());
String prefix = prefixJoiner + KEY_SEPARATOR;
// Real name and 0 index go to extras in case of unresolved named list elements.
extras.put(prefix, Map.of(
prefix + NamedListNode.NAME, keyPathNode.key,
prefix + NamedListNode.ORDER_IDX, 0
));
}
// Exceptional case, the only purpose of it is to ensure that named list element with given internal id does exist.
// That id must be resolved, otherwise method would already be completed in the loop above.
if (lastPathNode.key.equals(INTERNAL_ID) && !lastPathNode.unresolvedName && path.get(pathSize - 2).namedListEntry) {
assert !path.get(pathSize - 2).unresolvedName : path;
// Not very elegant, I know. <internal_id> is replaced with the <name> in the prefix.
// <name> always exists in named list element, and it's an easy way to check element's existence.
String nameStorageKey = prefixJoiner.toString().replaceAll(quote(INTERNAL_ID) + "$", NamedListNode.NAME);
// Data from the storage.
Serializable name = get(storage.readLatest(nameStorageKey));
if (name != null) {
// Id is already known.
return (T) UUID.fromString(path.get(pathSize - 2).key);
} else {
throw new NoSuchElementException(prefixJoiner.toString());
}
}
String prefix = prefixJoiner.toString();
// Reading all ids is also a special case.
if (lastPathNode.key.equals(INTERNAL_IDS) && !lastPathNode.unresolvedName && path.get(pathSize - 1).namedListEntry) {
prefix = prefix.replaceAll(quote(INTERNAL_IDS) + "$", NamedListNode.IDS + KEY_SEPARATOR);
Map<String, ? extends Serializable> storageData = get(storage.readAllLatest(prefix));
return (T) List.copyOf(storageData.values());
}
if (lastPathNode.key.equals(INTERNAL_ID) && !path.get(pathSize - 2).namedListEntry) {
// This is not particularly efficient, but there's no way someone will actually use this case for real outside of tests.
prefix = prefix.replaceAll(quote(KEY_SEPARATOR + INTERNAL_ID) + "$", "");
} else if (lastPathNode.key.contains(INJECTED_NAME)) {
prefix = prefix.replaceAll(quote(KEY_SEPARATOR + INJECTED_NAME), "");
}
// Data from the storage.
Map<String, ? extends Serializable> storageData = get(storage.readAllLatest(prefix));
// Data to be converted into the tree.
Map<String, Serializable> mergedData = new HashMap<>();
if (!storageData.isEmpty()) {
mergedData.putAll(storageData);
for (Entry<String, Map<String, Serializable>> extrasEntry : extras.entrySet()) {
for (String storageKey : storageData.keySet()) {
String extrasPrefix = extrasEntry.getKey();
if (storageKey.startsWith(extrasPrefix)) {
// Add extra order indexes and names before converting it to the tree.
for (Entry<String, Serializable> extrasEntryMap : extrasEntry.getValue().entrySet()) {
mergedData.putIfAbsent(extrasEntryMap.getKey(), extrasEntryMap.getValue());
}
break;
}
}
}
if (lastPathNode.namedListEntry) {
// Change element's order index to zero. Conversion won't work if indexes range is not continuous.
mergedData.put(prefix + KEY_SEPARATOR + NamedListNode.ORDER_IDX, 0);
}
}
// Super root that'll be filled from the storage data.
InnerNode rootNode = new SuperRoot(rootCreator());
fillFromPrefixMap(rootNode, toPrefixMap(mergedData));
// "addDefaults" won't work if regular root is missing.
if (storageData.isEmpty()) {
rootNode.construct(path.get(0).key, ConfigurationUtil.EMPTY_CFG_SRC, true);
}
addDefaults(rootNode);
return findEx(path, rootNode);
}