use of org.apache.ignite.internal.configuration.direct.KeyPathNode in project ignite-3 by apache.
the class ConfigurationUtil method findEx.
/**
* Finds a node or a leaf by the given path.
*
* @param path Path.
* @param rootNode Root node.
* @param <T> Arbitrary result type.
* @return Node or leaf.
*/
@Nullable
public static <T> T findEx(List<KeyPathNode> path, InnerNode rootNode) {
try {
var visitor = new ConfigurationVisitor<T>() {
private final int pathSize = path.size();
/**
* Current index of the key in the {@code path}.
*/
private int idx;
/**
* {@inheritDoc}
*/
@Override
public T visitLeafNode(String key, Serializable val) {
if (idx != pathSize) {
throw new KeyNotFoundException("Configuration value '" + joinPath(path.subList(0, idx)) + "' is a leaf");
} else {
return (T) val;
}
}
/**
* {@inheritDoc}
*/
@Override
public T visitInnerNode(String key, InnerNode node) {
if (node == null) {
throw new KeyNotFoundException("Configuration node '" + joinPath(path.subList(0, idx)) + "' is null");
} else if (idx == pathSize) {
return (T) node;
} else {
try {
KeyPathNode pathNode = path.get(idx++);
assert !pathNode.unresolvedName;
if (INTERNAL_ID.equals(pathNode.key)) {
// It's impossible to get this value with a regular traversal. Just call a method.
return (T) node.internalId();
} else if (INJECTED_NAME.equals(pathNode.key)) {
// It's impossible to get this value with a regular traversal. Just call a method.
return (T) node.getInjectedNameFieldValue();
}
return node.traverseChild(pathNode.key, this, true);
} catch (NoSuchElementException e) {
throw new KeyNotFoundException("Configuration value '" + joinPath(path.subList(0, idx)) + "' has not been found");
} catch (ConfigurationWrongPolymorphicTypeIdException e) {
assert false : e;
return null;
}
}
}
/**
* {@inheritDoc}
*/
@Override
public T visitNamedListNode(String key, NamedListNode<?> node) {
if (idx == pathSize) {
return (T) node;
} else {
KeyPathNode pathNode = path.get(idx++);
assert pathNode.namedListEntry;
String name = pathNode.unresolvedName ? pathNode.key : node.keyByInternalId(UUID.fromString(pathNode.key));
return visitInnerNode(name, node.getInnerNode(name));
}
}
};
return rootNode.accept(null, visitor);
} catch (KeyNotFoundException e) {
throw new NoSuchElementException(joinPath(path));
}
}
use of org.apache.ignite.internal.configuration.direct.KeyPathNode in project ignite-3 by apache.
the class DirectProxyAsmGenerator method addGetMethod.
/**
* Generates getter based on the field.
*/
private void addGetMethod(Field schemaField) {
Class<?> schemaFieldType = schemaField.getType();
String fieldName = schemaField.getName();
SchemaClassesInfo schemaClassInfo = cgen.schemaInfo(schemaFieldType);
ParameterizedType returnType;
// Return type is determined like in ConfigurationImpl class.
if (isConfigValue(schemaField)) {
returnType = typeFromJavaClassName(schemaClassInfo.cfgClassName);
} else if (isNamedConfigValue(schemaField)) {
returnType = type(NamedConfigurationTree.class);
} else {
assert isValue(schemaField) || isPolymorphicId(schemaField) || isInjectedName(schemaField) || isInternalId(schemaField) : schemaField;
returnType = type(ConfigurationValue.class);
}
MethodDefinition methodDef = classDef.declareMethod(of(PUBLIC), fieldName, returnType);
BytecodeBlock body = methodDef.getBody();
if (isValue(schemaField) || isPolymorphicId(schemaField) || isInjectedName(schemaField) || isInternalId(schemaField)) {
// new DirectValueProxy(appendKey(this.keys, new KeyPathNode("name")), changer);
// or
// new DirectValueProxy(appendKey(this.keys, new KeyPathNode("<internal_id>")), changer);
body.append(newInstance(DirectValueProxy.class, invokeStatic(APPEND_KEY, methodDef.getThis().getField("keys", List.class), newInstance(KeyPathNode.class, constantString(isInjectedName(schemaField) ? InnerNode.INJECTED_NAME : isInternalId(schemaField) ? InnerNode.INTERNAL_ID : fieldName))), methodDef.getThis().getField("changer", DynamicConfigurationChanger.class)));
} else {
SchemaClassesInfo fieldSchemaClassInfo = cgen.schemaInfo(schemaField.getType());
ParameterizedType resultType = typeFromJavaClassName(fieldSchemaClassInfo.directProxyClassName);
if (isConfigValue(schemaField)) {
// new BarDirectProxy(appendKey(this.keys, new KeyPathNode("name")), changer);
body.append(newInstance(resultType, invokeStatic(APPEND_KEY, methodDef.getThis().getField("keys", List.class), newInstance(KeyPathNode.class, constantString(fieldName))), methodDef.getThis().getField("changer", DynamicConfigurationChanger.class)));
} else {
// new DirectNamedListProxy(appendKey(this.keys, new KeyPathNode("name")), changer, BarDirectProxy::new);
body.append(newInstance(DirectNamedListProxy.class, invokeStatic(APPEND_KEY, methodDef.getThis().getField("keys", List.class), newInstance(KeyPathNode.class, constantString(fieldName))), methodDef.getThis().getField("changer", DynamicConfigurationChanger.class), newDirectProxyLambda(fieldSchemaClassInfo)));
}
}
// Return object from the above.
body.retObject();
}
use of org.apache.ignite.internal.configuration.direct.KeyPathNode in project ignite-3 by apache.
the class ConfigurationChanger method getLatest.
/**
* {@inheritDoc}
*/
@Override
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(escape(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 = storage.readLatest(unresolvedNameKey);
if (resolvedName == null) {
throw new NoSuchElementException(prefixJoiner + KEY_SEPARATOR + escape(keyPathNode.key));
}
assert resolvedName instanceof String : resolvedName;
// Resolved internal id from the map.
String internalId = (String) resolvedName;
// `*.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) UUID.fromString(internalId);
}
prefixJoiner.add(internalId);
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));
}
// 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 = 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();
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 = 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);
}
use of org.apache.ignite.internal.configuration.direct.KeyPathNode in project ignite-3 by apache.
the class ConfigurationNode method keyPath.
/**
* Converts {@link #keys} into a list of {@link KeyPathNode}. Result is used in implementations of {@link DirectPropertyProxy}.
*/
protected final List<KeyPathNode> keyPath() {
if (listenOnly) {
throw listenOnlyException();
}
ConfigurationVisitor<List<KeyPathNode>> visitor = new ConfigurationVisitor<>() {
/**
* List with the result.
*/
private List<KeyPathNode> res = new ArrayList<>(keys.size());
/**
* Current index.
*/
private int idx = 1;
/**
* {@inheritDoc}
*/
@Nullable
@Override
public List<KeyPathNode> visitLeafNode(String key, Serializable val) {
res.add(new KeyPathNode(key));
return res;
}
/**
* {@inheritDoc}
*/
@Nullable
@Override
public List<KeyPathNode> visitInnerNode(String key, InnerNode node) {
res.add(new KeyPathNode(key));
if (keys.size() == idx) {
return res;
}
node.traverseChild(keys.get(idx++), this, true);
return res;
}
/**
* {@inheritDoc}
*/
@Nullable
@Override
public List<KeyPathNode> visitNamedListNode(String key, NamedListNode node) {
res.add(new KeyPathNode(key));
if (keys.size() == idx) {
return res;
}
InnerNode innerNode = node.getInnerNode(keys.get(idx++));
if (innerNode == null) {
throw noSuchElementException();
}
// This is important, node is added as a resolved named list entry here.
res.add(new KeyPathNode(innerNode.internalId().toString(), false));
if (keys.size() == idx) {
return res;
}
innerNode.traverseChild(keys.get(idx++), this, true);
return res;
}
};
return changer.getRootNode(rootKey).accept(keys.get(0), visitor);
}
Aggregations