Search in sources :

Example 1 with WRITE_ATTRIBUTE_OPERATION

use of org.jboss.hal.dmr.ModelDescriptionConstants.WRITE_ATTRIBUTE_OPERATION in project console by hal.

the class OperationFactory method fromChangeSet.

/**
 * Turns a change-set into a composite operation containing
 * {@linkplain org.jboss.hal.dmr.ModelDescriptionConstants#WRITE_ATTRIBUTE_OPERATION write-attribute} and
 * {@linkplain org.jboss.hal.dmr.ModelDescriptionConstants#UNDEFINE_ATTRIBUTE_OPERATION undefine-attribute} operations.
 * <p>
 * The composite operation will contain {@linkplain org.jboss.hal.dmr.ModelDescriptionConstants#UNDEFINE_ATTRIBUTE_OPERATION
 * undefine-attribute} operations which reflect the alternative attributes as defined in the specified metadata.
 *
 * @param address the fq address used for the operations
 * @param changeSet the changed values
 * @param metadata the metadata which should contain the attribute definitions of the change-set
 */
public Composite fromChangeSet(ResourceAddress address, Map<String, Object> changeSet, Metadata metadata) {
    // TODO Is it safe to always use ATTRIBUTES as path when calling ResourceDescription methods?
    Map<String, Operation> operations = new HashMap<>();
    HashMap<String, Object> localChanges = new HashMap<>(changeSet);
    ResourceDescription resourceDescription = metadata.getDescription();
    // look for alternatives
    Set<String> conflicts = new HashSet<>();
    Map<String, List<String>> allAlternatives = localChanges.keySet().stream().filter(name -> {
        Object value = changeSet.get(name);
        return !isNullOrEmpty(value);
    }).collect(toMap(identity(), name -> resourceDescription.findAlternatives(ATTRIBUTES, name)));
    allAlternatives.forEach((attribute, alternatives) -> {
        logger.debug("Alternatives resolution for {} -> [{}]", attribute, String.join(", ", alternatives));
        HashSet<String> intersection = new HashSet<>(alternatives);
        intersection.retainAll(changeSet.keySet());
        if (intersection.isEmpty()) {
            // the easy part: no conflicts
            alternatives.forEach(alternative -> {
                boolean alternativeDoesntExist = resourceDescription.findAttribute(ATTRIBUTES, alternative) == null;
                if (resourceDescription.isDeprecated(ATTRIBUTES, alternative) || alternativeDoesntExist) {
                    logger.debug("Skip undefine operations for deprecated or non-existent alternative {}", alternative);
                } else {
                    logger.debug("Add undefine operations for alternative {}", alternative);
                    operations.putIfAbsent(alternative, undefineAttribute(address, alternative));
                    List<String> requires = resourceDescription.findRequires(ATTRIBUTES, alternative);
                    if (!requires.isEmpty()) {
                        logger.debug("Add undefine operations for attributes which require {}: [{}]", alternative, String.join(", ", requires));
                        requires.forEach(r -> operations.putIfAbsent(r, undefineAttribute(address, r)));
                    }
                }
            });
        } else {
            // possible conflicts: one or more alternatives are also in the change-set
            // just collect for now and resolve later
            conflicts.add(attribute);
            conflicts.addAll(intersection);
            logger.debug("Record conflict {} <-> [{}]", attribute, String.join(", ", intersection));
        }
        alternatives.forEach(localChanges::remove);
    });
    if (!conflicts.isEmpty()) {
        // try to resolve conflicts: only one of the conflicting attributes must have a value other than
        // null, empty or default
        logger.debug("Try to resolve conflicts between alternatives [{}]", String.join(", ", conflicts));
        Map<Boolean, List<String>> resolution = conflicts.stream().collect(groupingBy(conflict -> {
            Object value = changeSet.get(conflict);
            return isNullOrEmpty(value) || resourceDescription.isDefaultValue(ATTRIBUTES, conflict, value);
        }));
        List<String> undefine = resolution.getOrDefault(true, Collections.emptyList());
        List<String> write = resolution.getOrDefault(false, Collections.emptyList());
        if (write.size() > 1) {
            logger.error("More than one conflicting alternative attribute which is not null, empty or default: [{}]. This should have been caught by a form validation. Adding the write operations anyway to get an appropriate error message from the server.", String.join(", ", write));
        }
        logger.debug("Add undefine operations for [{}], write operation for [{}]", String.join(", ", undefine), String.join(", ", write));
        undefine.forEach(u -> {
            operations.putIfAbsent(u, undefineAttribute(address, u));
            localChanges.remove(u);
            // process requires of the current undefine attribute
            List<String> requires = resourceDescription.findRequires(ATTRIBUTES, u);
            requires.forEach(ur -> {
                operations.putIfAbsent(ur, undefineAttribute(address, ur));
                localChanges.remove(ur);
            });
        });
        write.forEach(w -> {
            operations.putIfAbsent(w, writeAttribute(address, w, changeSet.get(w), resourceDescription, true));
            localChanges.remove(w);
            List<String> writeAlternatives = resourceDescription.findAlternatives(ATTRIBUTES, w);
            // process alternatives of the current write attribute
            writeAlternatives.forEach(wa -> {
                operations.putIfAbsent(wa, undefineAttribute(address, wa));
                localChanges.remove(wa);
            });
        });
    }
    // handle the remaining attributes
    logger.debug("Process remaining attributes [{}]", String.join(", ", localChanges.keySet()));
    localChanges.forEach((name, value) -> operations.putIfAbsent(name, writeAttribute(address, name, value, resourceDescription, false)));
    return new Composite(operations.values().stream().filter(Objects::nonNull).collect(toList()));
}
Also used : ModelNode(org.jboss.hal.dmr.ModelNode) DEFAULT(org.jboss.hal.dmr.ModelDescriptionConstants.DEFAULT) TYPE(org.jboss.hal.dmr.ModelDescriptionConstants.TYPE) VALUE_TYPE(org.jboss.hal.dmr.ModelDescriptionConstants.VALUE_TYPE) LoggerFactory(org.slf4j.LoggerFactory) Collectors.groupingBy(java.util.stream.Collectors.groupingBy) HashMap(java.util.HashMap) Function(java.util.function.Function) ResourceAddress(org.jboss.hal.dmr.ResourceAddress) TreeSet(java.util.TreeSet) ArrayList(java.util.ArrayList) ModelType(org.jboss.hal.dmr.ModelType) HashSet(java.util.HashSet) BigDecimal(java.math.BigDecimal) Property(org.jboss.hal.dmr.Property) ResourceDescription(org.jboss.hal.meta.description.ResourceDescription) Strings(com.google.common.base.Strings) Collectors.toMap(java.util.stream.Collectors.toMap) VALUE(org.jboss.hal.dmr.ModelDescriptionConstants.VALUE) Map(java.util.Map) Expression(org.jboss.hal.core.expression.Expression) NAME(org.jboss.hal.dmr.ModelDescriptionConstants.NAME) UNDEFINE_ATTRIBUTE_OPERATION(org.jboss.hal.dmr.ModelDescriptionConstants.UNDEFINE_ATTRIBUTE_OPERATION) BigInteger(java.math.BigInteger) EXPRESSIONS_ALLOWED(org.jboss.hal.dmr.ModelDescriptionConstants.EXPRESSIONS_ALLOWED) Metadata(org.jboss.hal.meta.Metadata) Logger(org.slf4j.Logger) ACCESS_TYPE(org.jboss.hal.dmr.ModelDescriptionConstants.ACCESS_TYPE) READ_ONLY(org.jboss.hal.dmr.ModelDescriptionConstants.READ_ONLY) Operation(org.jboss.hal.dmr.Operation) ModelNodeHelper.failSafeList(org.jboss.hal.dmr.ModelNodeHelper.failSafeList) ALTERNATIVES(org.jboss.hal.dmr.ModelDescriptionConstants.ALTERNATIVES) Set(java.util.Set) WRITE_ATTRIBUTE_OPERATION(org.jboss.hal.dmr.ModelDescriptionConstants.WRITE_ATTRIBUTE_OPERATION) NILLABLE(org.jboss.hal.dmr.ModelDescriptionConstants.NILLABLE) Objects(java.util.Objects) Composite(org.jboss.hal.dmr.Composite) List(java.util.List) Collectors.toList(java.util.stream.Collectors.toList) ATTRIBUTES(org.jboss.hal.dmr.ModelDescriptionConstants.ATTRIBUTES) Function.identity(java.util.function.Function.identity) Collections(java.util.Collections) REQUIRES(org.jboss.hal.dmr.ModelDescriptionConstants.REQUIRES) Composite(org.jboss.hal.dmr.Composite) HashMap(java.util.HashMap) Operation(org.jboss.hal.dmr.Operation) ResourceDescription(org.jboss.hal.meta.description.ResourceDescription) Objects(java.util.Objects) ArrayList(java.util.ArrayList) ModelNodeHelper.failSafeList(org.jboss.hal.dmr.ModelNodeHelper.failSafeList) List(java.util.List) Collectors.toList(java.util.stream.Collectors.toList) HashSet(java.util.HashSet)

Aggregations

Strings (com.google.common.base.Strings)1 BigDecimal (java.math.BigDecimal)1 BigInteger (java.math.BigInteger)1 ArrayList (java.util.ArrayList)1 Collections (java.util.Collections)1 HashMap (java.util.HashMap)1 HashSet (java.util.HashSet)1 List (java.util.List)1 Map (java.util.Map)1 Objects (java.util.Objects)1 Set (java.util.Set)1 TreeSet (java.util.TreeSet)1 Function (java.util.function.Function)1 Function.identity (java.util.function.Function.identity)1 Collectors.groupingBy (java.util.stream.Collectors.groupingBy)1 Collectors.toList (java.util.stream.Collectors.toList)1 Collectors.toMap (java.util.stream.Collectors.toMap)1 Expression (org.jboss.hal.core.expression.Expression)1 Composite (org.jboss.hal.dmr.Composite)1 ACCESS_TYPE (org.jboss.hal.dmr.ModelDescriptionConstants.ACCESS_TYPE)1