View Javadoc
1   /**
2    * Copyright 2014 Internet2
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *   http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package edu.internet2.middleware.grouper.group;
17  
18  import java.util.Date;
19  import java.util.HashMap;
20  import java.util.Iterator;
21  import java.util.LinkedHashSet;
22  import java.util.Map;
23  import java.util.Set;
24  
25  import org.apache.commons.lang.StringUtils;
26  import org.apache.commons.lang.builder.EqualsBuilder;
27  import org.apache.commons.lang.builder.HashCodeBuilder;
28  
29  import edu.internet2.middleware.grouper.FieldFinder;
30  import edu.internet2.middleware.grouper.Group;
31  import edu.internet2.middleware.grouper.GroupFinder;
32  import edu.internet2.middleware.grouper.GrouperAPI;
33  import edu.internet2.middleware.grouper.GrouperSession;
34  import edu.internet2.middleware.grouper.cfg.GrouperConfig;
35  import edu.internet2.middleware.grouper.exception.GroupSetNotFoundException;
36  import edu.internet2.middleware.grouper.exception.GrouperSessionException;
37  import edu.internet2.middleware.grouper.hibernate.AuditControl;
38  import edu.internet2.middleware.grouper.hibernate.GrouperTransactionType;
39  import edu.internet2.middleware.grouper.hibernate.HibernateHandler;
40  import edu.internet2.middleware.grouper.hibernate.HibernateHandlerBean;
41  import edu.internet2.middleware.grouper.hibernate.HibernateSession;
42  import edu.internet2.middleware.grouper.internal.dao.GroupDAO;
43  import edu.internet2.middleware.grouper.internal.dao.GrouperDAOException;
44  import edu.internet2.middleware.grouper.internal.dao.StemDAO;
45  import edu.internet2.middleware.grouper.internal.dao.hib3.Hib3GrouperVersioned;
46  import edu.internet2.middleware.grouper.internal.util.GrouperUuid;
47  import edu.internet2.middleware.grouper.membership.MembershipType;
48  import edu.internet2.middleware.grouper.misc.GrouperDAOFactory;
49  import edu.internet2.middleware.grouper.misc.GrouperHasContext;
50  import edu.internet2.middleware.grouper.misc.GrouperSessionHandler;
51  import edu.internet2.middleware.grouper.pit.PITGroupSet;
52  import edu.internet2.middleware.grouper.util.GrouperUtil;
53  
54  /**
55   * @author shilen $Id: GroupSet.java,v 1.12 2009-12-07 07:31:09 mchyzer Exp $
56   *
57   */
58  @SuppressWarnings("serial")
59  public class GroupSet extends GrouperAPI implements GrouperHasContext, Hib3GrouperVersioned {
60    
61    /**
62     * @see Object#toString()
63     */
64    @Override
65    public String toString() {
66      final StringBuilder result = new StringBuilder();
67      
68      result.append("id: ").append(this.id);
69      result.append(", type: ").append(this.type);
70      result.append(", depth: ").append(this.depth);
71      result.append(", parent: ").append(this.parentId);
72      result.append(", field: ").append(FieldFinder.findById(this.fieldId, true).getName());
73      result.append(", memberField: ").append(FieldFinder.findById(this.memberFieldId, true).getName());
74      result.append(", parent: ").append(this.parentId);
75      
76      GrouperSession.callbackGrouperSession(GrouperSession.staticGrouperSession().internal_getRootSession(), new GrouperSessionHandler() {
77   
78        /**
79         * 
80         */
81        @Override
82        public Object callback(GrouperSession grouperSession) throws GrouperSessionException {
83          if (!StringUtils.isBlank(GroupSet.this.ownerGroupId)) {
84            Group ownerGroup = GroupFinder.findByUuid(grouperSession, GroupSet.this.ownerGroupId, true);
85            result.append(", ownerGroup: ").append(ownerGroup.getName());
86            Group memberGroup = GroupFinder.findByUuid(grouperSession, GroupSet.this.memberGroupId, true);
87            result.append(", memberGroup: ").append(memberGroup.getName());
88          }
89          return null;
90        }
91      });
92      
93      return result.toString();
94    }
95    
96    /** db id for this row */
97    public static final String COLUMN_ID = "id";
98    
99    /** Context id links together multiple operations into one high level action */
100   public static final String COLUMN_CONTEXT_ID = "context_id";
101 
102   /** field represented by this group set */
103   public static final String COLUMN_FIELD_ID = "field_id";
104   
105   /** type of membership represented by this group set, immediate or composite or effective */
106   public static final String COLUMN_MSHIP_TYPE = "mship_type";
107   
108   /** same as member_group_id if depth is greater than 0, otherwise null. */
109   public static final String COLUMN_VIA_GROUP_ID = "via_group_id";
110   
111   /** number of hops in directed graph */
112   public static final String COLUMN_DEPTH = "depth";
113   
114   /** parent group set */
115   public static final String COLUMN_PARENT_ID = "parent_id";
116   
117   /** member uuid of the creator of this record */
118   public static final String COLUMN_CREATOR_ID = "creator_id";
119   
120   /** number of millis since 1970 that this record was created */
121   public static final String COLUMN_CREATE_TIME = "create_time";
122 
123   /** owner id */
124   public static final String COLUMN_OWNER_ID = "owner_id";
125   
126   /** owner group if applicable */
127   public static final String COLUMN_OWNER_GROUP_ID = "owner_group_id";
128   
129   /** same as owner_group_id except nulls are replaced with the string '<NULL>' */
130   public static final String COLUMN_OWNER_GROUP_ID_NULL = "owner_group_id_null";
131   
132   /** owner attribute def if applicable */
133   public static final String COLUMN_OWNER_ATTR_DEF_ID = "owner_attr_def_id";
134   
135   /** same as owner_attr_def_id except nulls are replaced with the string '<NULL>' */
136   public static final String COLUMN_OWNER_ATTR_DEF_ID_NULL = "owner_attr_def_id_null";
137   
138   /** owner stem if applicable */
139   public static final String COLUMN_OWNER_STEM_ID = "owner_stem_id";
140   
141   /** same as owner_stem_id except nulls are replaced with the string '<NULL>' */
142   public static final String COLUMN_OWNER_STEM_ID_NULL = "owner_stem_id_null";
143   
144   /** member group if applicable */
145   public static final String COLUMN_MEMBER_GROUP_ID = "member_group_id";
146   
147   /** member attr def if applicable */
148   public static final String COLUMN_MEMBER_ATTR_DEF_ID = "member_attr_def_id";
149   
150   /** member stem if applicable */
151   public static final String COLUMN_MEMBER_STEM_ID = "member_stem_id";
152   
153   /** member id */
154   public static final String COLUMN_MEMBER_ID = "member_id";
155   
156   /** field id used in joining this record with entries in grouper_memberships */
157   public static final String COLUMN_MEMBER_FIELD_ID = "member_field_id";
158   
159   
160   //*****  START GENERATED WITH GenerateFieldConstants.java *****//
161 
162   /** constant for field name for: contextId */
163   public static final String FIELD_CONTEXT_ID = "contextId";
164 
165   /** constant for field name for: createTime */
166   public static final String FIELD_CREATE_TIME = "createTime";
167 
168   /** constant for field name for: creatorId */
169   public static final String FIELD_CREATOR_ID = "creatorId";
170 
171   /** constant for field name for: depth */
172   public static final String FIELD_DEPTH = "depth";
173   
174   /** constant for field name for: viaGroupId */
175   public static final String FIELD_VIA_GROUP_ID = "viaGroupId";
176 
177   /** constant for field name for: fieldId */
178   public static final String FIELD_FIELD_ID = "fieldId";
179 
180   /** constant for field name for: type */
181   public static final String FIELD_MSHIP_TYPE = "type";
182   
183   /** constant for field name for: id */
184   public static final String FIELD_ID = "id";
185 
186   /** constant for field name for: memberGroupId */
187   public static final String FIELD_MEMBER_GROUP_ID = "memberGroupId";
188 
189   /** constant for field name for: memberStemId */
190   public static final String FIELD_MEMBER_STEM_ID = "memberStemId";
191 
192   /** constant for field name for: ownerId */
193   public static final String FIELD_OWNER_ID = "ownerId";
194   
195   /** constant for field name for: ownerGroupId */
196   public static final String FIELD_OWNER_GROUP_ID = "ownerGroupId";
197 
198   /** constant for field name for: ownerGroupIdNull */
199   public static final String FIELD_OWNER_GROUP_ID_NULL = "ownerGroupIdNull";
200 
201   /** constant for field name for: ownerStemId */
202   public static final String FIELD_OWNER_STEM_ID = "ownerStemId";
203 
204   /** constant for field name for: ownerStemIdNull */
205   public static final String FIELD_OWNER_STEM_ID_NULL = "ownerStemIdNull";
206 
207   /** constant for field name for: parentId */
208   public static final String FIELD_PARENT_ID = "parentId";
209   
210   /** constant for field name for: memberFieldId */
211   public static final String FIELD_MEMBER_FIELD_ID = "memberFieldId";
212 
213   /**
214    * fields which are included in db version
215    */
216   /*
217   private static final Set<String> DB_VERSION_FIELDS = GrouperUtil.toSet(
218       FIELD_CONTEXT_ID, FIELD_CREATE_TIME, FIELD_CREATOR_ID, FIELD_DEPTH, FIELD_VIA_GROUP_ID, 
219       FIELD_FIELD_ID, FIELD_MSHIP_TYPE, FIELD_ID, FIELD_MEMBER_GROUP_ID, FIELD_MEMBER_GROUP_ID_NULL, 
220       FIELD_MEMBER_STEM_ID, FIELD_MEMBER_STEM_ID_NULL, FIELD_OWNER_GROUP_ID, FIELD_OWNER_GROUP_ID_NULL, 
221       FIELD_OWNER_STEM_ID, FIELD_OWNER_STEM_ID_NULL, FIELD_PARENT_ID);
222   */
223 
224   /**
225    * fields which are included in clone method
226    */
227   private static final Set<String> CLONE_FIELDS = GrouperUtil.toSet(
228       FIELD_CONTEXT_ID, FIELD_CREATE_TIME, FIELD_CREATOR_ID, FIELD_DEPTH, FIELD_VIA_GROUP_ID,
229       FIELD_FIELD_ID, FIELD_MSHIP_TYPE, FIELD_HIBERNATE_VERSION_NUMBER, FIELD_ID, FIELD_MEMBER_GROUP_ID, 
230       FIELD_MEMBER_STEM_ID, FIELD_OWNER_GROUP_ID, FIELD_MEMBER_FIELD_ID, FIELD_OWNER_ID,
231       FIELD_OWNER_GROUP_ID_NULL, FIELD_OWNER_STEM_ID, FIELD_OWNER_STEM_ID_NULL, FIELD_PARENT_ID);
232 
233   //*****  END GENERATED WITH GenerateFieldConstants.java *****//
234 
235 
236   /**
237    * name of the group set table in the database.
238    */
239   public static final String TABLE_GROUPER_GROUP_SET = "grouper_group_set";
240 
241   /** id of this type */
242   private String id;
243   
244   /** context id ties multiple db changes */
245   private String contextId;
246   
247   /** field associated with this record */
248   private String fieldId;
249   
250   /** membership type -- immediate, effective, or composite */
251   private String type = MembershipType.IMMEDIATE.getTypeString();
252 
253   /** depth - 0 for self records, 1 for immediate memberships, > 1 for effective */
254   private int depth;
255   
256   /** parent record */
257   private String parentId;
258   
259   /** creator */
260   private String creatorId;
261   
262   /** create time */
263   private Long createTime = new Date().getTime();
264   
265   /** owner id */
266   private String ownerId;
267   
268   /** group id for group memberships.  this is the owner. */
269   private String ownerGroupId;
270   
271   /** ownerGroupId except nulls are replaced with a string so we can use this in a unique constraint */
272   private String ownerGroupIdNull = GroupSet.nullColumnValue;
273 
274   /** stem id for stem memberships.  this is the owner. */
275   private String ownerStemId;
276   
277   /** ownerStemId except nulls are replaced with a string so we can use this in a unique constraint */
278   private String ownerStemIdNull = GroupSet.nullColumnValue;
279   
280   /** group id for group memberships.  this is the member. */
281   private String memberGroupId;
282 
283   /** stem id for stem memberships.  this is the member. */
284   private String memberStemId;
285   
286   /** member field id */
287   private String memberFieldId;
288 
289   /**
290    * the value we're storing in the db for nulls that need a value so that we can add a unique constraint.
291    */
292   public static final String nullColumnValue = "<NULL>";
293 
294   /**
295    * 
296    * @see java.lang.Object#equals(java.lang.Object)
297    */
298   public boolean equals(Object other) {
299     if (this == other) {
300       return true;
301     }
302     
303     if (!(other instanceof GroupSet)) {
304       return false;
305     }
306     
307     GroupSet../../../../../edu/internet2/middleware/grouper/group/GroupSet.html#GroupSet">GroupSet that = (GroupSet) other;
308     return new EqualsBuilder()
309       .append(this.fieldId, that.fieldId)
310       .append(this.type, that.type)
311       .append(this.depth, that.depth)
312       .append(this.parentId, that.parentId)
313       .append(this.ownerAttrDefId, that.ownerAttrDefId)
314       .append(this.ownerGroupId, that.ownerGroupId)
315       .append(this.ownerStemId, that.ownerStemId)
316       .append(this.memberAttrDefId, that.memberAttrDefId)
317       .append(this.memberGroupId, that.memberGroupId)
318       .append(this.memberStemId, that.memberStemId)
319       .isEquals();
320   } 
321 
322   /**
323    * 
324    * @see java.lang.Object#hashCode()
325    */
326   public int hashCode() {
327     return new HashCodeBuilder()
328       .append(this.fieldId)
329       .append(this.type)
330       .append(this.depth)
331       .append(this.parentId)
332       .append(this.ownerAttrDefId)
333       .append(this.ownerGroupId)
334       .append(this.ownerStemId)
335       .append(this.memberAttrDefId)
336       .append(this.memberGroupId)
337       .append(this.memberStemId)
338       .toHashCode();
339   }
340   
341   /**
342    * 
343    * @see edu.internet2.middleware.grouper.GrouperAPI#clone()
344    */
345   @Override
346   public GrouperAPI clone() {
347     return GrouperUtil.clone(this, CLONE_FIELDS);
348   }
349 
350   /** attr def id for attr def memberships.  this is the member. */
351   private String memberAttrDefId;
352 
353   /** attr def id for attr def memberships.  this is the owner. */
354   private String ownerAttrDefId;
355 
356   /** ownerAttrDefId except nulls are replaced with a string so we can use this in a unique constraint */
357   private String ownerAttrDefIdNull = GroupSet.nullColumnValue;
358 
359   /**
360    * 
361    * @see edu.internet2.middleware.grouper.GrouperAPI#onPreSave(edu.internet2.middleware.grouper.hibernate.HibernateSession)
362    */
363   @Override
364   public void onPreSave(HibernateSession hibernateSession) {
365     super.onPreSave(hibernateSession);
366 
367     if (this.createTime == null) {
368       this.createTime = System.currentTimeMillis();
369     }
370     
371     if (this.creatorId == null) {
372       this.creatorId = GrouperSession.staticGrouperSession().getMember().getUuid();
373     }
374     
375     if (this.depth == 0) {
376       this.memberFieldId = new String(this.fieldId);
377     } else {
378       this.memberFieldId = Group.getDefaultList().getUuid();
379     }
380   }
381   
382   /**
383    * 
384    * @see edu.internet2.middleware.grouper.GrouperAPI#onPostSave(edu.internet2.middleware.grouper.hibernate.HibernateSession)
385    */
386   @Override
387   public void onPostSave(HibernateSession hibernateSession) {
388     
389     // take care of effective group sets
390     if (this.getDepth() == 1 && this.getMemberGroupId() != null) {
391       Set<GroupSet> results = new LinkedHashSet<GroupSet>();
392       Set<GroupSet> groupSetHasMembers = GrouperDAOFactory.getFactory().getGroupSet().findAllByGroupOwnerAndField(
393           this.getMemberGroupId(), Group.getDefaultList());
394       
395       // Add members of member to owner group set
396       results.addAll(addHasMembersToOwner(this, groupSetHasMembers));
397   
398       // If we are working on a group, where is it a member and field is the default list
399       if (this.getOwnerGroupId() != null && this.getFieldId().equals(Group.getDefaultList().getUuid())) {
400         Set<GroupSet> groupSetIsMember = GrouperDAOFactory.getFactory().getGroupSet().findAllByMemberGroup(this.getOwnerGroupId());
401   
402         // Add member and members of member to where owner is member
403         results.addAll(addHasMembersToWhereGroupIsMember(this.getMemberGroupId(), groupSetIsMember, groupSetHasMembers));
404       }
405       
406       GrouperDAOFactory.getFactory().getGroupSet().save(results);
407       
408       // update last membership change time
409       this.updateLastMembershipChange(this, results);
410     }
411     
412     super.onPostSave(hibernateSession);
413   }
414 
415   
416   /**
417    * @see edu.internet2.middleware.grouper.GrouperAPI#onPreDelete(edu.internet2.middleware.grouper.hibernate.HibernateSession)
418    */
419   @Override
420   public void onPreDelete(HibernateSession hibernateSession) {
421     super.onPreDelete(hibernateSession);
422 
423     // take care of effective group sets
424     if (this.getDepth() == 1) {
425       Set<GroupSet> groupSetsToDelete = new LinkedHashSet<GroupSet>();
426       
427       // Get all children of this group set
428       Set<GroupSet> childResults = GrouperDAOFactory.getFactory().getGroupSet().findAllChildren(this);
429   
430       groupSetsToDelete.addAll(childResults);
431   
432       // Find all effective group sets that need deletion
433       if (this.getOwnerGroupId() != null && this.getFieldId().equals(Group.getDefaultList().getUuid())) {
434         Set<GroupSet> groupSetIsMember = GrouperDAOFactory.getFactory().getGroupSet().findAllByMemberGroup(this.getOwnerGroupId());
435         
436         Iterator<GroupSet> groupSetIsMemberIter = groupSetIsMember.iterator();
437         while (groupSetIsMemberIter.hasNext()) {
438           GroupSet currGroupSet = groupSetIsMemberIter.next();
439           GroupSet childToDelete = GrouperDAOFactory.getFactory().getGroupSet().findImmediateChildByParentAndMemberGroup(currGroupSet, this.getMemberGroupId());
440   
441           if (childToDelete != null) {
442             Set<GroupSet> childrenOfChildResults = GrouperDAOFactory.getFactory().getGroupSet().findAllChildren(childToDelete);
443     
444             groupSetsToDelete.addAll(childrenOfChildResults);
445             groupSetsToDelete.add(childToDelete);
446           }
447         }
448       }
449       
450       GrouperDAOFactory.getFactory().getGroupSet().delete(groupSetsToDelete);
451       
452       // update last membership change time
453       this.updateLastMembershipChange(this, groupSetsToDelete);
454     }
455   }
456   
457   /**
458    * If enabled, update last_membership_change for groups and stems
459    * @param immediateGroupSet
460    * @param effectiveGroupSets
461    */
462   private void updateLastMembershipChange(GroupSet immediateGroupSet, Set<GroupSet> effectiveGroupSets) {
463     Set<String> groupIds = new LinkedHashSet<String>();
464     Set<String> stemIds = new LinkedHashSet<String>();
465     Set<String> attrDefIds = new LinkedHashSet<String>();
466     
467     if (immediateGroupSet.getOwnerGroupId() != null) {
468       groupIds.add(immediateGroupSet.getOwnerGroupId());
469     } else if (immediateGroupSet.getOwnerStemId() != null) {
470       stemIds.add(immediateGroupSet.getOwnerStemId());
471     } else if (immediateGroupSet.getOwnerAttrDefId() != null) {
472       attrDefIds.add(immediateGroupSet.getOwnerAttrDefId());
473     } else {
474       throw new RuntimeException("Cant find owner! " + immediateGroupSet);
475     }
476     
477     Iterator<GroupSet> iter = effectiveGroupSets.iterator();
478     while (iter.hasNext()) {
479       GroupSet effectiveGroupSet = iter.next();
480       if (effectiveGroupSet.getOwnerGroupId() != null) {
481         groupIds.add(effectiveGroupSet.getOwnerGroupId());
482       } else if (effectiveGroupSet.getOwnerStemId() != null) {
483         stemIds.add(effectiveGroupSet.getOwnerStemId());
484       } else if (effectiveGroupSet.getOwnerAttrDefId() != null) {
485         attrDefIds.add(effectiveGroupSet.getOwnerAttrDefId());
486       } else {
487         throw new RuntimeException("Cant find owner! " + effectiveGroupSet);
488       }
489     }
490     
491     if (GrouperConfig.retrieveConfig().propertyValueBoolean("stems.updateLastMembershipTime", false)) {
492       StemDAO dao = GrouperDAOFactory.getFactory().getStem();
493       Iterator<String> stemIdsIter = stemIds.iterator();
494       while (stemIdsIter.hasNext()) {
495         dao.updateLastMembershipChange(stemIdsIter.next());
496       }
497     }
498     
499     if (GrouperConfig.retrieveConfig().propertyValueBoolean("groups.updateLastMembershipTime", false)) {
500       GroupDAO dao = GrouperDAOFactory.getFactory().getGroup();
501       Iterator<String> groupIdsIter = groupIds.iterator();
502       while (groupIdsIter.hasNext()) {
503         dao.updateLastMembershipChange(groupIdsIter.next());
504       }
505     }
506   }
507 
508   /**
509    * @param memberGroupId
510    * @param groupSetIsMember
511    * @param groupSetHasMembers
512    * @return group set
513    * @throws IllegalStateException
514    */
515   private Set<GroupSet> addHasMembersToWhereGroupIsMember(String memberGroupId,
516       Set<GroupSet> groupSetIsMember, Set<GroupSet> groupSetHasMembers)
517       throws IllegalStateException {
518     Set<GroupSet> groupSets = new LinkedHashSet();
519 
520     Iterator<GroupSet> isMembersIter = groupSetIsMember.iterator();
521     Map<String, Set<GroupSet>> parentToChildrenMap = getParentToChildrenMap(groupSetHasMembers);
522 
523     // lets get all the hasMembers with a depth of 1 before the while loop
524     Set<GroupSet> hasMembersOneDepth = new LinkedHashSet<GroupSet>();
525     Iterator<GroupSet> hasMembersIter = groupSetHasMembers.iterator();
526     while (hasMembersIter.hasNext()) {
527       GroupSet gs = hasMembersIter.next();
528       if (gs.getDepth() == 1) {
529         hasMembersOneDepth.add(gs);
530       }
531     }
532 
533     while (isMembersIter.hasNext()) {
534       GroupSet isGS = isMembersIter.next();
535 
536       String ownerGroupId = isGS.getOwnerGroupId();
537       String ownerStemId = isGS.getOwnerStemId();
538       String ownerAttrDefId = isGS.getOwnerAttrDefId();
539       String fieldId = isGS.getFieldId();
540       String id = isGS.getId();
541       int depth = isGS.getDepth();
542 
543       // If the isMember's owner is the same as the immediate member's owner and this is for a default member, 
544       // then we can skip this isMember.
545       if (fieldId.equals(Group.getDefaultList().getUuid())
546           && StringUtils.equals(isGS.getOwnerGroupId(), this.getOwnerGroupId())) {
547         continue;
548       }
549 
550       GroupSetper/group/GroupSet.html#GroupSet">GroupSet groupSet = new GroupSet();
551       groupSet.setId(GrouperUuid.getUuid());
552       groupSet.setCreatorId(this.getCreatorId());
553       groupSet.setCreateTime(this.getCreateTime());
554       groupSet.setDepth(depth + 1);
555       groupSet.setParentId(id);
556       groupSet.setFieldId(fieldId);
557       groupSet.setMemberGroupId(memberGroupId);
558       groupSet.setOwnerGroupId(ownerGroupId);
559       groupSet.setOwnerAttrDefId(ownerAttrDefId);
560       groupSet.setOwnerStemId(ownerStemId);
561       groupSet.setType(MembershipType.EFFECTIVE.getTypeString());
562 
563       // if we're forming a circular path, skip this isMember
564       if (internal_isCircular(groupSet, isGS)) {
565         continue;
566       }
567 
568       groupSets.add(groupSet);
569 
570       Iterator<GroupSet> itHM = hasMembersOneDepth.iterator();
571       while (itHM.hasNext()) {
572         GroupSet hasGS = itHM.next();
573         Set<GroupSet> newAdditions = addHasMembersRecursively(isGS, hasGS, groupSet,
574             parentToChildrenMap, ownerAttrDefId, ownerGroupId, ownerStemId, this.getCreatorId(), fieldId);
575         groupSets.addAll(newAdditions);
576       }
577     }
578 
579     return groupSets;
580   } 
581 
582   
583   /**
584    * @param immediateGroupSet
585    * @param hasMembers
586    * @return set
587    * @throws IllegalStateException
588    */
589   private Set<GroupSet> addHasMembersToOwner(GroupSet immediateGroupSet, Set<GroupSet> hasMembers) 
590     throws  IllegalStateException
591   {
592     Set<GroupSet> groupSets = new LinkedHashSet();
593     Iterator<GroupSet> it = hasMembers.iterator();
594 
595     // cache values outside of iterator
596     String ownerAttrDefId = immediateGroupSet.getOwnerAttrDefId();
597     String ownerGroupId = immediateGroupSet.getOwnerGroupId();
598     String ownerStemId = immediateGroupSet.getOwnerStemId();
599     String fieldId = immediateGroupSet.getFieldId();
600 
601     Map<String, Set<GroupSet>> parentToChildrenMap = getParentToChildrenMap(hasMembers);
602    
603     while (it.hasNext()) {
604       GroupSet gs = it.next();
605       if (gs.getDepth() == 1) {
606         Set<GroupSet> newAdditions = addHasMembersRecursively(immediateGroupSet, gs, 
607             immediateGroupSet, parentToChildrenMap, ownerAttrDefId, ownerGroupId, ownerStemId, this.getCreatorId(), fieldId);
608         groupSets.addAll(newAdditions);
609       }
610     }
611 
612     return groupSets;
613   }
614   
615   /**
616    * Given a set of group sets, return a map that will allow retrieval of 
617    * the children of a parent group set.
618    * @param members
619    * @return the map
620    */
621   private Map<String, Set<GroupSet>> getParentToChildrenMap(Set<GroupSet> members) {
622     Map<String, Set<GroupSet>> parentToChildrenMap = new HashMap<String, Set<GroupSet>>();
623 
624     Iterator<GroupSet> iterator = members.iterator();
625     while (iterator.hasNext()) {
626       GroupSet gs = iterator.next();
627       String parentId = gs.getParentId();
628 
629       if (parentId != null && !parentId.equals("")) {
630         Set<GroupSet> children = parentToChildrenMap.get(parentId);
631         if (children == null) {
632           children = new LinkedHashSet<GroupSet>();
633         }
634 
635         children.add(gs);
636         parentToChildrenMap.put(parentId, children);
637       }
638     }
639 
640     return parentToChildrenMap;
641   }
642   
643   /**
644    * @param startGroupSet
645    * @param gs
646    * @param parentGroupSet
647    * @param parentToChildrenMap
648    * @param ownerGroupId1
649    * @param ownerStemId1
650    * @param ownerAttrDefId1 
651    * @param creatorUUID
652    * @param fieldId1
653    * @return set of group sets
654    */
655   private Set<GroupSet> addHasMembersRecursively(GroupSet startGroupSet, 
656       GroupSetef="../../../../../edu/internet2/middleware/grouper/group/GroupSet.html#GroupSet">GroupSet gs, GroupSet parentGroupSet, Map<String, Set<GroupSet>> parentToChildrenMap, 
657       String ownerAttrDefId1,
658       String ownerGroupId1, String ownerStemId1, String creatorUUID, String fieldId1) {
659 
660       GroupSet/group/GroupSet.html#GroupSet">GroupSet newGroupSet = new GroupSet();
661       newGroupSet.setId(GrouperUuid.getUuid());
662       newGroupSet.setCreatorId(creatorUUID);
663       newGroupSet.setCreateTime(this.getCreateTime());
664       newGroupSet.setFieldId(fieldId1);
665       newGroupSet.setOwnerAttrDefId(ownerAttrDefId1);
666       newGroupSet.setOwnerGroupId(ownerGroupId1);
667       newGroupSet.setOwnerStemId(ownerStemId1);
668       newGroupSet.setMemberGroupId(gs.getMemberGroupId());
669       newGroupSet.setDepth(parentGroupSet.getDepth() + 1);
670       newGroupSet.setParentId(parentGroupSet.getId());
671       newGroupSet.setType(MembershipType.EFFECTIVE.getTypeString());
672 
673       // if we're forming a circular path, return an empty Set.
674       if (internal_isCircular(newGroupSet, startGroupSet)) {
675         return new LinkedHashSet<GroupSet>();
676       }
677 
678       Set<GroupSet> newGroupSets = new LinkedHashSet<GroupSet>();
679       newGroupSets.add(newGroupSet);
680 
681       Set<GroupSet> children = parentToChildrenMap.get(gs.getId());
682       if (children != null) {
683         Iterator<GroupSet> it = children.iterator();
684         while (it.hasNext()) {
685           GroupSet nextGroupSet = it.next();
686           Set<GroupSet> newAdditions = addHasMembersRecursively(startGroupSet, nextGroupSet, newGroupSet, 
687             parentToChildrenMap, ownerAttrDefId1, ownerGroupId1, ownerStemId1, creatorUUID, fieldId1);
688           newGroupSets.addAll(newAdditions);
689         }
690       }
691       
692       return newGroupSets;
693     }
694   
695   /**
696    * Check if the new group set being added will cause a circular group set.
697    *
698    * @param newGroupSet group set being added
699    * @param startGroupSet group set that's a parent of newGroupSet which will be used
700    *                        as a starting point to check if we're forming a circular group set
701    * @return true if the new group set will cause a circular group set.
702    */
703   public boolean internal_isCircular(GroupSet/../../../edu/internet2/middleware/grouper/group/GroupSet.html#GroupSet">GroupSet newGroupSet, GroupSet startGroupSet) {
704 
705     // for the default list, a group should not be an indirect member of itself ....
706     if (newGroupSet.getFieldId().equals(Group.getDefaultList().getUuid()) && 
707         newGroupSet.getMemberGroupId().equals(newGroupSet.getOwnerGroupId())) {
708       return true;
709     }
710 
711     // now let's go through the parents... 
712     // if the member of a parent is equal to the member of the new group set,
713     // then we have a circular group set.
714     if (newGroupSet.getDepth() < 3) {
715       return false;
716     }
717 
718     GroupSet currentGroupSet = startGroupSet;
719     while (true) {
720       if (currentGroupSet.getMemberGroupId().equals(newGroupSet.getMemberGroupId())) {
721         return true;
722       }
723       if (currentGroupSet.getDepth() > 1) {
724         currentGroupSet = currentGroupSet.getParentGroupSet();
725       } else {
726         break;
727       }
728     }
729 
730     return false;
731   }
732 
733   
734   /**
735    * @return the parent group set
736    */
737   public GroupSet getParentGroupSet() {
738     if (depth == 0) {
739       throw new GroupSetNotFoundException("no parent");
740     }
741     
742     GroupSet parent = GrouperDAOFactory.getFactory().getGroupSet().findParentGroupSet(this) ;
743     return parent;
744   }
745   
746   
747   /**
748    * @return id
749    */
750   public String getId() {
751     return id;
752   }
753 
754   
755   /**
756    * set id
757    * @param id
758    */
759   public void setId(String id) {
760     this.id = id;
761   }
762 
763   /**
764    * @return context id
765    */
766   public String getContextId() {
767     return contextId;
768   }
769 
770   
771   /**
772    * set context id
773    * @param contextId
774    */
775   public void setContextId(String contextId) {
776     this.contextId = contextId;
777   }
778 
779   /**
780    * @return field id
781    */
782   public String getFieldId() {
783     return fieldId;
784   }
785 
786   /**
787    * @param fieldId
788    */
789   public void setFieldId(String fieldId) {
790     this.fieldId = fieldId;
791   }
792   
793   /**
794    * @return field id used in joining with grouper_memberships table
795    */
796   public String getMemberFieldId() {    
797     return memberFieldId;
798   }
799 
800   /**
801    * Internal use only.
802    * @param memberFieldId
803    */
804   public void setMemberFieldId(String memberFieldId) {
805     this.memberFieldId = memberFieldId;
806   }
807 
808   
809   /**
810    * This is 0 for self memberships (where the owner and member are the same).
811    * Otherwise, it's the number of hops in a directed graph from the member to the group.
812    * @return depth
813    */
814   public int getDepth() {
815     return depth;
816   }
817 
818   
819   /**
820    * set depth
821    * @param depth
822    */
823   public void setDepth(int depth) {
824     this.depth = depth;
825   }
826 
827   /**
828    * @return via group id
829    */
830   public String getViaGroupId() {
831     if (depth == 0) {
832       return null;
833     }
834     
835     return memberGroupId;
836   }
837 
838   
839   /**
840    * Set via group id.  This is for internal use only.
841    * @param viaGroupId
842    */
843   public void setViaGroupId(@SuppressWarnings("unused") String viaGroupId) {
844     // not used
845   }
846   
847   /**
848    * @return parent id
849    */
850   public String getParentId() {
851     return parentId;
852   }
853 
854   
855   /**
856    * set parent id
857    * @param parentId
858    */
859   public void setParentId(String parentId) {
860     this.parentId = parentId;
861   }
862 
863   
864   /**
865    * @return creator
866    */
867   public String getCreatorId() {
868     return creatorId;
869   }
870 
871   
872   /**
873    * set creator
874    * @param creatorId
875    */
876   public void setCreatorId(String creatorId) {
877     this.creatorId = creatorId;
878   }
879 
880 
881   /**
882    * @return create time
883    */
884   public Long getCreateTime() {
885     return createTime;
886   }
887 
888   
889   /**
890    * set create time
891    * @param createTime
892    */
893   public void setCreateTime(Long createTime) {
894     this.createTime = createTime;
895   }
896 
897   /**
898    * @return owner id
899    */
900   public String getOwnerId() {
901     return ownerId;
902   }
903   
904   /**
905    * Set owner id.  This is for internal use only.
906    * @param ownerId
907    */
908   public void setOwnerId(String ownerId) {
909     this.ownerId = ownerId;
910   }
911   
912   /**
913    * @return group id for the owner if this is a group membership
914    */
915   public String getOwnerGroupId() {
916     return ownerGroupId;
917   }
918 
919   /**
920    * Set group id for the owner if this is a group membership
921    * @param ownerGroupId
922    */
923   public void setOwnerGroupId(String ownerGroupId) {
924     this.ownerGroupId = ownerGroupId;
925     setOwnerGroupIdNull(ownerGroupId);
926     if (ownerGroupId == null) {
927       setOwnerGroupIdNull(GroupSet.nullColumnValue);
928     } else {
929       setOwnerId(ownerGroupId);
930     }
931   }
932 
933   /**
934    * This is for internal use only.  This is the same as getOwnerGroupId() except nulls are replaced with
935    * a constant string.
936    * @return group id for the owner if this is a group membership
937    */
938   public String getOwnerGroupIdNull() {
939     return ownerGroupIdNull;
940   }
941 
942   
943   /**
944    * Set group id for the owner if this is a group membership.  This is for internal use only.
945    * @param ownerGroupIdNull
946    */
947   public void setOwnerGroupIdNull(String ownerGroupIdNull) {
948     this.ownerGroupIdNull = ownerGroupIdNull;
949   }
950 
951   /**
952    * @return stem id for the owner if this is a stem membership
953    */
954   public String getOwnerStemId() {
955     return ownerStemId;
956   }
957 
958   
959   /**
960    * Set stem id for the owner if this is a stem membership
961    * @param ownerStemId
962    */
963   public void setOwnerStemId(String ownerStemId) {
964     this.ownerStemId = ownerStemId;
965     setOwnerStemIdNull(ownerStemId);
966     if (ownerStemId == null) {
967       setOwnerStemIdNull(GroupSet.nullColumnValue);
968     } else {
969       setOwnerId(ownerStemId);
970     }
971   }
972 
973   
974   /**
975    * This is for internal use only.  This is the same as getOwnerStemId() except nulls are replaced with
976    * a constant string.
977    * @return stem id for the owner if this is a stem membership
978    */
979   public String getOwnerStemIdNull() {
980     return ownerStemIdNull;
981   }
982 
983   /**
984    * Set stem id for the owner if this is a stem membership.  This is for internal use only.
985    * @param ownerStemIdNull
986    */
987   public void setOwnerStemIdNull(String ownerStemIdNull) {
988     this.ownerStemIdNull = ownerStemIdNull;
989   }
990 
991   /**
992    * @return group id for the member if the member is a group
993    */
994   public String getMemberGroupId() {
995     return memberGroupId;
996   }
997 
998   
999   /**
1000    * Set group id for the member if the member is a group
1001    * @param memberGroupId
1002    */
1003   public void setMemberGroupId(String memberGroupId) {
1004     this.memberGroupId = memberGroupId;
1005   }
1006   
1007   /**
1008    * @return stem id for the member if the member is a stem
1009    */
1010   public String getMemberStemId() {
1011     return memberStemId;
1012   }
1013 
1014   
1015   /**
1016    * Set stem id for the member if the member is a stem
1017    * @param memberStemId
1018    */
1019   public void setMemberStemId(String memberStemId) {
1020     this.memberStemId = memberStemId;
1021   }
1022 
1023   
1024   /**
1025    * This is 'immediate' for self memberships (owner and member are the same) except if the group is a composite in which case this will be 'composite'.
1026    * For non-self memberships, this is 'effective'.
1027    * @return membership type (immediate, effective, or composite)
1028    */
1029   public String getType() {
1030     return type;
1031   }
1032 
1033   
1034   /**
1035    * set membership type
1036    * @param type
1037    */
1038   public void setType(String type) {
1039     this.type = type;
1040   }
1041 
1042   
1043   /**
1044    * Since pre hooks on effective memberships have to fire before the membership can be queried,
1045    * I have this method to manually combine a GroupSet and ImmediateMembershipEntry.
1046    * @param immediateOrCompositeMembership
1047    * @return membership
1048    */
1049   /*
1050   public Membership internal_createEffectiveMembershipObject(Membership immediateOrCompositeMembership) {
1051     Membership effectiveMembership = immediateOrCompositeMembership.clone();
1052     effectiveMembership.setUuid(immediateOrCompositeMembership.getImmediateMembershipId() + ":" + this.getId());
1053     effectiveMembership.setGroupSetId(this.getId());
1054     effectiveMembership.setFieldId(this.getFieldId());
1055     effectiveMembership.setOwnerAttrDefId(this.getOwnerAttrDefId());
1056     effectiveMembership.setOwnerGroupId(this.getOwnerGroupId());
1057     effectiveMembership.setOwnerStemId(this.getOwnerStemId());
1058     effectiveMembership.setViaGroupId(this.getViaGroupId());
1059     effectiveMembership.setViaCompositeId(null);
1060     effectiveMembership.setDepth(this.getDepth());
1061     effectiveMembership.setType(MembershipType.EFFECTIVE.getTypeString());
1062     effectiveMembership.setGroupSetParentId(this.getParentId());
1063     effectiveMembership.setGroupSetCreatorUuid(this.getCreatorId());
1064     effectiveMembership.setGroupSetCreateTimeLong(this.getCreateTime());
1065     
1066     return effectiveMembership;
1067   }
1068   */
1069 
1070   /**
1071    * @return group id for the member if the member is a group
1072    */
1073   public String getMemberAttrDefId() {
1074     return this.memberAttrDefId;
1075   }
1076 
1077   /**
1078    * @return attrdef id for the owner if this is a attrdef membership
1079    */
1080   public String getOwnerAttrDefId() {
1081     return this.ownerAttrDefId;
1082   }
1083 
1084   /**
1085    * This is for internal use only.  This is the same as getOwnerAttrDefId() except nulls are replaced with
1086    * a constant string.
1087    * @return attr def id for the owner if this is a attrdef membership
1088    */
1089   public String getOwnerAttrDefIdNull() {
1090     return this.ownerAttrDefIdNull;
1091   }
1092 
1093   /**
1094    * Set attr def id for the member if the member is a attrdef
1095    * @param memberAttrDefId1
1096    */
1097   public void setMemberAttrDefId(String memberAttrDefId1) {
1098     this.memberAttrDefId = memberAttrDefId1;
1099   }
1100 
1101   /**
1102    * Set attrdef id for the owner if this is a attrdef membership
1103    * @param ownerAttrDefId1
1104    */
1105   public void setOwnerAttrDefId(String ownerAttrDefId1) {
1106     this.ownerAttrDefId = ownerAttrDefId1;
1107     this.setOwnerAttrDefIdNull(ownerAttrDefId1);
1108     if (ownerAttrDefId1 == null) {
1109       this.setOwnerAttrDefIdNull(GroupSet.nullColumnValue);
1110     } else {
1111       setOwnerId(ownerAttrDefId1);
1112     }
1113   }
1114 
1115   /**
1116    * Set attrdef id for the owner if this is a attrdef membership.  This is for internal use only.
1117    * @param ownerAttrDefIdNull1
1118    */
1119   public void setOwnerAttrDefIdNull(String ownerAttrDefIdNull1) {
1120     this.ownerAttrDefIdNull = ownerAttrDefIdNull1;
1121   }
1122   
1123   /**
1124    * get the member id
1125    * @return the member id
1126    */
1127   public String getMemberId() {
1128     if (this.memberAttrDefId != null) {
1129       return this.memberAttrDefId;
1130     }
1131     
1132     if (this.memberGroupId != null) {
1133       return this.memberGroupId;
1134     }
1135     
1136     if (this.memberStemId != null) {
1137       return this.memberStemId;
1138     }
1139     
1140     throw new RuntimeException("No value for member.");
1141   }
1142   
1143   /**
1144    * This is for internal use only.
1145    * @param member
1146    */
1147   public void setMemberId(String member) {
1148     // not used
1149   }
1150   
1151   /**
1152    * @see edu.internet2.middleware.grouper.GrouperAPI#onPreUpdate(edu.internet2.middleware.grouper.hibernate.HibernateSession)
1153    */
1154   @Override
1155   public void onPreUpdate(HibernateSession hibernateSession) {
1156     super.onPreUpdate(hibernateSession);
1157     
1158     if (this.depth == 0) {
1159       this.memberFieldId = new String(this.fieldId);
1160     } else {
1161       this.memberFieldId = Group.getDefaultList().getUuid();
1162     }
1163   }
1164   
1165   /**
1166    * @param forceDisablePITEntry should only be used if we're removing a corrupt group set
1167    */
1168   public void delete(final boolean forceDisablePITEntry) {
1169     
1170     HibernateSession.callbackHibernateSession(
1171         GrouperTransactionType.READ_WRITE_OR_USE_EXISTING, AuditControl.WILL_NOT_AUDIT,
1172         new HibernateHandler() {
1173 
1174           public Object callback(HibernateHandlerBean hibernateHandlerBean)
1175               throws GrouperDAOException {
1176 
1177             hibernateHandlerBean.getHibernateSession().setCachingEnabled(false);
1178 
1179             GrouperDAOFactory.getFactory().getGroupSet().delete(GroupSet.this);
1180             
1181             if (forceDisablePITEntry) {
1182               PITGroupSet pit = GrouperDAOFactory.getFactory().getPITGroupSet().findBySourceIdActive(GroupSet.this.getId(), false);
1183               if (pit != null) {
1184                 pit.internal_disable();
1185               }
1186             }
1187             
1188             return null;
1189           }
1190         });
1191   }
1192 }