docs: private void processGroups(ConfigurationAdmin configAdmin,
                                 String roleName,
                                 Dictionary<String, Object> roleProps, Set<String> pids) {
  
          String[] groupPids = (String[]) roleProps.get(CFG_KEY_GROUP);
          if (groupPids == null || groupPids.length == 0) {
              if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) {
                  Tr.debug(tc, "No groups in role " + roleName);
              }
          } else {
              Set<String> badGroups = new HashSet<String>();
              Set<String> badAccessIds = new HashSet<String>();
              for (int i = 0; i < groupPids.length; i++) {
                  if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) {
                      Tr.debug(tc, "group pid " + i + ": " + groupPids[i]);
                  }
                  pids.add(groupPids[i]);
                  Configuration groupConfig = null;
                  try {
                      groupConfig = configAdmin.getConfiguration(groupPids[i], bundleLocation);
                  } catch (IOException ioe) {
                      if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) {
                          Tr.debug(tc, "Invalid group entry " + groupPids[i]);
                      }
                      continue;
                  }
                  if (groupConfig == null || groupConfig.getProperties() == null) {
                      if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) {
                          Tr.debug(tc, "Null group element", groupPids[i]);
                      }
                      continue;
                  }
  
                  Dictionary<String, Object> groupProps = groupConfig.getProperties();
                  final String name = (String) groupProps.get("name");
                  final String accessId = (String) groupProps.get("access-id");
                  if (name == null || name.trim().isEmpty()) {
                      continue;
                  }
                  // access id takes precedence
                  if (accessId != null && AccessIdUtil.isGroupAccessId(accessId)) {
                      if (badAccessIds.contains(accessId)) {
                          // This accessId is already flagged as a duplicate
                          continue;
                      }
                      if (!accessIds.add(accessId)) {
                          Tr.error(tc, "AUTHZ_TABLE_DUPLICATE_ROLE_MEMBER", getRoleName(), CFG_KEY_ACCESSID, accessId);
                          badAccessIds.add(accessId);
                          accessIds.remove(accessId);
                      }
                  } else {
                      // TODO: check for bad access id
                      if (badGroups.contains(name)) {
                          // This group is already flagged as a duplicate
                          continue;
                      }
                      if (name.trim().isEmpty()) {
                          // Empty entry, ignoring
                          continue;
                      }
                      if (!groups.add(name)) {
                          Tr.error(tc, "AUTHZ_TABLE_DUPLICATE_ROLE_MEMBER", getRoleName(), CFG_KEY_GROUP, name);
                          badGroups.add(name);
                          groups.remove(name);
                      }
                  }
              }
              if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) {
                  Tr.debug(tc, "Role " + roleName + " has groups:", groups);
              }
          }
      }