private Group getPeerGroupOf(
     Group group, AuthorizableManager authorizableManager, Map<String, Object> toSave)
     throws AccessDeniedException, StorageClientException {
   Group peerGroup = null;
   if (group.hasProperty(UserConstants.PROP_MANAGERS_GROUP)) {
     String managersGroupId = (String) group.getProperty(UserConstants.PROP_MANAGERS_GROUP);
     if (group.getId().equals(managersGroupId)) {
       return group;
     }
     peerGroup = (Group) toSave.get(managersGroupId);
     if (peerGroup == null) {
       if (LOGGER.isDebugEnabled()) {
         LOGGER.debug(
             "For {} Not in toSave List loading Managers Group from store {} ",
             group.getId(),
             managersGroupId);
       }
       peerGroup = (Group) authorizableManager.findAuthorizable(managersGroupId);
     } else {
       if (LOGGER.isDebugEnabled()) {
         LOGGER.debug(
             "For {} got Managers Group from save list {} ", group.getId(), managersGroupId);
       }
     }
   } else if (group.hasProperty(UserConstants.PROP_MANAGED_GROUP)) {
     String managedGroupId = (String) group.getProperty(UserConstants.PROP_MANAGED_GROUP);
     if (group.getId().equals(managedGroupId)) {
       return group;
     }
     peerGroup = (Group) toSave.get(managedGroupId);
     if (peerGroup == null) {
       if (LOGGER.isDebugEnabled()) {
         LOGGER.debug(
             "For {} Not in toSave List loading Managed Group from store {} ",
             group.getId(),
             managedGroupId);
       }
       peerGroup = (Group) authorizableManager.findAuthorizable(managedGroupId);
     } else {
       if (LOGGER.isDebugEnabled()) {
         LOGGER.debug(
             "For {} got Managed Group from save list {} ", group.getId(), managedGroupId);
       }
     }
   }
   return peerGroup;
 }
  @Before
  public void setUp() throws Exception {
    provider = new MyRelatedGroupsPropertyProvider(searchServiceFactory);
    when(request.getRemoteUser()).thenReturn("user1");

    when(repo.loginAdministrative()).thenReturn(session);
    when(session.getAuthorizableManager()).thenReturn(authMgr);
    when(authMgr.findAuthorizable("user1")).thenReturn(auth1);

    Group group1 = mock(Group.class);
    when(group1.getId()).thenReturn("group1");
    when(group1.getProperty(GROUP_TITLE_PROPERTY)).thenReturn("Group 1 Test");
    when(group1.getProperty("sakai:tag-uuid")).thenReturn(new String[] {"123-456"});

    when(auth1.memberOf(authMgr)).thenReturn(Sets.newHashSet(group1).iterator());

    when(searchServiceFactory.getSearchResultSet(eq(request), any(Query.class))).thenReturn(rs);
  }
  /**
   * @param request The request that contains the authorizables.
   * @param group The group that should be modified.
   * @param propertyName The name of the property on the group where the authorizable IDs should be
   *     added/deleted.
   * @param paramName The name of the parameter that contains the authorizable IDs. ie: :manager or
   *     :viewer. If :manager is specified, :manager@Delete will be used for deletes.
   * @param extraPropertyValues An array of authorizable IDs that should be added as well.
   * @param toSave
   * @throws RepositoryException
   */
  protected void handleAuthorizablesOnProperty(
      SlingHttpServletRequest request,
      Group group,
      String propertyName,
      String paramName,
      String[] extraPropertyValues,
      Map<String, Object> toSave) {
    Set<String> propertyValueSet = new HashSet<String>();

    if (group.hasProperty(propertyName)) {
      String[] existingProperties = (String[]) group.getProperty(propertyName);
      for (String property : existingProperties) {
        propertyValueSet.add(property);
      }
    }

    boolean changed = false;

    // Remove all the managers that are in the :manager@Delete request parameter.
    String[] propertiesToDelete =
        request.getParameterValues(paramName + SlingPostConstants.SUFFIX_DELETE);
    if (propertiesToDelete != null) {
      for (String propertyToDelete : propertiesToDelete) {
        propertyValueSet.remove(propertyToDelete);
        changed = true;
      }
    }

    // Add the new ones (if any)
    String[] proeprtiesToAdd = request.getParameterValues(paramName);
    if (proeprtiesToAdd != null) {
      for (String propertyToAdd : proeprtiesToAdd) {
        propertyValueSet.add(propertyToAdd);
        changed = true;
      }
    }

    // Add the extra ones (if any.)
    if (extraPropertyValues != null) {
      for (String propertyValue : extraPropertyValues) {
        propertyValueSet.add(propertyValue);
        changed = true;
      }
    }

    // Write the property.
    if (changed) {
      group.setProperty(
          propertyName, propertyValueSet.toArray(new String[propertyValueSet.size()]));
      if (LOGGER.isDebugEnabled()) {
        LOGGER.debug("Adding to save Queue {} {}", group.getId(), group.getSafeProperties());
      }
      toSave.put(group.getId(), group);
    }
  }
  @Override
  protected TreeMap<String, Group> getGroups(
      Authorizable member, AuthorizableManager userManager, SlingHttpServletRequest request)
      throws StorageClientException, AccessDeniedException {
    TreeMap<String, Group> managedGroups = new TreeMap<String, Group>();
    Iterator<Group> allGroupsIter = member.memberOf(userManager);
    for (String principal : member.getPrincipals()) {
      Group group = (Group) userManager.findAuthorizable(principal);
      if (group != null && !group.getId().equals(Group.EVERYONE)) {

        boolean isManager = false;

        if (isPseudoGroup(group) && isManagerGroup(group, userManager)) {
          // The group we want is the child of the pseudo group
          isManager = true;
          group =
              (Group)
                  userManager.findAuthorizable(
                      (String) group.getProperty(UserConstants.PROP_PSEUDO_GROUP_PARENT));
        } else {
          for (String managerId :
              StorageClientUtils.nonNullStringArray(
                  (String[]) group.getProperty(UserConstants.PROP_GROUP_MANAGERS))) {
            if (member.getId().equals(managerId)) {
              isManager = true;
              break;
            }
          }
        }

        if (isManager) {
          final String category = stringRequestParameter(request, "category", null);
          if (category == null) { // no filtering
            managedGroups.put(group.getId(), group);
          } else { // KERN-1865 category filter
            if (category.equals(group.getProperty("sakai:category"))) {
              managedGroups.put(group.getId(), group);
            }
          }
        }
      }
    }

    return managedGroups;
  }
  protected void updateGroupMembership(
      SlingHttpServletRequest request,
      Session session,
      Authorizable authorizable,
      String paramName,
      List<Modification> changes,
      Map<String, Object> toSave)
      throws AccessDeniedException, StorageClientException {
    if (authorizable instanceof Group) {
      Group group = ((Group) authorizable);
      String groupPath =
          LiteAuthorizableResourceProvider.SYSTEM_USER_MANAGER_GROUP_PREFIX + group.getId();

      boolean changed = false;

      AuthorizableManager authorizableManager = session.getAuthorizableManager();

      // first remove any members posted as ":member@Delete"
      String[] membersToDelete =
          request.getParameterValues(paramName + SlingPostConstants.SUFFIX_DELETE);
      if (membersToDelete != null) {
        toSave.put(group.getId(), group);
        LOGGER.info("Members to delete {} ", membersToDelete);
        for (String member : membersToDelete) {
          String memberId = getAuthIdFromParameter(member);
          group.removeMember(memberId);
          changed = true;
        }
      }

      Joinable groupJoin = getJoinable(group);

      // second add any members posted as ":member"
      String[] membersToAdd = request.getParameterValues(paramName);
      if (membersToAdd != null) {
        LOGGER.info("Members to add {} ", membersToAdd);
        Group peerGroup = getPeerGroupOf(group, authorizableManager, toSave);
        List<Authorizable> membersToRemoveFromPeer = new ArrayList<Authorizable>();
        for (String member : membersToAdd) {
          String memberId = getAuthIdFromParameter(member);
          Authorizable memberAuthorizable = (Authorizable) toSave.get(memberId);
          if (memberAuthorizable == null) {
            memberAuthorizable = authorizableManager.findAuthorizable(memberId);
          }
          if (memberAuthorizable != null) {
            if (!User.ADMIN_USER.equals(session.getUserId())
                && !UserConstants.ANON_USERID.equals(session.getUserId())
                && Joinable.yes.equals(groupJoin)
                && memberAuthorizable.getId().equals(session.getUserId())) {
              LOGGER.debug("Is Joinable {} {} ", groupJoin, session.getUserId());
              // we can grab admin session since group allows all users to join
              Session adminSession = getSession();
              try {
                AuthorizableManager adminAuthorizableManager =
                    adminSession.getAuthorizableManager();
                Group adminAuthGroup =
                    (Group) adminAuthorizableManager.findAuthorizable(group.getId());
                if (adminAuthGroup != null) {
                  adminAuthGroup.addMember(memberAuthorizable.getId());
                  adminAuthorizableManager.updateAuthorizable(adminAuthGroup);
                  changed = true;
                }
              } finally {
                ungetSession(adminSession);
              }
            } else {
              LOGGER.info(
                  "Group {} is not Joinable: User {} adding {}  ",
                  new Object[] {
                    group.getId(), session.getUserId(), memberAuthorizable.getId(),
                  });
              // group is restricted, so use the current user's authorization
              // to add the member to the group:

              group.addMember(memberAuthorizable.getId());
              if (LOGGER.isInfoEnabled()) {
                LOGGER.info(
                    "{} Membership now {} {} {}",
                    new Object[] {
                      group.getId(),
                      Arrays.toString(group.getMembers()),
                      Arrays.toString(group.getMembersAdded()),
                      Arrays.toString(group.getMembersRemoved())
                    });
              }
              toSave.put(group.getId(), group);
              Group gt = (Group) toSave.get(group.getId());
              if (LOGGER.isInfoEnabled()) {
                LOGGER.info(
                    "{} Membership now {} {} {}",
                    new Object[] {
                      group.getId(),
                      Arrays.toString(gt.getMembers()),
                      Arrays.toString(gt.getMembersAdded()),
                      Arrays.toString(gt.getMembersRemoved())
                    });
              }
              changed = true;
            }
            if (peerGroup != null && peerGroup.getId() != group.getId()) {
              Set<String> members = ImmutableSet.of(peerGroup.getMembers());
              if (members.contains(memberAuthorizable.getId())) {
                membersToRemoveFromPeer.add(memberAuthorizable);
              }
            }
          } else {
            LOGGER.warn("member not found {} ", memberId);
          }
        }
        if ((peerGroup != null) && (membersToRemoveFromPeer.size() > 0)) {
          for (Authorizable member : membersToRemoveFromPeer) {
            if (LOGGER.isInfoEnabled()) {
              LOGGER.info("Removing Member {} from {} ", member.getId(), peerGroup.getId());
            }
            peerGroup.removeMember(member.getId());
          }
          toSave.put(peerGroup.getId(), peerGroup);
          if (LOGGER.isInfoEnabled()) {
            LOGGER.info(
                "{} Just Updated Peer Group Membership now {} {} {}",
                new Object[] {
                  peerGroup.getId(),
                  Arrays.toString(peerGroup.getMembers()),
                  Arrays.toString(peerGroup.getMembersAdded()),
                  Arrays.toString(peerGroup.getMembersRemoved())
                });
          }
        }
      }

      if (changed) {
        // add an entry to the changes list to record the membership
        // change
        changes.add(Modification.onModified(groupPath + "/members"));
      }
    }
  }
  @SuppressWarnings("unchecked")
  public void onMessage(Message message) {
    log.debug("Receiving a message on {} : {}", SyncJMSMessageProducer.QUEUE_NAME, message);
    try {

      String topic = message.getJMSType();
      String groupId = (String) message.getStringProperty("path");

      String operation = "UNKNOWN";

      // A group was DELETED
      if ("org/sakaiproject/nakamura/lite/authorizables/DELETE".equals(topic)
          && config.getDeletesEnabled()) {
        Map<String, Object> attributes =
            (Map<String, Object>) message.getObjectProperty(StoreListener.BEFORE_EVENT_PROPERTY);
        grouperManager.deleteGroup(groupId, attributes);
        operation = "DELETED";
      }

      // A new group was ADDED or an existing group was UPDATED
      if ("org/sakaiproject/nakamura/lite/authorizables/ADDED".equals(topic)
          || "org/sakaiproject/nakamura/lite/authorizables/UPDATED".equals(topic)) {
        // These events should be under org/sakaiproject/nakamura/lite/authorizables/UPDATED
        // http://jira.sakaiproject.org/browse/KERN-1795
        String membersAdded =
            (String) message.getStringProperty(GrouperEventUtils.MEMBERS_ADDED_PROP);
        if (membersAdded != null) {
          // membership adds can be attached to the same event for the group add.
          grouperManager.createGroup(groupId, config.getGroupTypes());
          grouperManager.addMemberships(
              groupId, Arrays.asList(StringUtils.split(membersAdded, ",")));
          operation = "ADD_MEMBERS";
        }

        String membersRemoved =
            (String) message.getStringProperty(GrouperEventUtils.MEMBERS_REMOVED_PROP);
        if (membersRemoved != null) {
          grouperManager.removeMemberships(
              groupId, Arrays.asList(StringUtils.split(membersRemoved, ",")));
          operation = "REMOVE_MEMBERS";
        }

        if (membersAdded == null && membersRemoved == null) {
          org.sakaiproject.nakamura.api.lite.Session repositorySession =
              repository.loginAdministrative();
          AuthorizableManager am = repositorySession.getAuthorizableManager();
          Group group = (Group) am.findAuthorizable(groupId);
          repositorySession.logout();

          if (groupId.startsWith(ContactsGrouperNameProviderImpl.CONTACTS_GROUPID_PREFIX)) {
            // TODO Why are we not getting added and removed properties on the Message
            grouperManager.createGroup(groupId, null);
            grouperManager.addMemberships(groupId, Arrays.asList(group.getMembers()));
            operation = "UPDATE CONTACTS";
          } else {
            grouperManager.createGroup(groupId, config.getGroupTypes());
            grouperManager.addMemberships(groupId, Arrays.asList(group.getMembers()));
            operation = "CREATE";
          }
        }
      }

      // The message was processed successfully. No exceptions were thrown.
      // We acknowledge the message and its removed from the queue
      message.acknowledge();

      // We got a message that we didn't know what to do with.
      if (operation.equals("UNKNOWN")) {
        log.error(
            "I don't know what to do with this topic: {}. Turn on debug logs to see the message.",
            topic);
        log.debug(message.toString());
      } else {
        log.info("Successfully processed and acknowledged. {}, {}", operation, groupId);
        log.debug(message.toString());
      }

    } catch (JMSException jmse) {
      log.error("JMSException while processing message.", jmse);
    } catch (Exception e) {
      log.error("Exception while processing message.", e);
    }
  }