docs: private List<IPersonAttributes> getVisiblePersons(
              final IAuthorizationPrincipal principal,
              final Set<String> permittedAttributes,
              List<IPersonAttributes> peopleList) {
          List<Future<IPersonAttributes>> futures = new ArrayList<>();
          List<IPersonAttributes> list = new ArrayList<>();
  
          // Ugly.  PersonDirectory requires RequestContextHolder to be set for each thread, so pass
          // it
          // into the callable.
          RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
          // For each person in the list, check to see if the current user has permission to view this
          // user
          for (IPersonAttributes person : peopleList) {
              Callable<IPersonAttributes> worker =
                      new FetchVisiblePersonCallable(
                              principal, person, permittedAttributes, requestAttributes);
              Future<IPersonAttributes> task = executor.submit(worker);
              futures.add(task);
          }
          for (Future<IPersonAttributes> future : futures) {
              try {
                  final IPersonAttributes visiblePerson =
                          future.get(searchThreadTimeoutSeconds, TimeUnit.SECONDS);
                  if (visiblePerson != null) {
                      list.add(visiblePerson);
                  }
              } catch (InterruptedException e) {
                  logger.error("Processing person search interrupted", e);
              } catch (ExecutionException e) {
                  logger.error("Error Processing person search", e);
              } catch (TimeoutException e) {
                  future.cancel(true);
                  logger.warn(
                          "Exceeded {} ms waiting for getVisiblePerson to return result",
                          searchThreadTimeoutSeconds);
              }
          }
          logger.debug("Found {} results", list.size());
          return list;
      }