public PropagationByResource update()

in core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java [270:470]


    public PropagationByResource<String> update(final Group toBeUpdated, final GroupUR groupUR) {
        // Re-merge any pending change from workflow tasks
        Group group = groupDAO.save(toBeUpdated);

        GroupTO anyTO = AnyOperations.patch(getGroupTO(group, true), groupUR);

        // Save projection on Resources (before update)
        Map<String, ConnObject> beforeOnResources =
                onResources(group, groupDAO.findAllResourceKeys(group.getKey()), null, Set.of());

        SyncopeClientCompositeException scce = SyncopeClientException.buildComposite();

        // realm
        setRealm(group, groupUR);

        // name
        if (groupUR.getName() != null && StringUtils.isNotBlank(groupUR.getName().getValue())) {
            group.setName(groupUR.getName().getValue());
        }

        // owner
        PropagationByResource<String> ownerPropByRes = new PropagationByResource<>();
        if (groupUR.getUserOwner() != null) {
            if (groupUR.getUserOwner().getValue() == null) {
                if (group.getUserOwner() != null) {
                    group.setUserOwner(null);
                    ownerPropByRes.addAll(ResourceOperation.UPDATE, groupDAO.findAllResourceKeys(group.getKey()));
                }
            } else {
                User userOwner = userDAO.findById(groupUR.getUserOwner().getValue()).orElse(null);
                if (userOwner == null) {
                    LOG.debug("Unable to find user owner for group {} by key {}",
                            group.getKey(), groupUR.getUserOwner().getValue());
                    group.setUserOwner(null);
                } else {
                    group.setUserOwner(userOwner);
                    ownerPropByRes.addAll(ResourceOperation.UPDATE, groupDAO.findAllResourceKeys(group.getKey()));
                }
            }
        }
        if (groupUR.getGroupOwner() != null) {
            if (groupUR.getGroupOwner().getValue() == null) {
                if (group.getGroupOwner() != null) {
                    group.setGroupOwner(null);
                    ownerPropByRes.addAll(ResourceOperation.UPDATE, groupDAO.findAllResourceKeys(group.getKey()));
                }
            } else {
                Group groupOwner = groupDAO.findById(groupUR.getGroupOwner().getValue()).orElse(null);
                if (groupOwner == null) {
                    LOG.debug("Unable to find group owner for group {} by key {}",
                            group.getKey(), groupUR.getGroupOwner().getValue());
                    group.setGroupOwner(null);
                } else {
                    group.setGroupOwner(groupOwner);
                    ownerPropByRes.addAll(ResourceOperation.UPDATE, groupDAO.findAllResourceKeys(group.getKey()));
                }
            }
        }

        // attributes and resources
        fill(anyTO, group, groupUR, anyUtilsFactory.getInstance(AnyTypeKind.GROUP), scce);

        group = groupDAO.save(group);

        // dynamic membership
        if (groupUR.getUDynMembershipCond() == null) {
            if (group.getUDynMembership() != null) {
                group.getUDynMembership().setGroup(null);
                group.setUDynMembership(null);
                groupDAO.clearUDynMembers(group);
            }
        } else {
            setDynMembership(group, anyTypeDAO.getUser(), groupUR.getUDynMembershipCond());
        }
        for (Iterator<? extends ADynGroupMembership> itor = group.getADynMemberships().iterator(); itor.hasNext();) {
            ADynGroupMembership memb = itor.next();
            memb.setGroup(null);
            itor.remove();
        }
        groupDAO.clearADynMembers(group);
        for (Map.Entry<String, String> entry : groupUR.getADynMembershipConds().entrySet()) {
            AnyType anyType = anyTypeDAO.findById(entry.getKey()).orElse(null);
            if (anyType == null) {
                LOG.warn("Ignoring invalid {}: {}", AnyType.class.getSimpleName(), entry.getKey());
            } else {
                setDynMembership(group, anyType, entry.getValue());
            }
        }

        group = groupDAO.saveAndRefreshDynMemberships(group);

        // type extensions
        for (TypeExtensionTO typeExtTO : groupUR.getTypeExtensions()) {
            AnyType anyType = anyTypeDAO.findById(typeExtTO.getAnyType()).orElse(null);
            if (anyType == null) {
                LOG.warn("Ignoring invalid {}: {}", AnyType.class.getSimpleName(), typeExtTO.getAnyType());
            } else {
                TypeExtension typeExt = group.getTypeExtension(anyType).orElse(null);
                if (typeExt == null) {
                    typeExt = entityFactory.newEntity(TypeExtension.class);
                    typeExt.setAnyType(anyType);
                    typeExt.setGroup(group);
                    group.add(typeExt);
                }

                // add all classes contained in the TO
                for (String key : typeExtTO.getAuxClasses()) {
                    AnyTypeClass anyTypeClass = anyTypeClassDAO.findById(key).orElse(null);
                    if (anyTypeClass == null) {
                        LOG.warn("Ignoring invalid {}: {}", AnyTypeClass.class.getSimpleName(), key);
                    } else {
                        typeExt.add(anyTypeClass);
                    }
                }
                // remove all classes not contained in the TO
                typeExt.getAuxClasses().
                        removeIf(anyTypeClass -> !typeExtTO.getAuxClasses().contains(anyTypeClass.getKey()));

                // only consider non-empty type extensions
                if (typeExt.getAuxClasses().isEmpty()) {
                    group.getTypeExtensions().remove(typeExt);
                    typeExt.setGroup(null);
                }
            }
        }
        // remove all type extensions not contained in the TO
        group.getTypeExtensions().
                removeIf(typeExt -> groupUR.getTypeExtension(typeExt.getAnyType().getKey()).isEmpty());

        // relationships
        Set<Pair<String, String>> relationships = new HashSet<>();
        for (RelationshipUR patch : groupUR.getRelationships().stream().
                filter(patch -> patch.getRelationshipTO() != null).toList()) {

            RelationshipType relationshipType = relationshipTypeDAO.findById(patch.getRelationshipTO().getType()).
                    orElse(null);
            if (relationshipType == null) {
                LOG.debug("Ignoring invalid relationship type {}", patch.getRelationshipTO().getType());
            } else {
                GRelationship relationship = group.getRelationship(
                        relationshipType, patch.getRelationshipTO().getOtherEndKey()).orElse(null);
                if (relationship != null) {
                    group.getRelationships().remove(relationship);
                    relationship.setLeftEnd(null);
                }

                if (patch.getOperation() == PatchOperation.ADD_REPLACE) {
                    AnyObject otherEnd = anyObjectDAO.findById(patch.getRelationshipTO().getOtherEndKey()).orElse(null);
                    if (otherEnd == null) {
                        LOG.debug("Ignoring invalid any object {}", patch.getRelationshipTO().getOtherEndKey());
                    } else if (relationships.contains(
                            Pair.of(otherEnd.getKey(), patch.getRelationshipTO().getType()))) {

                        SyncopeClientException assigned =
                                SyncopeClientException.build(ClientExceptionType.InvalidRelationship);
                        assigned.getElements().add("Group was already in relationship "
                                + patch.getRelationshipTO().getType() + " with "
                                + otherEnd.getType().getKey() + " " + otherEnd.getName());
                        scce.addException(assigned);
                    } else if (patch.getRelationshipTO().getEnd() == RelationshipTO.End.RIGHT) {
                        SyncopeClientException noRight =
                                SyncopeClientException.build(ClientExceptionType.InvalidRelationship);
                        noRight.getElements().add(
                                "Relationships shall be created or updated only from their left end");
                        scce.addException(noRight);
                    } else if (group.getRealm().getFullPath().startsWith(otherEnd.getRealm().getFullPath())) {
                        relationships.add(Pair.of(otherEnd.getKey(), patch.getRelationshipTO().getType()));

                        GRelationship newRelationship = entityFactory.newEntity(GRelationship.class);
                        newRelationship.setType(relationshipType);
                        newRelationship.setRightEnd(otherEnd);
                        newRelationship.setLeftEnd(group);

                        group.add(newRelationship);
                    } else {
                        LOG.error("{} cannot be related to {}", otherEnd, group);

                        SyncopeClientException unrelatable =
                                SyncopeClientException.build(ClientExceptionType.InvalidRelationship);
                        unrelatable.getElements().add(otherEnd.getType().getKey() + " " + otherEnd.getName()
                                + " cannot be related");
                        scce.addException(unrelatable);
                    }
                }
            }
        }

        // Throw composite exception if there is at least one element set in the composing exceptions
        if (scce.hasExceptions()) {
            throw scce;
        }

        // Re-merge any pending change from above
        group = groupDAO.save(group);

        // Build final information for next stage (propagation)
        PropagationByResource<String> propByRes = propByRes(
                beforeOnResources, onResources(group, groupDAO.findAllResourceKeys(group.getKey()), null, Set.of()));
        propByRes.merge(ownerPropByRes);
        return propByRes;
    }