docs: @Nonnull
    public EChange setUserGroupData (@Nullable final String sUserGroupID,
                                     @Nonnull @Nonempty final String sNewName,
                                     @Nullable final String sNewDescription,
                                     @Nullable final Map <String, String> aNewCustomAttrs)
    {
      // Resolve user group
      final UserGroup aUserGroup = getOfID (sUserGroupID);
      if (aUserGroup == null)
      {
        AuditHelper.onAuditModifyFailure (UserGroup.OT, sUserGroupID, "no-such-usergroup-id");
        return EChange.UNCHANGED;
      }
  
      m_aRWLock.writeLock ().lock ();
      try
      {
        EChange eChange = aUserGroup.setName (sNewName);
        eChange = eChange.or (aUserGroup.setDescription (sNewDescription));
        eChange = eChange.or (aUserGroup.attrs ().setAll (aNewCustomAttrs));
        if (eChange.isUnchanged ())
          return EChange.UNCHANGED;
  
        BusinessObjectHelper.setLastModificationNow (aUserGroup);
        internalUpdateItem (aUserGroup);
      }
      finally
      {
        m_aRWLock.writeLock ().unlock ();
      }
      AuditHelper.onAuditModifySuccess (UserGroup.OT,
                                        "all",
                                        aUserGroup.getID (),
                                        sNewName,
                                        sNewDescription,
                                        aNewCustomAttrs);
  
      // Execute callback as the very last action
      m_aCallbacks.forEach (aCB -> aCB.onUserGroupUpdated (aUserGroup));
  
      return EChange.CHANGED;
    }